From 3d7073079ba0e57a8bd54e6bd10f6b36b9a20f0b Mon Sep 17 00:00:00 2001
From: sweber <webers@in.tum.de>
Date: Mon, 17 Aug 2015 19:43:41 +0200
Subject: [PATCH] [NRRPLT-2463] update three.js to r71

Change-Id: I39395d45d9d410371cde8ae8a4c20fb5960189ea
---
 gz3d/client/index.html                        |     2 +
 gz3d/client/js/include/ColladaLoader.js       |  1371 +-
 gz3d/client/js/include/Detector.js            |     9 +-
 gz3d/client/js/include/EffectComposer.js      |    21 +-
 gz3d/client/js/include/FirstPersonControls.js |   295 +
 gz3d/client/js/include/OrbitControls.js       |  1550 +-
 gz3d/client/js/include/Projector.js           |   904 +
 gz3d/client/js/include/SSAOShader.js          |    41 +-
 gz3d/client/js/include/ShaderPass.js          |    14 +-
 .../js/include/ThreeBackwardsCompatibility.js |    22 +
 gz3d/client/js/include/TrackballControls.js   |   242 +-
 gz3d/client/js/include/TransformControls.js   |  1374 +-
 gz3d/client/js/include/three.js               | 35769 ++++++++--------
 gz3d/client/js/include/three.min.js           |  1470 +-
 gz3d/src/gz.js                                |     2 +-
 gz3d/src/gziface.js                           |     4 +-
 gz3d/src/gzmanipulator.js                     |     6 +-
 gz3d/src/gzradialmenu.js                      |    10 +-
 gz3d/src/gzscene.js                           |    17 +-
 gz3d/src/gzspawnmodel.js                      |     3 +-
 20 files changed, 21758 insertions(+), 21368 deletions(-)
 create mode 100644 gz3d/client/js/include/FirstPersonControls.js
 create mode 100644 gz3d/client/js/include/Projector.js
 create mode 100644 gz3d/client/js/include/ThreeBackwardsCompatibility.js

diff --git a/gz3d/client/index.html b/gz3d/client/index.html
index 0b67c26..2dd977d 100644
--- a/gz3d/client/index.html
+++ b/gz3d/client/index.html
@@ -21,6 +21,8 @@
     <script src="js/include/RenderPass.js"></script>
     <script src="js/include/MaskPass.js"></script>
     <script src="js/include/ShaderPass.js"></script>
+    <script src="js/include/ThreeBackwardsCompatibility.js"></script>
+    <script src="js/include/FirstPersonControls.js"></script>
     <script src="js/include/xml2json.js"></script>
     <script src="gz3d.js"></script>
     <link rel="stylesheet" href="style/gz3d.css"/>
diff --git a/gz3d/client/js/include/ColladaLoader.js b/gz3d/client/js/include/ColladaLoader.js
index f6e35d1..353ad00 100644
--- a/gz3d/client/js/include/ColladaLoader.js
+++ b/gz3d/client/js/include/ColladaLoader.js
@@ -1,16 +1,18 @@
 /**
- * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
- */
+* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
+* @author Tony Parisi / http://www.tonyparisi.com/
+*/
 
 THREE.ColladaLoader = function () {
 
 	var COLLADA = null;
 	var scene = null;
-	var daeScene;
+	var visualScene;
+	var kinematicsModel;
 
 	var readyCallbackFunc = null;
 
- 	var sources = {};
+	var sources = {};
 	var images = {};
 	var animations = {};
 	var controllers = {};
@@ -21,7 +23,9 @@ THREE.ColladaLoader = function () {
 	var lights = {};
 
 	var animData;
+	var kinematics;
 	var visualScenes;
+	var kinematicsModels;
 	var baseUrl;
 	var morphs;
 	var skins;
@@ -52,7 +56,7 @@ THREE.ColladaLoader = function () {
 	var colladaUp = 'Y';
 	var upConversion = null;
 
-	function load ( url, readyCallback, progressCallback ) {
+	function load ( url, readyCallback, progressCallback, failCallback ) {
 
 		var length = 0;
 
@@ -62,9 +66,9 @@ THREE.ColladaLoader = function () {
 
 			request.onreadystatechange = function() {
 
-				if( request.readyState == 4 ) {
+				if ( request.readyState === 4 ) {
 
-					if( request.status == 0 || request.status == 200 ) {
+					if ( request.status === 0 || request.status === 200 ) {
 
 
 						if ( request.responseXML ) {
@@ -81,17 +85,25 @@ THREE.ColladaLoader = function () {
 
 						} else {
 
-							console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" );
+							if ( faillCallback ) {
+
+								failCallback();
+
+							} else {
+
+								console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" );
+
+							}
 
 						}
 
 					}
 
-				} else if ( request.readyState == 3 ) {
+				} else if ( request.readyState === 3 ) {
 
 					if ( progressCallback ) {
 
-						if ( length == 0 ) {
+						if ( length === 0 ) {
 
 							length = request.getResponseHeader( "Content-Length" );
 
@@ -114,7 +126,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-	};
+	}
 
 	function parse( doc, callBack, url ) {
 
@@ -131,25 +143,26 @@ THREE.ColladaLoader = function () {
 
 		parseAsset();
 		setUpConversion();
-		images = parseLib( "//dae:library_images/dae:image", _Image, "image" );
-		materials = parseLib( "//dae:library_materials/dae:material", Material, "material" );
-		effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
-		geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" );
-		cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" );
-		lights = parseLib( ".//dae:library_lights/dae:light", Light, "light" );
-		controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" );
-		animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" );
-		visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" );
+		images = parseLib( "library_images image", _Image, "image" );
+		materials = parseLib( "library_materials material", Material, "material" );
+		effects = parseLib( "library_effects effect", Effect, "effect" );
+		geometries = parseLib( "library_geometries geometry", Geometry, "geometry" );
+		cameras = parseLib( "library_cameras camera", Camera, "camera" );
+		lights = parseLib( "library_lights light", Light, "light" );
+		controllers = parseLib( "library_controllers controller", Controller, "controller" );
+		animations = parseLib( "library_animations animation", Animation, "animation" );
+		visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" );
+		kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" );
 
 		morphs = [];
 		skins = [];
 
-		daeScene = parseScene();
-		scene = new THREE.Object3D();
+		visualScene = parseScene();
+		scene = new THREE.Group();
 
-		for ( var i = 0; i < daeScene.nodes.length; i ++ ) {
+		for ( var i = 0; i < visualScene.nodes.length; i ++ ) {
 
-			scene.add( createSceneGraph( daeScene.nodes[ i ] ) );
+			scene.add( createSceneGraph( visualScene.nodes[ i ] ) );
 
 		}
 
@@ -158,12 +171,16 @@ THREE.ColladaLoader = function () {
 
 		createAnimations();
 
+		kinematicsModel = parseKinematicsModel();
+		createKinematics();
+
 		var result = {
 
 			scene: scene,
 			morphs: morphs,
 			skins: skins,
 			animations: animData,
+			kinematics: kinematics,
 			dae: {
 				images: images,
 				materials: materials,
@@ -174,7 +191,10 @@ THREE.ColladaLoader = function () {
 				controllers: controllers,
 				animations: animations,
 				visualScenes: visualScenes,
-				scene: daeScene
+				visualScene: visualScene,
+				scene: visualScene,
+				kinematicsModels: kinematicsModels,
+				kinematicsModel: kinematicsModel
 			}
 
 		};
@@ -187,19 +207,19 @@ THREE.ColladaLoader = function () {
 
 		return result;
 
-	};
+	}
 
 	function setPreferredShading ( shading ) {
 
 		preferredShading = shading;
 
-	};
+	}
 
 	function parseAsset () {
 
-		var elements = COLLADA.evaluate( '//dae:asset', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+		var elements = COLLADA.querySelectorAll('asset');
 
-		var element = elements.iterateNext();
+		var element = elements[0];
 
 		if ( element && element.childNodes ) {
 
@@ -232,33 +252,35 @@ THREE.ColladaLoader = function () {
 
 		}
 
-	};
+	}
 
 	function parseLib ( q, classSpec, prefix ) {
 
-		var elements = COLLADA.evaluate(q, COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null) ;
+		var elements = COLLADA.querySelectorAll(q);
 
 		var lib = {};
-		var element = elements.iterateNext();
+
 		var i = 0;
 
-		while ( element ) {
+		var elementsLength = elements.length;
+
+		for ( var j = 0; j < elementsLength; j ++ ) {
 
+			var element = elements[j];
 			var daeElement = ( new classSpec() ).parse( element );
-			if ( !daeElement.id || daeElement.id.length == 0 ) daeElement.id = prefix + ( i ++ );
-			lib[ daeElement.id ] = daeElement;
 
-			element = elements.iterateNext();
+			if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ );
+			lib[ daeElement.id ] = daeElement;
 
 		}
 
 		return lib;
 
-	};
+	}
 
 	function parseScene() {
 
-		var sceneElement = COLLADA.evaluate( './/dae:scene/dae:instance_visual_scene', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext();
+		var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0];
 
 		if ( sceneElement ) {
 
@@ -271,7 +293,24 @@ THREE.ColladaLoader = function () {
 
 		}
 
-	};
+	}
+
+	function parseKinematicsModel() {
+
+		var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0];
+
+		if ( kinematicsModelElement ) {
+
+			var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, '');
+			return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ];
+
+		} else {
+
+			return null;
+
+		}
+
+	}
 
 	function createAnimations() {
 
@@ -280,11 +319,11 @@ THREE.ColladaLoader = function () {
 		// fill in the keys
 		recurseHierarchy( scene );
 
-	};
+	}
 
 	function recurseHierarchy( node ) {
 
-		var n = daeScene.getChildById( node.name, true ),
+		var n = visualScene.getChildById( node.colladaId, true ),
 			newData = null;
 
 		if ( n && n.keys ) {
@@ -303,13 +342,13 @@ THREE.ColladaLoader = function () {
 
 			animData.push(newData);
 
-			for ( var i = 0, il = n.keys.length; i < il; i++ ) {
+			for ( var i = 0, il = n.keys.length; i < il; i ++ ) {
 
 				newData.length = Math.max( newData.length, n.keys[i].time );
 
 			}
 
-		} else  {
+		} else {
 
 			newData = {
 				hierarchy: [ {
@@ -320,7 +359,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		for ( var i = 0, il = node.children.length; i < il; i++ ) {
+		for ( var i = 0, il = node.children.length; i < il; i ++ ) {
 
 			var d = recurseHierarchy( node.children[i] );
 
@@ -337,21 +376,22 @@ THREE.ColladaLoader = function () {
 
 		return newData;
 
-	};
+	}
 
 	function calcAnimationBounds () {
 
 		var start = 1000000;
 		var end = -start;
 		var frames = 0;
-
+		var ID;
 		for ( var id in animations ) {
 
 			var animation = animations[ id ];
-
+			ID = ID || animation.id;
 			for ( var i = 0; i < animation.sampler.length; i ++ ) {
 
 				var sampler = animation.sampler[ i ];
+
 				sampler.create();
 
 				start = Math.min( start, sampler.startTime );
@@ -362,9 +402,9 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		return { start:start, end:end, frames:frames };
+		return { start:start, end:end, frames:frames,ID:ID };
 
-	};
+	}
 
 	function createMorph ( geometry, ctrl ) {
 
@@ -423,7 +463,7 @@ THREE.ColladaLoader = function () {
 		}
 
 		var skin = skinCtrl.skin;
-		var skeleton = daeScene.getChildById( ctrl.skeleton[ 0 ] );
+		var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] );
 		var hierarchy = [];
 
 		applyBindShape = applyBindShape !== undefined ? applyBindShape : true;
@@ -455,12 +495,14 @@ THREE.ColladaLoader = function () {
 
 		}
 
-	};
+	}
 
 	function setupSkeleton ( node, bones, frame, parent ) {
 
 		node.world = node.world || new THREE.Matrix4();
+		node.localworld = node.localworld || new THREE.Matrix4();
 		node.world.copy( node.matrix );
+		node.localworld.copy( node.matrix );
 
 		if ( node.channels && node.channels.length ) {
 
@@ -470,7 +512,9 @@ THREE.ColladaLoader = function () {
 			if ( m instanceof THREE.Matrix4 ) {
 
 				node.world.copy( m );
-
+				node.localworld.copy(m);
+				if (frame === 0)
+					node.matrix.copy(m);
 			}
 
 		}
@@ -489,7 +533,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-	};
+	}
 
 	function setupSkinningMatrices ( bones, skin ) {
 
@@ -504,7 +548,7 @@ THREE.ColladaLoader = function () {
 
 			for ( var j = 0; j < skin.joints.length; j ++ ) {
 
-				if ( bone.sid == skin.joints[ j ] ) {
+				if ( bone.sid === skin.joints[ j ] ) {
 
 					found = j;
 					break;
@@ -520,16 +564,18 @@ THREE.ColladaLoader = function () {
 				bone.invBindMatrix = inv;
 				bone.skinningMatrix = new THREE.Matrix4();
 				bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi)
+				bone.animatrix = new THREE.Matrix4();
 
+				bone.animatrix.copy(bone.localworld);
 				bone.weights = [];
 
 				for ( var j = 0; j < skin.weights.length; j ++ ) {
 
-					for (var k = 0; k < skin.weights[ j ].length; k ++) {
+					for (var k = 0; k < skin.weights[ j ].length; k ++ ) {
 
 						var w = skin.weights[ j ][ k ];
 
-						if ( w.joint == found ) {
+						if ( w.joint === found ) {
 
 							bone.weights.push( w );
 
@@ -541,13 +587,96 @@ THREE.ColladaLoader = function () {
 
 			} else {
 
-				throw 'ColladaLoader: Could not find joint \'' + bone.sid + '\'.';
+				console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." );
+
+				bone.skinningMatrix = new THREE.Matrix4();
+				bone.weights = [];
+
+			}
+		}
+
+	}
+
+	//Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix
+	function flattenSkeleton(skeleton) {
+
+		var list = [];
+		var walk = function(parentid, node, list) {
+
+			var bone = {};
+			bone.name = node.sid;
+			bone.parent = parentid;
+			bone.matrix = node.matrix;
+			var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ];
+			bone.matrix.decompose(data[0], data[1], data[2]);
+
+			bone.pos = [ data[0].x,data[0].y,data[0].z ];
+
+			bone.scl = [ data[2].x,data[2].y,data[2].z ];
+			bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ];
+			list.push(bone);
+
+			for (var i in node.nodes) {
+
+				walk(node.sid, node.nodes[i], list);
+
+			}
+
+		};
+
+		walk(-1, skeleton, list);
+		return list;
+
+	}
+
+	//Move the vertices into the pose that is proper for the start of the animation
+	function skinToBindPose(geometry,skeleton,skinController) {
+
+		var bones = [];
+		setupSkeleton( skeleton, bones, -1 );
+		setupSkinningMatrices( bones, skinController.skin );
+		v = new THREE.Vector3();
+		var skinned = [];
+
+		for (var i = 0; i < geometry.vertices.length; i ++) {
+
+			skinned.push(new THREE.Vector3());
+
+		}
+
+		for ( i = 0; i < bones.length; i ++ ) {
+
+			if ( bones[ i ].type != 'JOINT' ) continue;
+
+			for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
+
+				w = bones[ i ].weights[ j ];
+				vidx = w.index;
+				weight = w.weight;
+
+				o = geometry.vertices[vidx];
+				s = skinned[vidx];
 
+				v.x = o.x;
+				v.y = o.y;
+				v.z = o.z;
+
+				v.applyMatrix4( bones[i].skinningMatrix );
+
+				s.x += (v.x * weight);
+				s.y += (v.y * weight);
+				s.z += (v.z * weight);
 			}
 
 		}
 
-	};
+		for (var i = 0; i < geometry.vertices.length; i ++) {
+
+			geometry.vertices[i] = skinned[i];
+
+		}
+
+	}
 
 	function applySkin ( geometry, instanceCtrl, frame ) {
 
@@ -570,72 +699,336 @@ THREE.ColladaLoader = function () {
 		}
 
 		var animationBounds = calcAnimationBounds();
-		var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) ||
-					   daeScene.getChildBySid( instanceCtrl.skeleton[0], true );
+		var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true );
+
+		//flatten the skeleton into a list of bones
+		var bonelist = flattenSkeleton(skeleton);
+		var joints = skinController.skin.joints;
+
+		//sort that list so that the order reflects the order in the joint list
+		var sortedbones = [];
+		for (var i = 0; i < joints.length; i ++) {
+
+			for (var j = 0; j < bonelist.length; j ++) {
+
+				if (bonelist[j].name === joints[i]) {
+
+					sortedbones[i] = bonelist[j];
+
+				}
+
+			}
+
+		}
+
+		//hook up the parents by index instead of name
+		for (var i = 0; i < sortedbones.length; i ++) {
+
+			for (var j = 0; j < sortedbones.length; j ++) {
+
+				if (sortedbones[i].parent === sortedbones[j].name) {
+
+					sortedbones[i].parent = j;
+
+				}
+
+			}
+
+		}
+
 
 		var i, j, w, vidx, weight;
 		var v = new THREE.Vector3(), o, s;
 
 		// move vertices to bind shape
-
 		for ( i = 0; i < geometry.vertices.length; i ++ ) {
-
 			geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix );
+		}
+
+		var skinIndices = [];
+		var skinWeights = [];
+		var weights = skinController.skin.weights;
+
+		// hook up the skin weights
+		// TODO - this might be a good place to choose greatest 4 weights
+		for ( var i =0; i < weights.length; i ++ ) {
+
+			var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0);
+			var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0);
+
+			skinIndices.push(indicies);
+			skinWeights.push(weight);
 
 		}
 
+		geometry.skinIndices = skinIndices;
+		geometry.skinWeights = skinWeights;
+		geometry.bones = sortedbones;
 		// process animation, or simply pose the rig if no animation
 
-		for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
+		//create an animation for the animated bones
+		//NOTE: this has no effect when using morphtargets
+		var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] };
 
-			var bones = [];
-			var skinned = [];
+		for (var j = 0; j < sortedbones.length; j ++) {
+
+			animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] });
 
-			// zero skinned vertices
+		}
 
-			for ( i = 0; i < geometry.vertices.length; i++ ) {
+		console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' );
 
-				skinned.push( new THREE.Vector3() );
 
-			}
 
+		skinToBindPose(geometry, skeleton, skinController);
+
+
+		for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
+
+			var bones = [];
+			var skinned = [];
 			// process the frame and setup the rig with a fresh
 			// transform, possibly from the bone's animation channel(s)
 
 			setupSkeleton( skeleton, bones, frame );
 			setupSkinningMatrices( bones, skinController.skin );
 
-			// skin 'm
+			for (var i = 0; i < bones.length; i ++) {
+
+				for (var j = 0; j < animationdata.hierarchy.length; j ++) {
+
+					if (animationdata.hierarchy[j].name === bones[i].sid) {
+
+						var key = {};
+						key.time = (frame / 30);
+						key.matrix = bones[i].animatrix;
+
+						if (frame === 0)
+							bones[i].matrix = key.matrix;
+
+						var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ];
+						key.matrix.decompose(data[0], data[1], data[2]);
+
+						key.pos = [ data[0].x,data[0].y,data[0].z ];
+
+						key.scl = [ data[2].x,data[2].y,data[2].z ];
+						key.rot = data[1];
+
+						animationdata.hierarchy[j].keys.push(key);
+
+					}
+
+				}
+
+			}
+
+			geometry.animation = animationdata;
+
+		}
+
+	};
+
+	function createKinematics() {
+
+		if ( kinematicsModel && kinematicsModel.joints.length === 0 ) {
+			kinematics = undefined;
+			return;
+		}
+
+		var jointMap = {};
+
+		var _addToMap = function( jointIndex, parentVisualElement ) {
+
+			var parentVisualElementId = parentVisualElement.getAttribute( 'id' );
+			var colladaNode = visualScene.getChildById( parentVisualElementId, true );
+			var joint = kinematicsModel.joints[ jointIndex ];
+
+			scene.traverse(function( node ) {
+
+				if ( node.colladaId == parentVisualElementId ) {
+
+					jointMap[ jointIndex ] = {
+						node: node,
+						transforms: colladaNode.transforms,
+						joint: joint,
+						position: joint.zeroPosition
+					};
+
+				}
+
+			});
+
+		};
+
+		kinematics = {
+
+			joints: kinematicsModel && kinematicsModel.joints,
+
+			getJointValue: function( jointIndex ) {
+
+				var jointData = jointMap[ jointIndex ];
+
+				if ( jointData ) {
+
+					return jointData.position;
+
+				} else {
+
+					console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' );
+
+				}
+
+			},
+
+			setJointValue: function( jointIndex, value ) {
+
+				var jointData = jointMap[ jointIndex ];
+
+				if ( jointData ) {
+
+					var joint = jointData.joint;
+
+					if ( value > joint.limits.max || value < joint.limits.min ) {
+
+						console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' );
+
+					} else if ( joint.static ) {
+
+						console.log( 'setJointValue: joint ' + jointIndex + ' is static' );
+
+					} else {
+
+						var threejsNode = jointData.node;
+						var axis = joint.axis;
+						var transforms = jointData.transforms;
+
+						var matrix = new THREE.Matrix4();
+
+						for (i = 0; i < transforms.length; i ++ ) {
+
+							var transform = transforms[ i ];
+
+							// kinda ghetto joint detection
+							if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) {
 
-			for ( i = 0; i < bones.length; i ++ ) {
+								// apply actual joint value here
+								switch ( joint.type ) {
 
-				if ( bones[ i ].type != 'JOINT' ) continue;
+									case 'revolute':
 
-				for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
+										matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) );
+										break;
 
-					w = bones[ i ].weights[ j ];
-					vidx = w.index;
-					weight = w.weight;
+									case 'prismatic':
 
-					o = geometry.vertices[vidx];
-					s = skinned[vidx];
+										matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) );
+										break;
+
+									default:
+
+										console.warn( 'setJointValue: unknown joint type: ' + joint.type );
+										break;
+
+								}
+
+							} else {
+
+								var m1 = new THREE.Matrix4();
+
+								switch ( transform.type ) {
+
+									case 'matrix':
+
+										matrix.multiply( transform.obj );
+
+										break;
+
+									case 'translate':
+
+										matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) );
+
+										break;
+
+									case 'rotate':
+
+										matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) );
+
+										break;
+
+								}
+							}
+						}
 
-					v.x = o.x;
-					v.y = o.y;
-					v.z = o.z;
+						// apply the matrix to the threejs node
+						var elementsFloat32Arr = matrix.elements;
+						var elements = Array.prototype.slice.call( elementsFloat32Arr );
+
+						var elementsRowMajor = [
+							elements[ 0 ],
+							elements[ 4 ],
+							elements[ 8 ],
+							elements[ 12 ],
+							elements[ 1 ],
+							elements[ 5 ],
+							elements[ 9 ],
+							elements[ 13 ],
+							elements[ 2 ],
+							elements[ 6 ],
+							elements[ 10 ],
+							elements[ 14 ],
+							elements[ 3 ],
+							elements[ 7 ],
+							elements[ 11 ],
+							elements[ 15 ]
+						];
+
+						threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor );
+						threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale );
+					}
 
-					v.applyMatrix4( bones[i].skinningMatrix );
+				} else {
 
-					s.x += (v.x * weight);
-					s.y += (v.y * weight);
-					s.z += (v.z * weight);
+					console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' );
 
 				}
 
 			}
 
-			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
+		};
+
+		var element = COLLADA.querySelector('scene instance_kinematics_scene');
+
+		if ( element ) {
+
+			for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+				var child = element.childNodes[ i ];
+
+				if ( child.nodeType != 1 ) continue;
+
+				switch ( child.nodeName ) {
+
+					case 'bind_joint_axis':
+
+						var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop();
+						var axis = child.querySelector('axis param').textContent;
+						var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] );
+						var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' );
+
+						if ( visualTargetElement ) {
+							var parentVisualElement = visualTargetElement.parentElement;
+							_addToMap(jointIndex, parentVisualElement);
+						}
+
+						break;
+
+					default:
+
+						break;
+
+				}
 
+			}
 		}
 
 	};
@@ -735,7 +1128,7 @@ THREE.ColladaLoader = function () {
 				if ( !geometry.mesh || !geometry.mesh.primitives )
 					continue;
 
-				if ( obj.name.length == 0 ) {
+				if ( obj.name.length === 0 ) {
 
 					obj.name = geometry.id;
 
@@ -771,7 +1164,7 @@ THREE.ColladaLoader = function () {
 						used_materials[ instance_material.symbol ] = num_materials;
 						used_materials_array.push( material3js );
 						first_material = material3js;
-						first_material.name = mat.name == null || mat.name === '' ? mat.id : mat.name;
+						first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name;
 						num_materials ++;
 
 					}
@@ -779,7 +1172,7 @@ THREE.ColladaLoader = function () {
 				}
 
 				var mesh;
-				var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.FlatShading, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } );
+				var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } );
 				var geom = geometry.mesh.geometry3js;
 
 				if ( num_materials > 1 ) {
@@ -797,16 +1190,33 @@ THREE.ColladaLoader = function () {
 
 				if ( skinController !== undefined ) {
 
+
 					applySkin( geom, skinController );
 
-					material.morphTargets = true;
+					if ( geom.morphTargets.length > 0 ) {
+
+						material.morphTargets = true;
+						material.skinning = false;
+
+					} else {
+
+						material.morphTargets = false;
+						material.skinning = true;
+
+					}
+
 
 					mesh = new THREE.SkinnedMesh( geom, material, false );
-					mesh.skeleton = skinController.skeleton;
-					mesh.skinController = controllers[ skinController.url ];
-					mesh.skinInstanceController = skinController;
+
+
+					//mesh.skeleton = skinController.skeleton;
+					//mesh.skinController = controllers[ skinController.url ];
+					//mesh.skinInstanceController = skinController;
 					mesh.name = 'skin_' + skins.length;
 
+
+
+					//mesh.animationHandle.setKey(0);
 					skins.push( mesh );
 
 				} else if ( morphController !== undefined ) {
@@ -822,13 +1232,19 @@ THREE.ColladaLoader = function () {
 
 				} else {
 
-					mesh = new THREE.Mesh( geom, material );
-					mesh.geometry.name = geometry.name;
-					// mesh.geom.name = geometry.id;
+					if ( geom.isLineStrip === true ) {
+
+						mesh = new THREE.Line( geom );
+
+					} else {
+
+						mesh = new THREE.Mesh( geom, material );
+
+					}
 
 				}
 
-				node.geometries.length > 1 ? obj.add( mesh ) : obj = mesh;
+				obj.add(mesh);
 
 			}
 
@@ -839,13 +1255,15 @@ THREE.ColladaLoader = function () {
 			var instance_camera = node.cameras[i];
 			var cparams = cameras[instance_camera.url];
 
-			obj = new THREE.PerspectiveCamera(cparams.fov, parseFloat(cparams.aspect_ratio),
+			var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio),
 					parseFloat(cparams.znear), parseFloat(cparams.zfar));
 
+			obj.add(cam);
 		}
 
 		for ( i = 0; i < node.lights.length; i ++ ) {
 
+			var light = null;
 			var instance_light = node.lights[i];
 			var lparams = lights[instance_light.url];
 
@@ -853,7 +1271,7 @@ THREE.ColladaLoader = function () {
 
 				var color = lparams.color.getHex();
 				var intensity = lparams.intensity;
-				var distance = 0;
+				var distance = lparams.distance;
 				var angle = lparams.falloff_angle;
 				var exponent; // Intentionally undefined, don't know what this is yet
 
@@ -861,37 +1279,44 @@ THREE.ColladaLoader = function () {
 
 					case 'directional':
 
-						obj = new THREE.DirectionalLight( color, intensity, distance );
+						light = new THREE.DirectionalLight( color, intensity, distance );
+						light.position.set(0, 0, 1);
 						break;
 
 					case 'point':
 
-						obj = new THREE.PointLight( color, intensity, distance );
+						light = new THREE.PointLight( color, intensity, distance );
 						break;
 
 					case 'spot':
 
-						obj = new THREE.SpotLight( color, intensity, distance, angle, exponent );
+						light = new THREE.SpotLight( color, intensity, distance, angle, exponent );
+						light.position.set(0, 0, 1);
 						break;
 
 					case 'ambient':
 
-						obj = new THREE.AmbientLight( color );
+						light = new THREE.AmbientLight( color );
 						break;
 
 				}
 
 			}
 
+			if (light) {
+				obj.add(light);
+			}
 		}
 
 		obj.name = node.name || node.id || "";
+		obj.colladaId = node.id || "";
+		obj.layer = node.layer || "";
 		obj.matrix = node.matrix;
 		obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
 
 		if ( options.centerGeometry && obj.geometry ) {
 
-			var delta = THREE.GeometryUtils.center( obj.geometry );
+			var delta = obj.geometry.center();
 			delta.multiply( obj.scale );
 			delta.applyQuaternion( obj.quaternion );
 
@@ -913,7 +1338,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < skin.joints.length; i ++ ) {
 
-			if ( skin.joints[ i ] == id ) {
+			if ( skin.joints[ i ] === id ) {
 
 				return i;
 
@@ -925,11 +1350,25 @@ THREE.ColladaLoader = function () {
 
 	function getLibraryNode( id ) {
 
-		return COLLADA.evaluate( './/dae:library_nodes//dae:node[@id=\'' + id + '\']', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext();
+		var nodes = COLLADA.querySelectorAll('library_nodes node');
+
+		for ( var i = 0; i < nodes.length; i++ ) {
+
+			var attObj = nodes[i].attributes.getNamedItem('id');
+
+			if ( attObj && attObj.value === id ) {
+
+				return nodes[i];
+
+			}
+
+		}
+
+		return undefined;
 
 	};
 
-	function getChannelsForNode (node ) {
+	function getChannelsForNode ( node ) {
 
 		var channels = [];
 		var startTime = 1000000;
@@ -1067,7 +1506,7 @@ THREE.ColladaLoader = function () {
 			var keys = [],
 				sids = [];
 
-			for ( var i = 0, il = node.channels.length; i < il; i++ ) {
+			for ( var i = 0, il = node.channels.length; i < il; i ++ ) {
 
 				var channel = node.channels[i],
 					fullSid = channel.fullSid,
@@ -1080,7 +1519,7 @@ THREE.ColladaLoader = function () {
 
 					member = [];
 
-					for ( var j = 0, jl = channel.arrIndices.length; j < jl; j++ ) {
+					for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) {
 
 						member[ j ] = getConvertedIndex( channel.arrIndices[ j ] );
 
@@ -1100,17 +1539,17 @@ THREE.ColladaLoader = function () {
 
 					}
 
-					for ( var j = 0, jl = input.length; j < jl; j++ ) {
+					for ( var j = 0, jl = input.length; j < jl; j ++ ) {
 
 						var time = input[j],
-							data = sampler.getData( transform.type, j ),
+							data = sampler.getData( transform.type, j, member ),
 							key = findKey( keys, time );
 
 						if ( !key ) {
 
 							key = new Key( time );
 							var timeNdx = findTimeNdx( keys, time );
-							keys.splice( timeNdx == -1 ? keys.length : timeNdx, 0, key );
+							keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key );
 
 						}
 
@@ -1127,11 +1566,11 @@ THREE.ColladaLoader = function () {
 			}
 
 			// post process
-			for ( var i = 0; i < sids.length; i++ ) {
+			for ( var i = 0; i < sids.length; i ++ ) {
 
 				var sid = sids[ i ];
 
-				for ( var j = 0; j < keys.length; j++ ) {
+				for ( var j = 0; j < keys.length; j ++ ) {
 
 					var key = keys[ j ];
 
@@ -1156,7 +1595,7 @@ THREE.ColladaLoader = function () {
 
 		var retVal = null;
 
-		for ( var i = 0, il = keys.length; i < il && retVal == null; i++ ) {
+		for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) {
 
 			var key = keys[i];
 
@@ -1180,7 +1619,7 @@ THREE.ColladaLoader = function () {
 
 		var ndx = -1;
 
-		for ( var i = 0, il = keys.length; i < il && ndx == -1; i++ ) {
+		for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) {
 
 			var key = keys[i];
 
@@ -1198,8 +1637,8 @@ THREE.ColladaLoader = function () {
 
 	function interpolateKeys ( keys, key, ndx, fullSid ) {
 
-		var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx-1 : 0 ),
-			nextKey = getNextKeyWith( keys, fullSid, ndx+1 );
+		var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ),
+			nextKey = getNextKeyWith( keys, fullSid, ndx + 1 );
 
 		if ( prevKey && nextKey ) {
 
@@ -1217,7 +1656,7 @@ THREE.ColladaLoader = function () {
 
 				data = [];
 
-				for ( var i = 0; i < prevData.length; ++i ) {
+				for ( var i = 0; i < prevData.length; ++ i ) {
 
 					data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale;
 
@@ -1239,7 +1678,7 @@ THREE.ColladaLoader = function () {
 
 	function getNextKeyWith( keys, fullSid, ndx ) {
 
-		for ( ; ndx < keys.length; ndx++ ) {
+		for ( ; ndx < keys.length; ndx ++ ) {
 
 			var key = keys[ ndx ];
 
@@ -1261,7 +1700,7 @@ THREE.ColladaLoader = function () {
 
 		ndx = ndx >= 0 ? ndx : ndx + keys.length;
 
-		for ( ; ndx >= 0; ndx-- ) {
+		for ( ; ndx >= 0; ndx -- ) {
 
 			var key = keys[ ndx ];
 
@@ -1292,7 +1731,7 @@ THREE.ColladaLoader = function () {
 
 			var child = element.childNodes[ i ];
 
-			if ( child.nodeName == 'init_from' ) {
+			if ( child.nodeName === 'init_from' ) {
 
 				this.init_from = child.textContent;
 
@@ -1320,7 +1759,7 @@ THREE.ColladaLoader = function () {
 		this.name = element.getAttribute('name');
 		this.type = "none";
 
-		for ( var i = 0; i < element.childNodes.length; i++ ) {
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
 			var child = element.childNodes[ i ];
 
@@ -1523,11 +1962,11 @@ THREE.ColladaLoader = function () {
 					var input = ( new Input() ).parse( child );
 					var source = sources[ input.source ];
 
-					if ( input.semantic == 'JOINT' ) {
+					if ( input.semantic === 'JOINT' ) {
 
 						this.joints = source.read();
 
-					} else if ( input.semantic == 'INV_BIND_MATRIX' ) {
+					} else if ( input.semantic === 'INV_BIND_MATRIX' ) {
 
 						this.invBindMatrices = source.read();
 
@@ -1583,7 +2022,7 @@ THREE.ColladaLoader = function () {
 			var numBones = vcount[i];
 			var vertex_weights = [];
 
-			for ( var j = 0; j < numBones; j++ ) {
+			for ( var j = 0; j < numBones; j ++ ) {
 
 				var influence = {};
 
@@ -1632,7 +2071,7 @@ THREE.ColladaLoader = function () {
 		this.id = "";
 		this.name = "";
 		this.nodes = [];
-		this.scene = new THREE.Object3D();
+		this.scene = new THREE.Group();
 
 	};
 
@@ -1747,7 +2186,7 @@ THREE.ColladaLoader = function () {
 
 			}
 
-			if ( sid == transformSid ) {
+			if ( sid === transformSid ) {
 
 				channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices };
 				return channel;
@@ -1762,7 +2201,7 @@ THREE.ColladaLoader = function () {
 
 	Node.prototype.getChildById = function ( id, recursive ) {
 
-		if ( this.id == id ) {
+		if ( this.id === id ) {
 
 			return this;
 
@@ -1790,7 +2229,7 @@ THREE.ColladaLoader = function () {
 
 	Node.prototype.getChildBySid = function ( sid, recursive ) {
 
-		if ( this.sid == sid ) {
+		if ( this.sid === sid ) {
 
 			return this;
 
@@ -1819,7 +2258,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < this.transforms.length; i ++ ) {
 
-			if ( this.transforms[ i ].sid == sid ) return this.transforms[ i ];
+			if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ];
 
 		}
 
@@ -1835,8 +2274,9 @@ THREE.ColladaLoader = function () {
 		this.sid = element.getAttribute('sid');
 		this.name = element.getAttribute('name');
 		this.type = element.getAttribute('type');
+		this.layer = element.getAttribute('layer');
 
-		this.type = this.type == 'JOINT' ? this.type : 'NODE';
+		this.type = this.type === 'JOINT' ? this.type : 'NODE';
 
 		this.nodes = [];
 		this.transforms = [];
@@ -2202,21 +2642,16 @@ THREE.ColladaLoader = function () {
 
 				case 'bind_material':
 
-					var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
-
-					if ( instances ) {
-
-						var instance = instances.iterateNext();
-
-						while ( instance ) {
+					var instances = child.querySelectorAll('instance_material');
 
-							this.instance_material.push( (new InstanceMaterial()).parse(instance) );
-							instance = instances.iterateNext();
+					for ( var j = 0; j < instances.length; j ++ ) {
 
-						}
+						var instance = instances[j];
+						this.instance_material.push( (new InstanceMaterial()).parse(instance) );
 
 					}
 
+
 					break;
 
 				case 'extra':
@@ -2264,20 +2699,14 @@ THREE.ColladaLoader = function () {
 			var child = element.childNodes[i];
 			if ( child.nodeType != 1 ) continue;
 
-			if ( child.nodeName == 'bind_material' ) {
-
-				var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
-
-				if ( instances ) {
+			if ( child.nodeName === 'bind_material' ) {
 
-					var instance = instances.iterateNext();
+				var instances = child.querySelectorAll('instance_material');
 
-					while ( instance ) {
+				for ( var j = 0; j < instances.length; j ++ ) {
 
-						this.instance_material.push( (new InstanceMaterial()).parse(instance) );
-						instance = instances.iterateNext();
-
-					}
+					var instance = instances[j];
+					this.instance_material.push( (new InstanceMaterial()).parse(instance) );
 
 				}
 
@@ -2294,7 +2723,6 @@ THREE.ColladaLoader = function () {
 	function Geometry() {
 
 		this.id = "";
-		this.name = "";
 		this.mesh = null;
 
 	};
@@ -2302,7 +2730,6 @@ THREE.ColladaLoader = function () {
 	Geometry.prototype.parse = function ( element ) {
 
 		this.id = element.getAttribute('id');
-		this.name = element.getAttribute('name');
 
 		extractDoubleSided( this, element );
 
@@ -2340,13 +2767,11 @@ THREE.ColladaLoader = function () {
 
 	};
 
-	Mesh.prototype.parse = function( element ) {
+	Mesh.prototype.parse = function ( element ) {
 
 		this.primitives = [];
 
-		var i, j;
-
-		for ( i = 0; i < element.childNodes.length; i ++ ) {
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
 			var child = element.childNodes[ i ];
 
@@ -2362,6 +2787,11 @@ THREE.ColladaLoader = function () {
 					this.vertices = ( new Vertices() ).parse( child );
 					break;
 
+				case 'linestrips':
+
+					this.primitives.push( ( new LineStrips().parse( child ) ) );
+					break;
+
 				case 'triangles':
 
 					this.primitives.push( ( new Triangles().parse( child ) ) );
@@ -2386,15 +2816,23 @@ THREE.ColladaLoader = function () {
 
 		this.geometry3js = new THREE.Geometry();
 
+		if ( this.vertices === null ) {
+
+			// TODO (mrdoob): Study case when this is null (carrier.dae)
+
+			return this;
+
+		}
+
 		var vertexData = sources[ this.vertices.input['POSITION'].source ].data;
 
-		for ( i = 0; i < vertexData.length; i += 3 ) {
+		for ( var i = 0; i < vertexData.length; i += 3 ) {
 
 			this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() );
 
 		}
 
-		for ( i = 0; i < this.primitives.length; i ++ ) {
+		for ( var i = 0; i < this.primitives.length; i ++ ) {
 
 			var primitive = this.primitives[ i ];
 			primitive.setVertices( this.vertices );
@@ -2402,9 +2840,6 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		this.geometry3js.computeCentroids();
-		this.geometry3js.computeFaceNormals();
-
 		if ( this.geometry3js.calcNormals ) {
 
 			this.geometry3js.computeVertexNormals();
@@ -2412,13 +2847,20 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		// this.geometry3js.computeBoundingBox();
-
 		return this;
 
 	};
 
-	Mesh.prototype.handlePrimitive = function( primitive, geom ) {
+	Mesh.prototype.handlePrimitive = function ( primitive, geom ) {
+
+		if ( primitive instanceof LineStrips ) {
+
+			// TODO: Handle indices. Maybe easier with BufferGeometry?
+
+			geom.isLineStrip = true;
+			return;
+
+		}
 
 		var j, k, pList = primitive.p, inputs = primitive.inputs;
 		var input, index, idx32;
@@ -2429,8 +2871,9 @@ THREE.ColladaLoader = function () {
 		for ( j = 0; j < inputs.length; j ++ ) {
 
 			input = inputs[ j ];
+
 			var offset = input.offset + 1;
-			maxOffset = (maxOffset < offset)? offset : maxOffset;
+			maxOffset = (maxOffset < offset) ? offset : maxOffset;
 
 			switch ( input.semantic ) {
 
@@ -2442,7 +2885,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		for ( var pCount = 0; pCount < pList.length; ++pCount ) {
+		for ( var pCount = 0; pCount < pList.length; ++ pCount ) {
 
 			var p = pList[ pCount ], i = 0;
 
@@ -2514,7 +2957,7 @@ THREE.ColladaLoader = function () {
 
 				}
 
-				if ( ns.length == 0 ) {
+				if ( ns.length === 0 ) {
 
 					// check the vertices inputs
 					input = this.vertices.input.NORMAL;
@@ -2524,7 +2967,7 @@ THREE.ColladaLoader = function () {
 						source = sources[ input.source ];
 						numParams = source.accessor.params.length;
 
-						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
 
 							ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) );
 
@@ -2550,7 +2993,7 @@ THREE.ColladaLoader = function () {
 						source = sources[ input.source ];
 						numParams = source.accessor.params.length;
 
-						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
 
 							idx32 = vs[ ndx ] * numParams;
 							if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ];
@@ -2563,7 +3006,7 @@ THREE.ColladaLoader = function () {
 
 				}
 
-				if ( cs.length == 0 ) {
+				if ( cs.length === 0 ) {
 
 					// check the vertices inputs
 					input = this.vertices.input.COLOR;
@@ -2573,7 +3016,7 @@ THREE.ColladaLoader = function () {
 						source = sources[ input.source ];
 						numParams = source.accessor.params.length;
 
-						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
 
 							idx32 = vs[ ndx ] * numParams;
 							cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
@@ -2592,9 +3035,9 @@ THREE.ColladaLoader = function () {
 
 				} else if ( vcount === 4 ) {
 
-					faces.push( new THREE.Face3( vs[0], vs[1], vs[3], [ns[0], ns[1], ns[3]], cs.length ? [cs[0], cs[1], cs[3]] : new THREE.Color() ) );
+					faces.push( new THREE.Face3( vs[0], vs[1], vs[3], [ ns[0].clone(), ns[1].clone(), ns[3].clone() ], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) );
 
-					faces.push( new THREE.Face3( vs[1], vs[2], vs[3], [ns[1], ns[2], ns[3]], cs.length ? [cs[1], cs[2], cs[3]] : new THREE.Color() ) );
+					faces.push( new THREE.Face3( vs[1], vs[2], vs[3], [ ns[1].clone(), ns[2].clone(), ns[3].clone() ], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) );
 
 				} else if ( vcount > 4 && options.subdivideFaces ) {
 
@@ -2605,9 +3048,7 @@ THREE.ColladaLoader = function () {
 
 					for ( k = 1; k < vcount - 1; ) {
 
-						// FIXME: normals don't seem to be quite right
-
-						faces.push( new THREE.Face3( vs[0], vs[k], vs[k+1], [ ns[0], ns[k++], ns[k] ],  clr ) );
+						faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ], clr ) );
 
 					}
 
@@ -2621,14 +3062,14 @@ THREE.ColladaLoader = function () {
 						face.daeMaterial = primitive.material;
 						geom.faces.push( face );
 
-						for ( k = 0; k < texture_sets.length; k++ ) {
+						for ( k = 0; k < texture_sets.length; k ++ ) {
 
 							uv = ts[ texture_sets[k] ];
 
 							if ( vcount > 4 ) {
 
 								// Grab the right UVs for the vertices in this face
-								uvArr = [ uv[0], uv[ndx+1], uv[ndx+2] ];
+								uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ];
 
 							} else if ( vcount === 4 ) {
 
@@ -2669,6 +3110,7 @@ THREE.ColladaLoader = function () {
 				i += maxOffset * vcount;
 
 			}
+
 		}
 
 	};
@@ -2688,7 +3130,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < this.inputs.length; i ++ ) {
 
-			if ( this.inputs[ i ].source == vertices.id ) {
+			if ( this.inputs[ i ].source === vertices.id ) {
 
 				this.inputs[ i ].source = vertices.input[ 'POSITION' ].source;
 
@@ -2749,6 +3191,18 @@ THREE.ColladaLoader = function () {
 	};
 
 	Polylist.prototype = Object.create( Polygons.prototype );
+	Polylist.prototype.constructor = Polylist;
+
+	function LineStrips() {
+
+		Polygons.call( this );
+
+		this.vcount = 1;
+
+	};
+
+	LineStrips.prototype = Object.create( Polygons.prototype );
+	LineStrips.prototype.constructor = LineStrips;
 
 	function Triangles () {
 
@@ -2759,6 +3213,7 @@ THREE.ColladaLoader = function () {
 	};
 
 	Triangles.prototype = Object.create( Polygons.prototype );
+	Triangles.prototype.constructor = Triangles;
 
 	function Accessor() {
 
@@ -2780,7 +3235,7 @@ THREE.ColladaLoader = function () {
 
 			var child = element.childNodes[ i ];
 
-			if ( child.nodeName == 'param' ) {
+			if ( child.nodeName === 'param' ) {
 
 				var param = {};
 				param[ 'name' ] = child.getAttribute( 'name' );
@@ -2807,7 +3262,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
-			if ( element.childNodes[i].nodeName == 'input' ) {
+			if ( element.childNodes[i].nodeName === 'input' ) {
 
 				var input = ( new Input() ).parse( element.childNodes[ i ] );
 				this.input[ input.semantic ] = input;
@@ -2836,7 +3291,7 @@ THREE.ColladaLoader = function () {
 		this.set = _attr_as_int(element, 'set', -1);
 		this.offset = _attr_as_int(element, 'offset', 0);
 
-		if ( this.semantic == 'TEXCOORD' && this.set < 0 ) {
+		if ( this.semantic === 'TEXCOORD' && this.set < 0 ) {
 
 			this.set = 0;
 
@@ -2892,7 +3347,7 @@ THREE.ColladaLoader = function () {
 
 					for ( var j = 0; j < child.childNodes.length; j ++ ) {
 
-						if ( child.childNodes[ j ].nodeName == 'accessor' ) {
+						if ( child.childNodes[ j ].nodeName === 'accessor' ) {
 
 							this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] );
 							break;
@@ -2919,35 +3374,35 @@ THREE.ColladaLoader = function () {
 
 		//for (var i = 0; i < this.accessor.params.length; i++) {
 
-			var param = this.accessor.params[ 0 ];
+		var param = this.accessor.params[ 0 ];
 
 			//console.log(param.name + " " + param.type);
 
-			switch ( param.type ) {
+		switch ( param.type ) {
 
-				case 'IDREF':
-				case 'Name': case 'name':
-				case 'float':
+			case 'IDREF':
+			case 'Name': case 'name':
+			case 'float':
 
-					return this.data;
+				return this.data;
 
-				case 'float4x4':
+			case 'float4x4':
 
-					for ( var j = 0; j < this.data.length; j += 16 ) {
+				for ( var j = 0; j < this.data.length; j += 16 ) {
 
-						var s = this.data.slice( j, j + 16 );
-						var m = getConvertedMat4( s );
-						result.push( m );
-					}
+					var s = this.data.slice( j, j + 16 );
+					var m = getConvertedMat4( s );
+					result.push( m );
+				}
 
-					break;
+				break;
 
-				default:
+			default:
 
-					console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' );
-					break;
+				console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' );
+				break;
 
-			}
+		}
 
 		//}
 
@@ -2970,7 +3425,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
-			if ( element.childNodes[ i ].nodeName == 'instance_effect' ) {
+			if ( element.childNodes[ i ].nodeName === 'instance_effect' ) {
 
 				this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] );
 				break;
@@ -2997,7 +3452,7 @@ THREE.ColladaLoader = function () {
 
 	ColorOrTexture.prototype.isColor = function () {
 
-		return ( this.texture == null );
+		return ( this.texture === null );
 
 	};
 
@@ -3009,9 +3464,10 @@ THREE.ColladaLoader = function () {
 
 	ColorOrTexture.prototype.parse = function ( element ) {
 
-		if (element.nodeName == 'transparent')
-		{
+		if (element.nodeName === 'transparent') {
+
 			this.opaque = element.getAttribute('opaque');
+
 		}
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
@@ -3041,7 +3497,7 @@ THREE.ColladaLoader = function () {
 						repeatU: 1,
 						repeatV: 1,
 						wrapU: 1,
-						wrapV: 1,
+						wrapV: 1
 					};
 					this.parseTexture( child );
 					break;
@@ -3087,16 +3543,29 @@ THREE.ColladaLoader = function () {
 				case 'repeatV':
 
 					this.texOpts[ child.nodeName ] = parseFloat( child.textContent );
+
 					break;
 
-				case 'wrapU':
-				case 'wrapV':
+				case 'wrapU':
+				case 'wrapV':
+
+					// some dae have a value of true which becomes NaN via parseInt
+
+					if ( child.textContent.toUpperCase() === 'TRUE' ) {
+
+						this.texOpts[ child.nodeName ] = 1;
+
+					} else {
+
+						this.texOpts[ child.nodeName ] = parseInt( child.textContent );
 
-					this.texOpts[ child.nodeName ] = parseInt( child.textContent );
+					}
 					break;
 
 				default:
+
 					this.texOpts[ child.nodeName ] = child.textContent;
+
 					break;
 
 			}
@@ -3124,7 +3593,6 @@ THREE.ColladaLoader = function () {
 
 			switch ( child.nodeName ) {
 
-				case 'ambient':
 				case 'emission':
 				case 'diffuse':
 				case 'specular':
@@ -3133,12 +3601,34 @@ THREE.ColladaLoader = function () {
 					this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child );
 					break;
 
+				case 'bump':
+
+					// If 'bumptype' is 'heightfield', create a 'bump' property
+					// Else if 'bumptype' is 'normalmap', create a 'normal' property
+					// (Default to 'bump')
+					var bumpType = child.getAttribute( 'bumptype' );
+					if ( bumpType ) {
+						if ( bumpType.toLowerCase() === "heightfield" ) {
+							this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
+						} else if ( bumpType.toLowerCase() === "normalmap" ) {
+							this[ 'normal' ] = ( new ColorOrTexture() ).parse( child );
+						} else {
+							console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" );
+							this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
+						}
+					} else {
+						console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" );
+						this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
+					}
+
+					break;
+
 				case 'shininess':
 				case 'reflectivity':
 				case 'index_of_refraction':
 				case 'transparency':
 
-					var f = evaluateXPath( child, './/dae:float' );
+					var f = child.querySelectorAll('float');
 
 					if ( f.length > 0 )
 						this[ child.nodeName ] = parseFloat( f[ 0 ].textContent );
@@ -3162,23 +3652,30 @@ THREE.ColladaLoader = function () {
 		var props = {};
 
 		var transparent = false;
-		if (this['transparency'] !== undefined && this['transparent'] !== undefined)
-		{
+
+		if (this['transparency'] !== undefined && this['transparent'] !== undefined) {
 			// convert transparent color RBG to average value
 			var transparentColor = this['transparent'];
-			var transparencyLevel = (this.transparent.color.r +
-										this.transparent.color.g +
-										this.transparent.color.b)
-										/ 3 * this.transparency;
+			var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency;
 
-			if (transparencyLevel > 0)
-			{
+			if (transparencyLevel > 0) {
 				transparent = true;
 				props[ 'transparent' ] = true;
 				props[ 'opacity' ] = 1 - transparencyLevel;
+
 			}
+
 		}
 
+		var keys = {
+			'diffuse':'map',
+			'ambient':'lightMap',
+			'specular':'specularMap',
+			'emission':'emissionMap',
+			'bump':'bumpMap',
+			'normal':'normalMap'
+			};
+
 		for ( var prop in this ) {
 
 			switch ( prop ) {
@@ -3187,6 +3684,8 @@ THREE.ColladaLoader = function () {
 				case 'emission':
 				case 'diffuse':
 				case 'specular':
+				case 'bump':
+				case 'normal':
 
 					var cot = this[ prop ];
 
@@ -3195,36 +3694,51 @@ THREE.ColladaLoader = function () {
 						if ( cot.isTexture() ) {
 
 							var samplerId = cot.texture;
-
-
-							var image;
-
 							var surfaceId = this.effect.sampler[samplerId];
+
 							if ( surfaceId !== undefined && surfaceId.source !== undefined ) {
+
 								var surface = this.effect.surface[surfaceId.source];
-								image = images[surface.init_from];
-							}
-							else {
-                image = images[samplerId];
-							}
 
-								if (image) {
+								if ( surface !== undefined ) {
+
+									var image = images[ surface.init_from ];
+
+									if ( image ) {
+
+										var url = baseUrl + image.init_from;
+
+										var texture;
+										var loader = THREE.Loader.Handlers.get( url );
 
-									var texture = THREE.ImageUtils.loadTexture(baseUrl + image.init_from);
-									texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-									texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-									texture.offset.x = cot.texOpts.offsetU;
-									texture.offset.y = cot.texOpts.offsetV;
-									texture.repeat.x = cot.texOpts.repeatU;
-									texture.repeat.y = cot.texOpts.repeatV;
-									props['map'] = texture;
+										if ( loader !== null ) {
 
-									// Texture with baked lighting?
-									if (prop === 'emission') props['emissive'] = 0xffffff;
+											texture = loader.load( url );
+
+										} else {
+
+											texture = new THREE.Texture();
+
+											loadTextureImage( texture, url );
+
+										}
+
+										texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+										texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+										texture.offset.x = cot.texOpts.offsetU;
+										texture.offset.y = cot.texOpts.offsetV;
+										texture.repeat.x = cot.texOpts.repeatU;
+										texture.repeat.y = cot.texOpts.repeatV;
+										props[keys[prop]] = texture;
+
+										// Texture with baked lighting?
+										if (prop === 'emission') props['emissive'] = 0xffffff;
+
+									}
 
 								}
 
-//							}
+							}
 
 						} else if ( prop === 'diffuse' || !transparent ) {
 
@@ -3252,7 +3766,7 @@ THREE.ColladaLoader = function () {
 				case 'reflectivity':
 
 					props[ prop ] = this[ prop ];
-					if( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap;
+					if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap;
 					props['combine'] = THREE.MixOperation;	//mix regular shading with reflective component
 					break;
 
@@ -3420,7 +3934,7 @@ THREE.ColladaLoader = function () {
 
 	Effect.prototype.create = function () {
 
-		if ( this.shader == null ) {
+		if ( this.shader === null ) {
 
 			return null;
 
@@ -3545,7 +4059,7 @@ THREE.ColladaLoader = function () {
 
 	};
 
-	Effect.prototype.parseTechnique= function ( element ) {
+	Effect.prototype.parseTechnique = function ( element ) {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
@@ -3561,7 +4075,51 @@ THREE.ColladaLoader = function () {
 
 					this.shader = ( new Shader( child.nodeName, this ) ).parse( child );
 					break;
+				case 'extra':
+					this.parseExtra(child);
+					break;
+				default:
+					break;
+
+			}
+
+		}
+
+	};
+
+	Effect.prototype.parseExtra = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'technique':
+					this.parseExtraTechnique( child );
+					break;
+				default:
+					break;
+
+			}
+
+		}
+
+	};
+
+	Effect.prototype.parseExtraTechnique = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'bump':
+					this.shader.parse( element );
+					break;
 				default:
 					break;
 
@@ -3821,7 +4379,7 @@ THREE.ColladaLoader = function () {
 
 	};
 
-	Sampler.prototype.getData = function ( type, ndx ) {
+	Sampler.prototype.getData = function ( type, ndx, member ) {
 
 		var data;
 
@@ -3834,7 +4392,7 @@ THREE.ColladaLoader = function () {
 			data = [];
 			ndx *= this.strideOut;
 
-			for ( var i = 0; i < this.strideOut; ++i ) {
+			for ( var i = 0; i < this.strideOut; ++ i ) {
 
 				data[ i ] = this.output[ ndx + i ];
 
@@ -3867,6 +4425,10 @@ THREE.ColladaLoader = function () {
 
 			data = this.output[ ndx ];
 
+			if ( member && type === 'translate' ) {
+				data = getConvertedTranslation( member, data );
+			}
+
 		}
 
 		return data;
@@ -3893,7 +4455,7 @@ THREE.ColladaLoader = function () {
 
 	Key.prototype.apply = function ( opt_sid ) {
 
-		for ( var i = 0; i < this.targets.length; ++i ) {
+		for ( var i = 0; i < this.targets.length; ++ i ) {
 
 			var target = this.targets[ i ];
 
@@ -3909,7 +4471,7 @@ THREE.ColladaLoader = function () {
 
 	Key.prototype.getTarget = function ( fullSid ) {
 
-		for ( var i = 0; i < this.targets.length; ++i ) {
+		for ( var i = 0; i < this.targets.length; ++ i ) {
 
 			if ( this.targets[ i ].sid === fullSid ) {
 
@@ -3925,7 +4487,7 @@ THREE.ColladaLoader = function () {
 
 	Key.prototype.hasTarget = function ( fullSid ) {
 
-		for ( var i = 0; i < this.targets.length; ++i ) {
+		for ( var i = 0; i < this.targets.length; ++ i ) {
 
 			if ( this.targets[ i ].sid === fullSid ) {
 
@@ -3942,7 +4504,7 @@ THREE.ColladaLoader = function () {
 	// TODO: Currently only doing linear interpolation. Should support full COLLADA spec.
 	Key.prototype.interpolate = function ( nextKey, time ) {
 
-		for ( var i = 0; i < this.targets.length; ++i ) {
+		for ( var i = 0, l = this.targets.length; i < l; i ++ ) {
 
 			var target = this.targets[ i ],
 				nextTarget = nextKey.getTarget( target.sid ),
@@ -3954,20 +4516,14 @@ THREE.ColladaLoader = function () {
 					nextData = nextTarget.data,
 					prevData = target.data;
 
-				// check scale error
-
-				if ( scale < 0 || scale > 1 ) {
-
-					console.log( "Key.interpolate: Warning! Scale out of bounds:" + scale );
-					scale = scale < 0 ? 0 : 1;
-
-				}
+				if ( scale < 0 ) scale = 0;
+				if ( scale > 1 ) scale = 1;
 
 				if ( prevData.length ) {
 
 					data = [];
 
-					for ( var j = 0; j < prevData.length; ++j ) {
+					for ( var j = 0; j < prevData.length; ++ j ) {
 
 						data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale;
 
@@ -4032,7 +4588,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
-			if ( element.childNodes[ i ].nodeName == 'technique_common' ) {
+			if ( element.childNodes[ i ].nodeName === 'technique_common' ) {
 
 				var technique = element.childNodes[ i ];
 
@@ -4040,7 +4596,7 @@ THREE.ColladaLoader = function () {
 
 					this.technique = technique.childNodes[ j ].nodeName;
 
-					if ( this.technique == 'perspective' ) {
+					if ( this.technique === 'perspective' ) {
 
 						var perspective = technique.childNodes[ j ];
 
@@ -4070,7 +4626,7 @@ THREE.ColladaLoader = function () {
 
 						}
 
-					} else if ( this.technique == 'orthographic' ) {
+					} else if ( this.technique === 'orthographic' ) {
 
 						var orthographic = technique.childNodes[ j ];
 
@@ -4202,6 +4758,10 @@ THREE.ColladaLoader = function () {
 
 								this.falloff_angle = parseFloat( child.textContent );
 								break;
+
+							case 'quadratic_attenuation':
+								var f = parseFloat( child.textContent );
+								this.distance = f ? Math.sqrt( 1 / f ) : 0;
 						}
 
 					}
@@ -4251,6 +4811,231 @@ THREE.ColladaLoader = function () {
 
 	};
 
+	function KinematicsModel( ) {
+
+		this.id = '';
+		this.name = '';
+		this.joints = [];
+		this.links = [];
+
+	}
+
+	KinematicsModel.prototype.parse = function( element ) {
+
+		this.id = element.getAttribute('id');
+		this.name = element.getAttribute('name');
+		this.joints = [];
+		this.links = [];
+
+		for (var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'technique_common':
+
+					this.parseCommon(child);
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	KinematicsModel.prototype.parseCommon = function( element ) {
+
+		for (var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( element.childNodes[ i ].nodeName ) {
+
+				case 'joint':
+					this.joints.push( (new Joint()).parse(child) );
+					break;
+
+				case 'link':
+					this.links.push( (new Link()).parse(child) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Joint( ) {
+
+		this.sid = '';
+		this.name = '';
+		this.axis = new THREE.Vector3();
+		this.limits = {
+			min: 0,
+			max: 0
+		};
+		this.type = '';
+		this.static = false;
+		this.zeroPosition = 0.0;
+		this.middlePosition = 0.0;
+
+	}
+
+	Joint.prototype.parse = function( element ) {
+
+		this.sid = element.getAttribute('sid');
+		this.name = element.getAttribute('name');
+		this.axis = new THREE.Vector3();
+		this.limits = {
+			min: 0,
+			max: 0
+		};
+		this.type = '';
+		this.static = false;
+		this.zeroPosition = 0.0;
+		this.middlePosition = 0.0;
+
+		var axisElement = element.querySelector('axis');
+		var _axis = _floats(axisElement.textContent);
+		this.axis = getConvertedVec3(_axis, 0);
+
+		var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360;
+		var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360;
+
+		this.limits = {
+			min: min,
+			max: max
+		};
+
+		var jointTypes = [ 'prismatic', 'revolute' ];
+		for (var i = 0; i < jointTypes.length; i ++ ) {
+
+			var type = jointTypes[ i ];
+
+			var jointElement = element.querySelector(type);
+
+			if ( jointElement ) {
+
+				this.type = type;
+
+			}
+
+		}
+
+		// if the min is equal to or somehow greater than the max, consider the joint static
+		if ( this.limits.min >= this.limits.max ) {
+
+			this.static = true;
+
+		}
+
+		this.middlePosition = (this.limits.min + this.limits.max) / 2.0;
+		return this;
+
+	};
+
+	function Link( ) {
+
+		this.sid = '';
+		this.name = '';
+		this.transforms = [];
+		this.attachments = [];
+
+	}
+
+	Link.prototype.parse = function( element ) {
+
+		this.sid = element.getAttribute('sid');
+		this.name = element.getAttribute('name');
+		this.transforms = [];
+		this.attachments = [];
+
+		for (var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'attachment_full':
+					this.attachments.push( (new Attachment()).parse(child) );
+					break;
+
+				case 'rotate':
+				case 'translate':
+				case 'matrix':
+
+					this.transforms.push( (new Transform()).parse(child) );
+					break;
+
+				default:
+
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Attachment( ) {
+
+		this.joint = '';
+		this.transforms = [];
+		this.links = [];
+
+	}
+
+	Attachment.prototype.parse = function( element ) {
+
+		this.joint = element.getAttribute('joint').split('/').pop();
+		this.links = [];
+
+		for (var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'link':
+					this.links.push( (new Link()).parse(child) );
+					break;
+
+				case 'rotate':
+				case 'translate':
+				case 'matrix':
+
+					this.transforms.push( (new Transform()).parse(child) );
+					break;
+
+				default:
+
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
 	function _source( element ) {
 
 		var id = element.getAttribute( 'id' );
@@ -4268,7 +5053,7 @@ THREE.ColladaLoader = function () {
 
 	function _nsResolver( nsPrefix ) {
 
-		if ( nsPrefix == "dae" ) {
+		if ( nsPrefix === "dae" ) {
 
 			return "http://www.collada.org/2005/11/COLLADASchema";
 
@@ -4285,7 +5070,7 @@ THREE.ColladaLoader = function () {
 
 		for ( var i = 0, l = raw.length; i < l; i ++ ) {
 
-			data.push( (raw[i] == 'true' || raw[i] == '1') ? true : false );
+			data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false );
 
 		}
 
@@ -4398,7 +5183,7 @@ THREE.ColladaLoader = function () {
 		var parts = f.toString().split( '.' );
 		parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0";
 
-		while( parts[ 1 ].length < num ) {
+		while ( parts[ 1 ].length < num ) {
 
 			parts[ 1 ] += '0';
 
@@ -4408,21 +5193,16 @@ THREE.ColladaLoader = function () {
 
 	};
 
-	function evaluateXPath( node, query ) {
-
-		var instances = COLLADA.evaluate( query, node, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
-
-		var inst = instances.iterateNext();
-		var result = [];
+	function loadTextureImage ( texture, url ) {
 
-		while ( inst ) {
+		loader = new THREE.ImageLoader();
 
-			result.push( inst );
-			inst = instances.iterateNext();
+		loader.load( url, function ( image ) {
 
-		}
+			texture.image = image;
+			texture.needsUpdate = true;
 
-		return result;
+		} );
 
 	};
 
@@ -4430,12 +5210,10 @@ THREE.ColladaLoader = function () {
 
 		obj.doubleSided = false;
 
-		var node = COLLADA.evaluate( './/dae:extra//dae:double_sided', element, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+		var node = element.querySelectorAll('extra double_sided')[0];
 
 		if ( node ) {
 
-			node = node.iterateNext();
-
 			if ( node && parseInt( node.textContent, 10 ) === 1 ) {
 
 				obj.doubleSided = true;
@@ -4450,7 +5228,7 @@ THREE.ColladaLoader = function () {
 
 	function setUpConversion() {
 
-		if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+		if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
 
 			upConversion = null;
 
@@ -4481,7 +5259,7 @@ THREE.ColladaLoader = function () {
 
 	function fixCoords( data, sign ) {
 
-		if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+		if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
 
 			return;
 
@@ -4537,6 +5315,31 @@ THREE.ColladaLoader = function () {
 
 	};
 
+	function getConvertedTranslation( axis, data ) {
+
+		if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
+
+			return data;
+
+		}
+
+		switch ( axis ) {
+			case 'X':
+				data = upConversion === 'XtoY' ? data * -1 : data;
+				break;
+			case 'Y':
+				data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data;
+				break;
+			case 'Z':
+				data = upConversion === 'ZtoY' ? data * -1 : data ;
+				break;
+			default:
+				break;
+		}
+
+		return data;
+	};
+
 	function getConvertedVec3( data, offset ) {
 
 		var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ];
@@ -4593,7 +5396,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		return new THREE.Matrix4(
+		return new THREE.Matrix4().set(
 			data[0], data[1], data[2], data[3],
 			data[4], data[5], data[6], data[7],
 			data[8], data[9], data[10], data[11],
@@ -4606,7 +5409,7 @@ THREE.ColladaLoader = function () {
 
 		if ( index > -1 && index < 3 ) {
 
-			var members = ['X', 'Y', 'Z'],
+			var members = [ 'X', 'Y', 'Z' ],
 				indices = { X: 0, Y: 1, Z: 2 };
 
 			index = getConvertedMember( members[ index ] );
diff --git a/gz3d/client/js/include/Detector.js b/gz3d/client/js/include/Detector.js
index 4f34bf1..3a4fad5 100644
--- a/gz3d/client/js/include/Detector.js
+++ b/gz3d/client/js/include/Detector.js
@@ -6,7 +6,7 @@
 var Detector = {
 
 	canvas: !! window.CanvasRenderingContext2D,
-	webgl: ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
+	webgl: ( function () { try { var canvas = document.createElement( 'canvas' ); return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) ); } catch ( e ) { return false; } } )(),
 	workers: !! window.Worker,
 	fileapi: window.File && window.FileReader && window.FileList && window.Blob,
 
@@ -57,3 +57,10 @@ var Detector = {
 	}
 
 };
+
+// browserify support
+if ( typeof module === 'object' ) {
+
+	module.exports = Detector;
+
+}
diff --git a/gz3d/client/js/include/EffectComposer.js b/gz3d/client/js/include/EffectComposer.js
index ef540e8..55a4702 100644
--- a/gz3d/client/js/include/EffectComposer.js
+++ b/gz3d/client/js/include/EffectComposer.js
@@ -8,8 +8,10 @@ THREE.EffectComposer = function ( renderer, renderTarget ) {
 
 	if ( renderTarget === undefined ) {
 
-		var width = window.innerWidth || 1;
-		var height = window.innerHeight || 1;
+		var pixelRatio = renderer.getPixelRatio();
+
+		var width  = Math.floor( renderer.context.canvas.width  / pixelRatio ) || 1;
+		var height = Math.floor( renderer.context.canvas.height / pixelRatio ) || 1;
 		var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
 
 		renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
@@ -108,8 +110,10 @@ THREE.EffectComposer.prototype = {
 
 			renderTarget = this.renderTarget1.clone();
 
-			renderTarget.width = window.innerWidth;
-			renderTarget.height = window.innerHeight;
+			var pixelRatio = this.renderer.getPixelRatio();
+
+			renderTarget.width  = Math.floor( this.renderer.context.canvas.width  / pixelRatio );
+			renderTarget.height = Math.floor( this.renderer.context.canvas.height / pixelRatio );
 
 		}
 
@@ -133,12 +137,3 @@ THREE.EffectComposer.prototype = {
 	}
 
 };
-
-// shared ortho camera
-
-THREE.EffectComposer.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
-
-THREE.EffectComposer.quad = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), null );
-
-THREE.EffectComposer.scene = new THREE.Scene();
-THREE.EffectComposer.scene.add( THREE.EffectComposer.quad );
diff --git a/gz3d/client/js/include/FirstPersonControls.js b/gz3d/client/js/include/FirstPersonControls.js
new file mode 100644
index 0000000..35a202e
--- /dev/null
+++ b/gz3d/client/js/include/FirstPersonControls.js
@@ -0,0 +1,295 @@
+/**
+ * First person controls for the gz3d camera/scene.
+ * Adapted from https://threejsdoc.appspot.com/doc/three.js/src.source/extras/controls/FirstPersonControls.js.html.
+ *
+ * Note that there is a third parameter now, called 'domElementForKeyBindings' which specifies to which element in
+ * the DOM the key strokes are bound. (Before they were automatically bound to the parameter 'domElement').
+ */
+
+/* global THREE: true */
+
+THREE.FirstPersonControls = function(object, domElement, domElementForKeyBindings)
+{
+  'use strict';
+
+  this.object = object;
+  this.domElement = angular.isDefined(domElement) ? domElement : document;
+  domElementForKeyBindings = angular.isDefined(domElementForKeyBindings) ? domElementForKeyBindings : document;
+
+  if (this.domElement !== document) {
+    this.domElement.setAttribute('tabindex', -1);
+  }
+
+  // Set to false to disable this control
+  this.enabled = true;
+  this.keyBindingsEnabled = true;
+
+  this.movementSpeed = 0.05;
+  this.lookSpeed = 0.01;
+
+  this.target = new THREE.Vector3();
+  this.lookVertical = true;
+
+  this.activeLook = true;
+
+  this.azimuth = 0.0;
+  this.zenith = 0.0;
+  this.zenithMin = 0.0;
+  this.zenithMax = Math.PI;
+  this.azimuthOnMouseDown = 0.0;
+  this.zenithOnMouseDown = 0.0;
+
+  this.moveForward = false;
+  this.moveBackward = false;
+  this.moveLeft = false;
+  this.moveRight = false;
+  this.moveUp = false;
+  this.moveDown = false;
+  this.initPosition = false;
+
+  this.rotateLeft = false;
+  this.rotateRight = false;
+  this.rotateUp = false;
+  this.rotateDown = false;
+  this.initRotation = false;
+
+  this.freeze = false;
+
+  this.mouseDragOn = false;
+  this.mousePosOnKeyDown = new THREE.Vector2();
+  this.mousePosCurrent = new THREE.Vector2();
+
+  this.initialPosition = new THREE.Vector3().copy(this.object.position);
+  this.initialRotation = new THREE.Quaternion().copy(this.object.quaternion);
+
+  this.onMouseDown = function (event) {
+    // HBP-NRP: The next three lines are commented since this leads to problems in chrome with respect
+    // to AngularJS, also see: [NRRPLT-1992]
+    //if (this.domElement !== document) {
+    //  this.domElement.focus();
+    //}
+
+    event.preventDefault();
+    event.stopPropagation();
+
+    if (this.activeLook) {
+      switch (event.button) {
+        case 0:
+          this.updateSphericalAngles();
+          this.mousePosOnKeyDown.set(event.pageX, event.pageY);
+          this.azimuthOnMouseDown = this.azimuth;
+          this.zenithOnMouseDown = this.zenith;
+          this.mouseDragOn = true;
+          break;
+        case 2:
+      }
+    }
+  };
+
+  this.onMouseUp = function (event) {
+    event.preventDefault();
+
+    // We do not stop the event propagation here, since there may be other
+    // components sitting on top, which also may have registered a handler
+    // and expect the event to be fired.
+    //event.stopPropagation();
+
+    if (this.activeLook) {
+      switch (event.button) {
+        case 0:
+          this.mouseDragOn = false;
+          this.azimuthOnMouseDown = this.azimuth;
+          this.zenithOnMouseDown = this.zenith;
+          break;
+        case 2:
+      }
+    }
+  };
+
+  this.onMouseMove = function (event) {
+    this.mousePosCurrent.set(event.pageX, event.pageY);
+  };
+
+  this.onKeyDown = function (event) {
+    if(this.keyBindingsEnabled === false) {
+      return;
+    }
+    switch(event.keyCode) {
+      case 38: /*up*/
+      case 87: /*W*/ this.moveForward = true; break;
+
+      case 37: /*left*/
+      case 65: /*A*/ this.moveLeft = true; break;
+
+      case 40: /*down*/
+      case 83: /*S*/ this.moveBackward = true; break;
+
+      case 39: /*right*/
+      case 68: /*D*/ this.moveRight = true; break;
+
+      case 33: /*page up*/
+      case 82: /*R*/ this.moveUp = true; break;
+
+      case 34: /*page down*/
+      case 70: /*F*/ this.moveDown = true; break;
+    }
+  };
+
+  this.onKeyUp = function (event) {
+    if(this.keyBindingsEnabled === false) {
+      return;
+    }
+    switch(event.keyCode) {
+      case 38: /*up*/
+      case 87: /*W*/ this.moveForward = false; break;
+
+      case 37: /*left*/
+      case 65: /*A*/ this.moveLeft = false; break;
+
+      case 40: /*down*/
+      case 83: /*S*/ this.moveBackward = false; break;
+
+      case 39: /*right*/
+      case 68: /*D*/ this.moveRight = false; break;
+
+      case 33: /*page up*/
+      case 82: /*R*/ this.moveUp = false; break;
+
+      case 34: /*page down*/
+      case 70: /*F*/ this.moveDown = false; break;
+
+      case 81: /*Q*/ this.freeze = !this.freeze; break;// TODO(Luc): handles this from gz3d-view.js with some visual indication that the scene is frozen
+    }
+  };
+
+  this.fpRotate = function(rightAmount, upAmount) {
+    // rotate left/right
+    // rotation happens around the world up axis so up remains up (no upside-down)
+    var camera = this.object;
+    var q = new THREE.Quaternion();
+    q.setFromAxisAngle(new THREE.Vector3(0.0, 0.0, 1.0), rightAmount);
+    camera.quaternion.multiplyQuaternions(q, camera.quaternion);
+    // rotate up/down
+    camera.rotateX(upAmount);
+  };
+
+  this.update = function(delta) {
+    if (!this.enabled) {
+      return;
+    }
+
+    if (delta === undefined) {
+      delta = 1.0;
+    }
+
+    var speed = 0.0;
+
+    if (!this.freeze) {
+      /* --- translation --- */
+      if (this.initPosition) {
+        this.object.position.copy(this.initialPosition);
+      }
+
+      speed = delta * this.movementSpeed;
+
+      if (this.moveForward) {
+        this.object.translateZ(-speed);
+      }
+      if (this.moveBackward) {
+        this.object.translateZ(speed);
+      }
+      if (this.moveLeft) {
+        this.object.translateX(-speed);
+      }
+      if (this.moveRight) {
+        this.object.translateX(speed);
+      }
+      if (this.moveUp) {
+        this.object.translateY(speed);
+      }
+      if (this.moveDown) {
+        this.object.translateY(-speed);
+      }
+
+      /* --- rotation by means of a manipulator --- */
+      var ROTATION_SPEED_FACTOR = 0.1;
+      if (this.rotateUp || this.rotateDown) {
+        var sign = this.rotateUp ? 1.0 : -1.0;
+        this.fpRotate(0.0, sign * ROTATION_SPEED_FACTOR * speed);
+      }
+      if (this.rotateRight) {
+        this.fpRotate(ROTATION_SPEED_FACTOR * speed, 0.0);
+      }
+      if (this.rotateLeft) {
+        this.fpRotate(-ROTATION_SPEED_FACTOR * speed, 0.0);
+      }
+      if (this.initRotation) {
+        this.object.quaternion.copy(this.initialRotation);
+      }
+
+      /* --- rotation by means of a mouse drag --- */
+      if (this.mouseDragOn) {
+        var actualLookSpeed = delta * this.lookSpeed;
+        if (!this.activeLook) {
+          actualLookSpeed = 0;
+        }
+
+        var mouseDelta = new THREE.Vector2();
+        mouseDelta.x = this.mousePosCurrent.x - this.mousePosOnKeyDown.x;
+        mouseDelta.y = this.mousePosCurrent.y - this.mousePosOnKeyDown.y;
+
+        this.azimuth = this.azimuthOnMouseDown - mouseDelta.x * actualLookSpeed;
+        this.azimuth = this.azimuth % (2 * Math.PI);
+
+        if (this.lookVertical) {
+          this.zenith = this.zenithOnMouseDown + mouseDelta.y * actualLookSpeed;
+          this.zenith = Math.max(this.zenithMin, Math.min(this.zenithMax, this.zenith));
+        } else {
+          this.zenith = Math.PI / 2;
+        }
+
+        var targetPosition = this.target, position = this.object.position;
+
+        targetPosition.x = position.x + Math.sin(this.zenith) * Math.cos(this.azimuth);
+        targetPosition.y = position.y + Math.sin(this.zenith) * Math.sin(this.azimuth);
+        targetPosition.z = position.z + Math.cos(this.zenith);
+
+        this.object.lookAt(targetPosition);
+      }
+    }
+  };
+
+  this.onMouseDownManipulator = function(action) {
+    this[action] = true;
+  };
+
+  this.onMouseUpManipulator = function(action) {
+    this[action] = false;
+  };
+
+  this.updateSphericalAngles = function() {
+    var vecForward = new THREE.Vector3();
+    vecForward.set(object.matrix.elements[8], object.matrix.elements[9], object.matrix.elements[10]);
+    vecForward.normalize();
+
+    this.zenith = Math.acos(-vecForward.z);
+    this.azimuth = Math.atan(vecForward.y / vecForward.x) + Math.PI;
+  };
+
+  function bind(scope, fn) {
+    return function () {
+      fn.apply(scope, arguments);
+    };
+  }
+
+  this.domElement.addEventListener('contextmenu', function (event) { event.preventDefault(); }, false);
+  this.domElement.addEventListener('mousemove', bind(this, this.onMouseMove), false);
+  this.domElement.addEventListener('mousedown', bind(this, this.onMouseDown), false);
+  this.domElement.addEventListener('mouseup', bind(this, this.onMouseUp), false);
+
+  domElementForKeyBindings.addEventListener('keydown', bind(this, this.onKeyDown), false);
+  domElementForKeyBindings.addEventListener('keyup', bind(this, this.onKeyUp), false);
+
+};
+
+THREE.FirstPersonControls.prototype = Object.create(THREE.EventDispatcher.prototype);
diff --git a/gz3d/client/js/include/OrbitControls.js b/gz3d/client/js/include/OrbitControls.js
index eb740cc..d2bbd7f 100644
--- a/gz3d/client/js/include/OrbitControls.js
+++ b/gz3d/client/js/include/OrbitControls.js
@@ -28,781 +28,781 @@
 
 THREE.OrbitControls = function (object, domElement)
 {
-  this.object = object;
-  this.domElement = (domElement !== undefined) ? domElement : document;
-
-  // API
-
-  // Set to false to disable this control
-  this.enabled = true;
-
-  // "target" sets the location of focus, where the control orbits around
-  // and where it pans with respect to.
-  this.target = new THREE.Vector3();
-  this.targetIndicator = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 20),
-      new THREE.MeshPhongMaterial({emissive: 0x333300,
-      ambient: 0xffff00,
-      shading: THREE.SmoothShading}));
-  this.targetIndicator.visible = false;
-  this.showTargetIndicator = false;
-  // center is old, deprecated; use "target" instead
-  this.center = this.target;
-  this.object.lookAt(this.target);
-  // This option actually enables dollying in and out; left as "zoom" for
-  // backwards compatibility
-  this.noZoom = false;
-  this.zoomSpeed = 1.0;
-  // Limits to how far you can dolly in and out
-  this.minDistance = 0;
-  this.maxDistance = Infinity;
-
-  // Set to true to disable this control
-  this.noRotate = false;
-  this.rotateSpeed = 1.0;
-
-  // Set to true to disable this control
-  this.noPan = false;
-  this.keyPanSpeed = 7.0;	// pixels moved per arrow key push
-
-  // Set to true to automatically rotate around the target
-  this.autoRotate = false;
-  this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
-
-  // How far you can orbit vertically, upper and lower limits.
-  // Range is 0 to Math.PI radians.
-  this.minPolarAngle = 0; // radians
-  this.maxPolarAngle = Math.PI; // radians
-
-  // Set to true to disable use of the keys
-  this.noKeys = false;
-  // The four arrow keys
-  this.keys = {LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40};
-
-  ////////////
-  // internals
-
-  var scope = this;
-
-  var EPS = 0.000001;
-
-  var rotateStart = new THREE.Vector2();
-  var rotateEnd = new THREE.Vector2();
-  var rotateDelta = new THREE.Vector2();
-
-  var panStart = new THREE.Vector2();
-  var panEnd = new THREE.Vector2();
-  var panDelta = new THREE.Vector2();
-
-  var dollyStart = new THREE.Vector2();
-  var dollyEnd = new THREE.Vector2();
-  var dollyDelta = new THREE.Vector2();
-
-  var phiDelta = 0;
-  var thetaDelta = 0;
-  var scale = 1;
-  var pan = new THREE.Vector3();
-
-  var lastPosition = new THREE.Vector3();
-
-  var STATE = {NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3,
-      TOUCH_DOLLY_PAN : 4};
-  var state = STATE.NONE;
-
-  var scrollTime = null;
-
-  // events
-
-  var changeEvent =
-  {
-    type: 'change'
-  };
-
-  this.rotateLeft = function (angle)
-  {
-    if (angle === undefined)
-    {
-      angle = getAutoRotationAngle();
-    }
-    thetaDelta -= angle;
-  };
-
-  this.rotateUp = function (angle)
-  {
-    if (angle === undefined)
-    {
-      angle = getAutoRotationAngle();
-    }
-    phiDelta -= angle;
-  };
-
-  // pass in distance in world space to move left
-  this.panLeft = function (distance)
-  {
-    var panOffset = new THREE.Vector3();
-    var te = this.object.matrix.elements;
-    // get X column of matrix
-    panOffset.set(te[0], te[1], te[2]);
-    panOffset.multiplyScalar(-distance);
-
-    pan.add(panOffset);
-  };
-
-  // pass in distance in world space to move up
-  this.panUp = function (distance)
-  {
-    var panOffset = new THREE.Vector3();
-    var te = this.object.matrix.elements;
-    // get Y column of matrix
-    panOffset.set(te[4], te[5], te[6]);
-    panOffset.multiplyScalar(distance);
-
-    pan.add(panOffset);
-  };
-
-  // main entry point; pass in Vector2 of change desired in pixel space,
-  // right and down are positive
-  this.pan = function (delta)
-  {
-    var element = scope.domElement === document ?
-        scope.domElement.body : scope.domElement;
-
-    if (scope.object.fov !== undefined)
-    {
-      // perspective
-      var position = scope.object.position;
-      var offset = position.clone().sub(scope.target);
-
-      var targetDistance = Math.max(offset.length(), 15);
-
-      // half of the fov is center to top of screen
-      targetDistance *= Math.tan((scope.object.fov/2) * Math.PI / 180.0);
-      // we actually don't use screenWidth, since perspective camera is fixed
-      // to screen height
-      scope.panLeft(2 * delta.x * targetDistance / element.clientHeight);
-      scope.panUp(2 * delta.y * targetDistance / element.clientHeight);
-    }
-    else if (scope.object.top !== undefined)
-    {
-      // orthographic
-      scope.panLeft(delta.x * (scope.object.right - scope.object.left) /
-          element.clientWidth);
-      scope.panUp(delta.y * (scope.object.top - scope.object.bottom) /
-          element.clientHeight);
-    }
-    else
-    {
-      // camera neither orthographic or perspective - warn user
-      console.warn('WARNING: OrbitControls.js encountered an unknown'+
-                   'camera type - pan disabled.');
-    }
-  };
-
-  this.dollyIn = function (dollyScale)
-  {
-    if (dollyScale === undefined)
-    {
-      dollyScale = getZoomScale();
-    }
-
-    scale /= dollyScale;
-  };
-
-  this.dollyOut = function (dollyScale)
-  {
-    if (dollyScale === undefined)
-    {
-      dollyScale = getZoomScale();
-    }
-
-    scale *= dollyScale;
-  };
-
-  this.update = function ()
-  {
-    var position = this.object.position;
-    var offset = position.clone().sub(this.target);
-
-    // angle from y-axis around z-axis
-    var theta = Math.atan2(offset.x, offset.y);
-
-    // angle from z-axis
-    var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y),
-        offset.z);
-
-    if (this.autoRotate)
-    {
-      this.rotateLeft(getAutoRotationAngle());
-    }
-
-    var oldTheta = theta;
-    var oldPhi = phi;
-
-    theta -= thetaDelta;
-    phi += phiDelta;
-
-    // restrict phi to be between desired limits
-    phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));
-
-    // restrict phi to be betwee EPS and PI-EPS
-    phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));
-
-    var radius = offset.length() * scale;
-
-    // restrict radius to be between desired limits
-    radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));
-
-    // move target to panned location
-    this.target.add(pan);
-
-    offset.x = radius * Math.sin(phi) * Math.sin(theta);
-    offset.z = radius * Math.cos(phi);
-    offset.y = radius * Math.sin(phi) * Math.cos(theta);
-
-    if (thetaDelta || phiDelta)
-    {
-      var rotateAroundWorldAxis = function (object, axis, radians)
-      {
-        rotWorldMatrix = new THREE.Matrix4();
-        rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
-        rotWorldMatrix.multiply(object.matrix); // pre-multiply
-
-        rotWorldMatrix.decompose(object.position,
-            object.quaternion, object.scale);
-        object.updateMatrix();
-      }
-
-      this.object.position.sub(this.target);
-      this.object.updateMatrix();
-      rotateAroundWorldAxis(this.object, new THREE.Vector3(0, 0, 1),
-          oldTheta - theta);
-      var localPitch = new THREE.Vector3(1, 0, 0);
-      localPitch.applyQuaternion(this.object.quaternion);
-      rotateAroundWorldAxis(this.object, localPitch, phi - oldPhi);
-      this.object.position.add(this.target);
-      this.object.updateMatrix();
-
-      /*var refObj = new THREE.Object3D();
-      refObj.position.x = this.target.x;
-      refObj.position.y = this.target.y;
-      refObj.position.z = this.target.z;
-      refObj.updateMatrix();
-      refObj.updateMatrixWorld();
-
-      var parent = this.object.parent;
-      if (parent)
-      {
-        parent.remove(this.object);
-     }
-
-      refObj.add(this.object);
-      this.object.position.x = offset.x;
-      this.object.position.y = offset.y;
-      this.object.position.z = offset.z;
-
-//      console.log (offset.x + ' ' + offset.y + ' ');
-      this.object.updateMatrix();
-      this.object.updateMatrixWorld();
-
-      var quat = new THREE.Quaternion();
-      quat.setFromEuler(new THREE.Vector3(theta, 0, phi));
-//      console.log('p t ' + phi + ' ' + theta);
-      refObj.quaternion.w = quat.w;
-      refObj.quaternion.x = quat.x;
-      refObj.quaternion.y = quat.y;
-      refObj.quaternion.z = quat.z;
-      refObj.updateMatrix();
-      refObj.updateMatrixWorld();
-
-      var matrixWorld = new THREE.Matrix4();
-      matrixWorld.copy(this.object.matrixWorld);
-
-      refObj.remove(this.object);
-      if (parent)
-        parent.add(this.object);
-
-
-      matrixWorld.decompose(this.object.position, this.object.quaternion,
-          this.object.scale);
-      console.log('p t ' + this.object.position.x + ' ' + this.object.position.y + ' ' +
-          this.object.position.z);
-
-//      this.object.matrxiWorld = matrixWorld;
-      this.object.updateMatrix();
-      this.object.updateMatrixWorld();*/
-      //this.object.lookAt(this.target);
-    }
-    else
-    {
-      position.copy(this.target).add(offset);
+	this.object = object;
+	this.domElement = (domElement !== undefined) ? domElement : document;
+
+	// API
+
+	// Set to false to disable this control
+	this.enabled = true;
+
+	// "target" sets the location of focus, where the control orbits around
+	// and where it pans with respect to.
+	this.target = new THREE.Vector3();
+	this.targetIndicator = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 20),
+		new THREE.MeshPhongMaterial({emissive: 0x333300,
+			color: 0xffff00,
+			shading: THREE.SmoothShading}));
+	this.targetIndicator.visible = false;
+	this.showTargetIndicator = false;
+	// center is old, deprecated; use "target" instead
+	this.center = this.target;
+	this.object.lookAt(this.target);
+	// This option actually enables dollying in and out; left as "zoom" for
+	// backwards compatibility
+	this.noZoom = false;
+	this.zoomSpeed = 1.0;
+	// Limits to how far you can dolly in and out
+	this.minDistance = 0;
+	this.maxDistance = Infinity;
+
+	// Set to true to disable this control
+	this.noRotate = false;
+	this.rotateSpeed = 1.0;
+
+	// Set to true to disable this control
+	this.noPan = false;
+	this.keyPanSpeed = 7.0;	// pixels moved per arrow key push
+
+	// Set to true to automatically rotate around the target
+	this.autoRotate = false;
+	this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+	// How far you can orbit vertically, upper and lower limits.
+	// Range is 0 to Math.PI radians.
+	this.minPolarAngle = 0; // radians
+	this.maxPolarAngle = Math.PI; // radians
+
+	// Set to true to disable use of the keys
+	this.noKeys = false;
+	// The four arrow keys
+	this.keys = {LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40};
+
+	////////////
+	// internals
+
+	var scope = this;
+
+	var EPS = 0.000001;
+
+	var rotateStart = new THREE.Vector2();
+	var rotateEnd = new THREE.Vector2();
+	var rotateDelta = new THREE.Vector2();
+
+	var panStart = new THREE.Vector2();
+	var panEnd = new THREE.Vector2();
+	var panDelta = new THREE.Vector2();
+
+	var dollyStart = new THREE.Vector2();
+	var dollyEnd = new THREE.Vector2();
+	var dollyDelta = new THREE.Vector2();
+
+	var phiDelta = 0;
+	var thetaDelta = 0;
+	var scale = 1;
+	var pan = new THREE.Vector3();
+
+	var lastPosition = new THREE.Vector3();
+
+	var STATE = {NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3,
+		TOUCH_DOLLY_PAN : 4};
+	var state = STATE.NONE;
+
+	var scrollTime = null;
+
+	// events
+
+	var changeEvent =
+	{
+		type: 'change'
+	};
+
+	this.rotateLeft = function (angle)
+	{
+		if (angle === undefined)
+		{
+			angle = getAutoRotationAngle();
+		}
+		thetaDelta -= angle;
+	};
+
+	this.rotateUp = function (angle)
+	{
+		if (angle === undefined)
+		{
+			angle = getAutoRotationAngle();
+		}
+		phiDelta -= angle;
+	};
+
+	// pass in distance in world space to move left
+	this.panLeft = function (distance)
+	{
+		var panOffset = new THREE.Vector3();
+		var te = this.object.matrix.elements;
+		// get X column of matrix
+		panOffset.set(te[0], te[1], te[2]);
+		panOffset.multiplyScalar(-distance);
+
+		pan.add(panOffset);
+	};
+
+	// pass in distance in world space to move up
+	this.panUp = function (distance)
+	{
+		var panOffset = new THREE.Vector3();
+		var te = this.object.matrix.elements;
+		// get Y column of matrix
+		panOffset.set(te[4], te[5], te[6]);
+		panOffset.multiplyScalar(distance);
+
+		pan.add(panOffset);
+	};
+
+	// main entry point; pass in Vector2 of change desired in pixel space,
+	// right and down are positive
+	this.pan = function (delta)
+	{
+		var element = scope.domElement === document ?
+			scope.domElement.body : scope.domElement;
+
+		if (scope.object.fov !== undefined)
+		{
+			// perspective
+			var position = scope.object.position;
+			var offset = position.clone().sub(scope.target);
+
+			var targetDistance = Math.max(offset.length(), 15);
+
+			// half of the fov is center to top of screen
+			targetDistance *= Math.tan((scope.object.fov/2) * Math.PI / 180.0);
+			// we actually don't use screenWidth, since perspective camera is fixed
+			// to screen height
+			scope.panLeft(2 * delta.x * targetDistance / element.clientHeight);
+			scope.panUp(2 * delta.y * targetDistance / element.clientHeight);
+		}
+		else if (scope.object.top !== undefined)
+		{
+			// orthographic
+			scope.panLeft(delta.x * (scope.object.right - scope.object.left) /
+			element.clientWidth);
+			scope.panUp(delta.y * (scope.object.top - scope.object.bottom) /
+			element.clientHeight);
+		}
+		else
+		{
+			// camera neither orthographic or perspective - warn user
+			console.warn('WARNING: OrbitControls.js encountered an unknown'+
+			'camera type - pan disabled.');
+		}
+	};
+
+	this.dollyIn = function (dollyScale)
+	{
+		if (dollyScale === undefined)
+		{
+			dollyScale = getZoomScale();
+		}
+
+		scale /= dollyScale;
+	};
+
+	this.dollyOut = function (dollyScale)
+	{
+		if (dollyScale === undefined)
+		{
+			dollyScale = getZoomScale();
+		}
+
+		scale *= dollyScale;
+	};
+
+	this.update = function ()
+	{
+		var position = this.object.position;
+		var offset = position.clone().sub(this.target);
+
+		// angle from y-axis around z-axis
+		var theta = Math.atan2(offset.x, offset.y);
+
+		// angle from z-axis
+		var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y),
+			offset.z);
+
+		if (this.autoRotate)
+		{
+			this.rotateLeft(getAutoRotationAngle());
+		}
+
+		var oldTheta = theta;
+		var oldPhi = phi;
+
+		theta -= thetaDelta;
+		phi += phiDelta;
+
+		// restrict phi to be between desired limits
+		phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));
+
+		// restrict phi to be betwee EPS and PI-EPS
+		phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));
+
+		var radius = offset.length() * scale;
+
+		// restrict radius to be between desired limits
+		radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));
+
+		// move target to panned location
+		this.target.add(pan);
+
+		offset.x = radius * Math.sin(phi) * Math.sin(theta);
+		offset.z = radius * Math.cos(phi);
+		offset.y = radius * Math.sin(phi) * Math.cos(theta);
+
+		if (thetaDelta || phiDelta)
+		{
+			var rotateAroundWorldAxis = function (object, axis, radians)
+			{
+				rotWorldMatrix = new THREE.Matrix4();
+				rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
+				rotWorldMatrix.multiply(object.matrix); // pre-multiply
+
+				rotWorldMatrix.decompose(object.position,
+					object.quaternion, object.scale);
+				object.updateMatrix();
+			}
+
+			this.object.position.sub(this.target);
+			this.object.updateMatrix();
+			rotateAroundWorldAxis(this.object, new THREE.Vector3(0, 0, 1),
+				oldTheta - theta);
+			var localPitch = new THREE.Vector3(1, 0, 0);
+			localPitch.applyQuaternion(this.object.quaternion);
+			rotateAroundWorldAxis(this.object, localPitch, phi - oldPhi);
+			this.object.position.add(this.target);
+			this.object.updateMatrix();
+
+			/*var refObj = new THREE.Object3D();
+			 refObj.position.x = this.target.x;
+			 refObj.position.y = this.target.y;
+			 refObj.position.z = this.target.z;
+			 refObj.updateMatrix();
+			 refObj.updateMatrixWorld();
+
+			 var parent = this.object.parent;
+			 if (parent)
+			 {
+			 parent.remove(this.object);
+			 }
+
+			 refObj.add(this.object);
+			 this.object.position.x = offset.x;
+			 this.object.position.y = offset.y;
+			 this.object.position.z = offset.z;
+
+			 //      console.log (offset.x + ' ' + offset.y + ' ');
+			 this.object.updateMatrix();
+			 this.object.updateMatrixWorld();
+
+			 var quat = new THREE.Quaternion();
+			 quat.setFromEuler(new THREE.Vector3(theta, 0, phi));
+			 //      console.log('p t ' + phi + ' ' + theta);
+			 refObj.quaternion.w = quat.w;
+			 refObj.quaternion.x = quat.x;
+			 refObj.quaternion.y = quat.y;
+			 refObj.quaternion.z = quat.z;
+			 refObj.updateMatrix();
+			 refObj.updateMatrixWorld();
+
+			 var matrixWorld = new THREE.Matrix4();
+			 matrixWorld.copy(this.object.matrixWorld);
+
+			 refObj.remove(this.object);
+			 if (parent)
+			 parent.add(this.object);
+
+
+			 matrixWorld.decompose(this.object.position, this.object.quaternion,
+			 this.object.scale);
+			 console.log('p t ' + this.object.position.x + ' ' + this.object.position.y + ' ' +
+			 this.object.position.z);
+
+			 //      this.object.matrxiWorld = matrixWorld;
+			 this.object.updateMatrix();
+			 this.object.updateMatrixWorld();*/
+			//this.object.lookAt(this.target);
+		}
+		else
+		{
+			position.copy(this.target).add(offset);
 //		  position.copy(newPos).add(offset);
-    }
-
-    //console.log(offset.x + ' ' + offset.y + ' ' +  offset.z);
-
-    thetaDelta = 0;
-    phiDelta = 0;
-    scale = 1;
-    pan.set(0,0,0);
-
-    if (lastPosition.distanceTo(this.object.position) > 0)
-    {
-      this.dispatchEvent(changeEvent);
-      lastPosition.copy(this.object.position);
-    }
-
-    if (scope.enabled === false)
-    {
-      setTargetIndicatorVisible(false)
-    }
-
-    var millisecs = new Date().getTime();
-    if (scrollTime && millisecs - scrollTime > 400)
-    {
-      setTargetIndicatorVisible(false)
-      scrollTime = null;
-    }
-
-    if (scope.targetIndicator.visible)
-    {
-      var scaleVec = new THREE.Vector3();
-      scaleVec.copy(object.position).sub(scope.targetIndicator.position);
-      var indicatorScale = scaleVec.length()/100;
-      scope.targetIndicator.scale.set(
-          indicatorScale, indicatorScale, indicatorScale);
-    }
-
-  };
-
-  function setTargetIndicatorVisible(visible)
-  {
-    scope.targetIndicator.visible = visible;
-    if (!scope.showTargetIndicator)
-    {
-      scope.targetIndicator.visible = false;
-    }
-  }
-
-  function getAutoRotationAngle()
-  {
-    return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
-  }
-
-  function getZoomScale()
-  {
-    return Math.pow(0.95, scope.zoomSpeed);
-  }
-
-  function onMouseDown(event)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-    event.preventDefault();
-
-    if (event.shiftKey && event.button === 0)
-    {
-      if (scope.noRotate === true)
-      {
-        return;
-      }
-      state = STATE.ROTATE;
-      rotateStart.set(event.clientX, event.clientY);
-    }
-    else if (event.button === 1)
-    {
-      if (scope.noRotate === true)
-      {
-        return;
-      }
-
-      state = STATE.ROTATE;
-
-      rotateStart.set(event.clientX, event.clientY);
-    }
-    else if (event.button === 2)
-    {
-      if (scope.noZoom === true)
-      {
-        return;
-      }
-
-      state = STATE.DOLLY;
-
-      dollyStart.set(event.clientX, event.clientY);
-    }
-    else if (event.button === 0)
-    {
-      if (scope.noPan === true)
-      {
-        return;
-      }
-
-      state = STATE.PAN;
-
-      panStart.set(event.clientX, event.clientY);
-    }
-    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
-        scope.target.z);
-    setTargetIndicatorVisible(true)
-
-    scope.domElement.addEventListener('mousemove', onMouseMove, false);
-    scope.domElement.addEventListener('mouseup', onMouseUp, false);
-  }
-
-  function onMouseMove(event)
-  {
-    if (scope.enabled === false)
-    {
-      scope.domElement.removeEventListener('mousemove', onMouseMove, false);
-      scope.domElement.removeEventListener('mouseup', onMouseUp, false);
-      return;
-    }
-
-    event.preventDefault();
-
-    var element = scope.domElement === document ? scope.domElement.body :
-        scope.domElement;
-
-    if (state === STATE.ROTATE)
-    {
-      if (scope.noRotate === true)
-      {
-        return;
-      }
-
-      rotateEnd.set(event.clientX, event.clientY);
-      rotateDelta.subVectors(rotateEnd, rotateStart);
-
-      // rotating across whole screen goes 360 degrees around
-      scope.rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth *
-          scope.rotateSpeed);
-      // rotating up and down along whole screen attempts to go 360,
-      // but limited to 180
-      scope.rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight *
-          scope.rotateSpeed);
-
-      rotateStart.copy(rotateEnd);
-    }
-    else if (state === STATE.DOLLY)
-    {
-      if (scope.noZoom === true)
-      {
-        return;
-      }
-
-      dollyEnd.set(event.clientX, event.clientY);
-      dollyDelta.subVectors(dollyEnd, dollyStart);
-
-      if (dollyDelta.y > 0)
-      {
-        scope.dollyIn();
-      }
-      else
-      {
-        scope.dollyOut();
-      }
-
-      dollyStart.copy(dollyEnd);
-    }
-    else if (state === STATE.PAN)
-    {
-      if (scope.noPan === true)
-      {
-        return;
-      }
-
-      panEnd.set(event.clientX, event.clientY);
-      panDelta.subVectors(panEnd, panStart);
-
-      scope.pan(panDelta);
-
-      panStart.copy(panEnd);
-    }
-
-    scope.update();
-  }
-
-  function onMouseUp(/* event */)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-
-    scope.domElement.removeEventListener('mousemove', onMouseMove, false);
-    scope.domElement.removeEventListener('mouseup', onMouseUp, false);
-
-    state = STATE.NONE;
-    setTargetIndicatorVisible(false)
-  }
-
-  function onMouseWheel(event)
-  {
-    if (scope.enabled === false || scope.noZoom === true)
-    {
-      return;
-    }
-
-    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
-        scope.target.z);
-    setTargetIndicatorVisible(true)
-    scrollTime = new Date().getTime();
-
-    var delta = 0;
-
-    if (event.wheelDelta) // WebKit / Opera / Explorer 9
-    {
-      delta = event.wheelDelta;
-    }
-    else if (event.detail) // Firefox
-    {
-      delta = - event.detail;
-    }
-
-    if (delta > 0)
-    {
-        scope.dollyOut();
-    }
-    else
-    {
-      scope.dollyIn();
-    }
-  }
-
-  function onKeyDown(event)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-    if (scope.noKeys === true)
-    {
-      return;
-    }
-    if (scope.noPan === true)
-    {
-      return;
-    }
-
-    var needUpdate = false;
-
-    switch (event.keyCode)
-    {
-      case scope.keys.UP:
-          scope.pan(new THREE.Vector2(0, scope.keyPanSpeed));
-          needUpdate = true;
-          break;
-      case scope.keys.BOTTOM:
-          scope.pan(new THREE.Vector2(0, -scope.keyPanSpeed));
-          needUpdate = true;
-          break;
-      case scope.keys.LEFT:
-          scope.pan(new THREE.Vector2(scope.keyPanSpeed, 0));
-          needUpdate = true;
-          break;
-      case scope.keys.RIGHT:
-          scope.pan(new THREE.Vector2(-scope.keyPanSpeed, 0));
-          needUpdate = true;
-          break;
-    }
-
-    if (needUpdate)
-    {
-      scope.update();
-    }
-  }
-
-  function touchstart(event)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-
-    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
-        scope.target.z);
-    setTargetIndicatorVisible(true)
-
-    switch (event.touches.length)
-    {
-      case 1:	// one-fingered touch: rotate
-          if (scope.noRotate === true)
-          {
-            return;
-          }
-
-          state = STATE.TOUCH_ROTATE;
-
-          rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
-          break;
-
-      case 2:	// two-fingered touch: dolly + pan
-          if (scope.noZoom === false)
-          {
-            state = STATE.TOUCH_DOLLY_PAN;
-
-            var dx = event.touches[0].pageX - event.touches[1].pageX;
-            var dy = event.touches[0].pageY - event.touches[1].pageY;
-            var distance = Math.sqrt(dx * dx + dy * dy);
-            dollyStart.set(0, distance);
-          }
-
-          if (scope.noPan === false)
-          {
-            var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
-            var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
-
-            panStart.set(panAvgX, panAvgY);
-          }
-          break;
-
-      default:
-          state = STATE.NONE;
-    }
-  }
-
-  function touchmove(event)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-
-    event.preventDefault();
-    event.stopPropagation();
-
-    var element = scope.domElement === document ? scope.domElement.body :
-        scope.domElement;
-
-    switch (event.touches.length)
-    {
-      case 1: // one-fingered touch: rotate
-          if (scope.noRotate === true)
-          {
-            return;
-          }
-          if (state !== STATE.TOUCH_ROTATE)
-          {
-            return;
-          }
-
-          rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
-          rotateDelta.subVectors(rotateEnd, rotateStart);
-
-          // rotating across whole screen goes 360 degrees around
-          scope.rotateLeft(2 * Math.PI * rotateDelta.x /
-              element.clientWidth * scope.rotateSpeed);
-          // rotating up and down along whole screen attempts to go 360,
-          // but limited to 180
-          scope.rotateUp(2 * Math.PI * rotateDelta.y /
-              element.clientHeight * scope.rotateSpeed);
-
-          rotateStart.copy(rotateEnd);
-          break;
-
-      case 2: // two-fingered touch: dolly + pan
-          if (state !== STATE.TOUCH_DOLLY_PAN)
-          {
-            return;
-          }
-
-          // Dolly delta
-          if (scope.noZoom === false)
-          {
-            var dx = event.touches[0].pageX - event.touches[1].pageX;
-            var dy = event.touches[0].pageY - event.touches[1].pageY;
-            var distance = Math.sqrt(dx * dx + dy * dy);
-
-            dollyEnd.set(0, distance);
-            dollyDelta.subVectors(dollyEnd, dollyStart);
-            var dollyAbs = Math.abs(dollyDelta.y);
-            dollyStart.copy(dollyEnd);
-          }
-
-          // Pan delta
-          if (scope.noPan === false)
-          {
-            var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
-            var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
-
-            panEnd.set(panAvgX, panAvgY);
-            panDelta.subVectors(panEnd, panStart);
-            var panAbs = Math.max(Math.abs(panDelta.x),Math.abs(panDelta.y));
-            panStart.copy(panEnd);
-          }
-
-          // Choose one
-          if (scope.noPan === false && scope.noZoom === false)
-          {
-            if (dollyAbs > panAbs)
-            {
-              // Only dolly
-              scope.noPan = true;
-            }
-            else
-            {
-              // Only pan
-              scope.noZoom = true;
-            }
-          }
-
-          // Dolly
-          if (scope.noZoom === false)
-          {
-            // Threshold
-            if (Math.abs(dollyDelta.y) > 1.3)
-            {
-              if (dollyDelta.y > 0)
-              {
-                scope.dollyOut();
-              }
-              else
-              {
-                scope.dollyIn();
-              }
-            }
-          }
-
-          // Pan
-          if (scope.noPan === false)
-          {
-            scope.pan(panDelta);
-          }
-          break;
-
-      default:
-          state = STATE.NONE;
-    }
-  }
-
-  function touchend(/* event */)
-  {
-    if (scope.enabled === false)
-    {
-      return;
-    }
-
-    state = STATE.NONE;
-    scope.noPan = false;
-    scope.noZoom = false;
-
-    setTargetIndicatorVisible(false)
-  }
-
-  this.domElement.addEventListener('contextmenu', function (event)
-      {
-        event.preventDefault();
-      }, false);
-  this.domElement.addEventListener('mousedown', onMouseDown, false);
-  this.domElement.addEventListener('mousewheel', onMouseWheel, false);
-  this.domElement.addEventListener('DOMMouseScroll', onMouseWheel, false); // firefox
-
-  this.domElement.addEventListener('keydown', onKeyDown, false);
-
-  this.domElement.addEventListener('touchstart', touchstart, false);
-  this.domElement.addEventListener('touchend', touchend, false);
-  this.domElement.addEventListener('touchmove', touchmove, false);
+		}
+
+		//console.log(offset.x + ' ' + offset.y + ' ' +  offset.z);
+
+		thetaDelta = 0;
+		phiDelta = 0;
+		scale = 1;
+		pan.set(0,0,0);
+
+		if (lastPosition.distanceTo(this.object.position) > 0)
+		{
+			this.dispatchEvent(changeEvent);
+			lastPosition.copy(this.object.position);
+		}
+
+		if (scope.enabled === false)
+		{
+			setTargetIndicatorVisible(false)
+		}
+
+		var millisecs = new Date().getTime();
+		if (scrollTime && millisecs - scrollTime > 400)
+		{
+			setTargetIndicatorVisible(false)
+			scrollTime = null;
+		}
+
+		if (scope.targetIndicator.visible)
+		{
+			var scaleVec = new THREE.Vector3();
+			scaleVec.copy(object.position).sub(scope.targetIndicator.position);
+			var indicatorScale = scaleVec.length()/100;
+			scope.targetIndicator.scale.set(
+				indicatorScale, indicatorScale, indicatorScale);
+		}
+
+	};
+
+	function setTargetIndicatorVisible(visible)
+	{
+		scope.targetIndicator.visible = visible;
+		if (!scope.showTargetIndicator)
+		{
+			scope.targetIndicator.visible = false;
+		}
+	}
+
+	function getAutoRotationAngle()
+	{
+		return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
+	}
+
+	function getZoomScale()
+	{
+		return Math.pow(0.95, scope.zoomSpeed);
+	}
+
+	function onMouseDown(event)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+		event.preventDefault();
+
+		if (event.shiftKey && event.button === 0)
+		{
+			if (scope.noRotate === true)
+			{
+				return;
+			}
+			state = STATE.ROTATE;
+			rotateStart.set(event.clientX, event.clientY);
+		}
+		else if (event.button === 1)
+		{
+			if (scope.noRotate === true)
+			{
+				return;
+			}
+
+			state = STATE.ROTATE;
+
+			rotateStart.set(event.clientX, event.clientY);
+		}
+		else if (event.button === 2)
+		{
+			if (scope.noZoom === true)
+			{
+				return;
+			}
+
+			state = STATE.DOLLY;
+
+			dollyStart.set(event.clientX, event.clientY);
+		}
+		else if (event.button === 0)
+		{
+			if (scope.noPan === true)
+			{
+				return;
+			}
+
+			state = STATE.PAN;
+
+			panStart.set(event.clientX, event.clientY);
+		}
+		scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+			scope.target.z);
+		setTargetIndicatorVisible(true)
+
+		scope.domElement.addEventListener('mousemove', onMouseMove, false);
+		scope.domElement.addEventListener('mouseup', onMouseUp, false);
+	}
+
+	function onMouseMove(event)
+	{
+		if (scope.enabled === false)
+		{
+			scope.domElement.removeEventListener('mousemove', onMouseMove, false);
+			scope.domElement.removeEventListener('mouseup', onMouseUp, false);
+			return;
+		}
+
+		event.preventDefault();
+
+		var element = scope.domElement === document ? scope.domElement.body :
+			scope.domElement;
+
+		if (state === STATE.ROTATE)
+		{
+			if (scope.noRotate === true)
+			{
+				return;
+			}
+
+			rotateEnd.set(event.clientX, event.clientY);
+			rotateDelta.subVectors(rotateEnd, rotateStart);
+
+			// rotating across whole screen goes 360 degrees around
+			scope.rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth *
+			scope.rotateSpeed);
+			// rotating up and down along whole screen attempts to go 360,
+			// but limited to 180
+			scope.rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight *
+			scope.rotateSpeed);
+
+			rotateStart.copy(rotateEnd);
+		}
+		else if (state === STATE.DOLLY)
+		{
+			if (scope.noZoom === true)
+			{
+				return;
+			}
+
+			dollyEnd.set(event.clientX, event.clientY);
+			dollyDelta.subVectors(dollyEnd, dollyStart);
+
+			if (dollyDelta.y > 0)
+			{
+				scope.dollyIn();
+			}
+			else
+			{
+				scope.dollyOut();
+			}
+
+			dollyStart.copy(dollyEnd);
+		}
+		else if (state === STATE.PAN)
+		{
+			if (scope.noPan === true)
+			{
+				return;
+			}
+
+			panEnd.set(event.clientX, event.clientY);
+			panDelta.subVectors(panEnd, panStart);
+
+			scope.pan(panDelta);
+
+			panStart.copy(panEnd);
+		}
+
+		scope.update();
+	}
+
+	function onMouseUp(/* event */)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+
+		scope.domElement.removeEventListener('mousemove', onMouseMove, false);
+		scope.domElement.removeEventListener('mouseup', onMouseUp, false);
+
+		state = STATE.NONE;
+		setTargetIndicatorVisible(false)
+	}
+
+	function onMouseWheel(event)
+	{
+		if (scope.enabled === false || scope.noZoom === true)
+		{
+			return;
+		}
+
+		scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+			scope.target.z);
+		setTargetIndicatorVisible(true)
+		scrollTime = new Date().getTime();
+
+		var delta = 0;
+
+		if (event.wheelDelta) // WebKit / Opera / Explorer 9
+		{
+			delta = event.wheelDelta;
+		}
+		else if (event.detail) // Firefox
+		{
+			delta = - event.detail;
+		}
+
+		if (delta > 0)
+		{
+			scope.dollyOut();
+		}
+		else
+		{
+			scope.dollyIn();
+		}
+	}
+
+	function onKeyDown(event)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+		if (scope.noKeys === true)
+		{
+			return;
+		}
+		if (scope.noPan === true)
+		{
+			return;
+		}
+
+		var needUpdate = false;
+
+		switch (event.keyCode)
+		{
+			case scope.keys.UP:
+				scope.pan(new THREE.Vector2(0, scope.keyPanSpeed));
+				needUpdate = true;
+				break;
+			case scope.keys.BOTTOM:
+				scope.pan(new THREE.Vector2(0, -scope.keyPanSpeed));
+				needUpdate = true;
+				break;
+			case scope.keys.LEFT:
+				scope.pan(new THREE.Vector2(scope.keyPanSpeed, 0));
+				needUpdate = true;
+				break;
+			case scope.keys.RIGHT:
+				scope.pan(new THREE.Vector2(-scope.keyPanSpeed, 0));
+				needUpdate = true;
+				break;
+		}
+
+		if (needUpdate)
+		{
+			scope.update();
+		}
+	}
+
+	function touchstart(event)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+
+		scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+			scope.target.z);
+		setTargetIndicatorVisible(true)
+
+		switch (event.touches.length)
+		{
+			case 1:	// one-fingered touch: rotate
+				if (scope.noRotate === true)
+				{
+					return;
+				}
+
+				state = STATE.TOUCH_ROTATE;
+
+				rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
+				break;
+
+			case 2:	// two-fingered touch: dolly + pan
+				if (scope.noZoom === false)
+				{
+					state = STATE.TOUCH_DOLLY_PAN;
+
+					var dx = event.touches[0].pageX - event.touches[1].pageX;
+					var dy = event.touches[0].pageY - event.touches[1].pageY;
+					var distance = Math.sqrt(dx * dx + dy * dy);
+					dollyStart.set(0, distance);
+				}
+
+				if (scope.noPan === false)
+				{
+					var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
+					var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
+
+					panStart.set(panAvgX, panAvgY);
+				}
+				break;
+
+			default:
+				state = STATE.NONE;
+		}
+	}
+
+	function touchmove(event)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		var element = scope.domElement === document ? scope.domElement.body :
+			scope.domElement;
+
+		switch (event.touches.length)
+		{
+			case 1: // one-fingered touch: rotate
+				if (scope.noRotate === true)
+				{
+					return;
+				}
+				if (state !== STATE.TOUCH_ROTATE)
+				{
+					return;
+				}
+
+				rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
+				rotateDelta.subVectors(rotateEnd, rotateStart);
+
+				// rotating across whole screen goes 360 degrees around
+				scope.rotateLeft(2 * Math.PI * rotateDelta.x /
+				element.clientWidth * scope.rotateSpeed);
+				// rotating up and down along whole screen attempts to go 360,
+				// but limited to 180
+				scope.rotateUp(2 * Math.PI * rotateDelta.y /
+				element.clientHeight * scope.rotateSpeed);
+
+				rotateStart.copy(rotateEnd);
+				break;
+
+			case 2: // two-fingered touch: dolly + pan
+				if (state !== STATE.TOUCH_DOLLY_PAN)
+				{
+					return;
+				}
+
+				// Dolly delta
+				if (scope.noZoom === false)
+				{
+					var dx = event.touches[0].pageX - event.touches[1].pageX;
+					var dy = event.touches[0].pageY - event.touches[1].pageY;
+					var distance = Math.sqrt(dx * dx + dy * dy);
+
+					dollyEnd.set(0, distance);
+					dollyDelta.subVectors(dollyEnd, dollyStart);
+					var dollyAbs = Math.abs(dollyDelta.y);
+					dollyStart.copy(dollyEnd);
+				}
+
+				// Pan delta
+				if (scope.noPan === false)
+				{
+					var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
+					var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
+
+					panEnd.set(panAvgX, panAvgY);
+					panDelta.subVectors(panEnd, panStart);
+					var panAbs = Math.max(Math.abs(panDelta.x),Math.abs(panDelta.y));
+					panStart.copy(panEnd);
+				}
+
+				// Choose one
+				if (scope.noPan === false && scope.noZoom === false)
+				{
+					if (dollyAbs > panAbs)
+					{
+						// Only dolly
+						scope.noPan = true;
+					}
+					else
+					{
+						// Only pan
+						scope.noZoom = true;
+					}
+				}
+
+				// Dolly
+				if (scope.noZoom === false)
+				{
+					// Threshold
+					if (Math.abs(dollyDelta.y) > 1.3)
+					{
+						if (dollyDelta.y > 0)
+						{
+							scope.dollyOut();
+						}
+						else
+						{
+							scope.dollyIn();
+						}
+					}
+				}
+
+				// Pan
+				if (scope.noPan === false)
+				{
+					scope.pan(panDelta);
+				}
+				break;
+
+			default:
+				state = STATE.NONE;
+		}
+	}
+
+	function touchend(/* event */)
+	{
+		if (scope.enabled === false)
+		{
+			return;
+		}
+
+		state = STATE.NONE;
+		scope.noPan = false;
+		scope.noZoom = false;
+
+		setTargetIndicatorVisible(false)
+	}
+
+	this.domElement.addEventListener('contextmenu', function (event)
+	{
+		event.preventDefault();
+	}, false);
+	this.domElement.addEventListener('mousedown', onMouseDown, false);
+	this.domElement.addEventListener('mousewheel', onMouseWheel, false);
+	this.domElement.addEventListener('DOMMouseScroll', onMouseWheel, false); // firefox
+
+	this.domElement.addEventListener('keydown', onKeyDown, false);
+
+	this.domElement.addEventListener('touchstart', touchstart, false);
+	this.domElement.addEventListener('touchend', touchend, false);
+	this.domElement.addEventListener('touchmove', touchmove, false);
 };
 
-THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
+THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
\ No newline at end of file
diff --git a/gz3d/client/js/include/Projector.js b/gz3d/client/js/include/Projector.js
new file mode 100644
index 0000000..f207472
--- /dev/null
+++ b/gz3d/client/js/include/Projector.js
@@ -0,0 +1,904 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author julianwa / https://github.com/julianwa
+ */
+
+THREE.RenderableObject = function () {
+
+	this.id = 0;
+
+	this.object = null;
+	this.z = 0;
+
+};
+
+//
+
+THREE.RenderableFace = function () {
+
+	this.id = 0;
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+	this.v3 = new THREE.RenderableVertex();
+
+	this.normalModel = new THREE.Vector3();
+
+	this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+	this.vertexNormalsLength = 0;
+
+	this.color = new THREE.Color();
+	this.material = null;
+	this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ];
+
+	this.z = 0;
+
+};
+
+//
+
+THREE.RenderableVertex = function () {
+
+	this.position = new THREE.Vector3();
+	this.positionWorld = new THREE.Vector3();
+	this.positionScreen = new THREE.Vector4();
+
+	this.visible = true;
+
+};
+
+THREE.RenderableVertex.prototype.copy = function ( vertex ) {
+
+	this.positionWorld.copy( vertex.positionWorld );
+	this.positionScreen.copy( vertex.positionScreen );
+
+};
+
+//
+
+THREE.RenderableLine = function () {
+
+	this.id = 0;
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+
+	this.vertexColors = [ new THREE.Color(), new THREE.Color() ];
+	this.material = null;
+
+	this.z = 0;
+
+};
+
+//
+
+THREE.RenderableSprite = function () {
+
+	this.id = 0;
+
+	this.object = null;
+
+	this.x = 0;
+	this.y = 0;
+	this.z = 0;
+
+	this.rotation = 0;
+	this.scale = new THREE.Vector2();
+
+	this.material = null;
+
+};
+
+//
+
+THREE.Projector = function () {
+
+	var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
+	_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
+	_face, _faceCount, _facePool = [], _facePoolLength = 0,
+	_line, _lineCount, _linePool = [], _linePoolLength = 0,
+	_sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0,
+
+	_renderData = { objects: [], lights: [], elements: [] },
+
+	_vector3 = new THREE.Vector3(),
+	_vector4 = new THREE.Vector4(),
+
+	_clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ),
+	_boundingBox = new THREE.Box3(),
+	_points3 = new Array( 3 ),
+	_points4 = new Array( 4 ),
+
+	_viewMatrix = new THREE.Matrix4(),
+	_viewProjectionMatrix = new THREE.Matrix4(),
+
+	_modelMatrix,
+	_modelViewProjectionMatrix = new THREE.Matrix4(),
+
+	_normalMatrix = new THREE.Matrix3(),
+
+	_frustum = new THREE.Frustum(),
+
+	_clippedVertex1PositionScreen = new THREE.Vector4(),
+	_clippedVertex2PositionScreen = new THREE.Vector4();
+
+	//
+
+	this.projectVector = function ( vector, camera ) {
+
+		console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
+		vector.project( camera );
+
+	};
+
+	this.unprojectVector = function ( vector, camera ) {
+
+		console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
+		vector.unproject( camera );
+
+	};
+
+	this.pickingRay = function ( vector, camera ) {
+
+		console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
+
+	};
+
+	//
+
+	var RenderList = function () {
+
+		var normals = [];
+		var uvs = [];
+
+		var object = null;
+		var material = null;
+
+		var normalMatrix = new THREE.Matrix3();
+
+		var setObject = function ( value ) {
+
+			object = value;
+			material = object.material;
+
+			normalMatrix.getNormalMatrix( object.matrixWorld );
+
+			normals.length = 0;
+			uvs.length = 0;
+
+		};
+
+		var projectVertex = function ( vertex ) {
+
+			var position = vertex.position;
+			var positionWorld = vertex.positionWorld;
+			var positionScreen = vertex.positionScreen;
+
+			positionWorld.copy( position ).applyMatrix4( _modelMatrix );
+			positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix );
+
+			var invW = 1 / positionScreen.w;
+
+			positionScreen.x *= invW;
+			positionScreen.y *= invW;
+			positionScreen.z *= invW;
+
+			vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 &&
+					 positionScreen.y >= - 1 && positionScreen.y <= 1 &&
+					 positionScreen.z >= - 1 && positionScreen.z <= 1;
+
+		};
+
+		var pushVertex = function ( x, y, z ) {
+
+			_vertex = getNextVertexInPool();
+			_vertex.position.set( x, y, z );
+
+			projectVertex( _vertex );
+
+		};
+
+		var pushNormal = function ( x, y, z ) {
+
+			normals.push( x, y, z );
+
+		};
+
+		var pushUv = function ( x, y ) {
+
+			uvs.push( x, y );
+
+		};
+
+		var checkTriangleVisibility = function ( v1, v2, v3 ) {
+
+			if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;
+
+			_points3[ 0 ] = v1.positionScreen;
+			_points3[ 1 ] = v2.positionScreen;
+			_points3[ 2 ] = v3.positionScreen;
+
+			return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) );
+
+		};
+
+		var checkBackfaceCulling = function ( v1, v2, v3 ) {
+
+			return ( ( v3.positionScreen.x - v1.positionScreen.x ) *
+				    ( v2.positionScreen.y - v1.positionScreen.y ) -
+				    ( v3.positionScreen.y - v1.positionScreen.y ) *
+				    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
+
+		};
+
+		var pushLine = function ( a, b ) {
+
+			var v1 = _vertexPool[ a ];
+			var v2 = _vertexPool[ b ];
+
+			_line = getNextLineInPool();
+
+			_line.id = object.id;
+			_line.v1.copy( v1 );
+			_line.v2.copy( v2 );
+			_line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2;
+
+			_line.material = object.material;
+
+			_renderData.elements.push( _line );
+
+		};
+
+		var pushTriangle = function ( a, b, c ) {
+
+			var v1 = _vertexPool[ a ];
+			var v2 = _vertexPool[ b ];
+			var v3 = _vertexPool[ c ];
+
+			if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
+
+			if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
+
+				_face = getNextFaceInPool();
+
+				_face.id = object.id;
+				_face.v1.copy( v1 );
+				_face.v2.copy( v2 );
+				_face.v3.copy( v3 );
+				_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
+
+				for ( var i = 0; i < 3; i ++ ) {
+
+					var offset = arguments[ i ] * 3;
+					var normal = _face.vertexNormalsModel[ i ];
+
+					normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] );
+					normal.applyMatrix3( normalMatrix ).normalize();
+
+					var offset2 = arguments[ i ] * 2;
+
+					var uv = _face.uvs[ i ];
+					uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] );
+
+				}
+
+				_face.vertexNormalsLength = 3;
+
+				_face.material = object.material;
+
+				_renderData.elements.push( _face );
+
+			}
+
+		};
+
+		return {
+			setObject: setObject,
+			projectVertex: projectVertex,
+			checkTriangleVisibility: checkTriangleVisibility,
+			checkBackfaceCulling: checkBackfaceCulling,
+			pushVertex: pushVertex,
+			pushNormal: pushNormal,
+			pushUv: pushUv,
+			pushLine: pushLine,
+			pushTriangle: pushTriangle
+		}
+
+	};
+
+	var renderList = new RenderList();
+
+	this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
+
+		_faceCount = 0;
+		_lineCount = 0;
+		_spriteCount = 0;
+
+		_renderData.elements.length = 0;
+
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
+		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
+
+		_frustum.setFromMatrix( _viewProjectionMatrix );
+
+		//
+
+		_objectCount = 0;
+
+		_renderData.objects.length = 0;
+		_renderData.lights.length = 0;
+
+		scene.traverseVisible( function ( object ) {
+
+			if ( object instanceof THREE.Light ) {
+
+				_renderData.lights.push( object );
+
+			} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) {
+
+				if ( object.material.visible === false ) return;
+
+				if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+
+					_object = getNextObjectInPool();
+					_object.id = object.id;
+					_object.object = object;
+
+					_vector3.setFromMatrixPosition( object.matrixWorld );
+					_vector3.applyProjection( _viewProjectionMatrix );
+					_object.z = _vector3.z;
+
+					_renderData.objects.push( _object );
+
+				}
+
+			}
+
+		} );
+
+		if ( sortObjects === true ) {
+
+			_renderData.objects.sort( painterSort );
+
+		}
+
+		//
+
+		for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
+
+			var object = _renderData.objects[ o ].object;
+			var geometry = object.geometry;
+
+			renderList.setObject( object );
+
+			_modelMatrix = object.matrixWorld;
+
+			_vertexCount = 0;
+
+			if ( object instanceof THREE.Mesh ) {
+
+				if ( geometry instanceof THREE.BufferGeometry ) {
+
+					var attributes = geometry.attributes;
+					var offsets = geometry.offsets;
+
+					if ( attributes.position === undefined ) continue;
+
+					var positions = attributes.position.array;
+
+					for ( var i = 0, l = positions.length; i < l; i += 3 ) {
+
+						renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+
+					}
+
+					if ( attributes.normal !== undefined ) {
+
+						var normals = attributes.normal.array;
+
+						for ( var i = 0, l = normals.length; i < l; i += 3 ) {
+
+							renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );
+
+						}
+
+					}
+
+					if ( attributes.uv !== undefined ) {
+
+						var uvs = attributes.uv.array;
+
+						for ( var i = 0, l = uvs.length; i < l; i += 2 ) {
+
+							renderList.pushUv( uvs[ i ], uvs[ i + 1 ] );
+
+						}
+
+					}
+
+					if ( attributes.index !== undefined ) {
+
+						var indices = attributes.index.array;
+
+						if ( offsets.length > 0 ) {
+
+							for ( var o = 0; o < offsets.length; o ++ ) {
+
+								var offset = offsets[ o ];
+								var index = offset.index;
+
+								for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) {
+
+									renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index );
+
+								}
+
+							}
+
+						} else {
+
+							for ( var i = 0, l = indices.length; i < l; i += 3 ) {
+
+								renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+
+							}
+
+						}
+
+					} else {
+
+						for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) {
+
+							renderList.pushTriangle( i, i + 1, i + 2 );
+
+						}
+
+					}
+
+				} else if ( geometry instanceof THREE.Geometry ) {
+
+					var vertices = geometry.vertices;
+					var faces = geometry.faces;
+					var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
+
+					_normalMatrix.getNormalMatrix( _modelMatrix );
+
+					var material = object.material;
+					
+					var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+					var objectMaterials = isFaceMaterial === true ? object.material : null;
+
+					for ( var v = 0, vl = vertices.length; v < vl; v ++ ) {
+
+						var vertex = vertices[ v ];
+
+						_vector3.copy( vertex );
+
+						if ( material.morphTargets === true ) {
+
+							var morphTargets = geometry.morphTargets;
+							var morphInfluences = object.morphTargetInfluences;
+
+							for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
+
+								var influence = morphInfluences[ t ];
+
+								if ( influence === 0 ) continue;
+
+								var target = morphTargets[ t ];
+								var targetVertex = target.vertices[ v ];
+
+								_vector3.x += ( targetVertex.x - vertex.x ) * influence;
+								_vector3.y += ( targetVertex.y - vertex.y ) * influence;
+								_vector3.z += ( targetVertex.z - vertex.z ) * influence;
+
+							}
+
+						}
+
+						renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z );
+
+					}
+
+					for ( var f = 0, fl = faces.length; f < fl; f ++ ) {
+
+						var face = faces[ f ];
+
+						var material = isFaceMaterial === true
+							 ? objectMaterials.materials[ face.materialIndex ]
+							 : object.material;
+
+						if ( material === undefined ) continue;
+
+						var side = material.side;
+
+						var v1 = _vertexPool[ face.a ];
+						var v2 = _vertexPool[ face.b ];
+						var v3 = _vertexPool[ face.c ];
+
+						if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue;
+
+						var visible = renderList.checkBackfaceCulling( v1, v2, v3 );
+
+						if ( side !== THREE.DoubleSide ) {
+							if ( side === THREE.FrontSide && visible === false ) continue;
+							if ( side === THREE.BackSide && visible === true ) continue;
+						}
+
+						_face = getNextFaceInPool();
+
+						_face.id = object.id;
+						_face.v1.copy( v1 );
+						_face.v2.copy( v2 );
+						_face.v3.copy( v3 );
+
+						_face.normalModel.copy( face.normal );
+
+						if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+
+							_face.normalModel.negate();
+
+						}
+
+						_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
+
+						var faceVertexNormals = face.vertexNormals;
+
+						for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) {
+
+							var normalModel = _face.vertexNormalsModel[ n ];
+							normalModel.copy( faceVertexNormals[ n ] );
+
+							if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+
+								normalModel.negate();
+
+							}
+
+							normalModel.applyMatrix3( _normalMatrix ).normalize();
+
+						}
+
+						_face.vertexNormalsLength = faceVertexNormals.length;
+
+						var vertexUvs = faceVertexUvs[ f ];
+
+						if ( vertexUvs !== undefined ) {
+
+							for ( var u = 0; u < 3; u ++ ) {
+
+								_face.uvs[ u ].copy( vertexUvs[ u ] );
+
+							}
+
+						}
+
+						_face.color = face.color;
+						_face.material = material;
+
+						_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
+
+						_renderData.elements.push( _face );
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				if ( geometry instanceof THREE.BufferGeometry ) {
+
+					var attributes = geometry.attributes;
+
+					if ( attributes.position !== undefined ) {
+
+						var positions = attributes.position.array;
+
+						for ( var i = 0, l = positions.length; i < l; i += 3 ) {
+
+							renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+
+						}
+
+						if ( attributes.index !== undefined ) {
+
+							var indices = attributes.index.array;
+
+							for ( var i = 0, l = indices.length; i < l; i += 2 ) {
+
+								renderList.pushLine( indices[ i ], indices[ i + 1 ] );
+
+							}
+
+						} else {
+
+							var step = object.mode === THREE.LinePieces ? 2 : 1;
+
+							for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
+
+								renderList.pushLine( i, i + 1 );
+
+							}
+
+						}
+
+					}
+
+				} else if ( geometry instanceof THREE.Geometry ) {
+
+					_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
+
+					var vertices = object.geometry.vertices;
+
+					if ( vertices.length === 0 ) continue;
+
+					v1 = getNextVertexInPool();
+					v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
+
+					// Handle LineStrip and LinePieces
+					var step = object.mode === THREE.LinePieces ? 2 : 1;
+
+					for ( var v = 1, vl = vertices.length; v < vl; v ++ ) {
+
+						v1 = getNextVertexInPool();
+						v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix );
+
+						if ( ( v + 1 ) % step > 0 ) continue;
+
+						v2 = _vertexPool[ _vertexCount - 2 ];
+
+						_clippedVertex1PositionScreen.copy( v1.positionScreen );
+						_clippedVertex2PositionScreen.copy( v2.positionScreen );
+
+						if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) {
+
+							// Perform the perspective divide
+							_clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w );
+							_clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w );
+
+							_line = getNextLineInPool();
+
+							_line.id = object.id;
+							_line.v1.positionScreen.copy( _clippedVertex1PositionScreen );
+							_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
+
+							_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
+
+							_line.material = object.material;
+
+							if ( object.material.vertexColors === THREE.VertexColors ) {
+
+								_line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] );
+								_line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] );
+
+							}
+
+							_renderData.elements.push( _line );
+
+						}
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Sprite ) {
+
+				_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );
+				_vector4.applyMatrix4( _viewProjectionMatrix );
+
+				var invW = 1 / _vector4.w;
+
+				_vector4.z *= invW;
+
+				if ( _vector4.z >= - 1 && _vector4.z <= 1 ) {
+
+					_sprite = getNextSpriteInPool();
+					_sprite.id = object.id;
+					_sprite.x = _vector4.x * invW;
+					_sprite.y = _vector4.y * invW;
+					_sprite.z = _vector4.z;
+					_sprite.object = object;
+
+					_sprite.rotation = object.rotation;
+
+					_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );
+					_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );
+
+					_sprite.material = object.material;
+
+					_renderData.elements.push( _sprite );
+
+				}
+
+			}
+
+		}
+
+		if ( sortElements === true ) {
+
+			_renderData.elements.sort( painterSort );
+
+		}
+
+		return _renderData;
+
+	};
+
+	// Pools
+
+	function getNextObjectInPool() {
+
+		if ( _objectCount === _objectPoolLength ) {
+
+			var object = new THREE.RenderableObject();
+			_objectPool.push( object );
+			_objectPoolLength ++;
+			_objectCount ++;
+			return object;
+
+		}
+
+		return _objectPool[ _objectCount ++ ];
+
+	}
+
+	function getNextVertexInPool() {
+
+		if ( _vertexCount === _vertexPoolLength ) {
+
+			var vertex = new THREE.RenderableVertex();
+			_vertexPool.push( vertex );
+			_vertexPoolLength ++;
+			_vertexCount ++;
+			return vertex;
+
+		}
+
+		return _vertexPool[ _vertexCount ++ ];
+
+	}
+
+	function getNextFaceInPool() {
+
+		if ( _faceCount === _facePoolLength ) {
+
+			var face = new THREE.RenderableFace();
+			_facePool.push( face );
+			_facePoolLength ++;
+			_faceCount ++;
+			return face;
+
+		}
+
+		return _facePool[ _faceCount ++ ];
+
+
+	}
+
+	function getNextLineInPool() {
+
+		if ( _lineCount === _linePoolLength ) {
+
+			var line = new THREE.RenderableLine();
+			_linePool.push( line );
+			_linePoolLength ++;
+			_lineCount ++
+			return line;
+
+		}
+
+		return _linePool[ _lineCount ++ ];
+
+	}
+
+	function getNextSpriteInPool() {
+
+		if ( _spriteCount === _spritePoolLength ) {
+
+			var sprite = new THREE.RenderableSprite();
+			_spritePool.push( sprite );
+			_spritePoolLength ++;
+			_spriteCount ++
+			return sprite;
+
+		}
+
+		return _spritePool[ _spriteCount ++ ];
+
+	}
+
+	//
+
+	function painterSort( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else if ( a.id !== b.id ) {
+
+			return a.id - b.id;
+
+		} else {
+
+			return 0;
+
+		}
+
+	}
+
+	function clipLine( s1, s2 ) {
+
+		var alpha1 = 0, alpha2 = 1,
+
+		// Calculate the boundary coordinate of each vertex for the near and far clip planes,
+		// Z = -1 and Z = +1, respectively.
+		bc1near =  s1.z + s1.w,
+		bc2near =  s2.z + s2.w,
+		bc1far =  - s1.z + s1.w,
+		bc2far =  - s2.z + s2.w;
+
+		if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
+
+			// Both vertices lie entirely within all clip planes.
+			return true;
+
+		} else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {
+
+			// Both vertices lie entirely outside one of the clip planes.
+			return false;
+
+		} else {
+
+			// The line segment spans at least one clip plane.
+
+			if ( bc1near < 0 ) {
+
+				// v1 lies outside the near plane, v2 inside
+				alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
+
+			} else if ( bc2near < 0 ) {
+
+				// v2 lies outside the near plane, v1 inside
+				alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
+
+			}
+
+			if ( bc1far < 0 ) {
+
+				// v1 lies outside the far plane, v2 inside
+				alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
+
+			} else if ( bc2far < 0 ) {
+
+				// v2 lies outside the far plane, v2 inside
+				alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
+
+			}
+
+			if ( alpha2 < alpha1 ) {
+
+				// The line segment spans two boundaries, but is outside both of them.
+				// (This can't happen when we're only clipping against just near/far but good
+				//  to leave the check here for future usage if other clip planes are added.)
+				return false;
+
+			} else {
+
+				// Update the s1 and s2 vertices to match the clipped line segment.
+				s1.lerp( s2, alpha1 );
+				s2.lerp( s1, 1 - alpha2 );
+
+				return true;
+
+			}
+
+		}
+
+	}
+
+};
diff --git a/gz3d/client/js/include/SSAOShader.js b/gz3d/client/js/include/SSAOShader.js
index 4dee71e..31c47ce 100644
--- a/gz3d/client/js/include/SSAOShader.js
+++ b/gz3d/client/js/include/SSAOShader.js
@@ -8,7 +8,6 @@
  *   original technique is made by ArKano22 (http://www.gamedev.net/topic/550699-ssao-no-halo-artifacts/)
  * - modifications
  * - modified to use RGBA packed depth texture (use clear color 1,1,1,1 for depth pass)
- * - made fog more compatible with three.js linear fog
  * - refactoring and optimizations
  */
 
@@ -21,12 +20,9 @@ THREE.SSAOShader = {
 		"size":         { type: "v2", value: new THREE.Vector2( 512, 512 ) },
 		"cameraNear":   { type: "f", value: 1 },
 		"cameraFar":    { type: "f", value: 100 },
-		"fogNear":      { type: "f", value: 5 },
-		"fogFar":       { type: "f", value: 100 },
-		"fogEnabled":   { type: "i", value: 0 },
 		"onlyAO":       { type: "i", value: 0 },
-		"aoClamp":      { type: "f", value: 0.3 },
-		"lumInfluence": { type: "f", value: 0.9 }
+		"aoClamp":      { type: "f", value: 0.5 },
+		"lumInfluence": { type: "f", value: 0.5 }
 
 	},
 
@@ -49,10 +45,6 @@ THREE.SSAOShader = {
 		"uniform float cameraNear;",
 		"uniform float cameraFar;",
 
-		"uniform float fogNear;",
-		"uniform float fogFar;",
-
-		"uniform bool fogEnabled;",  // attenuate AO with linear fog
 		"uniform bool onlyAO;",      // use only ambient occlusion pass?
 
 		"uniform vec2 size;",        // texture width, height
@@ -89,9 +81,6 @@ THREE.SSAOShader = {
 		"const float diffArea = 0.4;",   // self-shadowing reduction
 		"const float gDisplace = 0.4;",  // gauss bell center
 
-		"const vec3 onlyAOColor = vec3( 1.0, 0.7, 0.5 );",
-		// "const vec3 onlyAOColor = vec3( 1.0, 1.0, 1.0 );",
-
 
 		// RGBA depth
 
@@ -129,15 +118,6 @@ THREE.SSAOShader = {
 
 		"}",
 
-		"float doFog() {",
-
-			"float zdepth = unpackDepth( texture2D( tDepth, vUv ) );",
-			"float depth = -cameraFar * cameraNear / ( zdepth * cameraFarMinusNear - cameraFar );",
-
-			"return smoothstep( fogNear, fogFar, depth );",
-
-		"}",
-
 		"float readDepth( const in vec2 coord ) {",
 
 			// "return ( 2.0 * cameraNear ) / ( cameraFar + cameraNear - unpackDepth( texture2D( tDepth, coord ) ) * ( cameraFar - cameraNear ) );",
@@ -206,10 +186,7 @@ THREE.SSAOShader = {
 			"float w = ( 1.0 / width )  / tt + ( noise.x * ( 1.0 - noise.x ) );",
 			"float h = ( 1.0 / height ) / tt + ( noise.y * ( 1.0 - noise.y ) );",
 
-			"float pw;",
-			"float ph;",
-
-			"float ao;",
+			"float ao = 0.0;",
 
 			"float dz = 1.0 / float( samples );",
 			"float z = 1.0 - dz / 2.0;",
@@ -219,8 +196,8 @@ THREE.SSAOShader = {
 
 				"float r = sqrt( 1.0 - z );",
 
-				"pw = cos( l ) * r;",
-				"ph = sin( l ) * r;",
+				"float pw = cos( l ) * r;",
+				"float ph = sin( l ) * r;",
 				"ao += calcAO( depth, pw * w, ph * h );",
 				"z = z - dz;",
 				"l = l + DL;",
@@ -230,12 +207,6 @@ THREE.SSAOShader = {
 			"ao /= float( samples );",
 			"ao = 1.0 - ao;",
 
-			"if ( fogEnabled ) {",
-
-				"ao = mix( ao, 1.0, doFog() );",
-
-			"}",
-
 			"vec3 color = texture2D( tDiffuse, vUv ).rgb;",
 
 			"vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );",
@@ -246,7 +217,7 @@ THREE.SSAOShader = {
 
 			"if ( onlyAO ) {",
 
-				"final = onlyAOColor * vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );",  // ambient occlusion only
+				"final = vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );",  // ambient occlusion only
 
 			"}",
 
diff --git a/gz3d/client/js/include/ShaderPass.js b/gz3d/client/js/include/ShaderPass.js
index e8531f4..eb3e33e 100644
--- a/gz3d/client/js/include/ShaderPass.js
+++ b/gz3d/client/js/include/ShaderPass.js
@@ -10,6 +10,7 @@ THREE.ShaderPass = function ( shader, textureID ) {
 
 	this.material = new THREE.ShaderMaterial( {
 
+        	defines: shader.defines || {},
 		uniforms: this.uniforms,
 		vertexShader: shader.vertexShader,
 		fragmentShader: shader.fragmentShader
@@ -22,6 +23,13 @@ THREE.ShaderPass = function ( shader, textureID ) {
 	this.needsSwap = true;
 	this.clear = false;
 
+
+	this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
+	this.scene  = new THREE.Scene();
+
+	this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
+	this.scene.add( this.quad );
+
 };
 
 THREE.ShaderPass.prototype = {
@@ -34,15 +42,15 @@ THREE.ShaderPass.prototype = {
 
 		}
 
-		THREE.EffectComposer.quad.material = this.material;
+		this.quad.material = this.material;
 
 		if ( this.renderToScreen ) {
 
-			renderer.render( THREE.EffectComposer.scene, THREE.EffectComposer.camera );
+			renderer.render( this.scene, this.camera );
 
 		} else {
 
-			renderer.render( THREE.EffectComposer.scene, THREE.EffectComposer.camera, writeBuffer, this.clear );
+			renderer.render( this.scene, this.camera, writeBuffer, this.clear );
 
 		}
 
diff --git a/gz3d/client/js/include/ThreeBackwardsCompatibility.js b/gz3d/client/js/include/ThreeBackwardsCompatibility.js
new file mode 100644
index 0000000..2260fda
--- /dev/null
+++ b/gz3d/client/js/include/ThreeBackwardsCompatibility.js
@@ -0,0 +1,22 @@
+/**
+ * Created by Sandro Weber (webers@in.tum.de) on 07.08.15.
+ */
+
+
+/**
+ * getDescendants() was removed in r68, this reimplements it to ensure compatibility
+ * all getDescendants() calls could eventually be replaced with Object3D.traverse(function(node){...});
+ */
+THREE.Object3D.prototype.getDescendants = function ( array ) {
+    if ( array === undefined ) array = [];
+
+    Array.prototype.push.apply( array, this.children );
+
+    for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+        this.children[ i ].getDescendants( array );
+
+    }
+
+    return array;
+};
\ No newline at end of file
diff --git a/gz3d/client/js/include/TrackballControls.js b/gz3d/client/js/include/TrackballControls.js
index 3fb0f51..4525da1 100644
--- a/gz3d/client/js/include/TrackballControls.js
+++ b/gz3d/client/js/include/TrackballControls.js
@@ -1,11 +1,14 @@
 /**
  * @author Eberhard Graether / http://egraether.com/
+ * @author Mark Lundin 	/ http://mark-lundin.com
+ * @author Simone Manini / http://daron1337.github.io
+ * @author Luca Antiga 	/ http://lantiga.github.io
  */
 
 THREE.TrackballControls = function ( object, domElement ) {
 
 	var _this = this;
-	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
+	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
 
 	this.object = object;
 	this.domElement = ( domElement !== undefined ) ? domElement : document;
@@ -23,7 +26,6 @@ THREE.TrackballControls = function ( object, domElement ) {
 	this.noRotate = false;
 	this.noZoom = false;
 	this.noPan = false;
-	this.noRoll = false;
 
 	this.staticMoving = false;
 	this.dynamicDampingFactor = 0.2;
@@ -37,6 +39,8 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	this.target = new THREE.Vector3();
 
+	var EPS = 0.000001;
+
 	var lastPosition = new THREE.Vector3();
 
 	var _state = STATE.NONE,
@@ -44,8 +48,11 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	_eye = new THREE.Vector3(),
 
-	_rotateStart = new THREE.Vector3(),
-	_rotateEnd = new THREE.Vector3(),
+	_movePrev = new THREE.Vector2(),
+	_moveCurr = new THREE.Vector2(),
+
+	_lastAxis = new THREE.Vector3(),
+	_lastAngle = 0,
 
 	_zoomStart = new THREE.Vector2(),
 	_zoomEnd = new THREE.Vector2(),
@@ -65,6 +72,8 @@ THREE.TrackballControls = function ( object, domElement ) {
 	// events
 
 	var changeEvent = { type: 'change' };
+	var startEvent = { type: 'start' };
+	var endEvent = { type: 'end' };
 
 
 	// methods
@@ -80,7 +89,13 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		} else {
 
-			this.screen = this.domElement.getBoundingClientRect();
+			var box = this.domElement.getBoundingClientRect();
+			// adjustments come from similar code in the jquery offset() function
+			var d = this.domElement.ownerDocument.documentElement;
+			this.screen.left = box.left + window.pageXOffset - d.clientLeft;
+			this.screen.top = box.top + window.pageYOffset - d.clientTop;
+			this.screen.width = box.width;
+			this.screen.height = box.height;
 
 		}
 
@@ -96,101 +111,110 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	};
 
-	this.getMouseOnScreen = function ( clientX, clientY ) {
+	var getMouseOnScreen = ( function () {
 
-		return new THREE.Vector2(
-			( clientX - _this.screen.left ) / _this.screen.width,
-			( clientY - _this.screen.top ) / _this.screen.height
-		);
-
-	};
+		var vector = new THREE.Vector2();
 
-	this.getMouseProjectionOnBall = function ( clientX, clientY ) {
+		return function ( pageX, pageY ) {
 
-		var mouseOnBall = new THREE.Vector3(
-			( clientX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
-			( _this.screen.height * 0.5 + _this.screen.top - clientY ) / (_this.screen.height*.5),
-			0.0
-		);
+			vector.set(
+				( pageX - _this.screen.left ) / _this.screen.width,
+				( pageY - _this.screen.top ) / _this.screen.height
+			);
 
-		var length = mouseOnBall.length();
+			return vector;
 
-		if ( _this.noRoll ) {
+		};
 
-			if ( length < Math.SQRT1_2 ) {
+	}() );
 
-				mouseOnBall.z = Math.sqrt( 1.0 - length*length );
+	var getMouseOnCircle = ( function () {
 
-			} else {
+		var vector = new THREE.Vector2();
 
-				mouseOnBall.z = .5 / length;
-				
-			}
+		return function ( pageX, pageY ) {
 
-		} else if ( length > 1.0 ) {
+			vector.set(
+				( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ),
+				( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional
+			);
 
-			mouseOnBall.normalize();
+			return vector;
+		};
 
-		} else {
+	}() );
 
-			mouseOnBall.z = Math.sqrt( 1.0 - length * length );
+	this.rotateCamera = (function() {
 
-		}
+		var axis = new THREE.Vector3(),
+			quaternion = new THREE.Quaternion(),
+			eyeDirection = new THREE.Vector3(),
+			objectUpDirection = new THREE.Vector3(),
+			objectSidewaysDirection = new THREE.Vector3(),
+			moveDirection = new THREE.Vector3(),
+			angle;
 
-		_eye.copy( _this.object.position ).sub( _this.target );
+		return function () {
 
-		var projection = _this.object.up.clone().setLength( mouseOnBall.y );
-		projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
-		projection.add( _eye.setLength( mouseOnBall.z ) );
+			moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
+			angle = moveDirection.length();
 
-		return projection;
+			if ( angle ) {
 
-	};
+				_eye.copy( _this.object.position ).sub( _this.target );
 
-	this.rotateCamera = function () {
+				eyeDirection.copy( _eye ).normalize();
+				objectUpDirection.copy( _this.object.up ).normalize();
+				objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
 
-		var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
+				objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
+				objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
 
-		if ( angle ) {
+				moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
 
-			var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize(),
-				quaternion = new THREE.Quaternion();
+				axis.crossVectors( moveDirection, _eye ).normalize();
 
-			angle *= _this.rotateSpeed;
+				angle *= _this.rotateSpeed;
+				quaternion.setFromAxisAngle( axis, angle );
 
-			quaternion.setFromAxisAngle( axis, -angle );
+				_eye.applyQuaternion( quaternion );
+				_this.object.up.applyQuaternion( quaternion );
 
-			_eye.applyQuaternion( quaternion );
-			_this.object.up.applyQuaternion( quaternion );
+				_lastAxis.copy( axis );
+				_lastAngle = angle;
 
-			_rotateEnd.applyQuaternion( quaternion );
+			}
 
-			if ( _this.staticMoving ) {
+			else if ( !_this.staticMoving && _lastAngle ) {
 
-				_rotateStart.copy( _rotateEnd );
+				_lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor );
+				_eye.copy( _this.object.position ).sub( _this.target );
+				quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
+				_eye.applyQuaternion( quaternion );
+				_this.object.up.applyQuaternion( quaternion );
 
-			} else {
+			}
 
-				quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
-				_rotateStart.applyQuaternion( quaternion );
+			_movePrev.copy( _moveCurr );
 
-			}
+		};
 
-		}
+	}());
 
-	};
 
 	this.zoomCamera = function () {
 
-		if ( _state === STATE.TOUCH_ZOOM ) {
+		var factor;
+
+		if ( _state === STATE.TOUCH_ZOOM_PAN ) {
 
-			var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+			factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
 			_touchZoomDistanceStart = _touchZoomDistanceEnd;
 			_eye.multiplyScalar( factor );
 
 		} else {
 
-			var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
+			factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
 
 			if ( factor !== 1.0 && factor > 0.0 ) {
 
@@ -212,33 +236,40 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	};
 
-	this.panCamera = function () {
+	this.panCamera = (function() {
 
-		var mouseChange = _panEnd.clone().sub( _panStart );
+		var mouseChange = new THREE.Vector2(),
+			objectUp = new THREE.Vector3(),
+			pan = new THREE.Vector3();
 
-		if ( mouseChange.lengthSq() ) {
+		return function () {
 
-			mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
+			mouseChange.copy( _panEnd ).sub( _panStart );
 
-			var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
-			pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
+			if ( mouseChange.lengthSq() ) {
 
-			_this.object.position.add( pan );
-			_this.target.add( pan );
+				mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
 
-			if ( _this.staticMoving ) {
+				pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
+				pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
 
-				_panStart = _panEnd;
+				_this.object.position.add( pan );
+				_this.target.add( pan );
 
-			} else {
+				if ( _this.staticMoving ) {
 
-				_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
+					_panStart.copy( _panEnd );
 
-			}
+				} else {
 
-		}
+					_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
 
-	};
+				}
+
+			}
+		};
+
+	}());
 
 	this.checkDistances = function () {
 
@@ -288,7 +319,7 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		_this.object.lookAt( _this.target );
 
-		if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
+		if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
 
 			_this.dispatchEvent( changeEvent );
 
@@ -372,24 +403,26 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		if ( _state === STATE.ROTATE && !_this.noRotate ) {
 
-			_rotateStart = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
-			_rotateEnd.copy(_rotateStart)
+			_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
+			_movePrev.copy(_moveCurr);
 
 		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
 
-			_zoomStart = _this.getMouseOnScreen( event.clientX, event.clientY );
+			_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 			_zoomEnd.copy(_zoomStart);
 
 		} else if ( _state === STATE.PAN && !_this.noPan ) {
 
-			_panStart = _this.getMouseOnScreen( event.clientX, event.clientY );
-			_panEnd.copy(_panStart)
+			_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+			_panEnd.copy(_panStart);
 
 		}
 
 		document.addEventListener( 'mousemove', mousemove, false );
 		document.addEventListener( 'mouseup', mouseup, false );
 
+		_this.dispatchEvent( startEvent );
+
 	}
 
 	function mousemove( event ) {
@@ -401,15 +434,16 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		if ( _state === STATE.ROTATE && !_this.noRotate ) {
 
-			_rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+			_movePrev.copy(_moveCurr);
+			_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
 
 		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
 
-			_zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+			_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
 		} else if ( _state === STATE.PAN && !_this.noPan ) {
 
-			_panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+			_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
 		}
 
@@ -426,6 +460,7 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		document.removeEventListener( 'mousemove', mousemove );
 		document.removeEventListener( 'mouseup', mouseup );
+		_this.dispatchEvent( endEvent );
 
 	}
 
@@ -449,6 +484,8 @@ THREE.TrackballControls = function ( object, domElement ) {
 		}
 
 		_zoomStart.y += delta * 0.01;
+		_this.dispatchEvent( startEvent );
+		_this.dispatchEvent( endEvent );
 
 	}
 
@@ -460,25 +497,28 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 			case 1:
 				_state = STATE.TOUCH_ROTATE;
-				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+				_movePrev.copy(_moveCurr);
 				break;
 
 			case 2:
-				_state = STATE.TOUCH_ZOOM;
+				_state = STATE.TOUCH_ZOOM_PAN;
 				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
 				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
 				_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
-				break;
 
-			case 3:
-				_state = STATE.TOUCH_PAN;
-				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panStart.copy( getMouseOnScreen( x, y ) );
+				_panEnd.copy( _panStart );
 				break;
 
 			default:
 				_state = STATE.NONE;
 
 		}
+		_this.dispatchEvent( startEvent );
+
 
 	}
 
@@ -492,17 +532,18 @@ THREE.TrackballControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 
 			case 1:
-				_rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				_movePrev.copy(_moveCurr);
+				_moveCurr.copy( getMouseOnCircle(  event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 				break;
 
 			case 2:
 				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
 				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
-				break;
+				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
 
-			case 3:
-				_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panEnd.copy( getMouseOnScreen( x, y ) );
 				break;
 
 			default:
@@ -519,20 +560,23 @@ THREE.TrackballControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 
 			case 1:
-				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				_movePrev.copy(_moveCurr);
+				_moveCurr.copy( getMouseOnCircle(  event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 				break;
 
 			case 2:
 				_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
-				break;
 
-			case 3:
-				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panEnd.copy( getMouseOnScreen( x, y ) );
+				_panStart.copy( _panEnd );
 				break;
 
 		}
 
 		_state = STATE.NONE;
+		_this.dispatchEvent( endEvent );
 
 	}
 
@@ -552,6 +596,10 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	this.handleResize();
 
+	// force an update at start
+	this.update();
+
 };
 
 THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
+THREE.TrackballControls.prototype.constructor = THREE.TrackballControls;
diff --git a/gz3d/client/js/include/TransformControls.js b/gz3d/client/js/include/TransformControls.js
index 7688c0b..a87b41a 100644
--- a/gz3d/client/js/include/TransformControls.js
+++ b/gz3d/client/js/include/TransformControls.js
@@ -1,658 +1,781 @@
 /**
  * @author arodic / https://github.com/arodic
  */
+ /*jshint sub:true*/
 
- //"use strict";
+(function () {
 
-THREE.TransformControls = function ( camera, domElement, doc ) {
+	'use strict';
 
-	// TODO: Make non-uniform scale and rotate play nice in hierarchies
-	// TODO: ADD RXYZ contol
+	var GizmoMaterial = function ( parameters ) {
 
-	this.camera = camera;
-	this.domElement = ( domElement !== undefined ) ? domElement : document;
-	this.document = ( doc !== undefined ) ? doc : document;
+		THREE.MeshBasicMaterial.call( this );
 
-	this.object = undefined;
+		this.depthTest = false;
+		this.depthWrite = false;
+		this.side = THREE.FrontSide;
+		this.transparent = true;
 
-	this.active = false;
-	this.hovered = false;
+		this.setValues( parameters );
 
-	this.mode = 'translate';
-	this.space = 'world';
-	this.scale = 1;
+		this.oldColor = this.color.clone();
+		this.oldOpacity = this.opacity;
 
-	this.snapDist = null;
-	this.modifierAxis = new THREE.Vector3( 1, 1, 1 );
-	this.gizmo = new THREE.Object3D();
+		this.highlight = function( highlighted ) {
 
-	var scope = this;
+			if ( highlighted ) {
 
-	var changeEvent = { type: 'change' };
+				this.color.setRGB( 1, 1, 0 );
+				this.opacity = 1;
 
-	var showPickers = false; // debug
+			} else {
 
-	var ray = new THREE.Raycaster();
-	var projector = new THREE.Projector();
-	var pointerVector = new THREE.Vector3();
+				this.color.copy( this.oldColor );
+				this.opacity = this.oldOpacity;
 
-	var point = new THREE.Vector3();
-	var offset = new THREE.Vector3();
+			}
 
-	var rotation = new THREE.Vector3();
-	var offsetRotation = new THREE.Vector3();
-	var scale = 1;
+		};
 
-	var lookAtMatrix = new THREE.Matrix4();
-	var eye = new THREE.Vector3()
+	};
 
-	var tempMatrix = new THREE.Matrix4();
-	var tempVector = new THREE.Vector3();
-	var tempQuaternion = new THREE.Quaternion();
-	var unitX = new THREE.Vector3( 1, 0, 0 );
-	var unitY = new THREE.Vector3( 0, 1, 0 );
-	var unitZ = new THREE.Vector3( 0, 0, 1 );
+	GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype );
+	GizmoMaterial.prototype.constructor = GizmoMaterial;
 
-	var quaternionXYZ = new THREE.Quaternion();
-	var quaternionX = new THREE.Quaternion();
-	var quaternionY = new THREE.Quaternion();
-	var quaternionZ = new THREE.Quaternion();
-	var quaternionE = new THREE.Quaternion();
+	var GizmoLineMaterial = function ( parameters ) {
 
-	var oldPosition = new THREE.Vector3();
-	var oldScale = new THREE.Vector3();
-	var oldRotationMatrix = new THREE.Matrix4();
+		THREE.LineBasicMaterial.call( this );
 
-	var parentRotationMatrix  = new THREE.Matrix4();
-	var parentScale = new THREE.Vector3();
+		this.depthTest = false;
+		this.depthWrite = false;
+		this.transparent = true;
+		this.linewidth = 1;
 
-	var worldPosition = new THREE.Vector3();
-//	var worldRotation = new THREE.Euler();
-	var worldRotation = new THREE.Vector3();
-	var worldRotationMatrix  = new THREE.Matrix4();
-	var camPosition = new THREE.Vector3();
-//	var camRotation = new THREE.Euler();
-	var camRotation = new THREE.Vector3();
+		this.setValues( parameters );
 
-	var displayAxes = {};
-	var pickerAxes = {};
-	var intersectionPlanes = {};
-	var intersectionPlaneList = ['XY','YZ','XZ','XYZE']; // E
-	var currentPlane = 'XY';
+		this.oldColor = this.color.clone();
+		this.oldOpacity = this.opacity;
 
-	// intersection planes
-	{
+		this.highlight = function( highlighted ) {
 
-		var planes = new THREE.Object3D();
-		this.gizmo.add(planes);
+			if ( highlighted ) {
 
-		for ( var i in intersectionPlaneList ){
+				this.color.setRGB( 1, 1, 0 );
+				this.opacity = 1;
 
-			intersectionPlanes[intersectionPlaneList[i]] = new THREE.Mesh( new THREE.PlaneGeometry( 500, 500 ) );
-			intersectionPlanes[intersectionPlaneList[i]].material.side = THREE.DoubleSide;
-			intersectionPlanes[intersectionPlaneList[i]].visible = false;
-			planes.add(intersectionPlanes[intersectionPlaneList[i]]);
+			} else {
 
-		}
+				this.color.copy( this.oldColor );
+				this.opacity = this.oldOpacity;
 
-		intersectionPlanes['YZ'].rotation.set( 0, Math.PI/2, 0 );
-		intersectionPlanes['XZ'].rotation.set( -Math.PI/2, 0, 0 );
-		bakeTransformations(intersectionPlanes['YZ']);
-		bakeTransformations(intersectionPlanes['XZ']);
-
-	}
-
-	// gizmo geometry
-	{
-
-		displayAxes["translate"] = new THREE.Object3D();
-		displayAxes["rotate"] = new THREE.Object3D();
-		displayAxes["scale"] = new THREE.Object3D();
-		this.gizmo.add( displayAxes["translate"] );
-		this.gizmo.add( displayAxes["rotate"] );
-		this.gizmo.add( displayAxes["scale"] );
-
-		pickerAxes["translate"] = new THREE.Object3D();
-		pickerAxes["rotate"] = new THREE.Object3D();
-		pickerAxes["scale"] = new THREE.Object3D();
-		this.gizmo.add( pickerAxes["translate"] );
-		this.gizmo.add( pickerAxes["rotate"] );
-		this.gizmo.add( pickerAxes["scale"] );
-
-		var HandleMaterial = function ( color, opacity ) {
-			var material = new THREE.MeshBasicMaterial();
-			material.color = color;
-			material.side = THREE.DoubleSide;
-			material.depthTest = false;
-			material.depthWrite = false;
-			material.opacity = opacity !== undefined ? opacity : 1;
-			material.transparent = true;
-			return material;
-		}
+			}
 
-		var LineMaterial = function ( color, opacity ) {
-			var material = new THREE.LineBasicMaterial();
-			material.color = color;
-			material.depthTest = false;
-			material.depthWrite = false;
-			material.opacity = opacity !== undefined ? opacity : 1;
-			material.transparent = true;
-			return material;
-		}
+		};
 
-		// materials by color
-		var white = new THREE.Color( 0xffffff );
-		var gray = new THREE.Color( 0x808080 );
-		var red = new THREE.Color( 0xff0000 );
-		var green = new THREE.Color( 0x00ff00 );
-		var blue = new THREE.Color( 0x0000ff );
-		var cyan = new THREE.Color( 0x00ffff );
-		var magenta = new THREE.Color( 0xff00ff );
-		var yellow = new THREE.Color( 0xffff00 );
-
-		var geometry, mesh;
-
-		// Line axes
-
-		geometry = new THREE.Geometry();
-		geometry.vertices.push(
-			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ),
-			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ),
-			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 )
-		);
-		geometry.colors.push(
-			red, red, green, green, blue, blue
-		);
-		material = new THREE.LineBasicMaterial( {
-			vertexColors: THREE.VertexColors,
-			depthTest: false,
-			depthWrite: false,
-			transparent: true
-		} );
-		mesh = new THREE.Line( geometry, material, THREE.LinePieces );
-		displayAxes['translate'].add( mesh );
-		displayAxes['scale'].add( mesh.clone() );
-
-		// Translate handles
-
-		mesh = new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), HandleMaterial( white, 0.25 ) );
-		mesh.name = 'TXYZ';
-		displayAxes['translate'].add( mesh );
-		pickerAxes['translate'].add( mesh.clone() );
-
-		geometry = new THREE.PlaneGeometry( 0.3, 0.3 );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( yellow, 0.25 ) );
-		mesh.position.set( 0.15, 0.15, 0 );
-		bakeTransformations( mesh );
-		mesh.name = 'TXY';
-		displayAxes['translate'].add( mesh );
-		pickerAxes['translate'].add( mesh.clone() );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( cyan, 0.25 ) );
-		mesh.position.set( 0, 0.15, 0.15 );
-		mesh.rotation.y = Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TYZ';
-		displayAxes['translate'].add( mesh );
-		pickerAxes['translate'].add( mesh.clone() );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( magenta, 0.25 ) );
-		mesh.position.set( 0.15, 0, 0.15 );
-		mesh.rotation.x = Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TXZ';
-		displayAxes['translate'].add( mesh );
-		pickerAxes['translate'].add( mesh.clone() );
-
-		geometry = new THREE.CylinderGeometry( 0, 0.05, 0.2, 4, 1, true );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
-		mesh.position.x = 1.1;
-		mesh.rotation.z = -Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TX';
-		displayAxes['translate'].add( mesh );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
-		mesh.position.y = 1.1;
-		bakeTransformations( mesh );
-		mesh.name = 'TY';
-		displayAxes['translate'].add( mesh );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
-		mesh.position.z = 1.1;
-		mesh.rotation.x = Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TZ';
-		displayAxes['translate'].add( mesh );
-
-		geometry = new THREE.CylinderGeometry( 0.2, 0.1, 0.8, 4, 1, false );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
-		mesh.position.x = 0.7;
-		mesh.rotation.z = -Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TX';
-		pickerAxes['translate'].add( mesh );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
-		mesh.position.y = 0.7;
-		bakeTransformations( mesh );
-		mesh.name = 'TY';
-		pickerAxes['translate'].add( mesh );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
-		mesh.position.z = 0.7;
-		mesh.rotation.x = Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'TZ';
-		pickerAxes['translate'].add( mesh );
-
-		// scale manipulators
-
-		geometry = new THREE.CubeGeometry( 0.125, 0.125, 0.125 );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( white, 0.25 ) );
-		mesh.name = 'SXYZ';
-		displayAxes['scale'].add( mesh );
-		pickerAxes['scale'].add( mesh.clone() );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
-		mesh.position.set( 1.05, 0, 0 );
-		bakeTransformations( mesh );
-		mesh.name = 'SX';
-		displayAxes['scale'].add( mesh );
-		pickerAxes['scale'].add( mesh.clone() );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
-		mesh.position.set( 0, 1.05, 0 );
-		bakeTransformations( mesh );
-		mesh.name = 'SY';
-		displayAxes['scale'].add( mesh );
-		pickerAxes['scale'].add( mesh.clone() );
-
-		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
-		mesh.position.set( 0, 0, 1.05 );
-		bakeTransformations( mesh );
-		mesh.name = 'SZ';
-		displayAxes['scale'].add( mesh );
-		pickerAxes['scale'].add( mesh.clone() );
-
-		// rotate manipulators
-
-		var Circle = function ( radius, facing, arc ) {
-
-			geometry = new THREE.Geometry();
-			arc = arc ? arc : 1;
-			for ( var i = 0; i <= 64 * arc; ++i ) {
-				if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
-				if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
-				if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) );
-			}
+	};
 
-			return geometry;
-		}
+	GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype );
+	GizmoLineMaterial.prototype.constructor = GizmoLineMaterial;
 
-		mesh = new THREE.Line( Circle( 1, 'x', 0.5 ), LineMaterial( red ) );
-		mesh.name = 'RX';
-		displayAxes['rotate'].add( mesh );
+	THREE.TransformGizmo = function () {
 
-		mesh = new THREE.Line( Circle( 1, 'y', 0.5 ), LineMaterial( green ) );
-		mesh.name = 'RY';
-		displayAxes['rotate'].add( mesh );
+		var scope = this;
+		var showPickers = false; //debug
+		var showActivePlane = false; //debug
 
-		mesh = new THREE.Line( Circle( 1, 'z', 0.5 ), LineMaterial( blue ) );
-		mesh.name = 'RZ';
-		displayAxes['rotate'].add( mesh );
+		this.init = function () {
 
-		mesh = new THREE.Line( Circle( 1, 'z' ), LineMaterial( gray ) );
-		mesh.name = 'RXYZE';
-		displayAxes['rotate'].add( mesh );
+			THREE.Object3D.call( this );
 
-		mesh = new THREE.Line( Circle( 1.25, 'z' ), LineMaterial( yellow, 0.25 ) );
-		mesh.name = 'RE';
-		displayAxes['rotate'].add( mesh );
+			this.handles = new THREE.Object3D();
+			this.pickers = new THREE.Object3D();
+			this.planes = new THREE.Object3D();
 
-		geometry = new THREE.TorusGeometry( 1, 0.15, 4, 6, Math.PI );
+			this.add(this.handles);
+			this.add(this.pickers);
+			this.add(this.planes);
 
-		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
-		mesh.rotation.z = -Math.PI/2;
-		mesh.rotation.y = -Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'RX';
-		pickerAxes['rotate'].add( mesh );
+			//// PLANES
 
-		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
-		mesh.rotation.z = Math.PI;
-		mesh.rotation.x = -Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'RY';
-		pickerAxes['rotate'].add( mesh );
+			var planeGeometry = new THREE.PlaneBufferGeometry( 50, 50, 2, 2 );
+			var planeMaterial = new THREE.MeshBasicMaterial( { wireframe: true } );
+			planeMaterial.side = THREE.DoubleSide;
 
-		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
-		mesh.rotation.z = -Math.PI/2;
-		bakeTransformations( mesh );
-		mesh.name = 'RZ';
-		pickerAxes['rotate'].add( mesh );
+			var planes = {
+				"XY":   new THREE.Mesh( planeGeometry, planeMaterial ),
+				"YZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
+				"XZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
+				"XYZE": new THREE.Mesh( planeGeometry, planeMaterial )
+			};
 
-		mesh = new THREE.Mesh( new THREE.SphereGeometry( 0.95, 12, 12 ), HandleMaterial( white, 0.25 ) );
-		mesh.name = 'RXYZE';
-		pickerAxes['rotate'].add( mesh );
+			this.activePlane = planes["XYZE"];
 
-		intersectionPlanes['SPHERE'] = new THREE.Mesh( new THREE.SphereGeometry( 0.95, 12, 12 ) );
-		intersectionPlanes['SPHERE'].visible = false;
-		planes.add(intersectionPlanes['SPHERE']);
+			planes["YZ"].rotation.set( 0, Math.PI / 2, 0 );
+			planes["XZ"].rotation.set( -Math.PI / 2, 0, 0 );
 
-		mesh = new THREE.Mesh( new THREE.TorusGeometry( 1.30, 0.15, 4, 12 ), HandleMaterial( yellow, 0.25 ) );
-		mesh.name = 'RE';
-		pickerAxes['rotate'].add( mesh );
+			for (var i in planes) {
+				planes[i].name = i;
+				this.planes.add(planes[i]);
+				this.planes[i] = planes[i];
+				planes[i].visible = false;
+			}
 
-		mesh = null;
+			//// HANDLES AND PICKERS
 
-	}
+			var setupGizmos = function( gizmoMap, parent ) {
 
-	this.attach = function ( object ) {
+				for ( var name in gizmoMap ) {
 
-		this.object = object;
-	 	this.setMode( scope.mode );
+					for ( i = gizmoMap[name].length; i --;) {
 
-		this.domElement.addEventListener( 'mousedown', onMouseDown, false );
-		this.domElement.addEventListener( 'mousemove', onMouseHover, false );
-		this.document.addEventListener( 'keydown', onKeyDown, false );
+						var object = gizmoMap[name][i][0];
+						var position = gizmoMap[name][i][1];
+						var rotation = gizmoMap[name][i][2];
 
-	}
+						object.name = name;
 
-	this.detach = function ( object ) {
+						if ( position ) object.position.set( position[0], position[1], position[2] );
+						if ( rotation ) object.rotation.set( rotation[0], rotation[1], rotation[2] );
 
-		this.object = undefined;
-		this.hovered = false;
+						parent.add( object );
 
-	 	this.hide();
+					}
 
-		this.domElement.removeEventListener( 'mousedown', onMouseDown, false );
-		this.domElement.removeEventListener( 'mousemove', onMouseHover, false );
-		this.document.removeEventListener( 'keydown', onKeyDown, false );
+				}
 
-	}
+			};
 
-	this.update = function () {
+			setupGizmos(this.handleGizmos, this.handles);
+			setupGizmos(this.pickerGizmos, this.pickers);
 
-		if ( this.object === undefined ) return;
+			// reset Transformations
 
-		this.object.updateMatrixWorld();
-		worldPosition.getPositionFromMatrix( this.object.matrixWorld );
-		// worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( this.object.matrixWorld ) );
+			this.traverse(function ( child ) {
+				if (child instanceof THREE.Mesh) {
+					child.updateMatrix();
 
-		this.camera.updateMatrixWorld();
-		camPosition.getPositionFromMatrix( this.camera.matrixWorld );
-		// camRotation.setFromRotationMatrix( tempMatrix.extractRotation( this.camera.matrixWorld ) );
+					var tempGeometry = child.geometry.clone();
+					tempGeometry.applyMatrix( child.matrix );
+					child.geometry = tempGeometry;
 
-		scale = worldPosition.distanceTo( camPosition ) / 6 * this.scale;
-		this.gizmo.position.copy( worldPosition )
-		this.gizmo.scale.set( scale, scale, scale );
+					child.position.set( 0, 0, 0 );
+					child.rotation.set( 0, 0, 0 );
+					child.scale.set( 1, 1, 1 );
+				}
+			});
+
+		};
+
+		this.hide = function () {
+			this.traverse(function( child ) {
+				child.visible = false;
+			});
+		};
+
+		this.show = function () {
+			this.traverse(function( child ) {
+				child.visible = true;
+				if (child.parent == scope.pickers ) child.visible = showPickers;
+				if (child.parent == scope.planes ) child.visible = false;
+			});
+			this.activePlane.visible = showActivePlane;
+		};
+
+		this.highlight = function ( axis ) {
+			this.traverse(function( child ) {
+				if ( child.material && child.material.highlight ) {
+					if ( child.name == axis ) {
+						child.material.highlight( true );
+					} else {
+						child.material.highlight( false );
+					}
+				}
+			});
+		};
 
-		for ( var i in this.gizmo.children ) {
+	};
 
-			for ( var j in this.gizmo.children[i].children ) {
+	THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype );
+	THREE.TransformGizmo.prototype.constructor = THREE.TransformGizmo;
 
-				var object = this.gizmo.children[i].children[j];
-				var name = object.name;
+	THREE.TransformGizmo.prototype.update = function ( rotation, eye ) {
 
-				if ( name.search('E') != -1 ){
+		var vec1 = new THREE.Vector3( 0, 0, 0 );
+		var vec2 = new THREE.Vector3( 0, 1, 0 );
+		var lookAtMatrix = new THREE.Matrix4();
 
-					lookAtMatrix.lookAt( camPosition, worldPosition, tempVector.set( 0, 1, 0 ));
-					object.rotation.setFromRotationMatrix( lookAtMatrix );
+		this.traverse(function(child) {
+			if ( child.name.search("E") != -1 ) {
+				child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) );
+			} else if ( child.name.search("X") != -1 || child.name.search("Y") != -1 || child.name.search("Z") != -1 ) {
+				child.quaternion.setFromEuler( rotation );
+			}
+		});
 
-				} else {
+	};
 
-					eye.copy( camPosition ).sub( worldPosition ).normalize();
+	THREE.TransformGizmoTranslate = function () {
+
+		THREE.TransformGizmo.call( this );
+
+		var arrowGeometry = new THREE.Geometry();
+		var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) );
+		mesh.position.y = 0.5;
+		mesh.updateMatrix();
+
+		arrowGeometry.merge( mesh.geometry, mesh.matrix );
+
+		var lineXGeometry = new THREE.Geometry();
+		lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );
+
+		var lineYGeometry = new THREE.Geometry();
+		lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
+
+		var lineZGeometry = new THREE.Geometry();
+		lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );
+
+		this.handleGizmos = {
+			X: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ],
+				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
+			],
+			Y: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
+				[	new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
+			],
+			Z: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
+				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
+			],
+			XYZ: [
+				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
+			],
+			XY: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ]
+			],
+			YZ: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ]
+			],
+			XZ: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ -Math.PI / 2, 0, 0 ] ]
+			]
+		};
+
+		this.pickerGizmos = {
+			X: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ]
+			],
+			Y: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
+			],
+			Z: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
+			],
+			XYZ: [
+				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
+			],
+			XY: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.2, 0.2, 0 ] ]
+			],
+			YZ: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
+			],
+			XZ: [
+				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.2, 0, 0.2 ], [ -Math.PI / 2, 0, 0 ] ]
+			]
+		};
+
+		this.setActivePlane = function ( axis, eye ) {
+
+			var tempMatrix = new THREE.Matrix4();
+			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
+
+			if ( axis == "X" ) {
+				this.activePlane = this.planes[ "XY" ];
+				if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
+			}
 
-					if ( this.space == 'local' ) {
+			if ( axis == "Y" ) {
+				this.activePlane = this.planes[ "XY" ];
+				if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
+			}
 
-						// tempQuaternion.setFromEuler( worldRotation );
-						tempQuaternion.setFromRotationMatrix( tempMatrix.extractRotation( this.object.matrixWorld ) );
+			if ( axis == "Z" ) {
+				this.activePlane = this.planes[ "XZ" ];
+				if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
+			}
 
-						if ( name.search('R') != -1 ){
+			if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
 
-							tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
-							eye.applyProjection( tempMatrix );
+			if ( axis == "XY" ) this.activePlane = this.planes[ "XY" ];
 
-							if ( name == 'RX' ) {
-								quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) );
-								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-							}
+			if ( axis == "YZ" ) this.activePlane = this.planes[ "YZ" ];
 
-							if ( name == 'RY' ) {
-								quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
-								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
-							}
+			if ( axis == "XZ" ) this.activePlane = this.planes[ "XZ" ];
 
-							if ( name == 'RZ' ) {
-								quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
-								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
-							}
+			this.hide();
+			this.show();
 
-						}
+		};
 
-						object.quaternion.copy( tempQuaternion );
+		this.init();
 
-					} else if ( this.space == 'world' ) {
+	};
 
-						object.rotation.set( 0, 0, 0 );
+	THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype );
+	THREE.TransformGizmoTranslate.prototype.constructor = THREE.TransformGizmoTranslate;
 
-						if ( name == 'RX' ) object.rotation.x = Math.atan2( -eye.y, eye.z );
-						if ( name == 'RY' ) object.rotation.y = Math.atan2(  eye.x, eye.z );
-						if ( name == 'RZ' ) object.rotation.z = Math.atan2(  eye.y, eye.x );
+	THREE.TransformGizmoRotate = function () {
 
-					}
+		THREE.TransformGizmo.call( this );
 
-				}
+		var CircleGeometry = function ( radius, facing, arc ) {
 
+			var geometry = new THREE.Geometry();
+			arc = arc ? arc : 1;
+			for ( var i = 0; i <= 64 * arc; ++ i ) {
+				if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
+				if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
+				if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) );
 			}
 
-		}
+			return geometry;
+		};
+
+		this.handleGizmos = {
+			X: [
+				[ new THREE.Line( new CircleGeometry(1,'x',0.5), new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
+			],
+			Y: [
+				[ new THREE.Line( new CircleGeometry(1,'y',0.5), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
+			],
+			Z: [
+				[ new THREE.Line( new CircleGeometry(1,'z',0.5), new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
+			],
+			E: [
+				[ new THREE.Line( new CircleGeometry(1.25,'z',1), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ]
+			],
+			XYZE: [
+				[ new THREE.Line( new CircleGeometry(1,'z',1), new GizmoLineMaterial( { color: 0x787878 } ) ) ]
+			]
+		};
+
+		this.pickerGizmos = {
+			X: [
+				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, -Math.PI / 2, -Math.PI / 2 ] ]
+			],
+			Y: [
+				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ]
+			],
+			Z: [
+				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ]
+			],
+			E: [
+				[ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ) ]
+			],
+			XYZE: [
+				[ new THREE.Mesh( new THREE.Geometry() ) ]// TODO
+			]
+		};
+
+		this.setActivePlane = function ( axis ) {
+
+			if ( axis == "E" ) this.activePlane = this.planes[ "XYZE" ];
+
+			if ( axis == "X" ) this.activePlane = this.planes[ "YZ" ];
+
+			if ( axis == "Y" ) this.activePlane = this.planes[ "XZ" ];
+
+			if ( axis == "Z" ) this.activePlane = this.planes[ "XY" ];
+
+			this.hide();
+			this.show();
+
+		};
+
+		this.update = function ( rotation, eye2 ) {
+
+			THREE.TransformGizmo.prototype.update.apply( this, arguments );
+
+			var group = {
+				handles: this["handles"],
+				pickers: this["pickers"],
+			};
+
+			var tempMatrix = new THREE.Matrix4();
+			var worldRotation = new THREE.Euler( 0, 0, 1 );
+			var tempQuaternion = new THREE.Quaternion();
+			var unitX = new THREE.Vector3( 1, 0, 0 );
+			var unitY = new THREE.Vector3( 0, 1, 0 );
+			var unitZ = new THREE.Vector3( 0, 0, 1 );
+			var quaternionX = new THREE.Quaternion();
+			var quaternionY = new THREE.Quaternion();
+			var quaternionZ = new THREE.Quaternion();
+			var eye = eye2.clone();
+
+			worldRotation.copy( this.planes["XY"].rotation );
+			tempQuaternion.setFromEuler( worldRotation );
+
+			tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
+			eye.applyMatrix4( tempMatrix );
+
+			this.traverse(function(child) {
+
+				tempQuaternion.setFromEuler( worldRotation );
+
+				if ( child.name == "X" ) {
+					quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+					child.quaternion.copy( tempQuaternion );
+				}
+
+				if ( child.name == "Y" ) {
+					quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
+					child.quaternion.copy( tempQuaternion );
+				}
 
-	}
+				if ( child.name == "Z" ) {
+					quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
+					child.quaternion.copy( tempQuaternion );
+				}
 
-	this.hide = function () {
+			});
 
-		for ( var i in displayAxes ) {
+		};
 
-			for ( var j in displayAxes[i].children ) {
+		this.init();
 
-				displayAxes[i].children[j].visible = false;
+	};
 
+	THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype );
+	THREE.TransformGizmoRotate.prototype.constructor = THREE.TransformGizmoRotate;
+
+	THREE.TransformGizmoScale = function () {
+
+		THREE.TransformGizmo.call( this );
+
+		var arrowGeometry = new THREE.Geometry();
+		var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) );
+		mesh.position.y = 0.5;
+		mesh.updateMatrix();
+
+		arrowGeometry.merge( mesh.geometry, mesh.matrix );
+
+		var lineXGeometry = new THREE.Geometry();
+		lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );
+
+		var lineYGeometry = new THREE.Geometry();
+		lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
+
+		var lineZGeometry = new THREE.Geometry();
+		lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );
+
+		this.handleGizmos = {
+			X: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ],
+				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
+			],
+			Y: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
+				[ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
+			],
+			Z: [
+				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
+				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
+			],
+			XYZ: [
+				[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
+			]
+		};
+
+		this.pickerGizmos = {
+			X: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ]
+			],
+			Y: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
+			],
+			Z: [
+				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
+			],
+			XYZ: [
+				[ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
+			]
+		};
+
+		this.setActivePlane = function ( axis, eye ) {
+
+			var tempMatrix = new THREE.Matrix4();
+			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
+
+			if ( axis == "X" ) {
+				this.activePlane = this.planes[ "XY" ];
+				if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
 			}
 
-		}
+			if ( axis == "Y" ) {
+				this.activePlane = this.planes[ "XY" ];
+				if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
+			}
 
-		for ( var i in pickerAxes ) {
+			if ( axis == "Z" ) {
+				this.activePlane = this.planes[ "XZ" ];
+				if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
+			}
 
-			for ( var j in pickerAxes[i].children ) {
+			if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
 
-				pickerAxes[i].children[j].visible = false;
+			this.hide();
+			this.show();
 
-			}
+		};
 
-		}
+		this.init();
 
-	}
+	};
 
-	this.setMode = function ( value ) {
+	THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype );
+	THREE.TransformGizmoScale.prototype.constructor = THREE.TransformGizmoScale;
 
-		scope.mode = value;
+	THREE.TransformControls = function ( camera, domElement ) {
 
-		this.hide();
+		// TODO: Make non-uniform scale and rotate play nice in hierarchies
+		// TODO: ADD RXYZ contol
 
-		if ( scope.mode == 'scale' ) scope.space = 'local';
+		THREE.Object3D.call( this );
 
-		for ( var i in displayAxes[this.mode].children ) {
+		domElement = ( domElement !== undefined ) ? domElement : document;
 
-			displayAxes[this.mode].children[i].visible = true;
+		this.gizmo = {};
+		this.gizmo["translate"] = new THREE.TransformGizmoTranslate();
+		this.gizmo["rotate"] = new THREE.TransformGizmoRotate();
+		this.gizmo["scale"] = new THREE.TransformGizmoScale();
 
-		}
+		this.add(this.gizmo["translate"]);
+		this.add(this.gizmo["rotate"]);
+		this.add(this.gizmo["scale"]);
 
-		for ( var i in pickerAxes[this.mode].children ) {
+		this.gizmo["translate"].hide();
+		this.gizmo["rotate"].hide();
+		this.gizmo["scale"].hide();
 
-			pickerAxes[this.mode].children[i].visible = showPickers;
+		this.object = undefined;
+		this.snap = null;
+		this.space = "world";
+		this.size = 1;
+		this.axis = null;
 
-		}
+		var scope = this;
 
-		scope.update();
+		var _dragging = false;
+		var _mode = "translate";
+		var _plane = "XY";
 
-	}
+		var changeEvent = { type: "change" };
+		var mouseDownEvent = { type: "mouseDown" };
+		var mouseUpEvent = { type: "mouseUp", mode: _mode };
+		var objectChangeEvent = { type: "objectChange" };
 
-	this.setIntersectionPlane = function () {
+		var ray = new THREE.Raycaster();
+		var pointerVector = new THREE.Vector3();
 
-		eye.copy( camPosition ).sub( worldPosition ).normalize();
+		var point = new THREE.Vector3();
+		var offset = new THREE.Vector3();
 
-		if ( this.space == 'local' ) {
+		var rotation = new THREE.Vector3();
+		var offsetRotation = new THREE.Vector3();
+		var scale = 1;
 
-			eye.applyMatrix4( tempMatrix.getInverse( scope.object.matrixWorld ) );
+		var lookAtMatrix = new THREE.Matrix4();
+		var eye = new THREE.Vector3();
 
-		}
+		var tempMatrix = new THREE.Matrix4();
+		var tempVector = new THREE.Vector3();
+		var tempQuaternion = new THREE.Quaternion();
+		var unitX = new THREE.Vector3( 1, 0, 0 );
+		var unitY = new THREE.Vector3( 0, 1, 0 );
+		var unitZ = new THREE.Vector3( 0, 0, 1 );
 
-		if ( isActive("X") ) {
+		var quaternionXYZ = new THREE.Quaternion();
+		var quaternionX = new THREE.Quaternion();
+		var quaternionY = new THREE.Quaternion();
+		var quaternionZ = new THREE.Quaternion();
+		var quaternionE = new THREE.Quaternion();
 
-			if ( eye.y > eye.z ) currentPlane = 'XZ';
-			else currentPlane = 'XY';
+		var oldPosition = new THREE.Vector3();
+		var oldScale = new THREE.Vector3();
+		var oldRotationMatrix = new THREE.Matrix4();
 
-		}
+		var parentRotationMatrix  = new THREE.Matrix4();
+		var parentScale = new THREE.Vector3();
 
-		if ( isActive("Y") ) {
+		var worldPosition = new THREE.Vector3();
+		var worldRotation = new THREE.Euler();
+		var worldRotationMatrix  = new THREE.Matrix4();
+		var camPosition = new THREE.Vector3();
+		var camRotation = new THREE.Euler();
 
-			if ( eye.x > eye.z ) currentPlane = 'YZ';
-			else currentPlane = 'XY';
+		domElement.addEventListener( "mousedown", onPointerDown, false );
+		domElement.addEventListener( "touchstart", onPointerDown, false );
 
-		}
+		domElement.addEventListener( "mousemove", onPointerHover, false );
+		domElement.addEventListener( "touchmove", onPointerHover, false );
 
-		if ( isActive("Z") ) {
+		domElement.addEventListener( "mousemove", onPointerMove, false );
+		domElement.addEventListener( "touchmove", onPointerMove, false );
 
-			if ( eye.x > eye.y ) currentPlane = 'YZ';
-			else currentPlane = 'XZ';
+		domElement.addEventListener( "mouseup", onPointerUp, false );
+		domElement.addEventListener( "mouseout", onPointerUp, false );
+		domElement.addEventListener( "touchend", onPointerUp, false );
+		domElement.addEventListener( "touchcancel", onPointerUp, false );
+		domElement.addEventListener( "touchleave", onPointerUp, false );
 
-		}
+		this.attach = function ( object ) {
 
-		if ( isActive("XY") ) {
+			scope.object = object;
 
-			currentPlane = 'XY';
+			this.gizmo["translate"].hide();
+			this.gizmo["rotate"].hide();
+			this.gizmo["scale"].hide();
+			this.gizmo[_mode].show();
 
-		}
+			scope.update();
 
-		if ( isActive("YZ") ) {
+		};
 
-			currentPlane = 'YZ';
+		this.detach = function ( object ) {
 
-		}
+			scope.object = undefined;
+			this.axis = null;
 
-		if ( isActive("XZ") ) {
+			this.gizmo["translate"].hide();
+			this.gizmo["rotate"].hide();
+			this.gizmo["scale"].hide();
 
-			currentPlane = 'XZ';
+		};
 
-		}
+		this.setMode = function ( mode ) {
 
-		if ( isActive("XYZ") || isActive("E") ) {
+			_mode = mode ? mode : _mode;
 
-			currentPlane = 'XYZE';
+			if ( _mode == "scale" ) scope.space = "local";
 
-		}
+			this.gizmo["translate"].hide();
+			this.gizmo["rotate"].hide();
+			this.gizmo["scale"].hide();
+			this.gizmo[_mode].show();
 
-	 	if ( isActive("RX") ) {
+			this.update();
+			scope.dispatchEvent( changeEvent );
 
-			currentPlane = 'YZ';
+		};
 
-		}
+		this.setSnap = function ( snap ) {
 
-		if ( isActive("RY") ) {
+			scope.snap = snap;
 
-			currentPlane = 'XZ';
+		};
 
-		}
+		this.setSize = function ( size ) {
 
-		if ( isActive("RZ") ) {
+			scope.size = size;
+			this.update();
+			scope.dispatchEvent( changeEvent );
 
-			currentPlane = 'XY';
+		};
 
-		}
+		this.setSpace = function ( space ) {
 
-		if ( isActive("RXYZ") ) {
+			scope.space = space;
+			this.update();
+			scope.dispatchEvent( changeEvent );
 
-			currentPlane = 'SPHERE';
+		};
 
-		}
+		this.update = function () {
 
-	}
+			if ( scope.object === undefined ) return;
 
-	var hovered = null;
-	var hoveredColor = new THREE.Color();
+			scope.object.updateMatrixWorld();
+			worldPosition.setFromMatrixPosition( scope.object.matrixWorld );
+			worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) );
 
-	function onMouseHover( event ) {
+			camera.updateMatrixWorld();
+			camPosition.setFromMatrixPosition( camera.matrixWorld );
+			camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );
 
-		event.preventDefault();
+			scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size;
+			this.position.copy( worldPosition );
+			this.scale.set( scale, scale, scale );
 
-		if ( event.button === 0 && scope.active === false ) {
+			eye.copy( camPosition ).sub( worldPosition ).normalize();
 
-			var intersect = intersectObjects( event, pickerAxes[scope.mode].children );
+			if ( scope.space == "local" )
+				this.gizmo[_mode].update( worldRotation, eye );
 
-			if ( intersect ) {
+			else if ( scope.space == "world" )
+				this.gizmo[_mode].update( new THREE.Euler(), eye );
 
-				if ( hovered !== intersect.object ) {
+			this.gizmo[_mode].highlight( scope.axis );
 
-					if ( hovered !== null ) {
+		};
 
-						hovered.material.color.copy( hoveredColor );
+		function onPointerHover( event ) {
 
-					}
+			if ( scope.object === undefined || _dragging === true ) return;
 
-					hovered = intersect.object;
-					hoveredColor.copy( hovered.material.color );
+			event.preventDefault();
 
-                    hovered.material.color.offsetHSL( 0, 0, -0.3 );
+			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
-					scope.dispatchEvent( changeEvent );
+			var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
 
-				}
+			var axis = null;
 
-				scope.hovered = true;
+			if ( intersect ) {
 
-			} else if ( hovered !== null ) {
+				axis = intersect.object.name;
 
-				hovered.material.color.copy( hoveredColor );
+			}
 
-				hovered = null;
+			if ( scope.axis !== axis ) {
 
+				scope.axis = axis;
+				scope.update();
 				scope.dispatchEvent( changeEvent );
 
-				scope.hovered = false;
-
 			}
 
 		}
 
-		scope.document.addEventListener( 'mousemove', onMouseMove, false );
-		scope.document.addEventListener( 'mouseup', onMouseUp, false );
+		function onPointerDown( event ) {
 
-	};
+			if ( scope.object === undefined || _dragging === true ) return;
 
-	function onMouseDown( event ) {
+			event.preventDefault();
+			event.stopPropagation();
 
-		event.preventDefault();
+			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
-		if ( event.button === 0 ) {
+			if ( pointer.button === 0 || pointer.button === undefined ) {
 
-			var intersect = intersectObjects( event, pickerAxes[scope.mode].children );
+				var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
 
-			if ( intersect ) {
+				if ( intersect ) {
 
-				scope.active = intersect.object.name;
+					scope.dispatchEvent( mouseDownEvent );
 
-				scope.update();
-				scope.setIntersectionPlane();
+					scope.axis = intersect.object.name;
+
+					scope.update();
+
+					eye.copy( camPosition ).sub( worldPosition ).normalize();
 
-				var planeIntersect = intersectObjects( event, [intersectionPlanes[currentPlane]] );
+					scope.gizmo[_mode].setActivePlane( scope.axis, eye );
 
-				if ( planeIntersect ) {
+					var planeIntersect = intersectObjects( pointer, [ scope.gizmo[_mode].activePlane ] );
 
 					oldPosition.copy( scope.object.position );
 					oldScale.copy( scope.object.scale );
@@ -661,7 +784,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 					worldRotationMatrix.extractRotation( scope.object.matrixWorld );
 
 					parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
-					parentScale.getScaleFromMatrix( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
+					parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
 
 					offset.copy( planeIntersect.point );
 
@@ -669,173 +792,168 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 
 			}
 
-		}
+			_dragging = true;
 
-		scope.document.addEventListener( 'mousemove', onMouseMove, false );
-		scope.document.addEventListener( 'mouseup', onMouseUp, false );
+		}
 
-	};
+		function onPointerMove( event ) {
 
-	function onMouseMove( event ) {
+			if ( scope.object === undefined || scope.axis === null || _dragging === false ) return;
 
-		if ( scope.active ) {
+			event.preventDefault();
+			event.stopPropagation();
 
-			var planeIntersect = intersectObjects( event, [intersectionPlanes[currentPlane]] );
+			var pointer = event.changedTouches ? event.changedTouches[0] : event;
 
-			if ( planeIntersect ) {
+			var planeIntersect = intersectObjects( pointer, [ scope.gizmo[_mode].activePlane ] );
 
-				point.copy( planeIntersect.point );
+			point.copy( planeIntersect.point );
 
-				if ( ( scope.mode == 'translate' ) && isActive("T") ) {
+			if ( _mode == "translate" ) {
 
-					point.sub( offset );
-					point.multiply(parentScale);
+				point.sub( offset );
+				point.multiply(parentScale);
 
-					if ( scope.space == 'local' ) {
+				if ( scope.space == "local" ) {
 
-						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
 
-						if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
-						if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
-						if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
-						if ( isActive("XYZ") ) point.set( 0, 0, 0 );
+					if ( scope.axis.search("X") == -1 ) point.x = 0;
+					if ( scope.axis.search("Y") == -1 ) point.y = 0;
+					if ( scope.axis.search("Z") == -1 ) point.z = 0;
 
-						point.applyMatrix4( oldRotationMatrix );
+					point.applyMatrix4( oldRotationMatrix );
 
-						scope.object.position.copy( oldPosition );
-						scope.object.position.add( point );
+					scope.object.position.copy( oldPosition );
+					scope.object.position.add( point );
 
-					}
+				}
 
-					if ( scope.space == 'world' || isActive("XYZ") ) {
+				if ( scope.space == "world" || scope.axis.search("XYZ") != -1 ) {
 
-						if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
-						if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
-						if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
+					if ( scope.axis.search("X") == -1 ) point.x = 0;
+					if ( scope.axis.search("Y") == -1 ) point.y = 0;
+					if ( scope.axis.search("Z") == -1 ) point.z = 0;
 
-						point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
+					point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
 
-						scope.object.position.copy( oldPosition );
-						scope.object.position.add( point );
+					scope.object.position.copy( oldPosition );
+					scope.object.position.add( point );
 
-						if ( scope.snapDist ) {
-							if ( isActive("X") ) scope.object.position.x = Math.round( scope.object.position.x / scope.snapDist ) * scope.snapDist;
-							if ( isActive("Y") ) scope.object.position.y = Math.round( scope.object.position.y / scope.snapDist ) * scope.snapDist;
-							if ( isActive("Z") ) scope.object.position.z = Math.round( scope.object.position.z / scope.snapDist ) * scope.snapDist;
-						}
+				}
 
-					}
+				if ( scope.snap !== null ) {
 
-				} else if ( ( scope.mode == 'scale') && isActive("S") ) {
+					if ( scope.axis.search("X") != -1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.snap ) * scope.snap;
+					if ( scope.axis.search("Y") != -1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.snap ) * scope.snap;
+					if ( scope.axis.search("Z") != -1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.snap ) * scope.snap;
 
-					point.sub( offset );
-					point.multiply(parentScale);
+				}
 
-					if ( scope.space == 'local' ) {
+			} else if ( _mode == "scale" ) {
 
-						if ( isActive("XYZ")) {
+				point.sub( offset );
+				point.multiply(parentScale);
 
-							scale = 1 + ( ( point.y ) / 50 );
+				if ( scope.space == "local" ) {
 
-							scope.object.scale.x = oldScale.x * scale;
-							scope.object.scale.y = oldScale.y * scale;
-							scope.object.scale.z = oldScale.z * scale;
+					if ( scope.axis == "XYZ") {
 
-						} else {
+						scale = 1 + ( ( point.y ) / 50 );
 
-							point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+						scope.object.scale.x = oldScale.x * scale;
+						scope.object.scale.y = oldScale.y * scale;
+						scope.object.scale.z = oldScale.z * scale;
 
-							if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
-							if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
-							if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
+					} else {
 
-							if ( isActive("X") ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
-							if ( isActive("Y") ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
-							if ( isActive("Z") ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );
+						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
 
-						}
+						if ( scope.axis == "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
+						if ( scope.axis == "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
+						if ( scope.axis == "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );
 
 					}
 
-				} else if ( ( scope.mode == 'rotate' ) && isActive("R") ) {
+				}
 
-					point.sub( worldPosition );
-					point.multiply(parentScale);
-					tempVector.copy(offset).sub( worldPosition );
-					tempVector.multiply(parentScale);
+			} else if ( _mode == "rotate" ) {
 
-					if ( scope.active == "RE" ) {
+				point.sub( worldPosition );
+				point.multiply(parentScale);
+				tempVector.copy(offset).sub( worldPosition );
+				tempVector.multiply(parentScale);
 
-						point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
-						tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
+				if ( scope.axis == "E" ) {
 
-						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+					point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
+					tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
 
-						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
 
-						quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
-						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
 
-						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
-						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+					quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
+					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
 
-						scope.object.quaternion.copy( tempQuaternion );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-					} else if ( scope.active == "RXYZE" ) {
+					scope.object.quaternion.copy( tempQuaternion );
 
-						quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
+				} else if ( scope.axis == "XYZE" ) {
 
-						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
-						quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
-						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+					quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
 
-						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+					quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
+					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
 
-						scope.object.quaternion.copy( tempQuaternion );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-					} else if ( scope.space == 'local' ) {
+					scope.object.quaternion.copy( tempQuaternion );
 
-						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+				} else if ( scope.space == "local" ) {
 
-						tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
 
-						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+					tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
 
-						quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
-						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
-						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
-						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
 
-						if ( scope.active == "RX" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
-						if ( scope.active == "RY" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
-						if ( scope.active == "RZ" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
+					quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
+					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
+					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
+					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
 
-						scope.object.quaternion.copy( quaternionXYZ );
+					if ( scope.axis == "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
+					if ( scope.axis == "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
+					if ( scope.axis == "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
 
-					} else if ( scope.space == 'world' ) {
+					scope.object.quaternion.copy( quaternionXYZ );
 
-						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+				} else if ( scope.space == "world" ) {
 
-						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
 
-						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
-						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
-						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
-						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
 
-						if ( scope.active == "RX" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-						if ( scope.active == "RY" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
-						if ( scope.active == "RZ" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
+					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
+					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
+					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
 
-						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+					if ( scope.axis == "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+					if ( scope.axis == "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
+					if ( scope.axis == "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
 
-						scope.object.quaternion.copy( tempQuaternion );
+					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-					}
+					scope.object.quaternion.copy( tempQuaternion );
 
 				}
 
@@ -843,104 +961,40 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 
 			scope.update();
 			scope.dispatchEvent( changeEvent );
+			scope.dispatchEvent( objectChangeEvent );
 
 		}
 
-	}
-
-	function onMouseUp( event ) {
-
-		scope.active = false;
-
-		scope.document.removeEventListener( 'mousemove', onMouseMove, false );
-		scope.document.removeEventListener( 'mouseup', onMouseUp, false );
+		function onPointerUp( event ) {
 
-	}
-
-	function onKeyDown( event ) {
-
-		var currentMode = scope.mode;
-		var currentSpace = scope.space;
-		var currentScale = scope.scale;
-
-		if ( event.keyCode == 84 ) { // T
-
-			if ( scope.mode == 'translate' ) scope.space = ( scope.space == 'world' ) ? 'local' : 'world';
-			scope.mode = 'translate';
-
-		}
-
-		if ( event.keyCode == 82 ) { // R
-
-			if ( scope.mode == 'rotate' ) scope.space = ( scope.space == 'world' ) ? 'local' : 'world';
-			scope.mode = 'rotate';
+			if ( _dragging && ( scope.axis !== null ) ) {
+				mouseUpEvent.mode = _mode;
+				scope.dispatchEvent( mouseUpEvent )
+			}
+			_dragging = false;
+			onPointerHover( event );
 
 		}
 
-		/*if ( event.keyCode == 82 ) { // R
-
-			scope.mode = 'scale';
-			scope.space = 'local';
-
-		}*/
-
-		if ( event.keyCode == 187 || event.keyCode == 107 ) { // +,=,num+
+		function intersectObjects( pointer, objects ) {
 
-			scope.scale += 0.1
+			var rect = domElement.getBoundingClientRect();
+			var x = ( pointer.clientX - rect.left ) / rect.width;
+			var y = ( pointer.clientY - rect.top ) / rect.height;
 
-		}
+			pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1, 0.5 );
+			pointerVector.unproject( camera );
 
-		if ( event.keyCode == 189 || event.keyCode == 109) { // -,_,num-
+			ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
 
-			scope.scale -= 0.1
-			scope.scale = Math.max( scope.scale, 0.1 );
+			var intersections = ray.intersectObjects( objects, true );
+			return intersections[0] ? intersections[0] : false;
 
 		}
 
-		if ( scope.mode !== currentMode || scope.space !== currentSpace || scope.scale !== currentScale ) {
-
-			scope.setMode( scope.mode );
-			scope.dispatchEvent( changeEvent );
-
-		}
-
-	}
-
-	function intersectObjects( event, objects ) {
-
-		pointerVector.set(
-			( event.layerX / scope.domElement.offsetWidth ) * 2 - 1,
-			- ( event.layerY / scope.domElement.offsetHeight ) * 2 + 1,
-			0.5
-		);
-
-		projector.unprojectVector( pointerVector, scope.camera );
-		ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
-
-		var intersections = ray.intersectObjects( objects, true );
-		return intersections[0] ? intersections[0] : false;
-
-	}
-
-	function isActive( name ) {
-
-		if ( scope.active.search( name ) != -1 ) return true;
-		else return false;
-
-	}
-
-	function bakeTransformations( object ) {
-
-		var tempGeometry = new THREE.Geometry();
-		THREE.GeometryUtils.merge( tempGeometry, object );
-		object.geometry = tempGeometry;
-		object.position.set( 0, 0, 0 );
-		object.rotation.set( 0, 0, 0 );
-		object.scale.set( 1, 1, 1 );
-
-	}
-
-};
+	};
 
-THREE.TransformControls.prototype = Object.create( THREE.EventDispatcher.prototype );
+	THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
+	THREE.TransformControls.prototype.constructor = THREE.TransformControls;
 
+}());
diff --git a/gz3d/client/js/include/three.js b/gz3d/client/js/include/three.js
index 8a94595..ae4b1be 100644
--- a/gz3d/client/js/include/three.js
+++ b/gz3d/client/js/include/three.js
@@ -1,100 +1,43 @@
+// File:src/Three.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
- * @author Larry Battle / http://bateru.com/news
- * @author bhouston / http://exocortex.com
  */
 
-var THREE = { REVISION: '62' };
-
-self.console = self.console || {
-
-	info: function () {},
-	log: function () {},
-	debug: function () {},
-	warn: function () {},
-	error: function () {}
-
-};
-
-String.prototype.trim = String.prototype.trim || function () {
-
-	return this.replace( /^\s+|\s+$/g, '' );
-
-};
-
-// based on https://github.com/documentcloud/underscore/blob/bf657be243a075b5e72acc8a83e6f12a564d8f55/underscore.js#L767
-THREE.extend = function ( obj, source ) {
-
-	// ECMAScript5 compatibility based on: http://www.nczonline.net/blog/2012/12/11/are-your-mixins-ecmascript-5-compatible/
-	if ( Object.keys ) {
-
-		var keys = Object.keys( source );
-
-		for (var i = 0, il = keys.length; i < il; i++) {
-
-			var prop = keys[i];
-			Object.defineProperty( obj, prop, Object.getOwnPropertyDescriptor( source, prop ) );
-
-		}
-
-	} else {
-
-		var safeHasOwnProperty = {}.hasOwnProperty;
-
-		for ( var prop in source ) {
-
-			if ( safeHasOwnProperty.call( source, prop ) ) {
-
-				obj[prop] = source[prop];
-
-			}
-
-		}
-
-	}
-
-	return obj;
+var THREE = { REVISION: '71' };
 
-};
+// browserify support
 
-// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
-// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+if ( typeof module === 'object' ) {
 
-// requestAnimationFrame polyfill by Erik Möller
-// fixes from Paul Irish and Tino Zijdel
-// using 'self' instead of 'window' for compatibility with both NodeJS and IE10.
-( function () {
+	module.exports = THREE;
 
-	var lastTime = 0;
-	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
+}
 
-	for ( var x = 0; x < vendors.length && !self.requestAnimationFrame; ++ x ) {
+// polyfills
 
-		self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ];
-		self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
+if ( Math.sign === undefined ) {
 
-	}
+	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
 
-	if ( self.requestAnimationFrame === undefined && self['setTimeout'] !== undefined ) {
+	Math.sign = function ( x ) {
 
-		self.requestAnimationFrame = function ( callback ) {
+		return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : +x;
 
-			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
-			var id = self.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
-			lastTime = currTime + timeToCall;
-			return id;
+	};
 
-		};
+}
 
-	}
 
-	if( self.cancelAnimationFrame === undefined && self['clearTimeout'] !== undefined ) {
+// set the default log handlers
+THREE.log = function() { console.log.apply( console, arguments ); }
+THREE.warn = function() { console.warn.apply( console, arguments ); }
+THREE.error = function() { console.error.apply( console, arguments ); }
 
-		self.cancelAnimationFrame = function ( id ) { self.clearTimeout( id ) };
 
-	}
+// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button
 
-}() );
+THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
 
 // GL STATE CONSTANTS
 
@@ -148,6 +91,8 @@ THREE.CustomBlending = 5;
 THREE.AddEquation = 100;
 THREE.SubtractEquation = 101;
 THREE.ReverseSubtractEquation = 102;
+THREE.MinEquation = 103;
+THREE.MaxEquation = 104;
 
 // custom blending destination factors
 
@@ -181,13 +126,15 @@ THREE.AddOperation = 2;
 
 // Mapping modes
 
-THREE.UVMapping = function () {};
+THREE.UVMapping = 300;
+
+THREE.CubeReflectionMapping = 301;
+THREE.CubeRefractionMapping = 302;
 
-THREE.CubeReflectionMapping = function () {};
-THREE.CubeRefractionMapping = function () {};
+THREE.EquirectangularReflectionMapping = 303;
+THREE.EquirectangularRefractionMapping = 304;
 
-THREE.SphericalReflectionMapping = function () {};
-THREE.SphericalRefractionMapping = function () {};
+THREE.SphericalReflectionMapping = 305;
 
 // Wrapping modes
 
@@ -213,6 +160,7 @@ THREE.UnsignedShortType = 1012;
 THREE.IntType = 1013;
 THREE.UnsignedIntType = 1014;
 THREE.FloatType = 1015;
+THREE.HalfFloatType = 1025;
 
 // Pixel types
 
@@ -228,31 +176,80 @@ THREE.RGBFormat = 1020;
 THREE.RGBAFormat = 1021;
 THREE.LuminanceFormat = 1022;
 THREE.LuminanceAlphaFormat = 1023;
+// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
+THREE.RGBEFormat = THREE.RGBAFormat; //1024;
 
-// Compressed texture formats
+// DDS / ST3C Compressed texture formats
 
 THREE.RGB_S3TC_DXT1_Format = 2001;
 THREE.RGBA_S3TC_DXT1_Format = 2002;
 THREE.RGBA_S3TC_DXT3_Format = 2003;
 THREE.RGBA_S3TC_DXT5_Format = 2004;
 
-/*
-// Potential future PVRTC compressed texture formats
+
+// PVRTC compressed texture formats
+
 THREE.RGB_PVRTC_4BPPV1_Format = 2100;
 THREE.RGB_PVRTC_2BPPV1_Format = 2101;
 THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
 THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
-*/
+
+
+// DEPRECATED
+
+THREE.Projector = function () {
+
+	THREE.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );
+
+	this.projectVector = function ( vector, camera ) {
+
+		THREE.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
+		vector.project( camera );
+
+	};
+
+	this.unprojectVector = function ( vector, camera ) {
+
+		THREE.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
+		vector.unproject( camera );
+
+	};
+
+	this.pickingRay = function ( vector, camera ) {
+
+		THREE.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
+
+	};
+
+};
+
+THREE.CanvasRenderer = function () {
+
+	THREE.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );
+
+	this.domElement = document.createElement( 'canvas' );
+	this.clear = function () {};
+	this.render = function () {};
+	this.setClearColor = function () {};
+	this.setSize = function () {};
+
+};
+
+// File:src/math/Color.js
 
 /**
  * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.Color = function ( value ) {
+THREE.Color = function ( color ) {
 
-	if ( value !== undefined ) this.set( value );
+	if ( arguments.length === 3 ) {
 
-	return this;
+		return this.setRGB( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ] );
+
+	}
+
+	return this.set( color )
 
 };
 
@@ -415,21 +412,27 @@ THREE.Color.prototype = {
 
 	},
 
-	copyGammaToLinear: function ( color ) {
+	copyGammaToLinear: function ( color, gammaFactor ) {
 
-		this.r = color.r * color.r;
-		this.g = color.g * color.g;
-		this.b = color.b * color.b;
+		if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+		this.r = Math.pow( color.r, gammaFactor );
+		this.g = Math.pow( color.g, gammaFactor );
+		this.b = Math.pow( color.b, gammaFactor );
 
 		return this;
 
 	},
 
-	copyLinearToGamma: function ( color ) {
+	copyLinearToGamma: function ( color, gammaFactor ) {
+
+		if ( gammaFactor === undefined ) gammaFactor = 2.0;
 
-		this.r = Math.sqrt( color.r );
-		this.g = Math.sqrt( color.g );
-		this.b = Math.sqrt( color.b );
+		var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
+
+		this.r = Math.pow( color.r, safeInverse );
+		this.g = Math.pow( color.g, safeInverse );
+		this.b = Math.pow( color.b, safeInverse );
 
 		return this;
 
@@ -469,54 +472,50 @@ THREE.Color.prototype = {
 
 	},
 
-	getHSL: function () {
-
-		var hsl = { h: 0, s: 0, l: 0 };
+	getHSL: function ( optionalTarget ) {
 
-		return function () {
-
-			// h,s,l ranges are in 0.0 - 1.0
-
-			var r = this.r, g = this.g, b = this.b;
+		// h,s,l ranges are in 0.0 - 1.0
 
-			var max = Math.max( r, g, b );
-			var min = Math.min( r, g, b );
+		var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
 
-			var hue, saturation;
-			var lightness = ( min + max ) / 2.0;
+		var r = this.r, g = this.g, b = this.b;
 
-			if ( min === max ) {
+		var max = Math.max( r, g, b );
+		var min = Math.min( r, g, b );
 
-				hue = 0;
-				saturation = 0;
+		var hue, saturation;
+		var lightness = ( min + max ) / 2.0;
 
-			} else {
+		if ( min === max ) {
 
-				var delta = max - min;
+			hue = 0;
+			saturation = 0;
 
-				saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+		} else {
 
-				switch ( max ) {
+			var delta = max - min;
 
-					case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
-					case g: hue = ( b - r ) / delta + 2; break;
-					case b: hue = ( r - g ) / delta + 4; break;
+			saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
 
-				}
+			switch ( max ) {
 
-				hue /= 6;
+				case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
+				case g: hue = ( b - r ) / delta + 2; break;
+				case b: hue = ( r - g ) / delta + 4; break;
 
 			}
 
-			hsl.h = hue;
-			hsl.s = saturation;
-			hsl.l = lightness;
+			hue /= 6;
 
-			return hsl;
+		}
 
-		};
+		hsl.h = hue;
+		hsl.s = saturation;
+		hsl.l = lightness;
 
-	}(),
+		return hsl;
+
+	},
 
 	getStyle: function () {
 
@@ -612,10 +611,16 @@ THREE.Color.prototype = {
 
 	},
 
-	toArray: function () {
+	toArray: function ( array, offset ) {
+
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
 
-		return [ this.r, this.g, this.b ];
+		array[ offset ] = this.r;
+		array[ offset + 1 ] = this.g;
+		array[ offset + 2 ] = this.b;
 
+		return array;
 	},
 
 	clone: function () {
@@ -626,30 +631,32 @@ THREE.Color.prototype = {
 
 };
 
-THREE.ColorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF,
-"beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2,
-"brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50,
-"cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B,
-"darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B,
-"darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F,
-"darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3,
-"deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222,
-"floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700,
-"goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4,
-"indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00,
-"lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3,
-"lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA,
-"lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32,
-"linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3,
-"mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC,
-"mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD,
-"navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6,
-"palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9,
-"peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "red": 0xFF0000, "rosybrown": 0xBC8F8F,
-"royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE,
-"sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA,
-"springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0,
-"violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 };
+THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
+'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
+'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
+'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
+'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
+'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
+'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
+'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
+'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
+'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
+'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
+'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
+'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
+'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
+'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
+'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
+'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
+'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
+'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
+'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
+'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
+'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
+'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
+
+// File:src/math/Quaternion.js
 
 /**
  * @author mikael emtinger / http://gomo.se/
@@ -673,18 +680,6 @@ THREE.Quaternion.prototype = {
 
 	_x: 0,_y: 0, _z: 0, _w: 0,
 
-	_euler: undefined,
-
-	_updateEuler: function ( callback ) {
-
-		if ( this._euler !== undefined ) {
-
-			this._euler.setFromQuaternion( this, undefined, false );
-
-		}
-
-	},
-
 	get x () {
 
 		return this._x;
@@ -694,7 +689,7 @@ THREE.Quaternion.prototype = {
 	set x ( value ) {
 
 		this._x = value;
-		this._updateEuler();
+		this.onChangeCallback();
 
 	},
 
@@ -707,7 +702,7 @@ THREE.Quaternion.prototype = {
 	set y ( value ) {
 
 		this._y = value;
-		this._updateEuler();
+		this.onChangeCallback();
 
 	},
 
@@ -720,7 +715,7 @@ THREE.Quaternion.prototype = {
 	set z ( value ) {
 
 		this._z = value;
-		this._updateEuler();
+		this.onChangeCallback();
 
 	},
 
@@ -733,7 +728,7 @@ THREE.Quaternion.prototype = {
 	set w ( value ) {
 
 		this._w = value;
-		this._updateEuler();
+		this.onChangeCallback();
 
 	},
 
@@ -744,7 +739,7 @@ THREE.Quaternion.prototype = {
 		this._z = z;
 		this._w = w;
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
@@ -752,12 +747,12 @@ THREE.Quaternion.prototype = {
 
 	copy: function ( quaternion ) {
 
-		this._x = quaternion._x;
-		this._y = quaternion._y;
-		this._z = quaternion._z;
-		this._w = quaternion._w;
+		this._x = quaternion.x;
+		this._y = quaternion.y;
+		this._z = quaternion.z;
+		this._w = quaternion.w;
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
@@ -767,7 +762,7 @@ THREE.Quaternion.prototype = {
 
 		if ( euler instanceof THREE.Euler === false ) {
 
-			throw new Error( 'ERROR: Quaternion\'s .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+			throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
 		}
 
 		// http://www.mathworks.com/matlabcentral/fileexchange/
@@ -825,7 +820,7 @@ THREE.Quaternion.prototype = {
 
 		}
 
-		if ( update !== false ) this._updateEuler();
+		if ( update !== false ) this.onChangeCallback();
 
 		return this;
 
@@ -833,8 +828,9 @@ THREE.Quaternion.prototype = {
 
 	setFromAxisAngle: function ( axis, angle ) {
 
-		// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
-		// axis have to be normalized
+		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+		// assumes axis is normalized
 
 		var halfAngle = angle / 2, s = Math.sin( halfAngle );
 
@@ -843,7 +839,7 @@ THREE.Quaternion.prototype = {
 		this._z = axis.z * s;
 		this._w = Math.cos( halfAngle );
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
@@ -857,9 +853,9 @@ THREE.Quaternion.prototype = {
 
 		var te = m.elements,
 
-			m11 = te[0], m12 = te[4], m13 = te[8],
-			m21 = te[1], m22 = te[5], m23 = te[9],
-			m31 = te[2], m32 = te[6], m33 = te[10],
+			m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+			m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+			m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
 
 			trace = m11 + m22 + m33,
 			s;
@@ -877,19 +873,19 @@ THREE.Quaternion.prototype = {
 
 			s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
 
-			this._w = (m32 - m23 ) / s;
+			this._w = ( m32 - m23 ) / s;
 			this._x = 0.25 * s;
-			this._y = (m12 + m21 ) / s;
-			this._z = (m13 + m31 ) / s;
+			this._y = ( m12 + m21 ) / s;
+			this._z = ( m13 + m31 ) / s;
 
 		} else if ( m22 > m33 ) {
 
 			s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
 
-			this._w = (m13 - m31 ) / s;
-			this._x = (m12 + m21 ) / s;
+			this._w = ( m13 - m31 ) / s;
+			this._x = ( m12 + m21 ) / s;
 			this._y = 0.25 * s;
-			this._z = (m23 + m32 ) / s;
+			this._z = ( m23 + m32 ) / s;
 
 		} else {
 
@@ -902,12 +898,61 @@ THREE.Quaternion.prototype = {
 
 		}
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
 	},
 
+	setFromUnitVectors: function () {
+
+		// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
+
+		// assumes direction vectors vFrom and vTo are normalized
+
+		var v1, r;
+
+		var EPS = 0.000001;
+
+		return function ( vFrom, vTo ) {
+
+			if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+			r = vFrom.dot( vTo ) + 1;
+
+			if ( r < EPS ) {
+
+				r = 0;
+
+				if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+					v1.set( - vFrom.y, vFrom.x, 0 );
+
+				} else {
+
+					v1.set( 0, - vFrom.z, vFrom.y );
+
+				}
+
+			} else {
+
+				v1.crossVectors( vFrom, vTo );
+
+			}
+
+			this._x = v1.x;
+			this._y = v1.y;
+			this._z = v1.z;
+			this._w = r;
+
+			this.normalize();
+
+			return this;
+
+		}
+
+	}(),
+
 	inverse: function () {
 
 		this.conjugate().normalize();
@@ -918,16 +963,22 @@ THREE.Quaternion.prototype = {
 
 	conjugate: function () {
 
-		this._x *= -1;
-		this._y *= -1;
-		this._z *= -1;
+		this._x *= - 1;
+		this._y *= - 1;
+		this._z *= - 1;
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
 	},
 
+	dot: function ( v ) {
+
+		return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+	},
+
 	lengthSq: function () {
 
 		return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
@@ -962,6 +1013,8 @@ THREE.Quaternion.prototype = {
 
 		}
 
+		this.onChangeCallback();
+
 		return this;
 
 	},
@@ -970,7 +1023,7 @@ THREE.Quaternion.prototype = {
 
 		if ( p !== undefined ) {
 
-			console.warn( 'DEPRECATED: Quaternion\'s .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+			THREE.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
 			return this.multiplyQuaternions( q, p );
 
 		}
@@ -991,7 +1044,7 @@ THREE.Quaternion.prototype = {
 		this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
 		this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
@@ -999,13 +1052,16 @@ THREE.Quaternion.prototype = {
 
 	multiplyVector3: function ( vector ) {
 
-		console.warn( 'DEPRECATED: Quaternion\'s .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+		THREE.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
 		return vector.applyQuaternion( this );
 
 	},
 
 	slerp: function ( qb, t ) {
 
+		if ( t === 0 ) return this;
+		if ( t === 1 ) return this.copy( qb );
+
 		var x = this._x, y = this._y, z = this._z, w = this._w;
 
 		// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
@@ -1014,12 +1070,12 @@ THREE.Quaternion.prototype = {
 
 		if ( cosHalfTheta < 0 ) {
 
-			this._w = -qb._w;
-			this._x = -qb._x;
-			this._y = -qb._y;
-			this._z = -qb._z;
+			this._w = - qb._w;
+			this._x = - qb._x;
+			this._y = - qb._y;
+			this._z = - qb._z;
 
-			cosHalfTheta = -cosHalfTheta;
+			cosHalfTheta = - cosHalfTheta;
 
 		} else {
 
@@ -1060,7 +1116,7 @@ THREE.Quaternion.prototype = {
 		this._y = ( y * ratioA + this._y * ratioB );
 		this._z = ( z * ratioA + this._z * ratioB );
 
-		this._updateEuler();
+		this.onChangeCallback();
 
 		return this;
 
@@ -1072,25 +1128,45 @@ THREE.Quaternion.prototype = {
 
 	},
 
-	fromArray: function ( array ) {
+	fromArray: function ( array, offset ) {
 
-		this._x = array[ 0 ];
-		this._y = array[ 1 ];
-		this._z = array[ 2 ];
-		this._w = array[ 3 ];
+		if ( offset === undefined ) offset = 0;
 
-		this._updateEuler();
+		this._x = array[ offset ];
+		this._y = array[ offset + 1 ];
+		this._z = array[ offset + 2 ];
+		this._w = array[ offset + 3 ];
+
+		this.onChangeCallback();
 
 		return this;
 
 	},
 
-	toArray: function () {
+	toArray: function ( array, offset ) {
+
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
 
-		return [ this._x, this._y, this._z, this._w ];
+		array[ offset ] = this._x;
+		array[ offset + 1 ] = this._y;
+		array[ offset + 2 ] = this._z;
+		array[ offset + 3 ] = this._w;
+
+		return array;
+
+	},
+
+	onChange: function ( callback ) {
+
+		this.onChangeCallback = callback;
+
+		return this;
 
 	},
 
+	onChangeCallback: function () {},
+
 	clone: function () {
 
 		return new THREE.Quaternion( this._x, this._y, this._z, this._w );
@@ -1105,6 +1181,8 @@ THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
 
 }
 
+// File:src/math/Vector2.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author philogb / http://blog.thejit.org/
@@ -1148,14 +1226,13 @@ THREE.Vector2.prototype = {
 
 	},
 
-
 	setComponent: function ( index, value ) {
 
 		switch ( index ) {
 
 			case 0: this.x = value; break;
 			case 1: this.y = value; break;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -1167,7 +1244,7 @@ THREE.Vector2.prototype = {
 
 			case 0: return this.x;
 			case 1: return this.y;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -1186,7 +1263,7 @@ THREE.Vector2.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector2\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
 			return this.addVectors( v, w );
 
 		}
@@ -1198,19 +1275,19 @@ THREE.Vector2.prototype = {
 
 	},
 
-	addVectors: function ( a, b ) {
+	addScalar: function ( s ) {
 
-		this.x = a.x + b.x;
-		this.y = a.y + b.y;
+		this.x += s;
+		this.y += s;
 
 		return this;
 
 	},
 
-	addScalar: function ( s ) {
+	addVectors: function ( a, b ) {
 
-		this.x += s;
-		this.y += s;
+		this.x = a.x + b.x;
+		this.y = a.y + b.y;
 
 		return this;
 
@@ -1220,7 +1297,7 @@ THREE.Vector2.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector2\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
 			return this.subVectors( v, w );
 
 		}
@@ -1232,6 +1309,15 @@ THREE.Vector2.prototype = {
 
 	},
 
+	subScalar: function ( s ) {
+
+		this.x -= s;
+		this.y -= s;
+
+		return this;
+
+	},
+
 	subVectors: function ( a, b ) {
 
 		this.x = a.x - b.x;
@@ -1241,6 +1327,15 @@ THREE.Vector2.prototype = {
 
 	},
 
+	multiply: function ( v ) {
+
+		this.x *= v.x;
+		this.y *= v.y;
+
+		return this;
+
+	},
+
 	multiplyScalar: function ( s ) {
 
 		this.x *= s;
@@ -1250,6 +1345,15 @@ THREE.Vector2.prototype = {
 
 	},
 
+	divide: function ( v ) {
+
+		this.x /= v.x;
+		this.y /= v.y;
+
+		return this;
+
+	},
+
 	divideScalar: function ( scalar ) {
 
 		if ( scalar !== 0 ) {
@@ -1330,13 +1434,73 @@ THREE.Vector2.prototype = {
 
 		}
 
+		return this;
+	},
+
+	clampScalar: ( function () {
+
+		var min, max;
+
+		return function ( minVal, maxVal ) {
+
+			if ( min === undefined ) {
+
+				min = new THREE.Vector2();
+				max = new THREE.Vector2();
+
+			}
+
+			min.set( minVal, minVal );
+			max.set( maxVal, maxVal );
+
+			return this.clamp( min, max );
+
+		};
+
+	} )(),
+
+	floor: function () {
+
+		this.x = Math.floor( this.x );
+		this.y = Math.floor( this.y );
+
+		return this;
+
+	},
+
+	ceil: function () {
+
+		this.x = Math.ceil( this.x );
+		this.y = Math.ceil( this.y );
+
+		return this;
+
+	},
+
+	round: function () {
+
+		this.x = Math.round( this.x );
+		this.y = Math.round( this.y );
+
+		return this;
+
+	},
+
+	roundToZero: function () {
+
+		this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+		this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
 		return this;
 
 	},
 
-	negate: function() {
+	negate: function () {
+
+		this.x = - this.x;
+		this.y = - this.y;
 
-		return this.multiplyScalar( - 1 );
+		return this;
 
 	},
 
@@ -1399,37 +1563,68 @@ THREE.Vector2.prototype = {
 
 	},
 
-	equals: function( v ) {
+	lerpVectors: function ( v1, v2, alpha ) {
+
+		this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+		return this;
+
+	},
+
+	equals: function ( v ) {
 
 		return ( ( v.x === this.x ) && ( v.y === this.y ) );
 
 	},
 
-	fromArray: function ( array ) {
+	fromArray: function ( array, offset ) {
 
-		this.x = array[ 0 ];
-		this.y = array[ 1 ];
+		if ( offset === undefined ) offset = 0;
+
+		this.x = array[ offset ];
+		this.y = array[ offset + 1 ];
 
 		return this;
 
 	},
 
-	toArray: function () {
+	toArray: function ( array, offset ) {
+
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
 
-		return [ this.x, this.y ];
+		array[ offset ] = this.x;
+		array[ offset + 1 ] = this.y;
+
+		return array;
 
 	},
 
-	clone: function () {
+	fromAttribute: function ( attribute, index, offset ) {
 
-		return new THREE.Vector2( this.x, this.y );
+		if ( offset === undefined ) offset = 0;
 
-	}
+		index = index * attribute.itemSize + offset;
 
-};
+		this.x = attribute.array[ index ];
+		this.y = attribute.array[ index + 1 ];
 
-/**
- * @author mrdoob / http://mrdoob.com/
+		return this;
+
+	},
+
+	clone: function () {
+
+		return new THREE.Vector2( this.x, this.y );
+
+	}
+
+};
+
+// File:src/math/Vector3.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
  * @author *kile / http://kile.stravaganza.org/
  * @author philogb / http://blog.thejit.org/
  * @author mikael emtinger / http://gomo.se/
@@ -1490,7 +1685,7 @@ THREE.Vector3.prototype = {
 			case 0: this.x = value; break;
 			case 1: this.y = value; break;
 			case 2: this.z = value; break;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -1503,7 +1698,7 @@ THREE.Vector3.prototype = {
 			case 0: return this.x;
 			case 1: return this.y;
 			case 2: return this.z;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -1523,7 +1718,7 @@ THREE.Vector3.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector3\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
 			return this.addVectors( v, w );
 
 		}
@@ -1560,7 +1755,7 @@ THREE.Vector3.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector3\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
 			return this.subVectors( v, w );
 
 		}
@@ -1572,6 +1767,16 @@ THREE.Vector3.prototype = {
 		return this;
 
 	},
+	
+	subScalar: function ( s ) {
+
+		this.x -= s;
+		this.y -= s;
+		this.z -= s;
+
+		return this;
+
+	},
 
 	subVectors: function ( a, b ) {
 
@@ -1587,7 +1792,7 @@ THREE.Vector3.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector3\'s .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
 			return this.multiplyVectors( v, w );
 
 		}
@@ -1620,6 +1825,44 @@ THREE.Vector3.prototype = {
 
 	},
 
+	applyEuler: function () {
+
+		var quaternion;
+
+		return function ( euler ) {
+
+			if ( euler instanceof THREE.Euler === false ) {
+
+				THREE.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+			}
+
+			if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+			this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+			return this;
+
+		};
+
+	}(),
+
+	applyAxisAngle: function () {
+
+		var quaternion;
+
+		return function ( axis, angle ) {
+
+			if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+			this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+			return this;
+
+		};
+
+	}(),
+
 	applyMatrix3: function ( m ) {
 
 		var x = this.x;
@@ -1628,9 +1871,9 @@ THREE.Vector3.prototype = {
 
 		var e = m.elements;
 
-		this.x = e[0] * x + e[3] * y + e[6] * z;
-		this.y = e[1] * x + e[4] * y + e[7] * z;
-		this.z = e[2] * x + e[5] * y + e[8] * z;
+		this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+		this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+		this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
 
 		return this;
 
@@ -1644,9 +1887,9 @@ THREE.Vector3.prototype = {
 
 		var e = m.elements;
 
-		this.x = e[0] * x + e[4] * y + e[8]  * z + e[12];
-		this.y = e[1] * x + e[5] * y + e[9]  * z + e[13];
-		this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
+		this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z + e[ 12 ];
+		this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z + e[ 13 ];
+		this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
 
 		return this;
 
@@ -1659,11 +1902,11 @@ THREE.Vector3.prototype = {
 		var x = this.x, y = this.y, z = this.z;
 
 		var e = m.elements;
-		var d = 1 / ( e[3] * x + e[7] * y + e[11] * z + e[15] ); // perspective divide
+		var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
 
-		this.x = ( e[0] * x + e[4] * y + e[8]  * z + e[12] ) * d;
-		this.y = ( e[1] * x + e[5] * y + e[9]  * z + e[13] ) * d;
-		this.z = ( e[2] * x + e[6] * y + e[10] * z + e[14] ) * d;
+		this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z + e[ 12 ] ) * d;
+		this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z + e[ 13 ] ) * d;
+		this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
 
 		return this;
 
@@ -1685,18 +1928,48 @@ THREE.Vector3.prototype = {
 		var ix =  qw * x + qy * z - qz * y;
 		var iy =  qw * y + qz * x - qx * z;
 		var iz =  qw * z + qx * y - qy * x;
-		var iw = -qx * x - qy * y - qz * z;
+		var iw = - qx * x - qy * y - qz * z;
 
 		// calculate result * inverse quat
 
-		this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
-		this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
-		this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+		this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+		this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+		this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
 
 		return this;
 
 	},
 
+	project: function () {
+
+		var matrix;
+
+		return function ( camera ) {
+
+			if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+			matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+			return this.applyProjection( matrix );
+
+		};
+
+	}(),
+
+	unproject: function () {
+
+		var matrix;
+
+		return function ( camera ) {
+
+			if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+			matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+			return this.applyProjection( matrix );
+
+		};
+
+	}(),
+
 	transformDirection: function ( m ) {
 
 		// input: THREE.Matrix4 affine matrix
@@ -1706,9 +1979,9 @@ THREE.Vector3.prototype = {
 
 		var e = m.elements;
 
-		this.x = e[0] * x + e[4] * y + e[8]  * z;
-		this.y = e[1] * x + e[5] * y + e[9]  * z;
-		this.z = e[2] * x + e[6] * y + e[10] * z;
+		this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z;
+		this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z;
+		this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
 
 		this.normalize();
 
@@ -1834,9 +2107,75 @@ THREE.Vector3.prototype = {
 
 	},
 
+	clampScalar: ( function () {
+
+		var min, max;
+
+		return function ( minVal, maxVal ) {
+
+			if ( min === undefined ) {
+
+				min = new THREE.Vector3();
+				max = new THREE.Vector3();
+
+			}
+
+			min.set( minVal, minVal, minVal );
+			max.set( maxVal, maxVal, maxVal );
+
+			return this.clamp( min, max );
+
+		};
+
+	} )(),
+
+	floor: function () {
+
+		this.x = Math.floor( this.x );
+		this.y = Math.floor( this.y );
+		this.z = Math.floor( this.z );
+
+		return this;
+
+	},
+
+	ceil: function () {
+
+		this.x = Math.ceil( this.x );
+		this.y = Math.ceil( this.y );
+		this.z = Math.ceil( this.z );
+
+		return this;
+
+	},
+
+	round: function () {
+
+		this.x = Math.round( this.x );
+		this.y = Math.round( this.y );
+		this.z = Math.round( this.z );
+
+		return this;
+
+	},
+
+	roundToZero: function () {
+
+		this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+		this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+		this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+		return this;
+
+	},
+
 	negate: function () {
 
-		return this.multiplyScalar( - 1 );
+		this.x = - this.x;
+		this.y = - this.y;
+		this.z = - this.z;
+
+		return this;
 
 	},
 
@@ -1893,11 +2232,19 @@ THREE.Vector3.prototype = {
 
 	},
 
+	lerpVectors: function ( v1, v2, alpha ) {
+
+		this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+		return this;
+
+	},
+
 	cross: function ( v, w ) {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector3\'s .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
 			return this.crossVectors( v, w );
 
 		}
@@ -1925,13 +2272,64 @@ THREE.Vector3.prototype = {
 
 	},
 
+	projectOnVector: function () {
+
+		var v1, dot;
+
+		return function ( vector ) {
+
+			if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+			v1.copy( vector ).normalize();
+
+			dot = this.dot( v1 );
+
+			return this.copy( v1 ).multiplyScalar( dot );
+
+		};
+
+	}(),
+
+	projectOnPlane: function () {
+
+		var v1;
+
+		return function ( planeNormal ) {
+
+			if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+			v1.copy( this ).projectOnVector( planeNormal );
+
+			return this.sub( v1 );
+
+		}
+
+	}(),
+
+	reflect: function () {
+
+		// reflect incident vector off plane orthogonal to normal
+		// normal is assumed to have unit length
+
+		var v1;
+
+		return function ( normal ) {
+
+			if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+			return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+		}
+
+	}(),
+
 	angleTo: function ( v ) {
 
 		var theta = this.dot( v ) / ( this.length() * v.length() );
 
 		// clamp, to handle numerical problems
 
-		return Math.acos( THREE.Math.clamp( theta, -1, 1 ) );
+		return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
 
 	},
 
@@ -1953,162 +2351,130 @@ THREE.Vector3.prototype = {
 
 	setEulerFromRotationMatrix: function ( m, order ) {
 
-		console.error( "REMOVED: Vector3\'s setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code.");
+		THREE.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
 
 	},
 
 	setEulerFromQuaternion: function ( q, order ) {
 
-		console.error( "REMOVED: Vector3\'s setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code.");
+		THREE.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
 
 	},
 
 	getPositionFromMatrix: function ( m ) {
 
-		this.x = m.elements[12];
-		this.y = m.elements[13];
-		this.z = m.elements[14];
+		THREE.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
 
-		return this;
+		return this.setFromMatrixPosition( m );
 
 	},
 
 	getScaleFromMatrix: function ( m ) {
 
-		var sx = this.set( m.elements[0], m.elements[1], m.elements[2] ).length();
-		var sy = this.set( m.elements[4], m.elements[5], m.elements[6] ).length();
-		var sz = this.set( m.elements[8], m.elements[9], m.elements[10] ).length();
-
-		this.x = sx;
-		this.y = sy;
-		this.z = sz;
+		THREE.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
 
-		return this;
+		return this.setFromMatrixScale( m );
 	},
 
 	getColumnFromMatrix: function ( index, matrix ) {
 
-		var offset = index * 4;
-
-		var me = matrix.elements;
-
-		this.x = me[ offset ];
-		this.y = me[ offset + 1 ];
-		this.z = me[ offset + 2 ];
-
-		return this;
-
-	},
-
-	equals: function ( v ) {
+		THREE.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
 
-		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+		return this.setFromMatrixColumn( index, matrix );
 
 	},
 
-	fromArray: function ( array ) {
+	setFromMatrixPosition: function ( m ) {
 
-		this.x = array[ 0 ];
-		this.y = array[ 1 ];
-		this.z = array[ 2 ];
+		this.x = m.elements[ 12 ];
+		this.y = m.elements[ 13 ];
+		this.z = m.elements[ 14 ];
 
 		return this;
 
 	},
 
-	toArray: function () {
-
-		return [ this.x, this.y, this.z ];
-
-	},
-
-	clone: function () {
-
-		return new THREE.Vector3( this.x, this.y, this.z );
-
-	}
-
-};
-
-THREE.extend( THREE.Vector3.prototype, {
-
-	applyEuler: function () {
-
-		var quaternion = new THREE.Quaternion();
-
-		return function ( euler ) {
-
-			if ( euler instanceof THREE.Euler === false ) {
-
-				console.error( 'ERROR: Vector3\'s .applyEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
-
-			}
+	setFromMatrixScale: function ( m ) {
 
-			this.applyQuaternion( quaternion.setFromEuler( euler ) );
+		var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[  2 ] ).length();
+		var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[  6 ] ).length();
+		var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
 
-			return this;
+		this.x = sx;
+		this.y = sy;
+		this.z = sz;
 
-		};
+		return this;
+	},
 
-	}(),
+	setFromMatrixColumn: function ( index, matrix ) {
+		
+		var offset = index * 4;
 
-	applyAxisAngle: function () {
+		var me = matrix.elements;
 
-		var quaternion = new THREE.Quaternion();
+		this.x = me[ offset ];
+		this.y = me[ offset + 1 ];
+		this.z = me[ offset + 2 ];
 
-		return function ( axis, angle ) {
+		return this;
 
-			this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+	},
 
-			return this;
+	equals: function ( v ) {
 
-		};
+		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
 
-	}(),
+	},
 
-	projectOnVector: function () {
+	fromArray: function ( array, offset ) {
 
-		var v1 = new THREE.Vector3();
+		if ( offset === undefined ) offset = 0;
 
-		return function ( vector ) {
+		this.x = array[ offset ];
+		this.y = array[ offset + 1 ];
+		this.z = array[ offset + 2 ];
 
-			v1.copy( vector ).normalize();
-			var d = this.dot( v1 );
-			return this.copy( v1 ).multiplyScalar( d );
+		return this;
 
-		};
+	},
 
-	}(),
+	toArray: function ( array, offset ) {
 
-	projectOnPlane: function () {
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
 
-		var v1 = new THREE.Vector3();
+		array[ offset ] = this.x;
+		array[ offset + 1 ] = this.y;
+		array[ offset + 2 ] = this.z;
 
-		return function ( planeNormal ) {
+		return array;
 
-			v1.copy( this ).projectOnVector( planeNormal );
+	},
 
-			return this.sub( v1 );
+	fromAttribute: function ( attribute, index, offset ) {
 
-		}
+		if ( offset === undefined ) offset = 0;
 
-	}(),
+		index = index * attribute.itemSize + offset;
 
-	reflect: function () {
+		this.x = attribute.array[ index ];
+		this.y = attribute.array[ index + 1 ];
+		this.z = attribute.array[ index + 2 ];
 
-		var v1 = new THREE.Vector3();
+		return this;
 
-		return function ( vector ) {
+	},
 
-		    v1.copy( this ).projectOnVector( vector ).multiplyScalar( 2 );
+	clone: function () {
 
-		    return this.subVectors( v1, this );
+		return new THREE.Vector3( this.x, this.y, this.z );
 
-		}
+	}
 
-	}()
+};
 
-} );
+// File:src/math/Vector4.js
 
 /**
  * @author supereggbert / http://www.paulbrunt.co.uk/
@@ -2182,7 +2548,7 @@ THREE.Vector4.prototype = {
 			case 1: this.y = value; break;
 			case 2: this.z = value; break;
 			case 3: this.w = value; break;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -2196,7 +2562,7 @@ THREE.Vector4.prototype = {
 			case 1: return this.y;
 			case 2: return this.z;
 			case 3: return this.w;
-			default: throw new Error( "index is out of range: " + index );
+			default: throw new Error( 'index is out of range: ' + index );
 
 		}
 
@@ -2217,7 +2583,7 @@ THREE.Vector4.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector4\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
 			return this.addVectors( v, w );
 
 		}
@@ -2257,7 +2623,7 @@ THREE.Vector4.prototype = {
 
 		if ( w !== undefined ) {
 
-			console.warn( 'DEPRECATED: Vector4\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			THREE.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
 			return this.subVectors( v, w );
 
 		}
@@ -2271,9 +2637,20 @@ THREE.Vector4.prototype = {
 
 	},
 
-	subVectors: function ( a, b ) {
+	subScalar: function ( s ) {
 
-		this.x = a.x - b.x;
+		this.x -= s;
+		this.y -= s;
+		this.z -= s;
+		this.w -= s;
+
+		return this;
+
+	},
+
+	subVectors: function ( a, b ) {
+
+		this.x = a.x - b.x;
 		this.y = a.y - b.y;
 		this.z = a.z - b.z;
 		this.w = a.w - b.w;
@@ -2302,10 +2679,10 @@ THREE.Vector4.prototype = {
 
 		var e = m.elements;
 
-		this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w;
-		this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w;
-		this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w;
-		this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w;
+		this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+		this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+		this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+		this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
 
 		return this;
 
@@ -2375,22 +2752,22 @@ THREE.Vector4.prototype = {
 
 			te = m.elements,
 
-			m11 = te[0], m12 = te[4], m13 = te[8],
-			m21 = te[1], m22 = te[5], m23 = te[9],
-			m31 = te[2], m32 = te[6], m33 = te[10];
+			m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+			m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+			m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
 
 		if ( ( Math.abs( m12 - m21 ) < epsilon )
-		  && ( Math.abs( m13 - m31 ) < epsilon )
-		  && ( Math.abs( m23 - m32 ) < epsilon ) ) {
+		   && ( Math.abs( m13 - m31 ) < epsilon )
+		   && ( Math.abs( m23 - m32 ) < epsilon ) ) {
 
 			// singularity found
 			// first check for identity matrix which must have +1 for all terms
 			// in leading diagonal and zero in other terms
 
 			if ( ( Math.abs( m12 + m21 ) < epsilon2 )
-			  && ( Math.abs( m13 + m31 ) < epsilon2 )
-			  && ( Math.abs( m23 + m32 ) < epsilon2 )
-			  && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+			   && ( Math.abs( m13 + m31 ) < epsilon2 )
+			   && ( Math.abs( m23 + m32 ) < epsilon2 )
+			   && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
 
 				// this singularity is identity matrix so angle = 0
 
@@ -2470,8 +2847,8 @@ THREE.Vector4.prototype = {
 		// as we have reached here there are no singularities so we can handle normally
 
 		var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
-						 + ( m13 - m31 ) * ( m13 - m31 )
-						 + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+						  + ( m13 - m31 ) * ( m13 - m31 )
+						  + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
 
 		if ( Math.abs( s ) < 0.001 ) s = 1;
 
@@ -2595,9 +2972,80 @@ THREE.Vector4.prototype = {
 
 	},
 
-	negate: function() {
+	clampScalar: ( function () {
+
+		var min, max;
+
+		return function ( minVal, maxVal ) {
+
+			if ( min === undefined ) {
+
+				min = new THREE.Vector4();
+				max = new THREE.Vector4();
+
+			}
+
+			min.set( minVal, minVal, minVal, minVal );
+			max.set( maxVal, maxVal, maxVal, maxVal );
+
+			return this.clamp( min, max );
+
+		};
+
+	} )(),
+
+  floor: function () {
+
+		this.x = Math.floor( this.x );
+		this.y = Math.floor( this.y );
+		this.z = Math.floor( this.z );
+		this.w = Math.floor( this.w );
+
+		return this;
+
+  },
+
+  ceil: function () {
+
+		this.x = Math.ceil( this.x );
+		this.y = Math.ceil( this.y );
+		this.z = Math.ceil( this.z );
+		this.w = Math.ceil( this.w );
+
+		return this;
+
+  },
+
+  round: function () {
+
+		this.x = Math.round( this.x );
+		this.y = Math.round( this.y );
+		this.z = Math.round( this.z );
+		this.w = Math.round( this.w );
+
+		return this;
+
+  },
+
+  roundToZero: function () {
+
+		this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+		this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+		this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+		this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+		return this;
+
+  },
+
+	negate: function () {
+
+		this.x = - this.x;
+		this.y = - this.y;
+		this.z = - this.z;
+		this.w = - this.w;
 
-		return this.multiplyScalar( -1 );
+		return this;
 
 	},
 
@@ -2656,26 +3104,59 @@ THREE.Vector4.prototype = {
 
 	},
 
+	lerpVectors: function ( v1, v2, alpha ) {
+
+		this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+		return this;
+
+	},
+
 	equals: function ( v ) {
 
 		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
 
 	},
 
-	fromArray: function ( array ) {
+	fromArray: function ( array, offset ) {
 
-		this.x = array[ 0 ];
-		this.y = array[ 1 ];
-		this.z = array[ 2 ];
-		this.w = array[ 3 ];
+		if ( offset === undefined ) offset = 0;
+
+		this.x = array[ offset ];
+		this.y = array[ offset + 1 ];
+		this.z = array[ offset + 2 ];
+		this.w = array[ offset + 3 ];
 
 		return this;
 
 	},
 
-	toArray: function () {
+	toArray: function ( array, offset ) {
+
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
+
+		array[ offset ] = this.x;
+		array[ offset + 1 ] = this.y;
+		array[ offset + 2 ] = this.z;
+		array[ offset + 3 ] = this.w;
+
+		return array;
+
+	},
+
+	fromAttribute: function ( attribute, index, offset ) {
 
-		return [ this.x, this.y, this.z, this.w ];
+		if ( offset === undefined ) offset = 0;
+
+		index = index * attribute.itemSize + offset;
+
+		this.x = attribute.array[ index ];
+		this.y = attribute.array[ index + 1 ];
+		this.z = attribute.array[ index + 2 ];
+		this.w = attribute.array[ index + 3 ];
+
+		return this;
 
 	},
 
@@ -2687,6 +3168,8 @@ THREE.Vector4.prototype = {
 
 };
 
+// File:src/math/Euler.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author WestLangley / http://github.com/WestLangley
@@ -2712,18 +3195,6 @@ THREE.Euler.prototype = {
 
 	_x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder,
 
-	_quaternion: undefined,
-
-	_updateQuaternion: function () {
-
-		if ( this._quaternion !== undefined ) {
-
-			this._quaternion.setFromEuler( this, false );
-
-		}
-
-	},
-
 	get x () {
 
 		return this._x;
@@ -2733,7 +3204,7 @@ THREE.Euler.prototype = {
 	set x ( value ) {
 
 		this._x = value;
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 	},
 
@@ -2746,7 +3217,7 @@ THREE.Euler.prototype = {
 	set y ( value ) {
 
 		this._y = value;
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 	},
 
@@ -2759,7 +3230,7 @@ THREE.Euler.prototype = {
 	set z ( value ) {
 
 		this._z = value;
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 	},
 
@@ -2772,7 +3243,7 @@ THREE.Euler.prototype = {
 	set order ( value ) {
 
 		this._order = value;
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 	},
 
@@ -2783,7 +3254,7 @@ THREE.Euler.prototype = {
 		this._z = z;
 		this._order = order || this._order;
 
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 		return this;
 
@@ -2796,34 +3267,28 @@ THREE.Euler.prototype = {
 		this._z = euler._z;
 		this._order = euler._order;
 
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 		return this;
 
 	},
 
-	setFromRotationMatrix: function ( m, order ) {
-
-		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
-		// clamp, to handle numerical problems
-
-		function clamp( x ) {
+	setFromRotationMatrix: function ( m, order, update ) {
 
-			return Math.min( Math.max( x, -1 ), 1 );
+		var clamp = THREE.Math.clamp;
 
-		}
+		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
 
 		var te = m.elements;
-		var m11 = te[0], m12 = te[4], m13 = te[8];
-		var m21 = te[1], m22 = te[5], m23 = te[9];
-		var m31 = te[2], m32 = te[6], m33 = te[10];
+		var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+		var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+		var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
 
 		order = order || this._order;
 
 		if ( order === 'XYZ' ) {
 
-			this._y = Math.asin( clamp( m13 ) );
+			this._y = Math.asin( clamp( m13, - 1, 1 ) );
 
 			if ( Math.abs( m13 ) < 0.99999 ) {
 
@@ -2839,7 +3304,7 @@ THREE.Euler.prototype = {
 
 		} else if ( order === 'YXZ' ) {
 
-			this._x = Math.asin( - clamp( m23 ) );
+			this._x = Math.asin( - clamp( m23, - 1, 1 ) );
 
 			if ( Math.abs( m23 ) < 0.99999 ) {
 
@@ -2855,7 +3320,7 @@ THREE.Euler.prototype = {
 
 		} else if ( order === 'ZXY' ) {
 
-			this._x = Math.asin( clamp( m32 ) );
+			this._x = Math.asin( clamp( m32, - 1, 1 ) );
 
 			if ( Math.abs( m32 ) < 0.99999 ) {
 
@@ -2871,7 +3336,7 @@ THREE.Euler.prototype = {
 
 		} else if ( order === 'ZYX' ) {
 
-			this._y = Math.asin( - clamp( m31 ) );
+			this._y = Math.asin( - clamp( m31, - 1, 1 ) );
 
 			if ( Math.abs( m31 ) < 0.99999 ) {
 
@@ -2887,7 +3352,7 @@ THREE.Euler.prototype = {
 
 		} else if ( order === 'YZX' ) {
 
-			this._z = Math.asin( clamp( m21 ) );
+			this._z = Math.asin( clamp( m21, - 1, 1 ) );
 
 			if ( Math.abs( m21 ) < 0.99999 ) {
 
@@ -2903,7 +3368,7 @@ THREE.Euler.prototype = {
 
 		} else if ( order === 'XZY' ) {
 
-			this._z = Math.asin( - clamp( m12 ) );
+			this._z = Math.asin( - clamp( m12, - 1, 1 ) );
 
 			if ( Math.abs( m12 ) < 0.99999 ) {
 
@@ -2919,86 +3384,37 @@ THREE.Euler.prototype = {
 
 		} else {
 
-			console.warn( 'WARNING: Euler.setFromRotationMatrix() given unsupported order: ' + order )
+			THREE.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
 
 		}
 
 		this._order = order;
 
-		this._updateQuaternion();
+		if ( update !== false ) this.onChangeCallback();
 
 		return this;
 
 	},
 
-	setFromQuaternion: function ( q, order, update ) {
-
-		// q is assumed to be normalized
-
-		// clamp, to handle numerical problems
-
-		function clamp( x ) {
-
-			return Math.min( Math.max( x, -1 ), 1 );
-
-		}
-
-		// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
-
-		var sqx = q.x * q.x;
-		var sqy = q.y * q.y;
-		var sqz = q.z * q.z;
-		var sqw = q.w * q.w;
-
-		order = order || this._order;
-
-		if ( order === 'XYZ' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
-			this._y = Math.asin(  clamp( 2 * ( q.x * q.z + q.y * q.w ) ) );
-			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order ===  'YXZ' ) {
+	setFromQuaternion: function () {
 
-			this._x = Math.asin(  clamp( 2 * ( q.x * q.w - q.y * q.z ) ) );
-			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
-			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
+		var matrix;
 
-		} else if ( order === 'ZXY' ) {
-
-			this._x = Math.asin(  clamp( 2 * ( q.x * q.w + q.y * q.z ) ) );
-			this._y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
-			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
-
-		} else if ( order === 'ZYX' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
-			this._y = Math.asin(  clamp( 2 * ( q.y * q.w - q.x * q.z ) ) );
-			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order === 'YZX' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
-			this._y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
-			this._z = Math.asin(  clamp( 2 * ( q.x * q.y + q.z * q.w ) ) );
+		return function ( q, order, update ) {
 
-		} else if ( order === 'XZY' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
-			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
-			this._z = Math.asin(  clamp( 2 * ( q.z * q.w - q.x * q.y ) ) );
-
-		} else {
+			if ( matrix === undefined ) matrix = new THREE.Matrix4();
+			matrix.makeRotationFromQuaternion( q );
+			this.setFromRotationMatrix( matrix, order, update );
 
-			console.warn( 'WARNING: Euler.setFromQuaternion() given unsupported order: ' + order )
+			return this;
 
-		}
+		};
 
-		this._order = order;
+	}(),
 
-		if ( update !== false ) this._updateQuaternion();
+	setFromVector3: function ( v, order ) {
 
-		return this;
+		return this.set( v.x, v.y, v.z, order || this._order );
 
 	},
 
@@ -3015,9 +3431,14 @@ THREE.Euler.prototype = {
 
 		};
 
-
 	}(),
 
+	equals: function ( euler ) {
+
+		return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+	},
+
 	fromArray: function ( array ) {
 
 		this._x = array[ 0 ];
@@ -3025,24 +3446,49 @@ THREE.Euler.prototype = {
 		this._z = array[ 2 ];
 		if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
 
-		this._updateQuaternion();
+		this.onChangeCallback();
 
 		return this;
 
 	},
 
-	toArray: function () {
+	toArray: function ( array, offset ) {
+
+		if ( array === undefined ) array = [];
+		if ( offset === undefined ) offset = 0;
 
-		return [ this._x, this._y, this._z, this._order ];
+		array[ offset ] = this._x;
+		array[ offset + 1 ] = this._y;
+		array[ offset + 2 ] = this._z;
+		array[ offset + 3 ] = this._order;
 
+		return array;
 	},
 
-	equals: function ( euler ) {
+	toVector3: function ( optionalResult ) {
 
-		return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+		if ( optionalResult ) {
+
+			return optionalResult.set( this._x, this._y, this._z );
+
+		} else {
+
+			return new THREE.Vector3( this._x, this._y, this._z );
+
+		}
+
+	},
+
+	onChange: function ( callback ) {
+
+		this.onChangeCallback = callback;
+
+		return this;
 
 	},
 
+	onChangeCallback: function () {},
+
 	clone: function () {
 
 		return new THREE.Euler( this._x, this._y, this._z, this._order );
@@ -3051,6 +3497,8 @@ THREE.Euler.prototype = {
 
 };
 
+// File:src/math/Line3.js
+
 /**
  * @author bhouston / http://exocortex.com
  */
@@ -3118,7 +3566,7 @@ THREE.Line3.prototype = {
 
 	},
 
-	closestPointToPointParameter: function() {
+	closestPointToPointParameter: function () {
 
 		var startP = new THREE.Vector3();
 		var startEnd = new THREE.Vector3();
@@ -3178,6 +3626,8 @@ THREE.Line3.prototype = {
 
 };
 
+// File:src/math/Box2.js
+
 /**
  * @author bhouston / http://exocortex.com
  */
@@ -3185,7 +3635,7 @@ THREE.Line3.prototype = {
 THREE.Box2 = function ( min, max ) {
 
 	this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity );
-	this.max = ( max !== undefined ) ? max : new THREE.Vector2( -Infinity, -Infinity );
+	this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity );
 
 };
 
@@ -3204,42 +3654,11 @@ THREE.Box2.prototype = {
 
 	setFromPoints: function ( points ) {
 
-		if ( points.length > 0 ) {
-
-			var point = points[ 0 ];
-
-			this.min.copy( point );
-			this.max.copy( point );
-
-			for ( var i = 1, il = points.length; i < il; i ++ ) {
-
-				point = points[ i ];
-
-				if ( point.x < this.min.x ) {
-
-					this.min.x = point.x;
-
-				} else if ( point.x > this.max.x ) {
-
-					this.max.x = point.x;
-
-				}
-
-				if ( point.y < this.min.y ) {
-
-					this.min.y = point.y;
-
-				} else if ( point.y > this.max.y ) {
-
-					this.max.y = point.y;
-
-				}
-
-			}
+		this.makeEmpty();
 
-		} else {
+		for ( var i = 0, il = points.length; i < il; i ++ ) {
 
-			this.makeEmpty();
+			this.expandByPoint( points[ i ] )
 
 		}
 
@@ -3275,7 +3694,7 @@ THREE.Box2.prototype = {
 	makeEmpty: function () {
 
 		this.min.x = this.min.y = Infinity;
-		this.max.x = this.max.y = -Infinity;
+		this.max.x = this.max.y = - Infinity;
 
 		return this;
 
@@ -3321,7 +3740,7 @@ THREE.Box2.prototype = {
 
 	expandByScalar: function ( scalar ) {
 
-		this.min.addScalar( -scalar );
+		this.min.addScalar( - scalar );
 		this.max.addScalar( scalar );
 
 		return this;
@@ -3353,12 +3772,14 @@ THREE.Box2.prototype = {
 
 	},
 
-	getParameter: function ( point ) {
+	getParameter: function ( point, optionalTarget ) {
 
 		// This can potentially have a divide by zero if the box
 		// has a size dimension of 0.
 
-		return new THREE.Vector2(
+		var result = optionalTarget || new THREE.Vector2();
+
+		return result.set(
 			( point.x - this.min.x ) / ( this.max.x - this.min.x ),
 			( point.y - this.min.y ) / ( this.max.y - this.min.y )
 		);
@@ -3441,6 +3862,8 @@ THREE.Box2.prototype = {
 
 };
 
+// File:src/math/Box3.js
+
 /**
  * @author bhouston / http://exocortex.com
  * @author WestLangley / http://github.com/WestLangley
@@ -3449,7 +3872,7 @@ THREE.Box2.prototype = {
 THREE.Box3 = function ( min, max ) {
 
 	this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity );
-	this.max = ( max !== undefined ) ? max : new THREE.Vector3( -Infinity, -Infinity, -Infinity );
+	this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity );
 
 };
 
@@ -3466,58 +3889,13 @@ THREE.Box3.prototype = {
 
 	},
 
-	addPoint: function ( point ) {
-
-		if ( point.x < this.min.x ) {
-
-			this.min.x = point.x;
-
-		} else if ( point.x > this.max.x ) {
-
-			this.max.x = point.x;
-
-		}
-
-		if ( point.y < this.min.y ) {
-
-			this.min.y = point.y;
-
-		} else if ( point.y > this.max.y ) {
-
-			this.max.y = point.y;
-
-		}
-
-		if ( point.z < this.min.z ) {
-
-			this.min.z = point.z;
-
-		} else if ( point.z > this.max.z ) {
-
-			this.max.z = point.z;
-
-		}
-
-	},
-
 	setFromPoints: function ( points ) {
 
-		if ( points.length > 0 ) {
-
-			var point = points[ 0 ];
-
-			this.min.copy( point );
-			this.max.copy( point );
-
-			for ( var i = 1, il = points.length; i < il; i ++ ) {
-
-				this.addPoint( points[ i ] )
-
-			}
+		this.makeEmpty();
 
-		} else {
+		for ( var i = 0, il = points.length; i < il; i ++ ) {
 
-			this.makeEmpty();
+			this.expandByPoint( points[ i ] )
 
 		}
 
@@ -3525,7 +3903,7 @@ THREE.Box3.prototype = {
 
 	},
 
-	setFromCenterAndSize: function() {
+	setFromCenterAndSize: function () {
 
 		var v1 = new THREE.Vector3();
 
@@ -3542,14 +3920,14 @@ THREE.Box3.prototype = {
 
 	}(),
 
-	setFromObject: function() {
+	setFromObject: function () {
 
 		// Computes the world-axis-aligned bounding box of an object (including its children),
 		// accounting for both the object's, and childrens', world transforms
 
 		var v1 = new THREE.Vector3();
 
-		return function( object ) {
+		return function ( object ) {
 
 			var scope = this;
 
@@ -3559,17 +3937,37 @@ THREE.Box3.prototype = {
 
 			object.traverse( function ( node ) {
 
-				if ( node.geometry !== undefined && node.geometry.vertices !== undefined ) {
+				var geometry = node.geometry;
+
+				if ( geometry !== undefined ) {
+
+					if ( geometry instanceof THREE.Geometry ) {
+
+						var vertices = geometry.vertices;
+
+						for ( var i = 0, il = vertices.length; i < il; i ++ ) {
 
-					var vertices = node.geometry.vertices;
+							v1.copy( vertices[ i ] );
 
-					for ( var i = 0, il = vertices.length; i < il; i++ ) {
+							v1.applyMatrix4( node.matrixWorld );
 
-						v1.copy( vertices[ i ] );
+							scope.expandByPoint( v1 );
 
-						v1.applyMatrix4( node.matrixWorld );
+						}
+
+					} else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {
+
+						var positions = geometry.attributes[ 'position' ].array;
+
+						for ( var i = 0, il = positions.length; i < il; i += 3 ) {
 
-						scope.expandByPoint( v1 );
+							v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+
+							v1.applyMatrix4( node.matrixWorld );
+
+							scope.expandByPoint( v1 );
+
+						}
 
 					}
 
@@ -3595,7 +3993,7 @@ THREE.Box3.prototype = {
 	makeEmpty: function () {
 
 		this.min.x = this.min.y = this.min.z = Infinity;
-		this.max.x = this.max.y = this.max.z = -Infinity;
+		this.max.x = this.max.y = this.max.z = - Infinity;
 
 		return this;
 
@@ -3643,7 +4041,7 @@ THREE.Box3.prototype = {
 
 	expandByScalar: function ( scalar ) {
 
-		this.min.addScalar( -scalar );
+		this.min.addScalar( - scalar );
 		this.max.addScalar( scalar );
 
 		return this;
@@ -3678,12 +4076,14 @@ THREE.Box3.prototype = {
 
 	},
 
-	getParameter: function ( point ) {
+	getParameter: function ( point, optionalTarget ) {
 
 		// This can potentially have a divide by zero if the box
 		// has a size dimension of 0.
 
-		return new THREE.Vector3(
+		var result = optionalTarget || new THREE.Vector3();
+
+		return result.set(
 			( point.x - this.min.x ) / ( this.max.x - this.min.x ),
 			( point.y - this.min.y ) / ( this.max.y - this.min.y ),
 			( point.z - this.min.z ) / ( this.max.z - this.min.z )
@@ -3714,7 +4114,7 @@ THREE.Box3.prototype = {
 
 	},
 
-	distanceToPoint: function() {
+	distanceToPoint: function () {
 
 		var v1 = new THREE.Vector3();
 
@@ -3727,7 +4127,7 @@ THREE.Box3.prototype = {
 
 	}(),
 
-	getBoundingSphere: function() {
+	getBoundingSphere: function () {
 
 		var v1 = new THREE.Vector3();
 
@@ -3762,7 +4162,7 @@ THREE.Box3.prototype = {
 
 	},
 
-	applyMatrix4: function() {
+	applyMatrix4: function () {
 
 		var points = [
 			new THREE.Vector3(),
@@ -3778,14 +4178,14 @@ THREE.Box3.prototype = {
 		return function ( matrix ) {
 
 			// NOTE: I am using a binary pattern to specify all 2^3 combinations below
-			points[0].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
-			points[1].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
-			points[2].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
-			points[3].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
-			points[4].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
-			points[5].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
-			points[6].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
-			points[7].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );  // 111
+			points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
+			points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
+			points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
+			points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
+			points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
+			points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
+			points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
+			points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );  // 111
 
 			this.makeEmpty();
 			this.setFromPoints( points );
@@ -3819,23 +4219,30 @@ THREE.Box3.prototype = {
 
 };
 
+// File:src/math/Matrix3.js
+
 /**
  * @author alteredq / http://alteredqualia.com/
  * @author WestLangley / http://github.com/WestLangley
  * @author bhouston / http://exocortex.com
  */
 
-THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+THREE.Matrix3 = function () {
 
-	this.elements = new Float32Array(9);
+	this.elements = new Float32Array( [
 
-	this.set(
+		1, 0, 0,
+		0, 1, 0,
+		0, 0, 1
 
-		( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0,
-		n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0,
-		n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1
+	] );
+
+	if ( arguments.length > 0 ) {
+
+		THREE.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
+
+	}
 
-	);
 };
 
 THREE.Matrix3.prototype = {
@@ -3846,9 +4253,9 @@ THREE.Matrix3.prototype = {
 
 		var te = this.elements;
 
-		te[0] = n11; te[3] = n12; te[6] = n13;
-		te[1] = n21; te[4] = n22; te[7] = n23;
-		te[2] = n31; te[5] = n32; te[8] = n33;
+		te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13;
+		te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23;
+		te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33;
 
 		return this;
 
@@ -3874,9 +4281,9 @@ THREE.Matrix3.prototype = {
 
 		this.set(
 
-			me[0], me[3], me[6],
-			me[1], me[4], me[7],
-			me[2], me[5], me[8]
+			me[ 0 ], me[ 3 ], me[ 6 ],
+			me[ 1 ], me[ 4 ], me[ 7 ],
+			me[ 2 ], me[ 5 ], me[ 8 ]
 
 		);
 
@@ -3886,32 +4293,42 @@ THREE.Matrix3.prototype = {
 
 	multiplyVector3: function ( vector ) {
 
-		console.warn( 'DEPRECATED: Matrix3\'s .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
+		THREE.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
 		return vector.applyMatrix3( this );
 
 	},
 
-	multiplyVector3Array: function() {
+	multiplyVector3Array: function ( a ) {
+
+		THREE.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+		return this.applyToVector3Array( a );
+
+	},
+
+	applyToVector3Array: function () {
 
 		var v1 = new THREE.Vector3();
 
-		return function ( a ) {
+		return function ( array, offset, length ) {
+
+			if ( offset === undefined ) offset = 0;
+			if ( length === undefined ) length = array.length;
 
-			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+			for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
 
-				v1.x = a[ i ];
-				v1.y = a[ i + 1 ];
-				v1.z = a[ i + 2 ];
+				v1.x = array[ j ];
+				v1.y = array[ j + 1 ];
+				v1.z = array[ j + 2 ];
 
-				v1.applyMatrix3(this);
+				v1.applyMatrix3( this );
 
-				a[ i ]     = v1.x;
-				a[ i + 1 ] = v1.y;
-				a[ i + 2 ] = v1.z;
+				array[ j ]     = v1.x;
+				array[ j + 1 ] = v1.y;
+				array[ j + 2 ] = v1.z;
 
 			}
 
-			return a;
+			return array;
 
 		};
 
@@ -3921,9 +4338,9 @@ THREE.Matrix3.prototype = {
 
 		var te = this.elements;
 
-		te[0] *= s; te[3] *= s; te[6] *= s;
-		te[1] *= s; te[4] *= s; te[7] *= s;
-		te[2] *= s; te[5] *= s; te[8] *= s;
+		te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+		te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+		te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
 
 		return this;
 
@@ -3933,11 +4350,11 @@ THREE.Matrix3.prototype = {
 
 		var te = this.elements;
 
-		var a = te[0], b = te[1], c = te[2],
-			d = te[3], e = te[4], f = te[5],
-			g = te[6], h = te[7], i = te[8];
+		var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+			d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+			g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
 
-		return a*e*i - a*f*h - b*d*i + b*f*g + c*d*h - c*e*g;
+		return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
 
 	},
 
@@ -3949,15 +4366,15 @@ THREE.Matrix3.prototype = {
 		var me = matrix.elements;
 		var te = this.elements;
 
-		te[ 0 ] =   me[10] * me[5] - me[6] * me[9];
-		te[ 1 ] = - me[10] * me[1] + me[2] * me[9];
-		te[ 2 ] =   me[6] * me[1] - me[2] * me[5];
-		te[ 3 ] = - me[10] * me[4] + me[6] * me[8];
-		te[ 4 ] =   me[10] * me[0] - me[2] * me[8];
-		te[ 5 ] = - me[6] * me[0] + me[2] * me[4];
-		te[ 6 ] =   me[9] * me[4] - me[5] * me[8];
-		te[ 7 ] = - me[9] * me[0] + me[1] * me[8];
-		te[ 8 ] =   me[5] * me[0] - me[1] * me[4];
+		te[ 0 ] =   me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ];
+		te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ];
+		te[ 2 ] =   me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ];
+		te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ];
+		te[ 4 ] =   me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ];
+		te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ];
+		te[ 6 ] =   me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ];
+		te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ];
+		te[ 8 ] =   me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ];
 
 		var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
 
@@ -3973,7 +4390,7 @@ THREE.Matrix3.prototype = {
 
 			} else {
 
-				console.warn( msg );
+				THREE.warn( msg );
 
 			}
 
@@ -3993,14 +4410,34 @@ THREE.Matrix3.prototype = {
 
 		var tmp, m = this.elements;
 
-		tmp = m[1]; m[1] = m[3]; m[3] = tmp;
-		tmp = m[2]; m[2] = m[6]; m[6] = tmp;
-		tmp = m[5]; m[5] = m[7]; m[7] = tmp;
+		tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+		tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+		tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
 
 		return this;
 
 	},
 
+	flattenToArrayOffset: function ( array, offset ) {
+
+		var te = this.elements;
+
+		array[ offset     ] = te[ 0 ];
+		array[ offset + 1 ] = te[ 1 ];
+		array[ offset + 2 ] = te[ 2 ];
+
+		array[ offset + 3 ] = te[ 3 ];
+		array[ offset + 4 ] = te[ 4 ];
+		array[ offset + 5 ] = te[ 5 ];
+
+		array[ offset + 6 ] = te[ 6 ];
+		array[ offset + 7 ] = te[ 7 ];
+		array[ offset + 8 ]  = te[ 8 ];
+
+		return array;
+
+	},
+
 	getNormalMatrix: function ( m ) {
 
 		// input: THREE.Matrix4
@@ -4029,22 +4466,36 @@ THREE.Matrix3.prototype = {
 
 	},
 
-	clone: function () {
+	fromArray: function ( array ) {
+
+		this.elements.set( array );
+
+		return this;
+
+	},
+
+	toArray: function () {
 
 		var te = this.elements;
 
-		return new THREE.Matrix3(
+		return [
+			te[ 0 ], te[ 1 ], te[ 2 ],
+			te[ 3 ], te[ 4 ], te[ 5 ],
+			te[ 6 ], te[ 7 ], te[ 8 ]
+		];
+
+	},
 
-			te[0], te[3], te[6],
-			te[1], te[4], te[7],
-			te[2], te[5], te[8]
+	clone: function () {
 
-		);
+		return new THREE.Matrix3().fromArray( this.elements );
 
 	}
 
 };
 
+// File:src/math/Matrix4.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author supereggbert / http://www.paulbrunt.co.uk/
@@ -4058,20 +4509,22 @@ THREE.Matrix3.prototype = {
  * @author WestLangley / http://github.com/WestLangley
  */
 
+THREE.Matrix4 = function () {
+
+	this.elements = new Float32Array( [
 
-THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+		1, 0, 0, 0,
+		0, 1, 0, 0,
+		0, 0, 1, 0,
+		0, 0, 0, 1
 
-	this.elements = new Float32Array( 16 );
+	] );
 
-	// TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix
-	//   we should not support semi specification of Matrix4, it is just weird.
+	if ( arguments.length > 0 ) {
 
-	var te = this.elements;
+		THREE.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
 
-	te[0] = ( n11 !== undefined ) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0;
-	te[1] = n21 || 0; te[5] = ( n22 !== undefined ) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0;
-	te[2] = n31 || 0; te[6] = n32 || 0; te[10] = ( n33 !== undefined ) ? n33 : 1; te[14] = n34 || 0;
-	te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = ( n44 !== undefined ) ? n44 : 1;
+	}
 
 };
 
@@ -4083,10 +4536,10 @@ THREE.Matrix4.prototype = {
 
 		var te = this.elements;
 
-		te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14;
-		te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24;
-		te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34;
-		te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44;
+		te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+		te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+		te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+		te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
 
 		return this;
 
@@ -4117,7 +4570,7 @@ THREE.Matrix4.prototype = {
 
 	extractPosition: function ( m ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .extractPosition() has been renamed to .copyPosition().' );
+		THREE.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
 		return this.copyPosition( m );
 
 	},
@@ -4127,38 +4580,63 @@ THREE.Matrix4.prototype = {
 		var te = this.elements;
 		var me = m.elements;
 
-		te[12] = me[12];
-		te[13] = me[13];
-		te[14] = me[14];
+		te[ 12 ] = me[ 12 ];
+		te[ 13 ] = me[ 13 ];
+		te[ 14 ] = me[ 14 ];
 
 		return this;
 
 	},
 
-	extractRotation: function () {
+	extractBasis: function ( xAxis, yAxis, zAxis ) {
+ 
+		var te = this.elements;
+ 
+		xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] );
+		yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] );
+		zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] );
+ 
+		return this;
+ 		
+	},
+ 
+	makeBasis: function ( xAxis, yAxis, zAxis ) {
 
-		var v1 = new THREE.Vector3();
+		this.set(
+			xAxis.x, yAxis.x, zAxis.x, 0,
+			xAxis.y, yAxis.y, zAxis.y, 0,
+			xAxis.z, yAxis.z, zAxis.z, 0,
+			0,       0,       0,       1
+		);
+
+		return this;
+
+	},
+
+	extractRotation: function () {
+
+		var v1 = new THREE.Vector3();
 
 		return function ( m ) {
 
 			var te = this.elements;
 			var me = m.elements;
 
-			var scaleX = 1 / v1.set( me[0], me[1], me[2] ).length();
-			var scaleY = 1 / v1.set( me[4], me[5], me[6] ).length();
-			var scaleZ = 1 / v1.set( me[8], me[9], me[10] ).length();
+			var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length();
+			var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length();
+			var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length();
 
-			te[0] = me[0] * scaleX;
-			te[1] = me[1] * scaleX;
-			te[2] = me[2] * scaleX;
+			te[ 0 ] = me[ 0 ] * scaleX;
+			te[ 1 ] = me[ 1 ] * scaleX;
+			te[ 2 ] = me[ 2 ] * scaleX;
 
-			te[4] = me[4] * scaleY;
-			te[5] = me[5] * scaleY;
-			te[6] = me[6] * scaleY;
+			te[ 4 ] = me[ 4 ] * scaleY;
+			te[ 5 ] = me[ 5 ] * scaleY;
+			te[ 6 ] = me[ 6 ] * scaleY;
 
-			te[8] = me[8] * scaleZ;
-			te[9] = me[9] * scaleZ;
-			te[10] = me[10] * scaleZ;
+			te[ 8 ] = me[ 8 ] * scaleZ;
+			te[ 9 ] = me[ 9 ] * scaleZ;
+			te[ 10 ] = me[ 10 ] * scaleZ;
 
 			return this;
 
@@ -4170,7 +4648,7 @@ THREE.Matrix4.prototype = {
 
 		if ( euler instanceof THREE.Euler === false ) {
 
-			console.error( 'ERROR: Matrix\'s .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+			THREE.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
 
 		}
 
@@ -4185,110 +4663,110 @@ THREE.Matrix4.prototype = {
 
 			var ae = a * e, af = a * f, be = b * e, bf = b * f;
 
-			te[0] = c * e;
-			te[4] = - c * f;
-			te[8] = d;
+			te[ 0 ] = c * e;
+			te[ 4 ] = - c * f;
+			te[ 8 ] = d;
 
-			te[1] = af + be * d;
-			te[5] = ae - bf * d;
-			te[9] = - b * c;
+			te[ 1 ] = af + be * d;
+			te[ 5 ] = ae - bf * d;
+			te[ 9 ] = - b * c;
 
-			te[2] = bf - ae * d;
-			te[6] = be + af * d;
-			te[10] = a * c;
+			te[ 2 ] = bf - ae * d;
+			te[ 6 ] = be + af * d;
+			te[ 10 ] = a * c;
 
 		} else if ( euler.order === 'YXZ' ) {
 
 			var ce = c * e, cf = c * f, de = d * e, df = d * f;
 
-			te[0] = ce + df * b;
-			te[4] = de * b - cf;
-			te[8] = a * d;
+			te[ 0 ] = ce + df * b;
+			te[ 4 ] = de * b - cf;
+			te[ 8 ] = a * d;
 
-			te[1] = a * f;
-			te[5] = a * e;
-			te[9] = - b;
+			te[ 1 ] = a * f;
+			te[ 5 ] = a * e;
+			te[ 9 ] = - b;
 
-			te[2] = cf * b - de;
-			te[6] = df + ce * b;
-			te[10] = a * c;
+			te[ 2 ] = cf * b - de;
+			te[ 6 ] = df + ce * b;
+			te[ 10 ] = a * c;
 
 		} else if ( euler.order === 'ZXY' ) {
 
 			var ce = c * e, cf = c * f, de = d * e, df = d * f;
 
-			te[0] = ce - df * b;
-			te[4] = - a * f;
-			te[8] = de + cf * b;
+			te[ 0 ] = ce - df * b;
+			te[ 4 ] = - a * f;
+			te[ 8 ] = de + cf * b;
 
-			te[1] = cf + de * b;
-			te[5] = a * e;
-			te[9] = df - ce * b;
+			te[ 1 ] = cf + de * b;
+			te[ 5 ] = a * e;
+			te[ 9 ] = df - ce * b;
 
-			te[2] = - a * d;
-			te[6] = b;
-			te[10] = a * c;
+			te[ 2 ] = - a * d;
+			te[ 6 ] = b;
+			te[ 10 ] = a * c;
 
 		} else if ( euler.order === 'ZYX' ) {
 
 			var ae = a * e, af = a * f, be = b * e, bf = b * f;
 
-			te[0] = c * e;
-			te[4] = be * d - af;
-			te[8] = ae * d + bf;
+			te[ 0 ] = c * e;
+			te[ 4 ] = be * d - af;
+			te[ 8 ] = ae * d + bf;
 
-			te[1] = c * f;
-			te[5] = bf * d + ae;
-			te[9] = af * d - be;
+			te[ 1 ] = c * f;
+			te[ 5 ] = bf * d + ae;
+			te[ 9 ] = af * d - be;
 
-			te[2] = - d;
-			te[6] = b * c;
-			te[10] = a * c;
+			te[ 2 ] = - d;
+			te[ 6 ] = b * c;
+			te[ 10 ] = a * c;
 
 		} else if ( euler.order === 'YZX' ) {
 
 			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
 
-			te[0] = c * e;
-			te[4] = bd - ac * f;
-			te[8] = bc * f + ad;
+			te[ 0 ] = c * e;
+			te[ 4 ] = bd - ac * f;
+			te[ 8 ] = bc * f + ad;
 
-			te[1] = f;
-			te[5] = a * e;
-			te[9] = - b * e;
+			te[ 1 ] = f;
+			te[ 5 ] = a * e;
+			te[ 9 ] = - b * e;
 
-			te[2] = - d * e;
-			te[6] = ad * f + bc;
-			te[10] = ac - bd * f;
+			te[ 2 ] = - d * e;
+			te[ 6 ] = ad * f + bc;
+			te[ 10 ] = ac - bd * f;
 
 		} else if ( euler.order === 'XZY' ) {
 
 			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
 
-			te[0] = c * e;
-			te[4] = - f;
-			te[8] = d * e;
+			te[ 0 ] = c * e;
+			te[ 4 ] = - f;
+			te[ 8 ] = d * e;
 
-			te[1] = ac * f + bd;
-			te[5] = a * e;
-			te[9] = ad * f - bc;
+			te[ 1 ] = ac * f + bd;
+			te[ 5 ] = a * e;
+			te[ 9 ] = ad * f - bc;
 
-			te[2] = bc * f - ad;
-			te[6] = b * e;
-			te[10] = bd * f + ac;
+			te[ 2 ] = bc * f - ad;
+			te[ 6 ] = b * e;
+			te[ 10 ] = bd * f + ac;
 
 		}
 
 		// last column
-		te[3] = 0;
-		te[7] = 0;
-		te[11] = 0;
+		te[ 3 ] = 0;
+		te[ 7 ] = 0;
+		te[ 11 ] = 0;
 
 		// bottom row
-		te[12] = 0;
-		te[13] = 0;
-		te[14] = 0;
-		te[15] = 1;
+		te[ 12 ] = 0;
+		te[ 13 ] = 0;
+		te[ 14 ] = 0;
+		te[ 15 ] = 1;
 
 		return this;
 
@@ -4296,7 +4774,7 @@ THREE.Matrix4.prototype = {
 
 	setRotationFromQuaternion: function ( q ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .setRotationFromQuaternion() has been deprecated in favor of makeRotationFromQuaternion.  Please update your code.' );
+		THREE.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );
 
 		return this.makeRotationFromQuaternion( q );
 
@@ -4312,34 +4790,34 @@ THREE.Matrix4.prototype = {
 		var yy = y * y2, yz = y * z2, zz = z * z2;
 		var wx = w * x2, wy = w * y2, wz = w * z2;
 
-		te[0] = 1 - ( yy + zz );
-		te[4] = xy - wz;
-		te[8] = xz + wy;
+		te[ 0 ] = 1 - ( yy + zz );
+		te[ 4 ] = xy - wz;
+		te[ 8 ] = xz + wy;
 
-		te[1] = xy + wz;
-		te[5] = 1 - ( xx + zz );
-		te[9] = yz - wx;
+		te[ 1 ] = xy + wz;
+		te[ 5 ] = 1 - ( xx + zz );
+		te[ 9 ] = yz - wx;
 
-		te[2] = xz - wy;
-		te[6] = yz + wx;
-		te[10] = 1 - ( xx + yy );
+		te[ 2 ] = xz - wy;
+		te[ 6 ] = yz + wx;
+		te[ 10 ] = 1 - ( xx + yy );
 
 		// last column
-		te[3] = 0;
-		te[7] = 0;
-		te[11] = 0;
+		te[ 3 ] = 0;
+		te[ 7 ] = 0;
+		te[ 11 ] = 0;
 
 		// bottom row
-		te[12] = 0;
-		te[13] = 0;
-		te[14] = 0;
-		te[15] = 1;
+		te[ 12 ] = 0;
+		te[ 13 ] = 0;
+		te[ 14 ] = 0;
+		te[ 15 ] = 1;
 
 		return this;
 
 	},
 
-	lookAt: function() {
+	lookAt: function () {
 
 		var x = new THREE.Vector3();
 		var y = new THREE.Vector3();
@@ -4369,9 +4847,9 @@ THREE.Matrix4.prototype = {
 			y.crossVectors( z, x );
 
 
-			te[0] = x.x; te[4] = y.x; te[8] = z.x;
-			te[1] = x.y; te[5] = y.y; te[9] = z.y;
-			te[2] = x.z; te[6] = y.z; te[10] = z.z;
+			te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
+			te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
+			te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
 
 			return this;
 
@@ -4383,7 +4861,7 @@ THREE.Matrix4.prototype = {
 
 		if ( n !== undefined ) {
 
-			console.warn( 'DEPRECATED: Matrix4\'s .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+			THREE.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
 			return this.multiplyMatrices( m, n );
 
 		}
@@ -4398,35 +4876,35 @@ THREE.Matrix4.prototype = {
 		var be = b.elements;
 		var te = this.elements;
 
-		var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12];
-		var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13];
-		var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14];
-		var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15];
+		var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+		var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+		var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+		var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
 
-		var b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12];
-		var b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13];
-		var b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14];
-		var b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15];
+		var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+		var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+		var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+		var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
 
-		te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
-		te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
-		te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
-		te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+		te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+		te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+		te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+		te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
 
-		te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
-		te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
-		te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
-		te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+		te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+		te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+		te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+		te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
 
-		te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
-		te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
-		te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
-		te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+		te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+		te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+		te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+		te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
 
-		te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
-		te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
-		te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
-		te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+		te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+		te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+		te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+		te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
 
 		return this;
 
@@ -4438,10 +4916,10 @@ THREE.Matrix4.prototype = {
 
 		this.multiplyMatrices( a, b );
 
-		r[ 0 ] = te[0]; r[ 1 ] = te[1]; r[ 2 ] = te[2]; r[ 3 ] = te[3];
-		r[ 4 ] = te[4]; r[ 5 ] = te[5]; r[ 6 ] = te[6]; r[ 7 ] = te[7];
-		r[ 8 ]  = te[8]; r[ 9 ]  = te[9]; r[ 10 ] = te[10]; r[ 11 ] = te[11];
-		r[ 12 ] = te[12]; r[ 13 ] = te[13]; r[ 14 ] = te[14]; r[ 15 ] = te[15];
+		r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];
+		r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];
+		r[ 8 ]  = te[ 8 ]; r[ 9 ]  = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];
+		r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];
 
 		return this;
 
@@ -4451,10 +4929,10 @@ THREE.Matrix4.prototype = {
 
 		var te = this.elements;
 
-		te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s;
-		te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s;
-		te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s;
-		te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s;
+		te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+		te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+		te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+		te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
 
 		return this;
 
@@ -4462,39 +4940,49 @@ THREE.Matrix4.prototype = {
 
 	multiplyVector3: function ( vector ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
+		THREE.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
 		return vector.applyProjection( this );
 
 	},
 
 	multiplyVector4: function ( vector ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+		THREE.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
 		return vector.applyMatrix4( this );
 
 	},
 
-	multiplyVector3Array: function() {
+	multiplyVector3Array: function ( a ) {
+
+		THREE.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+		return this.applyToVector3Array( a );
+
+	},
+
+	applyToVector3Array: function () {
 
 		var v1 = new THREE.Vector3();
 
-		return function ( a ) {
+		return function ( array, offset, length ) {
+
+			if ( offset === undefined ) offset = 0;
+			if ( length === undefined ) length = array.length;
 
-			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+			for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
 
-				v1.x = a[ i ];
-				v1.y = a[ i + 1 ];
-				v1.z = a[ i + 2 ];
+				v1.x = array[ j ];
+				v1.y = array[ j + 1 ];
+				v1.z = array[ j + 2 ];
 
-				v1.applyProjection( this );
+				v1.applyMatrix4( this );
 
-				a[ i ]     = v1.x;
-				a[ i + 1 ] = v1.y;
-				a[ i + 2 ] = v1.z;
+				array[ j ]     = v1.x;
+				array[ j + 1 ] = v1.y;
+				array[ j + 2 ] = v1.z;
 
 			}
 
-			return a;
+			return array;
 
 		};
 
@@ -4502,7 +4990,7 @@ THREE.Matrix4.prototype = {
 
 	rotateAxis: function ( v ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
+		THREE.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
 
 		v.transformDirection( this );
 
@@ -4510,7 +4998,7 @@ THREE.Matrix4.prototype = {
 
 	crossVector: function ( vector ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+		THREE.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
 		return vector.applyMatrix4( this );
 
 	},
@@ -4519,46 +5007,46 @@ THREE.Matrix4.prototype = {
 
 		var te = this.elements;
 
-		var n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12];
-		var n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13];
-		var n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14];
-		var n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15];
+		var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+		var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+		var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+		var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
 
 		//TODO: make this more efficient
 		//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
 
 		return (
 			n41 * (
-				+n14 * n23 * n32
-				-n13 * n24 * n32
-				-n14 * n22 * n33
-				+n12 * n24 * n33
-				+n13 * n22 * n34
-				-n12 * n23 * n34
+				+ n14 * n23 * n32
+				 - n13 * n24 * n32
+				 - n14 * n22 * n33
+				 + n12 * n24 * n33
+				 + n13 * n22 * n34
+				 - n12 * n23 * n34
 			) +
 			n42 * (
-				+n11 * n23 * n34
-				-n11 * n24 * n33
-				+n14 * n21 * n33
-				-n13 * n21 * n34
-				+n13 * n24 * n31
-				-n14 * n23 * n31
+				+ n11 * n23 * n34
+				 - n11 * n24 * n33
+				 + n14 * n21 * n33
+				 - n13 * n21 * n34
+				 + n13 * n24 * n31
+				 - n14 * n23 * n31
 			) +
 			n43 * (
-				+n11 * n24 * n32
-				-n11 * n22 * n34
-				-n14 * n21 * n32
-				+n12 * n21 * n34
-				+n14 * n22 * n31
-				-n12 * n24 * n31
+				+ n11 * n24 * n32
+				 - n11 * n22 * n34
+				 - n14 * n21 * n32
+				 + n12 * n21 * n34
+				 + n14 * n22 * n31
+				 - n12 * n24 * n31
 			) +
 			n44 * (
-				-n13 * n22 * n31
-				-n11 * n23 * n32
-				+n11 * n22 * n33
-				+n13 * n21 * n32
-				-n12 * n21 * n33
-				+n12 * n23 * n31
+				- n13 * n22 * n31
+				 - n11 * n23 * n32
+				 + n11 * n22 * n33
+				 + n13 * n21 * n32
+				 - n12 * n21 * n33
+				 + n12 * n23 * n31
 			)
 
 		);
@@ -4570,67 +5058,56 @@ THREE.Matrix4.prototype = {
 		var te = this.elements;
 		var tmp;
 
-		tmp = te[1]; te[1] = te[4]; te[4] = tmp;
-		tmp = te[2]; te[2] = te[8]; te[8] = tmp;
-		tmp = te[6]; te[6] = te[9]; te[9] = tmp;
+		tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+		tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+		tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
 
-		tmp = te[3]; te[3] = te[12]; te[12] = tmp;
-		tmp = te[7]; te[7] = te[13]; te[13] = tmp;
-		tmp = te[11]; te[11] = te[14]; te[14] = tmp;
+		tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+		tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+		tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
 
 		return this;
 
 	},
 
-	flattenToArray: function ( flat ) {
+	flattenToArrayOffset: function ( array, offset ) {
 
 		var te = this.elements;
-		flat[ 0 ] = te[0]; flat[ 1 ] = te[1]; flat[ 2 ] = te[2]; flat[ 3 ] = te[3];
-		flat[ 4 ] = te[4]; flat[ 5 ] = te[5]; flat[ 6 ] = te[6]; flat[ 7 ] = te[7];
-		flat[ 8 ] = te[8]; flat[ 9 ] = te[9]; flat[ 10 ] = te[10]; flat[ 11 ] = te[11];
-		flat[ 12 ] = te[12]; flat[ 13 ] = te[13]; flat[ 14 ] = te[14]; flat[ 15 ] = te[15];
 
-		return flat;
+		array[ offset     ] = te[ 0 ];
+		array[ offset + 1 ] = te[ 1 ];
+		array[ offset + 2 ] = te[ 2 ];
+		array[ offset + 3 ] = te[ 3 ];
 
-	},
-
-	flattenToArrayOffset: function( flat, offset ) {
-
-		var te = this.elements;
-		flat[ offset ] = te[0];
-		flat[ offset + 1 ] = te[1];
-		flat[ offset + 2 ] = te[2];
-		flat[ offset + 3 ] = te[3];
+		array[ offset + 4 ] = te[ 4 ];
+		array[ offset + 5 ] = te[ 5 ];
+		array[ offset + 6 ] = te[ 6 ];
+		array[ offset + 7 ] = te[ 7 ];
 
-		flat[ offset + 4 ] = te[4];
-		flat[ offset + 5 ] = te[5];
-		flat[ offset + 6 ] = te[6];
-		flat[ offset + 7 ] = te[7];
+		array[ offset + 8 ]  = te[ 8 ];
+		array[ offset + 9 ]  = te[ 9 ];
+		array[ offset + 10 ] = te[ 10 ];
+		array[ offset + 11 ] = te[ 11 ];
 
-		flat[ offset + 8 ]  = te[8];
-		flat[ offset + 9 ]  = te[9];
-		flat[ offset + 10 ] = te[10];
-		flat[ offset + 11 ] = te[11];
+		array[ offset + 12 ] = te[ 12 ];
+		array[ offset + 13 ] = te[ 13 ];
+		array[ offset + 14 ] = te[ 14 ];
+		array[ offset + 15 ] = te[ 15 ];
 
-		flat[ offset + 12 ] = te[12];
-		flat[ offset + 13 ] = te[13];
-		flat[ offset + 14 ] = te[14];
-		flat[ offset + 15 ] = te[15];
-
-		return flat;
+		return array;
 
 	},
 
-	getPosition: function() {
+	getPosition: function () {
 
 		var v1 = new THREE.Vector3();
 
 		return function () {
 
-			console.warn( 'DEPRECATED: Matrix4\'s .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.' );
+			THREE.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
 
 			var te = this.elements;
-			return v1.set( te[12], te[13], te[14] );
+			return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] );
 
 		};
 
@@ -4640,9 +5117,9 @@ THREE.Matrix4.prototype = {
 
 		var te = this.elements;
 
-		te[12] = v.x;
-		te[13] = v.y;
-		te[14] = v.z;
+		te[ 12 ] = v.x;
+		te[ 13 ] = v.y;
+		te[ 14 ] = v.z;
 
 		return this;
 
@@ -4654,33 +5131,33 @@ THREE.Matrix4.prototype = {
 		var te = this.elements;
 		var me = m.elements;
 
-		var n11 = me[0], n12 = me[4], n13 = me[8], n14 = me[12];
-		var n21 = me[1], n22 = me[5], n23 = me[9], n24 = me[13];
-		var n31 = me[2], n32 = me[6], n33 = me[10], n34 = me[14];
-		var n41 = me[3], n42 = me[7], n43 = me[11], n44 = me[15];
-
-		te[0] = n23*n34*n42 - n24*n33*n42 + n24*n32*n43 - n22*n34*n43 - n23*n32*n44 + n22*n33*n44;
-		te[4] = n14*n33*n42 - n13*n34*n42 - n14*n32*n43 + n12*n34*n43 + n13*n32*n44 - n12*n33*n44;
-		te[8] = n13*n24*n42 - n14*n23*n42 + n14*n22*n43 - n12*n24*n43 - n13*n22*n44 + n12*n23*n44;
-		te[12] = n14*n23*n32 - n13*n24*n32 - n14*n22*n33 + n12*n24*n33 + n13*n22*n34 - n12*n23*n34;
-		te[1] = n24*n33*n41 - n23*n34*n41 - n24*n31*n43 + n21*n34*n43 + n23*n31*n44 - n21*n33*n44;
-		te[5] = n13*n34*n41 - n14*n33*n41 + n14*n31*n43 - n11*n34*n43 - n13*n31*n44 + n11*n33*n44;
-		te[9] = n14*n23*n41 - n13*n24*n41 - n14*n21*n43 + n11*n24*n43 + n13*n21*n44 - n11*n23*n44;
-		te[13] = n13*n24*n31 - n14*n23*n31 + n14*n21*n33 - n11*n24*n33 - n13*n21*n34 + n11*n23*n34;
-		te[2] = n22*n34*n41 - n24*n32*n41 + n24*n31*n42 - n21*n34*n42 - n22*n31*n44 + n21*n32*n44;
-		te[6] = n14*n32*n41 - n12*n34*n41 - n14*n31*n42 + n11*n34*n42 + n12*n31*n44 - n11*n32*n44;
-		te[10] = n12*n24*n41 - n14*n22*n41 + n14*n21*n42 - n11*n24*n42 - n12*n21*n44 + n11*n22*n44;
-		te[14] = n14*n22*n31 - n12*n24*n31 - n14*n21*n32 + n11*n24*n32 + n12*n21*n34 - n11*n22*n34;
-		te[3] = n23*n32*n41 - n22*n33*n41 - n23*n31*n42 + n21*n33*n42 + n22*n31*n43 - n21*n32*n43;
-		te[7] = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43;
-		te[11] = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43;
-		te[15] = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33;
+		var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ];
+		var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ];
+		var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ];
+		var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ];
+
+		te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
+		te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
+		te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
+		te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+		te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44;
+		te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44;
+		te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44;
+		te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34;
+		te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44;
+		te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44;
+		te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44;
+		te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34;
+		te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43;
+		te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43;
+		te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43;
+		te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;
 
 		var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ];
 
 		if ( det == 0 ) {
 
-			var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0";
+			var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";
 
 			if ( throwOnInvertible || false ) {
 
@@ -4688,7 +5165,7 @@ THREE.Matrix4.prototype = {
 
 			} else {
 
-				console.warn( msg );
+				THREE.warn( msg );
 
 			}
 
@@ -4705,31 +5182,31 @@ THREE.Matrix4.prototype = {
 
 	translate: function ( v ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .translate() has been removed.');
+		THREE.error( 'THREE.Matrix4: .translate() has been removed.' );
 
 	},
 
 	rotateX: function ( angle ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .rotateX() has been removed.');
+		THREE.error( 'THREE.Matrix4: .rotateX() has been removed.' );
 
 	},
 
 	rotateY: function ( angle ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .rotateY() has been removed.');
+		THREE.error( 'THREE.Matrix4: .rotateY() has been removed.' );
 
 	},
 
 	rotateZ: function ( angle ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .rotateZ() has been removed.');
+		THREE.error( 'THREE.Matrix4: .rotateZ() has been removed.' );
 
 	},
 
 	rotateByAxis: function ( axis, angle ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .rotateByAxis() has been removed.');
+		THREE.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );
 
 	},
 
@@ -4738,10 +5215,10 @@ THREE.Matrix4.prototype = {
 		var te = this.elements;
 		var x = v.x, y = v.y, z = v.z;
 
-		te[0] *= x; te[4] *= y; te[8] *= z;
-		te[1] *= x; te[5] *= y; te[9] *= z;
-		te[2] *= x; te[6] *= y; te[10] *= z;
-		te[3] *= x; te[7] *= y; te[11] *= z;
+		te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+		te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+		te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+		te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
 
 		return this;
 
@@ -4751,9 +5228,9 @@ THREE.Matrix4.prototype = {
 
 		var te = this.elements;
 
-		var scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2];
-		var scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6];
-		var scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10];
+		var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+		var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+		var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
 
 		return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) );
 
@@ -4781,7 +5258,7 @@ THREE.Matrix4.prototype = {
 		this.set(
 
 			1, 0,  0, 0,
-			0, c, -s, 0,
+			0, c, - s, 0,
 			0, s,  c, 0,
 			0, 0,  0, 1
 
@@ -4799,7 +5276,7 @@ THREE.Matrix4.prototype = {
 
 			 c, 0, s, 0,
 			 0, 1, 0, 0,
-			-s, 0, c, 0,
+			- s, 0, c, 0,
 			 0, 0, 0, 1
 
 		);
@@ -4814,7 +5291,7 @@ THREE.Matrix4.prototype = {
 
 		this.set(
 
-			c, -s, 0, 0,
+			c, - s, 0, 0,
 			s,  c, 0, 0,
 			0,  0, 1, 0,
 			0,  0, 0, 1
@@ -4882,13 +5359,19 @@ THREE.Matrix4.prototype = {
 
 			var te = this.elements;
 
-			var sx = vector.set( te[0], te[1], te[2] ).length();
-			var sy = vector.set( te[4], te[5], te[6] ).length();
-			var sz = vector.set( te[8], te[9], te[10] ).length();
+			var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+			var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+			var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+			// if determine is negative, we need to invert one scale
+			var det = this.determinant();
+			if ( det < 0 ) {
+				sx = - sx;
+			}
 
-			position.x = te[12];
-			position.y = te[13];
-			position.z = te[14];
+			position.x = te[ 12 ];
+			position.y = te[ 13 ];
+			position.z = te[ 14 ];
 
 			// scale the rotation part
 
@@ -4898,17 +5381,17 @@ THREE.Matrix4.prototype = {
 			var invSY = 1 / sy;
 			var invSZ = 1 / sz;
 
-			matrix.elements[0] *= invSX;
-			matrix.elements[1] *= invSX;
-			matrix.elements[2] *= invSX;
+			matrix.elements[ 0 ] *= invSX;
+			matrix.elements[ 1 ] *= invSX;
+			matrix.elements[ 2 ] *= invSX;
 
-			matrix.elements[4] *= invSY;
-			matrix.elements[5] *= invSY;
-			matrix.elements[6] *= invSY;
+			matrix.elements[ 4 ] *= invSY;
+			matrix.elements[ 5 ] *= invSY;
+			matrix.elements[ 6 ] *= invSY;
 
-			matrix.elements[8] *= invSZ;
-			matrix.elements[9] *= invSZ;
-			matrix.elements[10] *= invSZ;
+			matrix.elements[ 8 ] *= invSZ;
+			matrix.elements[ 9 ] *= invSZ;
+			matrix.elements[ 10 ] *= invSZ;
 
 			quaternion.setFromRotationMatrix( matrix );
 
@@ -4933,10 +5416,10 @@ THREE.Matrix4.prototype = {
 		var c = - ( far + near ) / ( far - near );
 		var d = - 2 * far * near / ( far - near );
 
-		te[0] = x;	te[4] = 0;	te[8] = a;	te[12] = 0;
-		te[1] = 0;	te[5] = y;	te[9] = b;	te[13] = 0;
-		te[2] = 0;	te[6] = 0;	te[10] = c;	te[14] = d;
-		te[3] = 0;	te[7] = 0;	te[11] = - 1;	te[15] = 0;
+		te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
+		te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
+		te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
+		te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
 
 		return this;
 
@@ -4964,10 +5447,10 @@ THREE.Matrix4.prototype = {
 		var y = ( top + bottom ) / h;
 		var z = ( far + near ) / p;
 
-		te[0] = 2 / w;	te[4] = 0;	te[8] = 0;	te[12] = -x;
-		te[1] = 0;	te[5] = 2 / h;	te[9] = 0;	te[13] = -y;
-		te[2] = 0;	te[6] = 0;	te[10] = -2/p;	te[14] = -z;
-		te[3] = 0;	te[7] = 0;	te[11] = 0;	te[15] = 1;
+		te[ 0 ] = 2 / w;	te[ 4 ] = 0;	te[ 8 ] = 0;	te[ 12 ] = - x;
+		te[ 1 ] = 0;	te[ 5 ] = 2 / h;	te[ 9 ] = 0;	te[ 13 ] = - y;
+		te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = - 2 / p;	te[ 14 ] = - z;
+		te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = 0;	te[ 15 ] = 1;
 
 		return this;
 
@@ -4996,21 +5479,14 @@ THREE.Matrix4.prototype = {
 
 	clone: function () {
 
-		var te = this.elements;
-
-		return new THREE.Matrix4(
-
-			te[0], te[4], te[8], te[12],
-			te[1], te[5], te[9], te[13],
-			te[2], te[6], te[10], te[14],
-			te[3], te[7], te[11], te[15]
-
-		);
+		return new THREE.Matrix4().fromArray( this.elements );
 
 	}
 
 };
 
+// File:src/math/Ray.js
+
 /**
  * @author bhouston / http://exocortex.com
  */
@@ -5106,123 +5582,133 @@ THREE.Ray.prototype = {
 
 	}(),
 
-	distanceSqToSegment: function( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
+	distanceSqToSegment: function () {
+
+		var segCenter = new THREE.Vector3();
+		var segDir = new THREE.Vector3();
+		var diff = new THREE.Vector3();
+
+		return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
+
+			// from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
+			// It returns the min distance between the ray and the segment
+			// defined by v0 and v1
+			// It can also set two optional targets :
+			// - The closest point on the ray
+			// - The closest point on the segment
+
+			segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
+			segDir.copy( v1 ).sub( v0 ).normalize();
+			diff.copy( this.origin ).sub( segCenter );
+
+			var segExtent = v0.distanceTo( v1 ) * 0.5;
+			var a01 = - this.direction.dot( segDir );
+			var b0 = diff.dot( this.direction );
+			var b1 = - diff.dot( segDir );
+			var c = diff.lengthSq();
+			var det = Math.abs( 1 - a01 * a01 );
+			var s0, s1, sqrDist, extDet;
 
-		// from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
-		// It returns the min distance between the ray and the segment
-		// defined by v0 and v1
-		// It can also set two optional targets :
-		// - The closest point on the ray
-		// - The closest point on the segment
+			if ( det > 0 ) {
 
-		var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
-		var segDir = v1.clone().sub( v0 ).normalize();
-		var segExtent = v0.distanceTo( v1 ) * 0.5;
-		var diff = this.origin.clone().sub( segCenter );
-		var a01 = - this.direction.dot( segDir );
-		var b0 = diff.dot( this.direction );
-		var b1 = - diff.dot( segDir );
-		var c = diff.lengthSq();
-		var det = Math.abs( 1 - a01 * a01 );
-		var s0, s1, sqrDist, extDet;
+				// The ray and segment are not parallel.
 
-		if ( det >= 0 ) {
+				s0 = a01 * b1 - b0;
+				s1 = a01 * b0 - b1;
+				extDet = segExtent * det;
 
-			// The ray and segment are not parallel.
+				if ( s0 >= 0 ) {
 
-			s0 = a01 * b1 - b0;
-			s1 = a01 * b0 - b1;
-			extDet = segExtent * det;
+					if ( s1 >= - extDet ) {
 
-			if ( s0 >= 0 ) {
+						if ( s1 <= extDet ) {
 
-				if ( s1 >= - extDet ) {
+							// region 0
+							// Minimum at interior points of ray and segment.
 
-					if ( s1 <= extDet ) {
+							var invDet = 1 / det;
+							s0 *= invDet;
+							s1 *= invDet;
+							sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
 
-						// region 0
-						// Minimum at interior points of ray and segment.
+						} else {
+
+							// region 1
 
-						var invDet = 1 / det;
-						s0 *= invDet;
-						s1 *= invDet;
-						sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
+							s1 = segExtent;
+							s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+							sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+						}
 
 					} else {
 
-						// region 1
+						// region 5
 
-						s1 = segExtent;
-						s0 = Math.max( 0, - ( a01 * s1 + b0) );
+						s1 = - segExtent;
+						s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
 						sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
 					}
 
 				} else {
 
-					// region 5
+					if ( s1 <= - extDet ) {
 
-					s1 = - segExtent;
-					s0 = Math.max( 0, - ( a01 * s1 + b0) );
-					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+						// region 4
 
-				}
+						s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
+						s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+						sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
-			} else {
+					} else if ( s1 <= extDet ) {
 
-				if ( s1 <= - extDet) {
+						// region 3
 
-					// region 4
+						s0 = 0;
+						s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
+						sqrDist = s1 * ( s1 + 2 * b1 ) + c;
 
-					s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
-					s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
-					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+					} else {
 
-				} else if ( s1 <= extDet ) {
+						// region 2
 
-					// region 3
+						s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
+						s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+						sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
-					s0 = 0;
-					s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
-					sqrDist = s1 * ( s1 + 2 * b1 ) + c;
+					}
 
-				} else {
+				}
 
-					// region 2
+			} else {
 
-					s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
-					s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
-					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+				// Ray and segment are parallel.
 
-				}
+				s1 = ( a01 > 0 ) ? - segExtent : segExtent;
+				s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+				sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
 			}
 
-		} else {
-
-			// Ray and segment are parallel.
+			if ( optionalPointOnRay ) {
 
-			s1 = ( a01 > 0 ) ? - segExtent : segExtent;
-			s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
-			sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+				optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
 
-		}
-
-		if ( optionalPointOnRay ) {
+			}
 
-			optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) );
+			if ( optionalPointOnSegment ) {
 
-		}
+				optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
 
-		if ( optionalPointOnSegment ) {
+			}
 
-			optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) );
+			return sqrDist;
 
-		}
+		};
 
-		return sqrDist;
+	}(),
 
-	},
 
 	isIntersectionSphere: function ( sphere ) {
 
@@ -5230,6 +5716,47 @@ THREE.Ray.prototype = {
 
 	},
 
+	intersectSphere: function () {
+
+		// from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
+
+		var v1 = new THREE.Vector3();
+
+		return function ( sphere, optionalTarget ) {
+
+			v1.subVectors( sphere.center, this.origin );
+
+			var tca = v1.dot( this.direction );
+
+			var d2 = v1.dot( v1 ) - tca * tca;
+
+			var radius2 = sphere.radius * sphere.radius;
+
+			if ( d2 > radius2 ) return null;
+
+			var thc = Math.sqrt( radius2 - d2 );
+
+			// t0 = first intersect point - entrance on front of sphere
+			var t0 = tca - thc;
+
+			// t1 = second intersect point - exit point on back of sphere
+			var t1 = tca + thc;
+
+			// test to see if both t0 and t1 are behind the ray - if so, return null
+			if ( t0 < 0 && t1 < 0 ) return null;
+
+			// test to see if t0 is behind the ray:
+			// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
+			// in order to always return an intersect point that is in front of the ray.
+			if ( t0 < 0 ) return this.at( t1, optionalTarget );
+
+			// else t0 is in front of the ray, so return the first collision point scaled by t0 
+			return this.at( t0, optionalTarget );
+
+		}
+
+	}(),
+
 	isIntersectionPlane: function ( plane ) {
 
 		// check if the ray lies on the plane first
@@ -5246,7 +5773,7 @@ THREE.Ray.prototype = {
 
 		if ( denominator * distToPoint < 0 ) {
 
-			return true
+			return true;
 
 		}
 
@@ -5262,7 +5789,7 @@ THREE.Ray.prototype = {
 		if ( denominator == 0 ) {
 
 			// line is coplanar, return origin
-			if( plane.distanceToPoint( this.origin ) == 0 ) {
+			if ( plane.distanceToPoint( this.origin ) == 0 ) {
 
 				return 0;
 
@@ -5303,69 +5830,69 @@ THREE.Ray.prototype = {
 
 			return this.intersectBox( box, v ) !== null;
 
-		}
+		};
 
 	}(),
 
-	intersectBox: function ( box , optionalTarget ) {
+	intersectBox: function ( box, optionalTarget ) {
 
 		// http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
 
 		var tmin,tmax,tymin,tymax,tzmin,tzmax;
 
-		var invdirx = 1/this.direction.x,
-			invdiry = 1/this.direction.y,
-			invdirz = 1/this.direction.z;
+		var invdirx = 1 / this.direction.x,
+			invdiry = 1 / this.direction.y,
+			invdirz = 1 / this.direction.z;
 
 		var origin = this.origin;
 
-		if (invdirx >= 0) {
+		if ( invdirx >= 0 ) {
 
-			tmin = (box.min.x - origin.x) * invdirx;
-			tmax = (box.max.x - origin.x) * invdirx;
+			tmin = ( box.min.x - origin.x ) * invdirx;
+			tmax = ( box.max.x - origin.x ) * invdirx;
 
 		} else {
 
-			tmin = (box.max.x - origin.x) * invdirx;
-			tmax = (box.min.x - origin.x) * invdirx;
+			tmin = ( box.max.x - origin.x ) * invdirx;
+			tmax = ( box.min.x - origin.x ) * invdirx;
 		}
 
-		if (invdiry >= 0) {
+		if ( invdiry >= 0 ) {
 
-			tymin = (box.min.y - origin.y) * invdiry;
-			tymax = (box.max.y - origin.y) * invdiry;
+			tymin = ( box.min.y - origin.y ) * invdiry;
+			tymax = ( box.max.y - origin.y ) * invdiry;
 
 		} else {
 
-			tymin = (box.max.y - origin.y) * invdiry;
-			tymax = (box.min.y - origin.y) * invdiry;
+			tymin = ( box.max.y - origin.y ) * invdiry;
+			tymax = ( box.min.y - origin.y ) * invdiry;
 		}
 
-		if ((tmin > tymax) || (tymin > tmax)) return null;
+		if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
 
 		// These lines also handle the case where tmin or tmax is NaN
 		// (result of 0 * Infinity). x !== x returns true if x is NaN
 
-		if (tymin > tmin || tmin !== tmin ) tmin = tymin;
+		if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
 
-		if (tymax < tmax || tmax !== tmax ) tmax = tymax;
+		if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
 
-		if (invdirz >= 0) {
+		if ( invdirz >= 0 ) {
 
-			tzmin = (box.min.z - origin.z) * invdirz;
-			tzmax = (box.max.z - origin.z) * invdirz;
+			tzmin = ( box.min.z - origin.z ) * invdirz;
+			tzmax = ( box.max.z - origin.z ) * invdirz;
 
 		} else {
 
-			tzmin = (box.max.z - origin.z) * invdirz;
-			tzmax = (box.min.z - origin.z) * invdirz;
+			tzmin = ( box.max.z - origin.z ) * invdirz;
+			tzmax = ( box.min.z - origin.z ) * invdirz;
 		}
 
-		if ((tmin > tzmax) || (tzmin > tmax)) return null;
+		if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
 
-		if (tzmin > tmin || tmin !== tmin ) tmin = tzmin;
+		if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
 
-		if (tzmax < tmax || tmax !== tmax ) tmax = tzmax;
+		if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
 
 		//return point closest to the ray (positive side)
 
@@ -5375,7 +5902,7 @@ THREE.Ray.prototype = {
 
 	},
 
-	intersectTriangle: function() {
+	intersectTriangle: function () {
 
 		// Compute the offset origin, edges, and normal.
 		var diff = new THREE.Vector3();
@@ -5454,7 +5981,7 @@ THREE.Ray.prototype = {
 			// Ray intersects triangle.
 			return this.at( QdN / DdN, optionalTarget );
 
-		}
+		};
 
 	}(),
 
@@ -5482,6 +6009,8 @@ THREE.Ray.prototype = {
 
 };
 
+// File:src/math/Sphere.js
+
 /**
  * @author bhouston / http://exocortex.com
  * @author mrdoob / http://mrdoob.com/
@@ -5506,12 +6035,11 @@ THREE.Sphere.prototype = {
 		return this;
 	},
 
-
 	setFromPoints: function () {
 
 		var box = new THREE.Box3();
 
-		return function ( points, optionalCenter )  {
+		return function ( points, optionalCenter ) {
 
 			var center = this.center;
 
@@ -5537,7 +6065,7 @@ THREE.Sphere.prototype = {
 
 			return this;
 
- 		};
+		};
 
 	}(),
 
@@ -5636,6 +6164,8 @@ THREE.Sphere.prototype = {
 
 };
 
+// File:src/math/Frustum.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author alteredq / http://alteredqualia.com/
@@ -5665,12 +6195,12 @@ THREE.Frustum.prototype = {
 
 		var planes = this.planes;
 
-		planes[0].copy( p0 );
-		planes[1].copy( p1 );
-		planes[2].copy( p2 );
-		planes[3].copy( p3 );
-		planes[4].copy( p4 );
-		planes[5].copy( p5 );
+		planes[ 0 ].copy( p0 );
+		planes[ 1 ].copy( p1 );
+		planes[ 2 ].copy( p2 );
+		planes[ 3 ].copy( p3 );
+		planes[ 4 ].copy( p4 );
+		planes[ 5 ].copy( p5 );
 
 		return this;
 
@@ -5680,9 +6210,9 @@ THREE.Frustum.prototype = {
 
 		var planes = this.planes;
 
-		for( var i = 0; i < 6; i ++ ) {
+		for ( var i = 0; i < 6; i ++ ) {
 
-			planes[i].copy( frustum.planes[i] );
+			planes[ i ].copy( frustum.planes[ i ] );
 
 		}
 
@@ -5694,10 +6224,10 @@ THREE.Frustum.prototype = {
 
 		var planes = this.planes;
 		var me = m.elements;
-		var me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3];
-		var me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7];
-		var me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11];
-		var me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15];
+		var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+		var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+		var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+		var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
 
 		planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
 		planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
@@ -5733,7 +6263,7 @@ THREE.Frustum.prototype = {
 
 		var planes = this.planes;
 		var center = sphere.center;
-		var negRadius = -sphere.radius;
+		var negRadius = - sphere.radius;
 
 		for ( var i = 0; i < 6; i ++ ) {
 
@@ -5751,18 +6281,18 @@ THREE.Frustum.prototype = {
 
 	},
 
-	intersectsBox : function() {
+	intersectsBox: function () {
 
 		var p1 = new THREE.Vector3(),
 			p2 = new THREE.Vector3();
 
-		return function( box ) {
+		return function ( box ) {
 
 			var planes = this.planes;
 
 			for ( var i = 0; i < 6 ; i ++ ) {
 
-				var plane = planes[i];
+				var plane = planes[ i ];
 
 				p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
 				p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
@@ -5815,6 +6345,8 @@ THREE.Frustum.prototype = {
 
 };
 
+// File:src/math/Plane.js
+
 /**
  * @author bhouston / http://exocortex.com
  */
@@ -5857,7 +6389,7 @@ THREE.Plane.prototype = {
 
 	},
 
-	setFromCoplanarPoints: function() {
+	setFromCoplanarPoints: function () {
 
 		var v1 = new THREE.Vector3();
 		var v2 = new THREE.Vector3();
@@ -5900,7 +6432,7 @@ THREE.Plane.prototype = {
 
 	negate: function () {
 
-		this.constant *= -1;
+		this.constant *= - 1;
 		this.normal.negate();
 
 		return this;
@@ -5945,7 +6477,7 @@ THREE.Plane.prototype = {
 
 	},
 
-	intersectLine: function() {
+	intersectLine: function () {
 
 		var v1 = new THREE.Vector3();
 
@@ -5960,7 +6492,7 @@ THREE.Plane.prototype = {
 			if ( denominator == 0 ) {
 
 				// line is coplanar, return origin
-				if( this.distanceToPoint( line.start ) == 0 ) {
+				if ( this.distanceToPoint( line.start ) == 0 ) {
 
 					return result.copy( line.start );
 
@@ -5973,7 +6505,7 @@ THREE.Plane.prototype = {
 
 			var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
 
-			if( t < 0 || t > 1 ) {
+			if ( t < 0 || t > 1 ) {
 
 				return undefined;
 
@@ -5993,17 +6525,18 @@ THREE.Plane.prototype = {
 
 	},
 
-	applyMatrix4: function() {
+	applyMatrix4: function () {
 
 		var v1 = new THREE.Vector3();
 		var v2 = new THREE.Vector3();
+		var m1 = new THREE.Matrix3();
 
 		return function ( matrix, optionalNormalMatrix ) {
 
 			// compute new normal based on theory here:
 			// http://www.songho.ca/opengl/gl_normaltransform.html
-			optionalNormalMatrix = optionalNormalMatrix || new THREE.Matrix3().getNormalMatrix( matrix );
-			var newNormal = v1.copy( this.normal ).applyMatrix3( optionalNormalMatrix );
+			var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
+			var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix );
 
 			var newCoplanarPoint = this.coplanarPoint( v2 );
 			newCoplanarPoint.applyMatrix4( matrix );
@@ -6038,6 +6571,8 @@ THREE.Plane.prototype = {
 
 };
 
+// File:src/math/Math.js
+
 /**
  * @author alteredq / http://alteredqualia.com/
  * @author mrdoob / http://mrdoob.com/
@@ -6045,14 +6580,12 @@ THREE.Plane.prototype = {
 
 THREE.Math = {
 
-	PI2: Math.PI * 2,
-
 	generateUUID: function () {
 
 		// http://www.broofa.com/Tools/Math.uuid.htm
 
-		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
-		var uuid = new Array(36);
+		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+		var uuid = new Array( 36 );
 		var rnd = 0, r;
 
 		return function () {
@@ -6069,15 +6602,15 @@ THREE.Math = {
 
 				} else {
 
-					if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
+					if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
 					r = rnd & 0xf;
 					rnd = rnd >> 4;
-					uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
+					uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ];
 
 				}
 			}
 
-			return uuid.join('');
+			return uuid.join( '' );
 
 		};
 
@@ -6114,9 +6647,9 @@ THREE.Math = {
 		if ( x <= min ) return 0;
 		if ( x >= max ) return 1;
 
-		x = ( x - min )/( max - min );
+		x = ( x - min ) / ( max - min );
 
-		return x*x*(3 - 2*x);
+		return x * x * ( 3 - 2 * x );
 
 	},
 
@@ -6125,9 +6658,9 @@ THREE.Math = {
 		if ( x <= min ) return 0;
 		if ( x >= max ) return 1;
 
-		x = ( x - min )/( max - min );
+		x = ( x - min ) / ( max - min );
 
-		return x*x*x*(x*(x*6 - 15) + 10);
+		return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
 
 	},
 
@@ -6144,7 +6677,7 @@ THREE.Math = {
 
 	randInt: function ( low, high ) {
 
-		return low + Math.floor( Math.random() * ( high - low + 1 ) );
+		return Math.floor( this.randFloat( low, high ) );
 
 	},
 
@@ -6164,13 +6697,7 @@ THREE.Math = {
 
 	},
 
-	sign: function ( x ) {
-
-		return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 );
-
-	},
-
-	degToRad: function() {
+	degToRad: function () {
 
 		var degreeToRadiansFactor = Math.PI / 180;
 
@@ -6182,7 +6709,7 @@ THREE.Math = {
 
 	}(),
 
-	radToDeg: function() {
+	radToDeg: function () {
 
 		var radianToDegreesFactor = 180 / Math.PI;
 
@@ -6192,10 +6719,32 @@ THREE.Math = {
 
 		};
 
-	}()
+	}(),
+
+	isPowerOfTwo: function ( value ) {
+
+		return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+	},
+
+	nextPowerOfTwo: function ( value ) {
+
+		value --;
+		value |= value >> 1;
+		value |= value >> 2;
+		value |= value >> 4;
+		value |= value >> 8;
+		value |= value >> 16;
+		value ++;
+
+		return value;
+
+	}
 
 };
 
+// File:src/math/Spline.js
+
 /**
  * Spline from Tween.js, slightly optimized (and trashed)
  * http://sole.github.com/tween.js/examples/05_spline.html
@@ -6212,11 +6761,11 @@ THREE.Spline = function ( points ) {
 	point, intPoint, weight, w2, w3,
 	pa, pb, pc, pd;
 
-	this.initFromArray = function( a ) {
+	this.initFromArray = function ( a ) {
 
 		this.points = [];
 
-		for ( var i = 0; i < a.length; i++ ) {
+		for ( var i = 0; i < a.length; i ++ ) {
 
 			this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
 
@@ -6282,7 +6831,7 @@ THREE.Spline = function ( points ) {
 
 		chunkLengths[ 0 ] = 0;
 
-		if ( !nSubDivisions ) nSubDivisions = 100;
+		if ( ! nSubDivisions ) nSubDivisions = 100;
 
 		nSamples = this.points.length * nSubDivisions;
 
@@ -6323,7 +6872,7 @@ THREE.Spline = function ( points ) {
 
 		var i, j,
 			index, indexCurrent, indexNext,
-			linearDistance, realDistance,
+			realDistance,
 			sampling, position,
 			newpoints = [],
 			tmpVec = new THREE.Vector3(),
@@ -6331,7 +6880,7 @@ THREE.Spline = function ( points ) {
 
 		newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
 
-		for ( i = 1; i < this.points.length; i++ ) {
+		for ( i = 1; i < this.points.length; i ++ ) {
 
 			//tmpVec.copy( this.points[ i - 1 ] );
 			//linearDistance = tmpVec.distanceTo( this.points[ i ] );
@@ -6343,7 +6892,7 @@ THREE.Spline = function ( points ) {
 			indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
 			indexNext = i / ( this.points.length - 1 );
 
-			for ( j = 1; j < sampling - 1; j++ ) {
+			for ( j = 1; j < sampling - 1; j ++ ) {
 
 				index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
 
@@ -6373,6 +6922,8 @@ THREE.Spline = function ( points ) {
 
 };
 
+// File:src/math/Triangle.js
+
 /**
  * @author bhouston / http://exocortex.com
  * @author mrdoob / http://mrdoob.com/
@@ -6386,7 +6937,7 @@ THREE.Triangle = function ( a, b, c ) {
 
 };
 
-THREE.Triangle.normal = function() {
+THREE.Triangle.normal = function () {
 
 	var v0 = new THREE.Vector3();
 
@@ -6399,7 +6950,7 @@ THREE.Triangle.normal = function() {
 		result.cross( v0 );
 
 		var resultLengthSq = result.lengthSq();
-		if( resultLengthSq > 0 ) {
+		if ( resultLengthSq > 0 ) {
 
 			return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
 
@@ -6413,7 +6964,7 @@ THREE.Triangle.normal = function() {
 
 // static/instance method to calculate barycoordinates
 // based on: http://www.blackpawn.com/texts/pointinpoly/default.html
-THREE.Triangle.barycoordFromPoint = function() {
+THREE.Triangle.barycoordFromPoint = function () {
 
 	var v0 = new THREE.Vector3();
 	var v1 = new THREE.Vector3();
@@ -6436,10 +6987,10 @@ THREE.Triangle.barycoordFromPoint = function() {
 		var result = optionalTarget || new THREE.Vector3();
 
 		// colinear or singular triangle
-		if( denom == 0 ) {
+		if ( denom == 0 ) {
 			// arbitrary location outside of triangle?
 			// not sure if this is the best idea, maybe should be returning undefined
-			return result.set( -2, -1, -1 );
+			return result.set( - 2, - 1, - 1 );
 		}
 
 		var invDenom = 1 / denom;
@@ -6453,7 +7004,7 @@ THREE.Triangle.barycoordFromPoint = function() {
 
 }();
 
-THREE.Triangle.containsPoint = function() {
+THREE.Triangle.containsPoint = function () {
 
 	var v1 = new THREE.Vector3();
 
@@ -6483,9 +7034,9 @@ THREE.Triangle.prototype = {
 
 	setFromPointsAndIndices: function ( points, i0, i1, i2 ) {
 
-		this.a.copy( points[i0] );
-		this.b.copy( points[i1] );
-		this.c.copy( points[i2] );
+		this.a.copy( points[ i0 ] );
+		this.b.copy( points[ i1 ] );
+		this.c.copy( points[ i2 ] );
 
 		return this;
 
@@ -6501,7 +7052,7 @@ THREE.Triangle.prototype = {
 
 	},
 
-	area: function() {
+	area: function () {
 
 		var v0 = new THREE.Vector3();
 		var v1 = new THREE.Vector3();
@@ -6564,27 +7115,7 @@ THREE.Triangle.prototype = {
 
 };
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.Vertex = function ( v ) {
-
-	console.warn( 'THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.')
-	return v;
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.UV = function ( u, v ) {
-
-	console.warn( 'THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.')
-	return new THREE.Vector2( u, v );
-
-};
+// File:src/core/Clock.js
 
 /**
  * @author alteredq / http://alteredqualia.com/
@@ -6609,8 +7140,8 @@ THREE.Clock.prototype = {
 	start: function () {
 
 		this.startTime = self.performance !== undefined && self.performance.now !== undefined
-					? self.performance.now()
-					: Date.now();
+					 ? self.performance.now()
+					 : Date.now();
 
 		this.oldTime = this.startTime;
 		this.running = true;
@@ -6643,8 +7174,8 @@ THREE.Clock.prototype = {
 		if ( this.running ) {
 
 			var newTime = self.performance !== undefined && self.performance.now !== undefined
-					? self.performance.now()
-					: Date.now();
+					 ? self.performance.now()
+					 : Date.now();
 
 			diff = 0.001 * ( newTime - this.oldTime );
 			this.oldTime = newTime;
@@ -6659,6 +7190,8 @@ THREE.Clock.prototype = {
 
 };
 
+// File:src/core/EventDispatcher.js
+
 /**
  * https://github.com/mrdoob/eventdispatcher.js/
  */
@@ -6719,53 +7252,56 @@ THREE.EventDispatcher.prototype = {
 		if ( this._listeners === undefined ) return;
 
 		var listeners = this._listeners;
-		var index = listeners[ type ].indexOf( listener );
+		var listenerArray = listeners[ type ];
 
-		if ( index !== - 1 ) {
+		if ( listenerArray !== undefined ) {
 
-			listeners[ type ].splice( index, 1 );
+			var index = listenerArray.indexOf( listener );
 
-		}
+			if ( index !== - 1 ) {
 
-	},
+				listenerArray.splice( index, 1 );
 
-	dispatchEvent: function () {
+			}
 
-		var array = [];
+		}
 
-		return function ( event ) {
+	},
 
-			if ( this._listeners === undefined ) return;
+	dispatchEvent: function ( event ) {
 
-			var listeners = this._listeners;
-			var listenerArray = listeners[ event.type ];
+		if ( this._listeners === undefined ) return;
 
-			if ( listenerArray !== undefined ) {
+		var listeners = this._listeners;
+		var listenerArray = listeners[ event.type ];
 
-				event.target = this;
+		if ( listenerArray !== undefined ) {
 
-				var length = listenerArray.length;
+			event.target = this;
 
-				for ( var i = 0; i < length; i ++ ) {
+			var array = [];
+			var length = listenerArray.length;
 
-					array[ i ] = listenerArray[ i ];
+			for ( var i = 0; i < length; i ++ ) {
 
-				}
+				array[ i ] = listenerArray[ i ];
 
-				for ( var i = 0; i < length; i ++ ) {
+			}
 
-					array[ i ].call( this, event );
+			for ( var i = 0; i < length; i ++ ) {
 
-				}
+				array[ i ].call( this, event );
 
 			}
 
-		};
+		}
 
-	}()
+	}
 
 };
 
+// File:src/core/Raycaster.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author bhouston / http://exocortex.com/
@@ -6782,15 +7318,15 @@ THREE.EventDispatcher.prototype = {
 		this.near = near || 0;
 		this.far = far || Infinity;
 
-	};
-
-	var sphere = new THREE.Sphere();
-	var localRay = new THREE.Ray();
-	var facePlane = new THREE.Plane();
-	var intersectPoint = new THREE.Vector3();
-	var matrixPosition = new THREE.Vector3();
+		this.params = {
+			Sprite: {},
+			Mesh: {},
+			PointCloud: { threshold: 1 },
+			LOD: {},
+			Line: {}
+		};
 
-	var inverseMatrix = new THREE.Matrix4();
+	};
 
 	var descSort = function ( a, b ) {
 
@@ -6798,506 +7334,229 @@ THREE.EventDispatcher.prototype = {
 
 	};
 
-	var vA = new THREE.Vector3();
-	var vB = new THREE.Vector3();
-	var vC = new THREE.Vector3();
+	var intersectObject = function ( object, raycaster, intersects, recursive ) {
 
-	var intersectObject = function ( object, raycaster, intersects ) {
+		object.raycast( raycaster, intersects );
 
-		if ( object instanceof THREE.Sprite ) {
+		if ( recursive === true ) {
 
-			matrixPosition.getPositionFromMatrix( object.matrixWorld );
-			var distance = raycaster.ray.distanceToPoint( matrixPosition );
+			var children = object.children;
 
-			if ( distance > object.scale.x ) {
+			for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-				return intersects;
+				intersectObject( children[ i ], raycaster, intersects, true );
 
 			}
 
-			intersects.push( {
-
-				distance: distance,
-				point: object.position,
-				face: null,
-				object: object
-
-			} );
+		}
 
-		} else if ( object instanceof THREE.LOD ) {
+	};
 
-			matrixPosition.getPositionFromMatrix( object.matrixWorld );
-			var distance = raycaster.ray.origin.distanceTo( matrixPosition );
+	//
 
-			intersectObject( object.getObjectForDistance( distance ), raycaster, intersects );
+	THREE.Raycaster.prototype = {
 
-		} else if ( object instanceof THREE.Mesh ) {
+		constructor: THREE.Raycaster,
 
-			var geometry = object.geometry;
+		precision: 0.0001,
+		linePrecision: 1,
 
-			// Checking boundingSphere distance to ray
+		set: function ( origin, direction ) {
 
-			if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+			// direction is assumed to be normalized (for accurate distance calculations)
 
-			sphere.copy( geometry.boundingSphere );
-			sphere.applyMatrix4( object.matrixWorld );
+			this.ray.set( origin, direction );
 
-			if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
+		},
 
-				return intersects;
+		setFromCamera: function ( coords, camera ) {
 
-			}
+			// camera is assumed _not_ to be a child of a transformed object
 
-			// Check boundingBox before continuing
+			if ( camera instanceof THREE.PerspectiveCamera ) {
 
-			inverseMatrix.getInverse( object.matrixWorld );
-			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+				this.ray.origin.copy( camera.position );
+				this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( camera.position ).normalize();
 
-			if ( geometry.boundingBox !== null ) {
+			} else if ( camera instanceof THREE.OrthographicCamera ) {
 
-				if ( localRay.isIntersectionBox( geometry.boundingBox ) === false )  {
+				this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera );
+				this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
 
-					return intersects;
+			} else {
 
-				}
+				THREE.error( 'THREE.Raycaster: Unsupported camera type.' );
 
 			}
 
-			if ( geometry instanceof THREE.BufferGeometry ) {
+		},
 
-				var material = object.material;
+		intersectObject: function ( object, recursive ) {
 
-				if ( material === undefined ) return intersects;
-				if ( geometry.dynamic === false ) return intersects;
+			var intersects = [];
 
-				var a, b, c;
-				var precision = raycaster.precision;
+			intersectObject( object, this, intersects, recursive );
 
-				if ( geometry.attributes.index !== undefined ) {
+			intersects.sort( descSort );
 
-					var offsets = geometry.offsets;
-					var indices = geometry.attributes.index.array;
-					var positions = geometry.attributes.position.array;
-					var offLength = geometry.offsets.length;
+			return intersects;
 
-					var fl = geometry.attributes.index.array.length / 3;
+		},
 
-					for ( var oi = 0; oi < offLength; ++oi ) {
+		intersectObjects: function ( objects, recursive ) {
 
-						var start = offsets[ oi ].start;
-						var count = offsets[ oi ].count;
-						var index = offsets[ oi ].index;
+			var intersects = [];
 
-						for ( var i = start, il = start + count; i < il; i += 3 ) {
+			if ( objects instanceof Array === false ) {
 
-							a = index + indices[ i ];
-							b = index + indices[ i + 1 ];
-							c = index + indices[ i + 2 ];
+				THREE.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
+				return intersects;
 
-							vA.set(
-								positions[ a * 3 ],
-								positions[ a * 3 + 1 ],
-								positions[ a * 3 + 2 ]
-							);
-							vB.set(
-								positions[ b * 3 ],
-								positions[ b * 3 + 1 ],
-								positions[ b * 3 + 2 ]
-							);
-							vC.set(
-								positions[ c * 3 ],
-								positions[ c * 3 + 1 ],
-								positions[ c * 3 + 2 ]
-							);
+			}
 
+			for ( var i = 0, l = objects.length; i < l; i ++ ) {
 
-							if ( material.side === THREE.BackSide ) {
+				intersectObject( objects[ i ], this, intersects, recursive );
 
-								var intersectionPoint = localRay.intersectTriangle( vC, vB, vA, true );
+			}
 
-							} else {
+			intersects.sort( descSort );
 
-								var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
+			return intersects;
 
-							}
+		}
 
-							if ( intersectionPoint === null ) continue;
+	};
 
-							intersectionPoint.applyMatrix4( object.matrixWorld );
+}( THREE ) );
 
-							var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
+// File:src/core/Object3D.js
 
-							if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
 
-							intersects.push( {
+THREE.Object3D = function () {
 
-								distance: distance,
-								point: intersectionPoint,
-								face: null,
-								faceIndex: null,
-								object: object
+	Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } );
 
-							} );
+	this.uuid = THREE.Math.generateUUID();
 
-						}
+	this.name = '';
+	this.type = 'Object3D';
 
-					}
+	this.parent = undefined;
+	this.children = [];
 
-				} else {
+	this.up = THREE.Object3D.DefaultUp.clone();
 
-					var offsets = geometry.offsets;
-					var positions = geometry.attributes.position.array;
-					var offLength = geometry.offsets.length;
+	var position = new THREE.Vector3();
+	var rotation = new THREE.Euler();
+	var quaternion = new THREE.Quaternion();
+	var scale = new THREE.Vector3( 1, 1, 1 );
 
-					var fl = geometry.attributes.position.array.length;
+	var onRotationChange = function () {
+		quaternion.setFromEuler( rotation, false );
+	};
 
-					for ( var i = 0; i < fl; i += 3 ) {
+	var onQuaternionChange = function () {
+		rotation.setFromQuaternion( quaternion, undefined, false );
+	};
 
-						a = i;
-						b = i + 1;
-						c = i + 2;
+	rotation.onChange( onRotationChange );
+	quaternion.onChange( onQuaternionChange );
 
-						vA.set(
-							positions[ a * 3 ],
-							positions[ a * 3 + 1 ],
-							positions[ a * 3 + 2 ]
-						);
-						vB.set(
-							positions[ b * 3 ],
-							positions[ b * 3 + 1 ],
-							positions[ b * 3 + 2 ]
-						);
-						vC.set(
-							positions[ c * 3 ],
-							positions[ c * 3 + 1 ],
-							positions[ c * 3 + 2 ]
-						);
+	Object.defineProperties( this, {
+		position: {
+			enumerable: true,
+			value: position
+		},
+		rotation: {
+			enumerable: true,
+			value: rotation
+		},
+		quaternion: {
+			enumerable: true,
+			value: quaternion
+		},
+		scale: {
+			enumerable: true,
+			value: scale
+		}
+	} );
 
+	this.rotationAutoUpdate = true;
 
-						if ( material.side === THREE.BackSide ) {
+	this.matrix = new THREE.Matrix4();
+	this.matrixWorld = new THREE.Matrix4();
 
-							var intersectionPoint = localRay.intersectTriangle( vC, vB, vA, true );
+	this.matrixAutoUpdate = true;
+	this.matrixWorldNeedsUpdate = false;
 
-						} else {
+	this.visible = true;
 
-							var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
+	this.castShadow = false;
+	this.receiveShadow = false;
 
-						}
+	this.frustumCulled = true;
+	this.renderOrder = 0;
 
-						if ( intersectionPoint === null ) continue;
+	this.userData = {};
 
-						intersectionPoint.applyMatrix4( object.matrixWorld );
+};
 
-						var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
+THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 );
 
-						if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
+THREE.Object3D.prototype = {
 
-						intersects.push( {
+	constructor: THREE.Object3D,
 
-							distance: distance,
-							point: intersectionPoint,
-							face: null,
-							faceIndex: null,
-							object: object
+	get eulerOrder () {
 
-						} );
+		THREE.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' );
 
-					}
+		return this.rotation.order;
 
-				}
+	},
 
-			} else if ( geometry instanceof THREE.Geometry ) {
+	set eulerOrder ( value ) {
 
-				var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
-				var objectMaterials = isFaceMaterial === true ? object.material.materials : null;
+		THREE.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' );
 
-				var a, b, c, d;
-				var precision = raycaster.precision;
+		this.rotation.order = value;
 
-				var vertices = geometry.vertices;
+	},
 
-				for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+	get useQuaternion () {
 
-					var face = geometry.faces[ f ];
+		THREE.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
 
-					var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : object.material;
+	},
 
-					if ( material === undefined ) continue;
+	set useQuaternion ( value ) {
 
-					a = vertices[ face.a ];
-					b = vertices[ face.b ];
-					c = vertices[ face.c ];
+		THREE.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
 
-					if ( material.side === THREE.BackSide ) {
+	},
 
-						var intersectionPoint = localRay.intersectTriangle( c, b, a, true );
+	applyMatrix: function ( matrix ) {
 
-					} else {
+		this.matrix.multiplyMatrices( matrix, this.matrix );
 
-						var intersectionPoint = localRay.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide );
+		this.matrix.decompose( this.position, this.quaternion, this.scale );
 
-					}
+	},
 
-					if ( intersectionPoint === null ) continue;
+	setRotationFromAxisAngle: function ( axis, angle ) {
 
-					intersectionPoint.applyMatrix4( object.matrixWorld );
+		// assumes axis is normalized
 
-					var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
+		this.quaternion.setFromAxisAngle( axis, angle );
 
-					if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
-
-					intersects.push( {
-
-						distance: distance,
-						point: intersectionPoint,
-						face: face,
-						faceIndex: f,
-						object: object
-
-					} );
-
-				}
-
-			}
-
-		} else if ( object instanceof THREE.Line ) {
-
-			var precision = raycaster.linePrecision;
-			var precisionSq = precision * precision;
-
-			var geometry = object.geometry;
-
-			if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
-
-			// Checking boundingSphere distance to ray
-
-			sphere.copy( geometry.boundingSphere );
-			sphere.applyMatrix4( object.matrixWorld );
-
-			if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
-
-				return intersects;
-
-			}
-
-			inverseMatrix.getInverse( object.matrixWorld );
-			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
-
-			/* if ( geometry instanceof THREE.BufferGeometry ) {
-
-			} else */ if ( geometry instanceof THREE.Geometry ) {
-
-				var vertices = geometry.vertices;
-				var nbVertices = vertices.length;
-				var interSegment = new THREE.Vector3();
-				var interRay = new THREE.Vector3();
-				var step = object.type === THREE.LineStrip ? 1 : 2;
-
-				for ( var i = 0; i < nbVertices - 1; i = i + step ) {
-
-					var distSq = localRay.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
-
-					if ( distSq > precisionSq ) continue;
-
-					var distance = localRay.origin.distanceTo( interRay );
-
-					if ( distance < raycaster.near || distance > raycaster.far ) continue;
-
-					intersects.push( {
-
-						distance: distance,
-						// What do we want? intersection point on the ray or on the segment??
-						// point: raycaster.ray.at( distance ),
-						point: interSegment.clone().applyMatrix4( object.matrixWorld ),
-						face: null,
-						faceIndex: null,
-						object: object
-
-					} );
-
-				}
-
-			}
-
-		}
-
-	};
-
-	var intersectDescendants = function ( object, raycaster, intersects ) {
-
-		var descendants = object.getDescendants();
-
-		for ( var i = 0, l = descendants.length; i < l; i ++ ) {
-
-			intersectObject( descendants[ i ], raycaster, intersects );
-
-		}
-	};
-
-	//
-
-	THREE.Raycaster.prototype.precision = 0.0001;
-	THREE.Raycaster.prototype.linePrecision = 1;
-
-	THREE.Raycaster.prototype.set = function ( origin, direction ) {
-
-		this.ray.set( origin, direction );
-		// direction is assumed to be normalized (for accurate distance calculations)
-
-	};
-
-	THREE.Raycaster.prototype.intersectObject = function ( object, recursive ) {
-
-		var intersects = [];
-
-		if ( recursive === true ) {
-
-			intersectDescendants( object, this, intersects );
-
-		}
-
-		intersectObject( object, this, intersects );
-
-		intersects.sort( descSort );
-
-		return intersects;
-
-	};
-
-	THREE.Raycaster.prototype.intersectObjects = function ( objects, recursive ) {
-
-		var intersects = [];
-
-		for ( var i = 0, l = objects.length; i < l; i ++ ) {
-
-			intersectObject( objects[ i ], this, intersects );
-
-			if ( recursive === true ) {
-
-				intersectDescendants( objects[ i ], this, intersects );
-
-			}
-
-		}
-
-		intersects.sort( descSort );
-
-		return intersects;
-
-	};
-
-}( THREE ) );
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- */
-
-THREE.Object3D = function () {
-
-	this.id = THREE.Object3DIdCount ++;
-	this.uuid = THREE.Math.generateUUID();
-
-	this.name = '';
-
-	this.parent = undefined;
-	this.children = [];
-
-	this.up = new THREE.Vector3( 0, 1, 0 );
-
-	this.position = new THREE.Vector3();
-	this.rotation = new THREE.Euler();
-	this.quaternion = new THREE.Quaternion();
-	this.scale = new THREE.Vector3( 1, 1, 1 );
-
-	// keep rotation and quaternion in sync
-
-	this.rotation._quaternion = this.quaternion;
-	this.quaternion._euler = this.rotation;
-
-	this.renderDepth = null;
-
-	this.rotationAutoUpdate = true;
-
-	this.matrix = new THREE.Matrix4();
-	this.matrixWorld = new THREE.Matrix4();
-
-	this.matrixAutoUpdate = true;
-	this.matrixWorldNeedsUpdate = true;
-
-	this.visible = true;
-
-	this.castShadow = false;
-	this.receiveShadow = false;
-
-	this.frustumCulled = true;
-
-	this.userData = {};
-
-};
-
-
-THREE.Object3D.prototype = {
-
-	constructor: THREE.Object3D,
-
-	get eulerOrder () {
-
-		console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' );
-
-		return this.rotation.order;
-
-	},
-
-	set eulerOrder ( value ) {
-
-		console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' );
-
-		this.rotation.order = value;
-
-	},
-
-	get useQuaternion () {
-
-		console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' );
-
-	},
-
-	set useQuaternion ( value ) {
-
-		console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' );
-
-	},
-
-	applyMatrix: function () {
-
-		var m1 = new THREE.Matrix4();
-
-		return function ( matrix ) {
-
-			this.matrix.multiplyMatrices( matrix, this.matrix );
-
-			this.position.getPositionFromMatrix( this.matrix );
-
-			this.scale.getScaleFromMatrix( this.matrix );
-
-			m1.extractRotation( this.matrix );
-
-			this.quaternion.setFromRotationMatrix( m1 );
-
-		}
-
-	}(),
-
-	setRotationFromAxisAngle: function ( axis, angle ) {
-
-		// assumes axis is normalized
-
-		this.quaternion.setFromAxisAngle( axis, angle );
-
-	},
+	},
 
 	setRotationFromEuler: function ( euler ) {
 
@@ -7321,7 +7580,7 @@ THREE.Object3D.prototype = {
 
 	},
 
-	rotateOnAxis: function() {
+	rotateOnAxis: function () {
 
 		// rotate object on axis in object space
 		// axis is assumed to be normalized
@@ -7385,9 +7644,7 @@ THREE.Object3D.prototype = {
 
 		return function ( axis, distance ) {
 
-			v1.copy( axis );
-
-			v1.applyQuaternion( this.quaternion );
+			v1.copy( axis ).applyQuaternion( this.quaternion );
 
 			this.position.add( v1.multiplyScalar( distance ) );
 
@@ -7399,7 +7656,7 @@ THREE.Object3D.prototype = {
 
 	translate: function ( distance, axis ) {
 
-		console.warn( 'DEPRECATED: Object3D\'s .translate() has been removed. Use .translateOnAxis( axis, distance ) instead. Note args have been changed.' );
+		THREE.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
 		return this.translateOnAxis( axis, distance );
 
 	},
@@ -7476,10 +7733,22 @@ THREE.Object3D.prototype = {
 
 	add: function ( object ) {
 
+		if ( arguments.length > 1 ) {
+
+			for ( var i = 0; i < arguments.length; i ++ ) {
+
+				this.add( arguments[ i ] );
+
+			}
+
+			return this;
+
+		};
+
 		if ( object === this ) {
 
-			console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
-			return;
+			THREE.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+			return this;
 
 		}
 
@@ -7496,90 +7765,73 @@ THREE.Object3D.prototype = {
 
 			this.children.push( object );
 
-			// add to scene
+		} else {
 
-			var scene = this;
+			THREE.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
 
-			while ( scene.parent !== undefined ) {
+		}
 
-				scene = scene.parent;
+		return this;
 
-			}
+	},
 
-			if ( scene !== undefined && scene instanceof THREE.Scene )  {
+	remove: function ( object ) {
 
-				scene.__addObject( object );
+		if ( arguments.length > 1 ) {
 
-			}
+			for ( var i = 0; i < arguments.length; i ++ ) {
 
-		}
+				this.remove( arguments[ i ] );
 
-	},
+			}
 
-	remove: function ( object ) {
+		};
 
 		var index = this.children.indexOf( object );
 
 		if ( index !== - 1 ) {
 
 			object.parent = undefined;
+
 			object.dispatchEvent( { type: 'removed' } );
 
 			this.children.splice( index, 1 );
 
-			// remove from scene
-
-			var scene = this;
-
-			while ( scene.parent !== undefined ) {
-
-				scene = scene.parent;
-
-			}
-
-			if ( scene !== undefined && scene instanceof THREE.Scene ) {
-
-				scene.__removeObject( object );
-
-			}
-
 		}
 
 	},
 
-	traverse: function ( callback ) {
+	getChildByName: function ( name ) {
 
-		callback( this );
+		THREE.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
+		return this.getObjectByName( name );
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+	},
 
-			this.children[ i ].traverse( callback );
+	getObjectById: function ( id ) {
 
-		}
+		return this.getObjectByProperty( 'id', id );
 
 	},
 
-	getObjectById: function ( id, recursive ) {
-
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
-
-			var child = this.children[ i ];
+	getObjectByName: function ( name ) {
 
-			if ( child.id === id ) {
+		return this.getObjectByProperty( 'name', name );
 
-				return child;
+	},
 
-			}
+	getObjectByProperty: function ( name, value ) {
 
-			if ( recursive === true ) {
+		if ( this[ name ] === value ) return this;
 
-				child = child.getObjectById( id, recursive );
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-				if ( child !== undefined ) {
+			var child = this.children[ i ];
+			var object = child.getObjectByProperty( name, value );
 
-					return child;
+			if ( object !== undefined ) {
 
-				}
+				return object;
 
 			}
 
@@ -7589,56 +7841,123 @@ THREE.Object3D.prototype = {
 
 	},
 
-	getObjectByName: function ( name, recursive ) {
+	getWorldPosition: function ( optionalTarget ) {
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+		var result = optionalTarget || new THREE.Vector3();
 
-			var child = this.children[ i ];
+		this.updateMatrixWorld( true );
 
-			if ( child.name === name ) {
+		return result.setFromMatrixPosition( this.matrixWorld );
 
-				return child;
+	},
 
-			}
+	getWorldQuaternion: function () {
 
-			if ( recursive === true ) {
+		var position = new THREE.Vector3();
+		var scale = new THREE.Vector3();
 
-				child = child.getObjectByName( name, recursive );
+		return function ( optionalTarget ) {
 
-				if ( child !== undefined ) {
+			var result = optionalTarget || new THREE.Quaternion();
 
-					return child;
+			this.updateMatrixWorld( true );
 
-				}
+			this.matrixWorld.decompose( position, result, scale );
 
-			}
+			return result;
 
 		}
 
-		return undefined;
+	}(),
 
-	},
+	getWorldRotation: function () {
 
-	getChildByName: function ( name, recursive ) {
+		var quaternion = new THREE.Quaternion();
 
-		console.warn( 'DEPRECATED: Object3D\'s .getChildByName() has been renamed to .getObjectByName().' );
-		return this.getObjectByName( name, recursive );
+		return function ( optionalTarget ) {
 
-	},
+			var result = optionalTarget || new THREE.Euler();
 
-	getDescendants: function ( array ) {
+			this.getWorldQuaternion( quaternion );
 
-		if ( array === undefined ) array = [];
+			return result.setFromQuaternion( quaternion, this.rotation.order, false );
 
-		Array.prototype.push.apply( array, this.children );
+		}
+
+	}(),
+
+	getWorldScale: function () {
+
+		var position = new THREE.Vector3();
+		var quaternion = new THREE.Quaternion();
+
+		return function ( optionalTarget ) {
+
+			var result = optionalTarget || new THREE.Vector3();
+
+			this.updateMatrixWorld( true );
+
+			this.matrixWorld.decompose( position, quaternion, result );
+
+			return result;
+
+		}
+
+	}(),
+
+	getWorldDirection: function () {
+
+		var quaternion = new THREE.Quaternion();
+
+		return function ( optionalTarget ) {
+
+			var result = optionalTarget || new THREE.Vector3();
+
+			this.getWorldQuaternion( quaternion );
+
+			return result.set( 0, 0, 1 ).applyQuaternion( quaternion );
+
+		}
+
+	}(),
+
+	raycast: function () {},
+
+	traverse: function ( callback ) {
+
+		callback( this );
 
 		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-			this.children[ i ].getDescendants( array );
+			this.children[ i ].traverse( callback );
 
 		}
 
-		return array;
+	},
+
+	traverseVisible: function ( callback ) {
+
+		if ( this.visible === false ) return;
+
+		callback( this );
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].traverseVisible( callback );
+
+		}
+
+	},
+
+	traverseAncestors: function ( callback ) {
+
+		if ( this.parent ) {
+
+			callback( this.parent );
+
+			this.parent.traverseAncestors( callback );
+
+		}
 
 	},
 
@@ -7682,1123 +8001,1080 @@ THREE.Object3D.prototype = {
 
 	},
 
-	clone: function ( object, recursive ) {
-
-		if ( object === undefined ) object = new THREE.Object3D();
-		if ( recursive === undefined ) recursive = true;
-
-		object.name = this.name;
-
-		object.up.copy( this.up );
+	toJSON: function () {
 
-		object.position.copy( this.position );
-		object.quaternion.copy( this.quaternion );
-		object.scale.copy( this.scale );
+		var output = {
+			metadata: {
+				version: 4.3,
+				type: 'Object',
+				generator: 'ObjectExporter'
+			}
+		};
 
-		object.renderDepth = this.renderDepth;
+		//
 
-		object.rotationAutoUpdate = this.rotationAutoUpdate;
+		var geometries = {};
 
-		object.matrix.copy( this.matrix );
-		object.matrixWorld.copy( this.matrixWorld );
+		var parseGeometry = function ( geometry ) {
 
-		object.matrixAutoUpdate = this.matrixAutoUpdate;
-		object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
+			if ( output.geometries === undefined ) {
 
-		object.visible = this.visible;
+				output.geometries = [];
 
-		object.castShadow = this.castShadow;
-		object.receiveShadow = this.receiveShadow;
+			}
 
-		object.frustumCulled = this.frustumCulled;
+			if ( geometries[ geometry.uuid ] === undefined ) {
 
-		object.userData = JSON.parse( JSON.stringify( this.userData ) );
+				var json = geometry.toJSON();
 
-		if ( recursive === true ) {
+				delete json.metadata;
 
-			for ( var i = 0; i < this.children.length; i ++ ) {
+				geometries[ geometry.uuid ] = json;
 
-				var child = this.children[ i ];
-				object.add( child.clone() );
+				output.geometries.push( json );
 
 			}
 
-		}
-
-		return object;
-
-	}
-
-};
+			return geometry.uuid;
 
-THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype );
+		};
 
-THREE.Object3DIdCount = 0;
+		//
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author supereggbert / http://www.paulbrunt.co.uk/
- * @author julianwa / https://github.com/julianwa
- */
+		var materials = {};
 
-THREE.Projector = function () {
+		var parseMaterial = function ( material ) {
 
-	var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
-	_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
-	_face, _face3Count, _face3Pool = [], _face3PoolLength = 0,
-	_line, _lineCount, _linePool = [], _linePoolLength = 0,
-	_sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0,
+			if ( output.materials === undefined ) {
 
-	_renderData = { objects: [], sprites: [], lights: [], elements: [] },
+				output.materials = [];
 
-	_vector3 = new THREE.Vector3(),
-	_vector4 = new THREE.Vector4(),
+			}
 
-	_clipBox = new THREE.Box3( new THREE.Vector3( -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) ),
-	_boundingBox = new THREE.Box3(),
-	_points3 = new Array( 3 ),
-	_points4 = new Array( 4 ),
+			if ( materials[ material.uuid ] === undefined ) {
 
-	_viewMatrix = new THREE.Matrix4(),
-	_viewProjectionMatrix = new THREE.Matrix4(),
+				var json = material.toJSON();
 
-	_modelMatrix,
-	_modelViewProjectionMatrix = new THREE.Matrix4(),
+				delete json.metadata;
 
-	_normalMatrix = new THREE.Matrix3(),
-	_normalViewMatrix = new THREE.Matrix3(),
+				materials[ material.uuid ] = json;
 
-	_centroid = new THREE.Vector3(),
+				output.materials.push( json );
 
-	_frustum = new THREE.Frustum(),
+			}
 
-	_clippedVertex1PositionScreen = new THREE.Vector4(),
-	_clippedVertex2PositionScreen = new THREE.Vector4();
+			return material.uuid;
 
-	this.projectVector = function ( vector, camera ) {
+		};
 
-		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+		//
 
-		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		var parseObject = function ( object ) {
 
-		return vector.applyProjection( _viewProjectionMatrix );
+			var data = {};
 
-	};
+			data.uuid = object.uuid;
+			data.type = object.type;
 
-	this.unprojectVector = function ( vector, camera ) {
+			if ( object.name !== '' ) data.name = object.name;
+			if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData;
+			if ( object.visible !== true ) data.visible = object.visible;
 
-		camera.projectionMatrixInverse.getInverse( camera.projectionMatrix );
+			if ( object instanceof THREE.PerspectiveCamera ) {
 
-		_viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, camera.projectionMatrixInverse );
+				data.fov = object.fov;
+				data.aspect = object.aspect;
+				data.near = object.near;
+				data.far = object.far;
 
-		return vector.applyProjection( _viewProjectionMatrix );
+			} else if ( object instanceof THREE.OrthographicCamera ) {
 
-	};
+				data.left = object.left;
+				data.right = object.right;
+				data.top = object.top;
+				data.bottom = object.bottom;
+				data.near = object.near;
+				data.far = object.far;
 
-	this.pickingRay = function ( vector, camera ) {
+			} else if ( object instanceof THREE.AmbientLight ) {
 
-		// set two vectors with opposing z values
-		vector.z = -1.0;
-		var end = new THREE.Vector3( vector.x, vector.y, 1.0 );
+				data.color = object.color.getHex();
 
-		this.unprojectVector( vector, camera );
-		this.unprojectVector( end, camera );
+			} else if ( object instanceof THREE.DirectionalLight ) {
 
-		// find direction from vector to end
-		end.sub( vector ).normalize();
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
 
-		return new THREE.Raycaster( vector, end );
+			} else if ( object instanceof THREE.PointLight ) {
 
-	};
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
+				data.distance = object.distance;
+				data.decay = object.decay;
 
-	var getObject = function ( object ) {
+			} else if ( object instanceof THREE.SpotLight ) {
 
-		_object = getNextObjectInPool();
-		_object.id = object.id;
-		_object.object = object;
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
+				data.distance = object.distance;
+				data.angle = object.angle;
+				data.exponent = object.exponent;
+				data.decay = object.decay;
 
-		if ( object.renderDepth !== null ) {
+			} else if ( object instanceof THREE.HemisphereLight ) {
 
-			_object.z = object.renderDepth;
+				data.color = object.color.getHex();
+				data.groundColor = object.groundColor.getHex();
 
-		} else {
+			} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.PointCloud ) {
 
-			_vector3.getPositionFromMatrix( object.matrixWorld );
-			_vector3.applyProjection( _viewProjectionMatrix );
-			_object.z = _vector3.z;
+				data.geometry = parseGeometry( object.geometry );
+				data.material = parseMaterial( object.material );
 
-		}
+				if ( object instanceof THREE.Line ) data.mode = object.mode;
 
-		return _object;
+			} else if ( object instanceof THREE.Sprite ) {
 
-	};
+				data.material = parseMaterial( object.material );
 
-	var projectObject = function ( object ) {
+			}
 
-		if ( object.visible === false ) return;
+			data.matrix = object.matrix.toArray();
 
-		if ( object instanceof THREE.Light ) {
+			if ( object.children.length > 0 ) {
 
-			_renderData.lights.push( object );
+				data.children = [];
 
-		} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line ) {
+				for ( var i = 0; i < object.children.length; i ++ ) {
 
-			if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+					data.children.push( parseObject( object.children[ i ] ) );
 
-				_renderData.objects.push( getObject( object ) );
+				}
 
 			}
 
-		} else if ( object instanceof THREE.Sprite ) {
-
-			_renderData.sprites.push( getObject( object ) );
+			return data;
 
 		}
 
-		for ( var i = 0, l = object.children.length; i < l; i ++ ) {
+		output.object = parseObject( this );
 
-			projectObject( object.children[ i ] );
+		return output;
 
-		}
+	},
 
-	};
+	clone: function ( object, recursive ) {
 
-	var projectGraph = function ( root, sortObjects ) {
+		if ( object === undefined ) object = new THREE.Object3D();
+		if ( recursive === undefined ) recursive = true;
 
-		_objectCount = 0;
+		object.name = this.name;
 
-		_renderData.objects.length = 0;
-		_renderData.sprites.length = 0;
-		_renderData.lights.length = 0;
+		object.up.copy( this.up );
 
-		projectObject( root );
+		object.position.copy( this.position );
+		object.quaternion.copy( this.quaternion );
+		object.scale.copy( this.scale );
 
-		if ( sortObjects === true ) {
+		object.rotationAutoUpdate = this.rotationAutoUpdate;
 
-			_renderData.objects.sort( painterSort );
+		object.matrix.copy( this.matrix );
+		object.matrixWorld.copy( this.matrixWorld );
 
-		}
+		object.matrixAutoUpdate = this.matrixAutoUpdate;
+		object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
 
-	};
+		object.visible = this.visible;
 
-	this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
+		object.castShadow = this.castShadow;
+		object.receiveShadow = this.receiveShadow;
 
-		var visible = false,
-		o, ol, v, vl, f, fl, n, nl, c, cl, u, ul, object,
-		geometry, vertices, faces, face, faceVertexNormals, faceVertexUvs, uvs,
-		v1, v2, v3, v4, isFaceMaterial, objectMaterials;
+		object.frustumCulled = this.frustumCulled;
 
-		_face3Count = 0;
-		_lineCount = 0;
-		_spriteCount = 0;
+		object.userData = JSON.parse( JSON.stringify( this.userData ) );
 
-		_renderData.elements.length = 0;
+		if ( recursive === true ) {
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
-		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+			for ( var i = 0; i < this.children.length; i ++ ) {
 
-		_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
-		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
+				var child = this.children[ i ];
+				object.add( child.clone() );
 
-		_normalViewMatrix.getNormalMatrix( _viewMatrix );
+			}
 
-		_frustum.setFromMatrix( _viewProjectionMatrix );
+		}
 
-		projectGraph( scene, sortObjects );
+		return object;
 
-		for ( o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
+	}
 
-			object = _renderData.objects[ o ].object;
+};
 
-			_modelMatrix = object.matrixWorld;
+THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype );
 
-			_vertexCount = 0;
+THREE.Object3DIdCount = 0;
 
-			if ( object instanceof THREE.Mesh ) {
+// File:src/core/Face3.js
 
-				geometry = object.geometry;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-				vertices = geometry.vertices;
-				faces = geometry.faces;
-				faceVertexUvs = geometry.faceVertexUvs;
+THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
 
-				_normalMatrix.getNormalMatrix( _modelMatrix );
+	this.a = a;
+	this.b = b;
+	this.c = c;
 
-				isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
-				objectMaterials = isFaceMaterial === true ? object.material : null;
+	this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
+	this.vertexNormals = normal instanceof Array ? normal : [];
 
-				for ( v = 0, vl = vertices.length; v < vl; v ++ ) {
+	this.color = color instanceof THREE.Color ? color : new THREE.Color();
+	this.vertexColors = color instanceof Array ? color : [];
 
-					_vertex = getNextVertexInPool();
+	this.vertexTangents = [];
 
-					_vertex.positionWorld.copy( vertices[ v ] ).applyMatrix4( _modelMatrix );
-					_vertex.positionScreen.copy( _vertex.positionWorld ).applyMatrix4( _viewProjectionMatrix );
+	this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
 
-					var invW = 1 / _vertex.positionScreen.w;
+};
 
-					_vertex.positionScreen.x *= invW;
-					_vertex.positionScreen.y *= invW;
-					_vertex.positionScreen.z *= invW;
+THREE.Face3.prototype = {
 
-					_vertex.visible = ! ( _vertex.positionScreen.x < -1 || _vertex.positionScreen.x > 1 ||
-							      _vertex.positionScreen.y < -1 || _vertex.positionScreen.y > 1 ||
-							      _vertex.positionScreen.z < -1 || _vertex.positionScreen.z > 1 );
+	constructor: THREE.Face3,
 
-				}
+	clone: function () {
 
-				for ( f = 0, fl = faces.length; f < fl; f ++ ) {
+		var face = new THREE.Face3( this.a, this.b, this.c );
 
-					face = faces[ f ];
+		face.normal.copy( this.normal );
+		face.color.copy( this.color );
 
-					var material = isFaceMaterial === true
-						? objectMaterials.materials[ face.materialIndex ]
-						: object.material;
+		face.materialIndex = this.materialIndex;
 
-					if ( material === undefined ) continue;
+		for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) {
 
-					var side = material.side;
+			face.vertexNormals[ i ] = this.vertexNormals[ i ].clone();
 
-					v1 = _vertexPool[ face.a ];
-					v2 = _vertexPool[ face.b ];
-					v3 = _vertexPool[ face.c ];
+		}
 
-					_points3[ 0 ] = v1.positionScreen;
-					_points3[ 1 ] = v2.positionScreen;
-					_points3[ 2 ] = v3.positionScreen;
+		for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) {
 
-					if ( v1.visible === true || v2.visible === true || v3.visible === true ||
-						_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ) ) {
+			face.vertexColors[ i ] = this.vertexColors[ i ].clone();
 
-						visible = ( ( v3.positionScreen.x - v1.positionScreen.x ) *
-							    ( v2.positionScreen.y - v1.positionScreen.y ) -
-							    ( v3.positionScreen.y - v1.positionScreen.y ) *
-							    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
+		}
 
-						if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) {
+		for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) {
 
-							_face = getNextFace3InPool();
+			face.vertexTangents[ i ] = this.vertexTangents[ i ].clone();
 
-							_face.id = object.id;
-							_face.v1.copy( v1 );
-							_face.v2.copy( v2 );
-							_face.v3.copy( v3 );
+		}
 
-						} else {
+		return face;
 
-							continue;
+	}
 
-						}
+};
 
-					} else {
+// File:src/core/Face4.js
 
-						continue;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-					}
+THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
 
-					_face.normalModel.copy( face.normal );
+	THREE.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' )
+	return new THREE.Face3( a, b, c, normal, color, materialIndex );
 
-					if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+};
 
-						_face.normalModel.negate();
+// File:src/core/BufferAttribute.js
 
-					}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-					_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
+THREE.BufferAttribute = function ( array, itemSize ) {
 
-					_face.normalModelView.copy( _face.normalModel ).applyMatrix3( _normalViewMatrix );
+	this.array = array;
+	this.itemSize = itemSize;
 
-					_face.centroidModel.copy( face.centroid ).applyMatrix4( _modelMatrix );
+	this.needsUpdate = false;
 
-					faceVertexNormals = face.vertexNormals;
+};
 
-					for ( n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) {
+THREE.BufferAttribute.prototype = {
 
-						var normalModel = _face.vertexNormalsModel[ n ];
-						normalModel.copy( faceVertexNormals[ n ] );
+	constructor: THREE.BufferAttribute,
 
-						if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+	get length () {
 
-							normalModel.negate();
+		return this.array.length;
 
-						}
+	},
 
-						normalModel.applyMatrix3( _normalMatrix ).normalize();
+	copyAt: function ( index1, attribute, index2 ) {
 
-						var normalModelView = _face.vertexNormalsModelView[ n ];
-						normalModelView.copy( normalModel ).applyMatrix3( _normalViewMatrix );
+		index1 *= this.itemSize;
+		index2 *= attribute.itemSize;
 
-					}
+		for ( var i = 0, l = this.itemSize; i < l; i ++ ) {
 
-					_face.vertexNormalsLength = faceVertexNormals.length;
+			this.array[ index1 + i ] = attribute.array[ index2 + i ];
 
-					for ( c = 0, cl = Math.min( faceVertexUvs.length, 3 ); c < cl; c ++ ) {
+		}
 
-						uvs = faceVertexUvs[ c ][ f ];
+		return this;
 
-						if ( uvs === undefined ) continue;
+	},
 
-						for ( u = 0, ul = uvs.length; u < ul; u ++ ) {
+	set: function ( value, offset ) {
 
-							_face.uvs[ c ][ u ] = uvs[ u ];
+		if ( offset === undefined ) offset = 0;
 
-						}
+		this.array.set( value, offset );
 
-					}
+		return this;
 
-					_face.color = face.color;
-					_face.material = material;
+	},
 
-					_centroid.copy( _face.centroidModel ).applyProjection( _viewProjectionMatrix );
+	setX: function ( index, x ) {
 
-					_face.z = _centroid.z;
+		this.array[ index * this.itemSize ] = x;
 
-					_renderData.elements.push( _face );
+		return this;
 
-				}
+	},
 
-			} else if ( object instanceof THREE.Line ) {
+	setY: function ( index, y ) {
 
-				_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
+		this.array[ index * this.itemSize + 1 ] = y;
 
-				vertices = object.geometry.vertices;
+		return this;
 
-				v1 = getNextVertexInPool();
-				v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
+	},
 
-				// Handle LineStrip and LinePieces
-				var step = object.type === THREE.LinePieces ? 2 : 1;
+	setZ: function ( index, z ) {
 
-				for ( v = 1, vl = vertices.length; v < vl; v ++ ) {
+		this.array[ index * this.itemSize + 2 ] = z;
 
-					v1 = getNextVertexInPool();
-					v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix );
+		return this;
 
-					if ( ( v + 1 ) % step > 0 ) continue;
+	},
 
-					v2 = _vertexPool[ _vertexCount - 2 ];
+	setXY: function ( index, x, y ) {
 
-					_clippedVertex1PositionScreen.copy( v1.positionScreen );
-					_clippedVertex2PositionScreen.copy( v2.positionScreen );
+		index *= this.itemSize;
 
-					if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) {
+		this.array[ index     ] = x;
+		this.array[ index + 1 ] = y;
 
-						// Perform the perspective divide
-						_clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w );
-						_clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w );
+		return this;
 
-						_line = getNextLineInPool();
+	},
 
-						_line.id = object.id;
-						_line.v1.positionScreen.copy( _clippedVertex1PositionScreen );
-						_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
+	setXYZ: function ( index, x, y, z ) {
 
-						_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
+		index *= this.itemSize;
 
-						_line.material = object.material;
+		this.array[ index     ] = x;
+		this.array[ index + 1 ] = y;
+		this.array[ index + 2 ] = z;
 
-						if ( object.material.vertexColors === THREE.VertexColors ) {
+		return this;
 
-							_line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] );
-							_line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] );
+	},
 
-						}
+	setXYZW: function ( index, x, y, z, w ) {
 
-						_renderData.elements.push( _line );
+		index *= this.itemSize;
 
-					}
+		this.array[ index     ] = x;
+		this.array[ index + 1 ] = y;
+		this.array[ index + 2 ] = z;
+		this.array[ index + 3 ] = w;
 
-				}
+		return this;
 
-			}
+	},
 
-		}
+	clone: function () {
 
-		for ( o = 0, ol = _renderData.sprites.length; o < ol; o++ ) {
+		return new THREE.BufferAttribute( new this.array.constructor( this.array ), this.itemSize );
 
-			object = _renderData.sprites[ o ].object;
+	}
 
-			_modelMatrix = object.matrixWorld;
+};
 
-			if ( object instanceof THREE.Sprite ) {
+//
 
-				_vector4.set( _modelMatrix.elements[12], _modelMatrix.elements[13], _modelMatrix.elements[14], 1 );
-				_vector4.applyMatrix4( _viewProjectionMatrix );
+THREE.Int8Attribute = function ( data, itemSize ) {
 
-				var invW = 1 / _vector4.w;
+	THREE.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-				_vector4.z *= invW;
+};
 
-				if ( _vector4.z > -1 && _vector4.z < 1 ) {
+THREE.Uint8Attribute = function ( data, itemSize ) {
 
-					_sprite = getNextSpriteInPool();
-					_sprite.id = object.id;
-					_sprite.x = _vector4.x * invW;
-					_sprite.y = _vector4.y * invW;
-					_sprite.z = _vector4.z;
-					_sprite.object = object;
+	THREE.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-					_sprite.rotation = object.rotation;
+};
 
-					_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[0] ) / ( _vector4.w + camera.projectionMatrix.elements[12] ) );
-					_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[5] ) / ( _vector4.w + camera.projectionMatrix.elements[13] ) );
+THREE.Uint8ClampedAttribute = function ( data, itemSize ) {
 
-					_sprite.material = object.material;
+	THREE.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-					_renderData.elements.push( _sprite );
 
-				}
+};
 
-			}
+THREE.Int16Attribute = function ( data, itemSize ) {
 
-		}
+	THREE.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-		if ( sortElements === true ) _renderData.elements.sort( painterSort );
+};
 
-		return _renderData;
+THREE.Uint16Attribute = function ( data, itemSize ) {
 
-	};
+	THREE.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-	// Pools
+};
 
-	function getNextObjectInPool() {
+THREE.Int32Attribute = function ( data, itemSize ) {
 
-		if ( _objectCount === _objectPoolLength ) {
+	THREE.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-			var object = new THREE.RenderableObject();
-			_objectPool.push( object );
-			_objectPoolLength ++;
-			_objectCount ++;
-			return object;
+};
 
-		}
+THREE.Uint32Attribute = function ( data, itemSize ) {
 
-		return _objectPool[ _objectCount ++ ];
+	THREE.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-	}
+};
 
-	function getNextVertexInPool() {
+THREE.Float32Attribute = function ( data, itemSize ) {
 
-		if ( _vertexCount === _vertexPoolLength ) {
+	THREE.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-			var vertex = new THREE.RenderableVertex();
-			_vertexPool.push( vertex );
-			_vertexPoolLength ++;
-			_vertexCount ++;
-			return vertex;
+};
 
-		}
+THREE.Float64Attribute = function ( data, itemSize ) {
 
-		return _vertexPool[ _vertexCount ++ ];
+	THREE.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' );
+	return new THREE.BufferAttribute( data, itemSize );
 
-	}
+};
 
-	function getNextFace3InPool() {
+// File:src/core/DynamicBufferAttribute.js
 
-		if ( _face3Count === _face3PoolLength ) {
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			var face = new THREE.RenderableFace3();
-			_face3Pool.push( face );
-			_face3PoolLength ++;
-			_face3Count ++;
-			return face;
+THREE.DynamicBufferAttribute = function ( array, itemSize ) {
 
-		}
+	THREE.BufferAttribute.call( this, array, itemSize );
 
-		return _face3Pool[ _face3Count ++ ];
+	this.updateRange = { offset: 0, count: -1 };
 
+};
 
-	}
+THREE.DynamicBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype );
+THREE.DynamicBufferAttribute.prototype.constructor = THREE.DynamicBufferAttribute;
 
-	function getNextLineInPool() {
+THREE.DynamicBufferAttribute.prototype.clone = function () {
 
-		if ( _lineCount === _linePoolLength ) {
+	return new THREE.DynamicBufferAttribute( new this.array.constructor( this.array ), this.itemSize );
 
-			var line = new THREE.RenderableLine();
-			_linePool.push( line );
-			_linePoolLength ++;
-			_lineCount ++
-			return line;
+};
 
-		}
+// File:src/core/BufferGeometry.js
 
-		return _linePool[ _lineCount ++ ];
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	}
+THREE.BufferGeometry = function () {
 
-	function getNextSpriteInPool() {
+	Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
 
-		if ( _spriteCount === _spritePoolLength ) {
+	this.uuid = THREE.Math.generateUUID();
 
-			var sprite = new THREE.RenderableSprite();
-			_spritePool.push( sprite );
-			_spritePoolLength ++;
-			_spriteCount ++
-			return sprite;
+	this.name = '';
+	this.type = 'BufferGeometry';
 
-		}
+	this.attributes = {};
+	this.attributesKeys = [];
 
-		return _spritePool[ _spriteCount ++ ];
+	this.drawcalls = [];
+	this.offsets = this.drawcalls; // backwards compatibility
 
-	}
+	this.boundingBox = null;
+	this.boundingSphere = null;
 
-	//
+};
 
-	function painterSort( a, b ) {
+THREE.BufferGeometry.prototype = {
 
-		if ( a.z !== b.z ) {
+	constructor: THREE.BufferGeometry,
 
-			return b.z - a.z;
+	addAttribute: function ( name, attribute ) {
 
-		} else if ( a.id !== b.id ) {
+		if ( attribute instanceof THREE.BufferAttribute === false ) {
 
-			return a.id - b.id;
+			THREE.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
 
-		} else {
+			this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] };
 
-			return 0;
+			return;
 
 		}
 
-	}
-
-	function clipLine( s1, s2 ) {
-
-		var alpha1 = 0, alpha2 = 1,
-
-		// Calculate the boundary coordinate of each vertex for the near and far clip planes,
-		// Z = -1 and Z = +1, respectively.
-		bc1near =  s1.z + s1.w,
-		bc2near =  s2.z + s2.w,
-		bc1far =  - s1.z + s1.w,
-		bc2far =  - s2.z + s2.w;
+		this.attributes[ name ] = attribute;
+		this.attributesKeys = Object.keys( this.attributes );
 
-		if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
-
-			// Both vertices lie entirely within all clip planes.
-			return true;
+	},
 
-		} else if ( ( bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0 ) ) {
+	getAttribute: function ( name ) {
 
-			// Both vertices lie entirely outside one of the clip planes.
-			return false;
+		return this.attributes[ name ];
 
-		} else {
+	},
 
-			// The line segment spans at least one clip plane.
+	addDrawCall: function ( start, count, indexOffset ) {
 
-			if ( bc1near < 0 ) {
+		this.drawcalls.push( {
 
-				// v1 lies outside the near plane, v2 inside
-				alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
+			start: start,
+			count: count,
+			index: indexOffset !== undefined ? indexOffset : 0
 
-			} else if ( bc2near < 0 ) {
+		} );
 
-				// v2 lies outside the near plane, v1 inside
-				alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
+	},
 
-			}
+	applyMatrix: function ( matrix ) {
 
-			if ( bc1far < 0 ) {
+		var position = this.attributes.position;
 
-				// v1 lies outside the far plane, v2 inside
-				alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
+		if ( position !== undefined ) {
 
-			} else if ( bc2far < 0 ) {
+			matrix.applyToVector3Array( position.array );
+			position.needsUpdate = true;
 
-				// v2 lies outside the far plane, v2 inside
-				alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
+		}
 
-			}
+		var normal = this.attributes.normal;
 
-			if ( alpha2 < alpha1 ) {
+		if ( normal !== undefined ) {
 
-				// The line segment spans two boundaries, but is outside both of them.
-				// (This can't happen when we're only clipping against just near/far but good
-				//  to leave the check here for future usage if other clip planes are added.)
-				return false;
+			var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
 
-			} else {
+			normalMatrix.applyToVector3Array( normal.array );
+			normal.needsUpdate = true;
 
-				// Update the s1 and s2 vertices to match the clipped line segment.
-				s1.lerp( s2, alpha1 );
-				s2.lerp( s1, 1 - alpha2 );
+		}
 
-				return true;
+		if ( this.boundingBox !== null ) {
 
-			}
+			this.computeBoundingBox();
 
 		}
 
-	}
+		if ( this.boundingSphere !== null ) {
 
-};
+			this.computeBoundingSphere();
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+		}
 
-THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
+	},
 
-	this.a = a;
-	this.b = b;
-	this.c = c;
+	center: function () {
 
-	this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
-	this.vertexNormals = normal instanceof Array ? normal : [ ];
+		this.computeBoundingBox();
 
-	this.color = color instanceof THREE.Color ? color : new THREE.Color();
-	this.vertexColors = color instanceof Array ? color : [];
+		var offset = this.boundingBox.center().negate();
 
-	this.vertexTangents = [];
+		this.applyMatrix( new THREE.Matrix4().setPosition( offset ) );
 
-	this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+		return offset;
 
-	this.centroid = new THREE.Vector3();
+	},
 
-};
+	fromGeometry: function ( geometry, settings ) {
 
-THREE.Face3.prototype = {
+		settings = settings || { 'vertexColors': THREE.NoColors };
 
-	constructor: THREE.Face3,
+		var vertices = geometry.vertices;
+		var faces = geometry.faces;
+		var faceVertexUvs = geometry.faceVertexUvs;
+		var vertexColors = settings.vertexColors;
+		var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0;
+		var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3;
 
-	clone: function () {
+		var positions = new Float32Array( faces.length * 3 * 3 );
+		this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
 
-		var face = new THREE.Face3( this.a, this.b, this.c );
+		var normals = new Float32Array( faces.length * 3 * 3 );
+		this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
 
-		face.normal.copy( this.normal );
-		face.color.copy( this.color );
-		face.centroid.copy( this.centroid );
+		if ( vertexColors !== THREE.NoColors ) {
 
-		face.materialIndex = this.materialIndex;
+			var colors = new Float32Array( faces.length * 3 * 3 );
+			this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
 
-		var i, il;
-		for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone();
-		for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone();
-		for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone();
+		}
 
-		return face;
+		if ( hasFaceVertexUv === true ) {
 
-	}
+			var uvs = new Float32Array( faces.length * 3 * 2 );
+			this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
-};
+		}
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) {
 
-THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
+			var face = faces[ i ];
 
-	console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.')
+			var a = vertices[ face.a ];
+			var b = vertices[ face.b ];
+			var c = vertices[ face.c ];
 
-	return new THREE.Face3( a, b, c, normal, color, materialIndex );
+			positions[ i3     ] = a.x;
+			positions[ i3 + 1 ] = a.y;
+			positions[ i3 + 2 ] = a.z;
 
-};
+			positions[ i3 + 3 ] = b.x;
+			positions[ i3 + 4 ] = b.y;
+			positions[ i3 + 5 ] = b.z;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author kile / http://kile.stravaganza.org/
- * @author alteredq / http://alteredqualia.com/
- * @author mikael emtinger / http://gomo.se/
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * @author bhouston / http://exocortex.com
- */
+			positions[ i3 + 6 ] = c.x;
+			positions[ i3 + 7 ] = c.y;
+			positions[ i3 + 8 ] = c.z;
 
-THREE.Geometry = function () {
+			if ( hasFaceVertexNormals === true ) {
 
-	this.id = THREE.GeometryIdCount ++;
-	this.uuid = THREE.Math.generateUUID();
+				var na = face.vertexNormals[ 0 ];
+				var nb = face.vertexNormals[ 1 ];
+				var nc = face.vertexNormals[ 2 ];
 
-	this.name = '';
+				normals[ i3     ] = na.x;
+				normals[ i3 + 1 ] = na.y;
+				normals[ i3 + 2 ] = na.z;
 
-	this.vertices = [];
-	this.colors = [];  // one-to-one vertex colors, used in ParticleSystem and Line
+				normals[ i3 + 3 ] = nb.x;
+				normals[ i3 + 4 ] = nb.y;
+				normals[ i3 + 5 ] = nb.z;
 
-	this.faces = [];
+				normals[ i3 + 6 ] = nc.x;
+				normals[ i3 + 7 ] = nc.y;
+				normals[ i3 + 8 ] = nc.z;
 
-	this.faceVertexUvs = [[]];
+			} else {
 
-	this.morphTargets = [];
-	this.morphColors = [];
-	this.morphNormals = [];
+				var n = face.normal;
 
-	this.skinWeights = [];
-	this.skinIndices = [];
+				normals[ i3     ] = n.x;
+				normals[ i3 + 1 ] = n.y;
+				normals[ i3 + 2 ] = n.z;
 
-	this.lineDistances = [];
+				normals[ i3 + 3 ] = n.x;
+				normals[ i3 + 4 ] = n.y;
+				normals[ i3 + 5 ] = n.z;
 
-	this.boundingBox = null;
-	this.boundingSphere = null;
+				normals[ i3 + 6 ] = n.x;
+				normals[ i3 + 7 ] = n.y;
+				normals[ i3 + 8 ] = n.z;
 
-	this.hasTangents = false;
+			}
 
-	this.dynamic = true; // the intermediate typed arrays will be deleted when set to false
+			if ( vertexColors === THREE.FaceColors ) {
 
-	// update flags
+				var fc = face.color;
 
-	this.verticesNeedUpdate = false;
-	this.elementsNeedUpdate = false;
-	this.uvsNeedUpdate = false;
-	this.normalsNeedUpdate = false;
-	this.tangentsNeedUpdate = false;
-	this.colorsNeedUpdate = false;
-	this.lineDistancesNeedUpdate = false;
+				colors[ i3     ] = fc.r;
+				colors[ i3 + 1 ] = fc.g;
+				colors[ i3 + 2 ] = fc.b;
 
-	this.buffersNeedUpdate = false;
+				colors[ i3 + 3 ] = fc.r;
+				colors[ i3 + 4 ] = fc.g;
+				colors[ i3 + 5 ] = fc.b;
 
-};
+				colors[ i3 + 6 ] = fc.r;
+				colors[ i3 + 7 ] = fc.g;
+				colors[ i3 + 8 ] = fc.b;
 
-THREE.Geometry.prototype = {
+			} else if ( vertexColors === THREE.VertexColors ) {
 
-	constructor: THREE.Geometry,
+				var vca = face.vertexColors[ 0 ];
+				var vcb = face.vertexColors[ 1 ];
+				var vcc = face.vertexColors[ 2 ];
 
-	applyMatrix: function ( matrix ) {
+				colors[ i3     ] = vca.r;
+				colors[ i3 + 1 ] = vca.g;
+				colors[ i3 + 2 ] = vca.b;
 
-		var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+				colors[ i3 + 3 ] = vcb.r;
+				colors[ i3 + 4 ] = vcb.g;
+				colors[ i3 + 5 ] = vcb.b;
 
-		for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
+				colors[ i3 + 6 ] = vcc.r;
+				colors[ i3 + 7 ] = vcc.g;
+				colors[ i3 + 8 ] = vcc.b;
 
-			var vertex = this.vertices[ i ];
-			vertex.applyMatrix4( matrix );
+			}
 
-		}
+			if ( hasFaceVertexUv === true ) {
 
-		for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
+				var uva = faceVertexUvs[ 0 ][ i ][ 0 ];
+				var uvb = faceVertexUvs[ 0 ][ i ][ 1 ];
+				var uvc = faceVertexUvs[ 0 ][ i ][ 2 ];
 
-			var face = this.faces[ i ];
-			face.normal.applyMatrix3( normalMatrix ).normalize();
+				uvs[ i2     ] = uva.x;
+				uvs[ i2 + 1 ] = uva.y;
 
-			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+				uvs[ i2 + 2 ] = uvb.x;
+				uvs[ i2 + 3 ] = uvb.y;
 
-				face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
+				uvs[ i2 + 4 ] = uvc.x;
+				uvs[ i2 + 5 ] = uvc.y;
 
 			}
 
-			face.centroid.applyMatrix4( matrix );
-
 		}
 
-		if ( this.boundingBox instanceof THREE.Box3 ) {
+		this.computeBoundingSphere()
 
-			this.computeBoundingBox();
+		return this;
 
-		}
+	},
 
-		if ( this.boundingSphere instanceof THREE.Sphere ) {
+	computeBoundingBox: function () {
 
-			this.computeBoundingSphere();
+		var vector = new THREE.Vector3();
 
-		}
+		return function () {
 
-	},
+			if ( this.boundingBox === null ) {
 
-	computeCentroids: function () {
+				this.boundingBox = new THREE.Box3();
 
-		var f, fl, face;
+			}
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			var positions = this.attributes.position.array;
 
-			face = this.faces[ f ];
-			face.centroid.set( 0, 0, 0 );
+			if ( positions ) {
 
-			face.centroid.add( this.vertices[ face.a ] );
-			face.centroid.add( this.vertices[ face.b ] );
-			face.centroid.add( this.vertices[ face.c ] );
-			face.centroid.divideScalar( 3 );
+				var bb = this.boundingBox;
+				bb.makeEmpty();
 
-		}
+				for ( var i = 0, il = positions.length; i < il; i += 3 ) {
 
-	},
+					vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+					bb.expandByPoint( vector );
 
-	computeFaceNormals: function () {
+				}
 
-		var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+			}
 
-		for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			if ( positions === undefined || positions.length === 0 ) {
 
-			var face = this.faces[ f ];
+				this.boundingBox.min.set( 0, 0, 0 );
+				this.boundingBox.max.set( 0, 0, 0 );
 
-			var vA = this.vertices[ face.a ];
-			var vB = this.vertices[ face.b ];
-			var vC = this.vertices[ face.c ];
+			}
 
-			cb.subVectors( vC, vB );
-			ab.subVectors( vA, vB );
-			cb.cross( ab );
+			if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
 
-			cb.normalize();
+				THREE.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' );
 
-			face.normal.copy( cb );
+			}
 
 		}
 
-	},
-
-	computeVertexNormals: function ( areaWeighted ) {
-
-		var v, vl, f, fl, face, vertices;
+	}(),
 
-		// create internal buffers for reuse when calling this method repeatedly
-		// (otherwise memory allocation / deallocation every frame is big resource hog)
+	computeBoundingSphere: function () {
 
-		if ( this.__tmpVertices === undefined ) {
+		var box = new THREE.Box3();
+		var vector = new THREE.Vector3();
 
-			this.__tmpVertices = new Array( this.vertices.length );
-			vertices = this.__tmpVertices;
+		return function () {
 
-			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+			if ( this.boundingSphere === null ) {
 
-				vertices[ v ] = new THREE.Vector3();
+				this.boundingSphere = new THREE.Sphere();
 
 			}
 
-			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			var positions = this.attributes.position.array;
 
-				face = this.faces[ f ];
-				face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+			if ( positions ) {
 
-			}
+				box.makeEmpty();
 
-		} else {
+				var center = this.boundingSphere.center;
 
-			vertices = this.__tmpVertices;
+				for ( var i = 0, il = positions.length; i < il; i += 3 ) {
 
-			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+					vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+					box.expandByPoint( vector );
 
-				vertices[ v ].set( 0, 0, 0 );
+				}
 
-			}
+				box.center( center );
 
-		}
+				// hoping to find a boundingSphere with a radius smaller than the
+				// boundingSphere of the boundingBox:  sqrt(3) smaller in the best case
 
-		if ( areaWeighted ) {
+				var maxRadiusSq = 0;
 
-			// vertex normals weighted by triangle areas
-			// http://www.iquilezles.org/www/articles/normals/normals.htm
+				for ( var i = 0, il = positions.length; i < il; i += 3 ) {
 
-			var vA, vB, vC, vD;
-			var cb = new THREE.Vector3(), ab = new THREE.Vector3(),
-				db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3();
+					vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
 
-			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+				}
 
-				face = this.faces[ f ];
+				this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
 
-				vA = this.vertices[ face.a ];
-				vB = this.vertices[ face.b ];
-				vC = this.vertices[ face.c ];
+				if ( isNaN( this.boundingSphere.radius ) ) {
 
-				cb.subVectors( vC, vB );
-				ab.subVectors( vA, vB );
-				cb.cross( ab );
+					THREE.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' );
 
-				vertices[ face.a ].add( cb );
-				vertices[ face.b ].add( cb );
-				vertices[ face.c ].add( cb );
+				}
 
 			}
 
-		} else {
+		}
 
-			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+	}(),
 
-				face = this.faces[ f ];
+	computeFaceNormals: function () {
 
-				vertices[ face.a ].add( face.normal );
-				vertices[ face.b ].add( face.normal );
-				vertices[ face.c ].add( face.normal );
+		// backwards compatibility
 
-			}
+	},
 
-		}
+	computeVertexNormals: function () {
 
-		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+		var attributes = this.attributes;
 
-			vertices[ v ].normalize();
+		if ( attributes.position ) {
 
-		}
+			var positions = attributes.position.array;
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			if ( attributes.normal === undefined ) {
 
-			face = this.faces[ f ];
+				this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) );
 
-			face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
-			face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
-			face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
+			} else {
 
-		}
+				// reset existing normals to zero
 
-	},
+				var normals = attributes.normal.array;
 
-	computeMorphNormals: function () {
+				for ( var i = 0, il = normals.length; i < il; i ++ ) {
 
-		var i, il, f, fl, face;
+					normals[ i ] = 0;
 
-		// save original normals
-		// - create temp variables on first access
-		//   otherwise just copy (for faster repeated calls)
+				}
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			}
 
-			face = this.faces[ f ];
+			var normals = attributes.normal.array;
 
-			if ( ! face.__originalFaceNormal ) {
+			var vA, vB, vC,
 
-				face.__originalFaceNormal = face.normal.clone();
+			pA = new THREE.Vector3(),
+			pB = new THREE.Vector3(),
+			pC = new THREE.Vector3(),
 
-			} else {
+			cb = new THREE.Vector3(),
+			ab = new THREE.Vector3();
 
-				face.__originalFaceNormal.copy( face.normal );
+			// indexed elements
 
-			}
+			if ( attributes.index ) {
 
-			if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
+				var indices = attributes.index.array;
 
-			for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
+				var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] );
 
-				if ( ! face.__originalVertexNormals[ i ] ) {
+				for ( var j = 0, jl = offsets.length; j < jl; ++ j ) {
 
-					face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
+					var start = offsets[ j ].start;
+					var count = offsets[ j ].count;
+					var index = offsets[ j ].index;
 
-				} else {
+					for ( var i = start, il = start + count; i < il; i += 3 ) {
 
-					face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
+						vA = ( index + indices[ i     ] ) * 3;
+						vB = ( index + indices[ i + 1 ] ) * 3;
+						vC = ( index + indices[ i + 2 ] ) * 3;
 
-				}
+						pA.fromArray( positions, vA );
+						pB.fromArray( positions, vB );
+						pC.fromArray( positions, vC );
 
-			}
+						cb.subVectors( pC, pB );
+						ab.subVectors( pA, pB );
+						cb.cross( ab );
 
-		}
+						normals[ vA     ] += cb.x;
+						normals[ vA + 1 ] += cb.y;
+						normals[ vA + 2 ] += cb.z;
 
-		// use temp geometry to compute face and vertex normals for each morph
+						normals[ vB     ] += cb.x;
+						normals[ vB + 1 ] += cb.y;
+						normals[ vB + 2 ] += cb.z;
 
-		var tmpGeo = new THREE.Geometry();
-		tmpGeo.faces = this.faces;
+						normals[ vC     ] += cb.x;
+						normals[ vC + 1 ] += cb.y;
+						normals[ vC + 2 ] += cb.z;
 
-		for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
+					}
 
-			// create on first access
+				}
 
-			if ( ! this.morphNormals[ i ] ) {
+			} else {
 
-				this.morphNormals[ i ] = {};
-				this.morphNormals[ i ].faceNormals = [];
-				this.morphNormals[ i ].vertexNormals = [];
+				// non-indexed elements (unconnected triangle soup)
 
-				var dstNormalsFace = this.morphNormals[ i ].faceNormals;
-				var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
+				for ( var i = 0, il = positions.length; i < il; i += 9 ) {
 
-				var faceNormal, vertexNormals;
+					pA.fromArray( positions, i );
+					pB.fromArray( positions, i + 3 );
+					pC.fromArray( positions, i + 6 );
 
-				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+					cb.subVectors( pC, pB );
+					ab.subVectors( pA, pB );
+					cb.cross( ab );
 
-					face = this.faces[ f ];
+					normals[ i     ] = cb.x;
+					normals[ i + 1 ] = cb.y;
+					normals[ i + 2 ] = cb.z;
 
-					faceNormal = new THREE.Vector3();
-					vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
+					normals[ i + 3 ] = cb.x;
+					normals[ i + 4 ] = cb.y;
+					normals[ i + 5 ] = cb.z;
 
-					dstNormalsFace.push( faceNormal );
-					dstNormalsVertex.push( vertexNormals );
+					normals[ i + 6 ] = cb.x;
+					normals[ i + 7 ] = cb.y;
+					normals[ i + 8 ] = cb.z;
 
 				}
 
 			}
 
-			var morphNormals = this.morphNormals[ i ];
+			this.normalizeNormals();
 
-			// set vertices to morph target
+			attributes.normal.needsUpdate = true;
 
-			tmpGeo.vertices = this.morphTargets[ i ].vertices;
+		}
 
-			// compute morph normals
+	},
 
-			tmpGeo.computeFaceNormals();
-			tmpGeo.computeVertexNormals();
+	computeTangents: function () {
 
-			// store morph normals
+		// based on http://www.terathon.com/code/tangent.html
+		// (per vertex tangents)
 
-			var faceNormal, vertexNormals;
+		if ( this.attributes.index === undefined ||
+			 this.attributes.position === undefined ||
+			 this.attributes.normal === undefined ||
+			 this.attributes.uv === undefined ) {
 
-			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+			THREE.warn( 'THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' );
+			return;
 
-				face = this.faces[ f ];
+		}
 
-				faceNormal = morphNormals.faceNormals[ f ];
-				vertexNormals = morphNormals.vertexNormals[ f ];
+		var indices = this.attributes.index.array;
+		var positions = this.attributes.position.array;
+		var normals = this.attributes.normal.array;
+		var uvs = this.attributes.uv.array;
 
-				faceNormal.copy( face.normal );
+		var nVertices = positions.length / 3;
 
-				vertexNormals.a.copy( face.vertexNormals[ 0 ] );
-				vertexNormals.b.copy( face.vertexNormals[ 1 ] );
-				vertexNormals.c.copy( face.vertexNormals[ 2 ] );
+		if ( this.attributes.tangent === undefined ) {
 
-			}
+			this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) );
 
 		}
 
-		// restore original normals
+		var tangents = this.attributes.tangent.array;
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+		var tan1 = [], tan2 = [];
 
-			face = this.faces[ f ];
+		for ( var k = 0; k < nVertices; k ++ ) {
 
-			face.normal = face.__originalFaceNormal;
-			face.vertexNormals = face.__originalVertexNormals;
+			tan1[ k ] = new THREE.Vector3();
+			tan2[ k ] = new THREE.Vector3();
 
 		}
 
-	},
-
-	computeTangents: function () {
+		var vA = new THREE.Vector3(),
+			vB = new THREE.Vector3(),
+			vC = new THREE.Vector3(),
 
-		// based on http://www.terathon.com/code/tangent.html
-		// tangents go to vertices
+			uvA = new THREE.Vector2(),
+			uvB = new THREE.Vector2(),
+			uvC = new THREE.Vector2(),
 
-		var f, fl, v, vl, i, il, vertexIndex,
-			face, uv, vA, vB, vC, uvA, uvB, uvC,
 			x1, x2, y1, y2, z1, z2,
-			s1, s2, t1, t2, r, t, test,
-			tan1 = [], tan2 = [],
-			sdir = new THREE.Vector3(), tdir = new THREE.Vector3(),
-			tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(),
-			n = new THREE.Vector3(), w;
-
-		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
-
-			tan1[ v ] = new THREE.Vector3();
-			tan2[ v ] = new THREE.Vector3();
+			s1, s2, t1, t2, r;
 
-		}
+		var sdir = new THREE.Vector3(), tdir = new THREE.Vector3();
 
-		function handleTriangle( context, a, b, c, ua, ub, uc ) {
+		function handleTriangle( a, b, c ) {
 
-			vA = context.vertices[ a ];
-			vB = context.vertices[ b ];
-			vC = context.vertices[ c ];
+			vA.fromArray( positions, a * 3 );
+			vB.fromArray( positions, b * 3 );
+			vC.fromArray( positions, c * 3 );
 
-			uvA = uv[ ua ];
-			uvB = uv[ ub ];
-			uvC = uv[ uc ];
+			uvA.fromArray( uvs, a * 2 );
+			uvB.fromArray( uvs, b * 2 );
+			uvC.fromArray( uvs, c * 2 );
 
 			x1 = vB.x - vA.x;
 			x2 = vC.x - vA.x;
+
 			y1 = vB.y - vA.y;
 			y2 = vC.y - vA.y;
+
 			z1 = vB.z - vA.z;
 			z2 = vC.z - vA.z;
 
 			s1 = uvB.x - uvA.x;
 			s2 = uvC.x - uvA.x;
+
 			t1 = uvB.y - uvA.y;
 			t2 = uvC.y - uvA.y;
 
 			r = 1.0 / ( s1 * t2 - s2 * t1 );
-			sdir.set( ( t2 * x1 - t1 * x2 ) * r,
-					  ( t2 * y1 - t1 * y2 ) * r,
-					  ( t2 * z1 - t1 * z2 ) * r );
-			tdir.set( ( s1 * x2 - s2 * x1 ) * r,
-					  ( s1 * y2 - s2 * y1 ) * r,
-					  ( s1 * z2 - s2 * z1 ) * r );
+
+			sdir.set(
+				( t2 * x1 - t1 * x2 ) * r,
+				( t2 * y1 - t1 * y2 ) * r,
+				( t2 * z1 - t1 * z2 ) * r
+			);
+
+			tdir.set(
+				( s1 * x2 - s2 * x1 ) * r,
+				( s1 * y2 - s2 * y1 ) * r,
+				( s1 * z2 - s2 * z1 ) * r
+			);
 
 			tan1[ a ].add( sdir );
 			tan1[ b ].add( sdir );
@@ -8810,218 +9086,369 @@ THREE.Geometry.prototype = {
 
 		}
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+		var i, il;
+		var j, jl;
+		var iA, iB, iC;
 
-			face = this.faces[ f ];
-			uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents
+		if ( this.drawcalls.length === 0 ) {
 
-			handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
+			this.addDrawCall( 0, indices.length, 0 );
 
 		}
 
-		var faceIndex = [ 'a', 'b', 'c', 'd' ];
+		var drawcalls = this.drawcalls;
 
-		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+		for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) {
 
-			face = this.faces[ f ];
+			var start = drawcalls[ j ].start;
+			var count = drawcalls[ j ].count;
+			var index = drawcalls[ j ].index;
 
-			for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i++ ) {
+			for ( i = start, il = start + count; i < il; i += 3 ) {
 
-				n.copy( face.vertexNormals[ i ] );
+				iA = index + indices[ i ];
+				iB = index + indices[ i + 1 ];
+				iC = index + indices[ i + 2 ];
 
-				vertexIndex = face[ faceIndex[ i ] ];
+				handleTriangle( iA, iB, iC );
 
-				t = tan1[ vertexIndex ];
+			}
 
-				// Gram-Schmidt orthogonalize
+		}
 
-				tmp.copy( t );
-				tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
+		var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
+		var n = new THREE.Vector3(), n2 = new THREE.Vector3();
+		var w, t, test;
 
-				// Calculate handedness
+		function handleVertex( v ) {
 
-				tmp2.crossVectors( face.vertexNormals[ i ], t );
-				test = tmp2.dot( tan2[ vertexIndex ] );
-				w = (test < 0.0) ? -1.0 : 1.0;
+			n.fromArray( normals, v * 3 );
+			n2.copy( n );
 
-				face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w );
+			t = tan1[ v ];
 
-			}
+			// Gram-Schmidt orthogonalize
 
-		}
+			tmp.copy( t );
+			tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
 
-		this.hasTangents = true;
+			// Calculate handedness
 
-	},
+			tmp2.crossVectors( n2, t );
+			test = tmp2.dot( tan2[ v ] );
+			w = ( test < 0.0 ) ? - 1.0 : 1.0;
 
-	computeLineDistances: function ( ) {
+			tangents[ v * 4     ] = tmp.x;
+			tangents[ v * 4 + 1 ] = tmp.y;
+			tangents[ v * 4 + 2 ] = tmp.z;
+			tangents[ v * 4 + 3 ] = w;
 
-		var d = 0;
-		var vertices = this.vertices;
+		}
 
-		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+		for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) {
 
-			if ( i > 0 ) {
+			var start = drawcalls[ j ].start;
+			var count = drawcalls[ j ].count;
+			var index = drawcalls[ j ].index;
 
-				d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
+			for ( i = start, il = start + count; i < il; i += 3 ) {
 
-			}
+				iA = index + indices[ i ];
+				iB = index + indices[ i + 1 ];
+				iC = index + indices[ i + 2 ];
 
-			this.lineDistances[ i ] = d;
+				handleVertex( iA );
+				handleVertex( iB );
+				handleVertex( iC );
+
+			}
 
 		}
 
 	},
 
-	computeBoundingBox: function () {
+	/*
+	Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices.
+	This method will effectively rewrite the index buffer and remap all attributes to match the new indices.
+	WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets.
+	size - Defaults to 65535, but allows for larger or smaller chunks.
+	*/
+	computeOffsets: function ( size ) {
 
-		if ( this.boundingBox === null ) {
+		if ( size === undefined ) size = 65535; // WebGL limits type of index buffer values to 16-bit.
 
-			this.boundingBox = new THREE.Box3();
+		var indices = this.attributes.index.array;
+		var vertices = this.attributes.position.array;
 
-		}
+		var facesCount = ( indices.length / 3 );
 
-		this.boundingBox.setFromPoints( this.vertices );
+		/*
+		console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length);
+		console.log("Faces to process: "+(indices.length/3));
+		console.log("Reordering "+verticesCount+" vertices.");
+		*/
 
-	},
+		var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers
+		var indexPtr = 0;
+		var vertexPtr = 0;
 
-	computeBoundingSphere: function () {
+		var offsets = [ { start:0, count:0, index:0 } ];
+		var offset = offsets[ 0 ];
 
-		if ( this.boundingSphere === null ) {
+		var duplicatedVertices = 0;
+		var newVerticeMaps = 0;
+		var faceVertices = new Int32Array( 6 );
+		var vertexMap = new Int32Array( vertices.length );
+		var revVertexMap = new Int32Array( vertices.length );
+		for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; }
 
-			this.boundingSphere = new THREE.Sphere();
+		/*
+			Traverse every face and reorder vertices in the proper offsets of 65k.
+			We can have more than 65k entries in the index buffer per offset, but only reference 65k values.
+		*/
+		for ( var findex = 0; findex < facesCount; findex ++ ) {
+			newVerticeMaps = 0;
+
+			for ( var vo = 0; vo < 3; vo ++ ) {
+				var vid = indices[ findex * 3 + vo ];
+				if ( vertexMap[ vid ] == - 1 ) {
+					//Unmapped vertice
+					faceVertices[ vo * 2 ] = vid;
+					faceVertices[ vo * 2 + 1 ] = - 1;
+					newVerticeMaps ++;
+				} else if ( vertexMap[ vid ] < offset.index ) {
+					//Reused vertices from previous block (duplicate)
+					faceVertices[ vo * 2 ] = vid;
+					faceVertices[ vo * 2 + 1 ] = - 1;
+					duplicatedVertices ++;
+				} else {
+					//Reused vertice in the current block
+					faceVertices[ vo * 2 ] = vid;
+					faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ];
+				}
+			}
 
-		}
+			var faceMax = vertexPtr + newVerticeMaps;
+			if ( faceMax > ( offset.index + size ) ) {
+				var new_offset = { start:indexPtr, count:0, index:vertexPtr };
+				offsets.push( new_offset );
+				offset = new_offset;
 
-		this.boundingSphere.setFromPoints( this.vertices );
+				//Re-evaluate reused vertices in light of new offset.
+				for ( var v = 0; v < 6; v += 2 ) {
+					var new_vid = faceVertices[ v + 1 ];
+					if ( new_vid > - 1 && new_vid < offset.index )
+						faceVertices[ v + 1 ] = - 1;
+				}
+			}
 
-	},
+			//Reindex the face.
+			for ( var v = 0; v < 6; v += 2 ) {
+				var vid = faceVertices[ v ];
+				var new_vid = faceVertices[ v + 1 ];
 
-	/*
-	 * Checks for duplicate vertices with hashmap.
-	 * Duplicated vertices are removed
-	 * and faces' vertices are updated.
-	 */
+				if ( new_vid === - 1 )
+					new_vid = vertexPtr ++;
 
-	mergeVertices: function () {
+				vertexMap[ vid ] = new_vid;
+				revVertexMap[ new_vid ] = vid;
+				sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit
+				offset.count ++;
+			}
+		}
 
-		var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique)
-		var unique = [], changes = [];
+		/* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */
+		this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr );
+		this.offsets = offsets; // TODO: Deprecate
+		this.drawcalls = offsets;
 
-		var v, key;
-		var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001
-		var precision = Math.pow( 10, precisionPoints );
-		var i,il, face;
-		var indices, k, j, jl, u;
+		/*
+		var orderTime = Date.now();
+		console.log("Reorder time: "+(orderTime-s)+"ms");
+		console.log("Duplicated "+duplicatedVertices+" vertices.");
+		console.log("Compute Buffers time: "+(Date.now()-s)+"ms");
+		console.log("Draw offsets: "+offsets.length);
+		*/
 
-		// reset cache of vertices as it now will be changing.
-		this.__tmpVertices = undefined;
+		return offsets;
 
-		for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
+	},
 
-			v = this.vertices[ i ];
-			key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
+	merge: function ( geometry, offset ) {
 
-			if ( verticesMap[ key ] === undefined ) {
+		if ( geometry instanceof THREE.BufferGeometry === false ) {
 
-				verticesMap[ key ] = i;
-				unique.push( this.vertices[ i ] );
-				changes[ i ] = unique.length - 1;
+			THREE.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
+			return;
 
-			} else {
+		}
 
-				//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
-				changes[ i ] = changes[ verticesMap[ key ] ];
+		if ( offset === undefined ) offset = 0;
 
-			}
+		var attributes = this.attributes;
 
-		};
+		for ( var key in attributes ) {
 
+			if ( geometry.attributes[ key ] === undefined ) continue;
 
-		// if faces are completely degenerate after merging vertices, we
-		// have to remove them from the geometry.
-		var faceIndicesToRemove = [];
+			var attribute1 = attributes[ key ];
+			var attributeArray1 = attribute1.array;
 
-		for( i = 0, il = this.faces.length; i < il; i ++ ) {
+			var attribute2 = geometry.attributes[ key ];
+			var attributeArray2 = attribute2.array;
 
-			face = this.faces[ i ];
+			var attributeSize = attribute2.itemSize;
 
-			face.a = changes[ face.a ];
-			face.b = changes[ face.b ];
-			face.c = changes[ face.c ];
+			for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {
 
-			indices = [ face.a, face.b, face.c ];
+				attributeArray1[ j ] = attributeArray2[ i ];
 
-			var dupIndex = -1;
+			}
 
-			// if any duplicate vertices are found in a Face3
-			// we have to remove the face as nothing can be saved
-			for ( var n = 0; n < 3; n ++ ) {
-				if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) {
+		}
 
-					dupIndex = n;
-					faceIndicesToRemove.push( i );
-					break;
+		return this;
 
-				}
-			}
+	},
 
-		}
+	normalizeNormals: function () {
 
-		for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
-			var idx = faceIndicesToRemove[ i ];
+		var normals = this.attributes.normal.array;
 
-			this.faces.splice( idx, 1 );
+		var x, y, z, n;
 
-			for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
+		for ( var i = 0, il = normals.length; i < il; i += 3 ) {
 
-				this.faceVertexUvs[ j ].splice( idx, 1 );
+			x = normals[ i ];
+			y = normals[ i + 1 ];
+			z = normals[ i + 2 ];
 
-			}
+			n = 1.0 / Math.sqrt( x * x + y * y + z * z );
+
+			normals[ i     ] *= n;
+			normals[ i + 1 ] *= n;
+			normals[ i + 2 ] *= n;
 
 		}
 
-		// Use unique set of vertices
+	},
 
-		var diff = this.vertices.length - unique.length;
-		this.vertices = unique;
-		return diff;
+	/*
+		reoderBuffers:
+		Reorder attributes based on a new indexBuffer and indexMap.
+		indexBuffer - Uint16Array of the new ordered indices.
+		indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex.
+		vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack).
+	*/
+	reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) {
+
+		/* Create a copy of all attributes for reordering. */
+		var sortedAttributes = {};
+		for ( var attr in this.attributes ) {
+			if ( attr == 'index' )
+				continue;
+			var sourceArray = this.attributes[ attr ].array;
+			sortedAttributes[ attr ] = new sourceArray.constructor( this.attributes[ attr ].itemSize * vertexCount );
+		}
+
+		/* Move attribute positions based on the new index map */
+		for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) {
+			var vid = indexMap[ new_vid ];
+			for ( var attr in this.attributes ) {
+				if ( attr == 'index' )
+					continue;
+				var attrArray = this.attributes[ attr ].array;
+				var attrSize = this.attributes[ attr ].itemSize;
+				var sortedAttr = sortedAttributes[ attr ];
+				for ( var k = 0; k < attrSize; k ++ )
+					sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ];
+			}
+		}
 
+		/* Carry the new sorted buffers locally */
+		this.attributes[ 'index' ].array = indexBuffer;
+		for ( var attr in this.attributes ) {
+			if ( attr == 'index' )
+				continue;
+			this.attributes[ attr ].array = sortedAttributes[ attr ];
+			this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount;
+		}
 	},
 
-	clone: function () {
+	toJSON: function () {
 
-		var geometry = new THREE.Geometry();
+		var output = {
+			metadata: {
+				version: 4.0,
+				type: 'BufferGeometry',
+				generator: 'BufferGeometryExporter'
+			},
+			uuid: this.uuid,
+			type: this.type,
+			data: {
+				attributes: {}
+			}
+		};
 
-		var vertices = this.vertices;
+		var attributes = this.attributes;
+		var offsets = this.offsets;
+		var boundingSphere = this.boundingSphere;
 
-		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+		for ( var key in attributes ) {
 
-			geometry.vertices.push( vertices[ i ].clone() );
+			var attribute = attributes[ key ];
+
+			var array = Array.prototype.slice.call( attribute.array );
+
+			output.data.attributes[ key ] = {
+				itemSize: attribute.itemSize,
+				type: attribute.array.constructor.name,
+				array: array
+			}
 
 		}
 
-		var faces = this.faces;
+		if ( offsets.length > 0 ) {
 
-		for ( var i = 0, il = faces.length; i < il; i ++ ) {
+			output.data.offsets = JSON.parse( JSON.stringify( offsets ) );
 
-			geometry.faces.push( faces[ i ].clone() );
+		}
+
+		if ( boundingSphere !== null ) {
+
+			output.data.boundingSphere = {
+				center: boundingSphere.center.toArray(),
+				radius: boundingSphere.radius
+			}
 
 		}
 
-		var uvs = this.faceVertexUvs[ 0 ];
+		return output;
 
-		for ( var i = 0, il = uvs.length; i < il; i ++ ) {
+	},
 
-			var uv = uvs[ i ], uvCopy = [];
+	clone: function () {
 
-			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+		var geometry = new THREE.BufferGeometry();
 
-				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+		for ( var attr in this.attributes ) {
 
-			}
+			var sourceAttr = this.attributes[ attr ];
+			geometry.addAttribute( attr, sourceAttr.clone() );
 
-			geometry.faceVertexUvs[ 0 ].push( uvCopy );
+		}
+
+		for ( var i = 0, il = this.offsets.length; i < il; i ++ ) {
+
+			var offset = this.offsets[ i ];
+
+			geometry.offsets.push( {
+
+				start: offset.start,
+				index: offset.index,
+				count: offset.count
+
+			} );
 
 		}
 
@@ -9037,10467 +9464,8223 @@ THREE.Geometry.prototype = {
 
 };
 
-THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );
+THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype );
 
-THREE.GeometryIdCount = 0;
+// File:src/core/Geometry.js
 
 /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author kile / http://kile.stravaganza.org/
  * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * @author bhouston / http://exocortex.com
  */
 
-THREE.BufferGeometry = function () {
+THREE.Geometry = function () {
+
+	Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
 
-	this.id = THREE.GeometryIdCount ++;
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = '';
+	this.type = 'Geometry';
 
-	// attributes
-
-	this.attributes = {};
+	this.vertices = [];
+	this.colors = [];  // one-to-one vertex colors, used in Points and Line
 
-	// attributes typed arrays are kept only if dynamic flag is set
+	this.faces = [];
 
-	this.dynamic = true;
+	this.faceVertexUvs = [ [] ];
 
-	// offsets for chunks when using indexed elements
+	this.morphTargets = [];
+	this.morphColors = [];
+	this.morphNormals = [];
 
-	this.offsets = [];
+	this.skinWeights = [];
+	this.skinIndices = [];
 
-	// boundings
+	this.lineDistances = [];
 
 	this.boundingBox = null;
 	this.boundingSphere = null;
 
 	this.hasTangents = false;
 
-	// for compatibility
-
-	this.morphTargets = [];
-
-};
-
-THREE.BufferGeometry.prototype = {
+	this.dynamic = true; // the intermediate typed arrays will be deleted when set to false
 
-	constructor: THREE.BufferGeometry,
+	// update flags
 
-	addAttribute: function( name, type, numItems, itemSize ) {
+	this.verticesNeedUpdate = false;
+	this.elementsNeedUpdate = false;
+	this.uvsNeedUpdate = false;
+	this.normalsNeedUpdate = false;
+	this.tangentsNeedUpdate = false;
+	this.colorsNeedUpdate = false;
+	this.lineDistancesNeedUpdate = false;
 
-		this.attributes[ name ] = {
+	this.groupsNeedUpdate = false;
 
-			itemSize: itemSize,
-			array: new type( numItems * itemSize )
+};
 
-		};
+THREE.Geometry.prototype = {
 
-	},
+	constructor: THREE.Geometry,
 
 	applyMatrix: function ( matrix ) {
 
-		var positionArray;
-		var normalArray;
-
-		if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array;
-		if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array;
+		var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
 
-		if ( positionArray !== undefined ) {
+		for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
 
-			matrix.multiplyVector3Array( positionArray );
-			this.verticesNeedUpdate = true;
+			var vertex = this.vertices[ i ];
+			vertex.applyMatrix4( matrix );
 
 		}
 
-		if ( normalArray !== undefined ) {
+		for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
 
-			var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+			var face = this.faces[ i ];
+			face.normal.applyMatrix3( normalMatrix ).normalize();
 
-			normalMatrix.multiplyVector3Array( normalArray );
+			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
 
-			this.normalizeNormals();
+				face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
 
-			this.normalsNeedUpdate = true;
+			}
 
 		}
 
-	},
-
-	computeBoundingBox: function () {
-
-		if ( this.boundingBox === null ) {
+		if ( this.boundingBox !== null ) {
 
-			this.boundingBox = new THREE.Box3();
+			this.computeBoundingBox();
 
 		}
 
-		var positions = this.attributes[ "position" ].array;
-
-		if ( positions ) {
+		if ( this.boundingSphere !== null ) {
 
-			var bb = this.boundingBox;
-			var x, y, z;
-
-			if( positions.length >= 3 ) {
-				bb.min.x = bb.max.x = positions[ 0 ];
-				bb.min.y = bb.max.y = positions[ 1 ];
-				bb.min.z = bb.max.z = positions[ 2 ];
-			}
+			this.computeBoundingSphere();
 
-			for ( var i = 3, il = positions.length; i < il; i += 3 ) {
+		}
 
-				x = positions[ i ];
-				y = positions[ i + 1 ];
-				z = positions[ i + 2 ];
+		this.verticesNeedUpdate = true;
+		this.normalsNeedUpdate = true;
 
-				// bounding box
+	},
 
-				if ( x < bb.min.x ) {
+	fromBufferGeometry: function ( geometry ) {
 
-					bb.min.x = x;
+		var scope = this;
 
-				} else if ( x > bb.max.x ) {
+		var attributes = geometry.attributes;
 
-					bb.max.x = x;
+		var vertices = attributes.position.array;
+		var indices = attributes.index !== undefined ? attributes.index.array : undefined;
+		var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
+		var colors = attributes.color !== undefined ? attributes.color.array : undefined;
+		var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
 
-				}
+		var tempNormals = [];
+		var tempUVs = [];
 
-				if ( y < bb.min.y ) {
+		for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) {
 
-					bb.min.y = y;
+			scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
 
-				} else if ( y > bb.max.y ) {
+			if ( normals !== undefined ) {
 
-					bb.max.y = y;
+				tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );
 
-				}
+			}
 
-				if ( z < bb.min.z ) {
+			if ( colors !== undefined ) {
 
-					bb.min.z = z;
+				scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
 
-				} else if ( z > bb.max.z ) {
+			}
 
-					bb.max.z = z;
+			if ( uvs !== undefined ) {
 
-				}
+				tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) );
 
 			}
 
 		}
 
-		if ( positions === undefined || positions.length === 0 ) {
+		var addFace = function ( a, b, c ) {
 
-			this.boundingBox.min.set( 0, 0, 0 );
-			this.boundingBox.max.set( 0, 0, 0 );
+			var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];
+			var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];
 
-		}
+			scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) );
 
-	},
+			if ( uvs !== undefined ) {
 
-	computeBoundingSphere: function () {
+				scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );
 
-		var box = new THREE.Box3();
-		var vector = new THREE.Vector3();
+			}
 
-		return function () {
+		};
 
-			if ( this.boundingSphere === null ) {
+		if ( indices !== undefined ) {
 
-				this.boundingSphere = new THREE.Sphere();
+			var drawcalls = geometry.drawcalls;
 
-			}
+			if ( drawcalls.length > 0 ) {
 
-			var positions = this.attributes[ "position" ].array;
+				for ( var i = 0; i < drawcalls.length; i ++ ) {
 
-			if ( positions ) {
+					var drawcall = drawcalls[ i ];
 
-				var center = this.boundingSphere.center;
+					var start = drawcall.start;
+					var count = drawcall.count;
+					var index = drawcall.index;
 
-				for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+					for ( var j = start, jl = start + count; j < jl; j += 3 ) {
 
-					vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
-					box.addPoint( vector );
+						addFace( index + indices[ j ], index + indices[ j + 1 ], index + indices[ j + 2 ] );
 
-				}
+					}
 
-				box.center( center );
+				}
 
-				var maxRadiusSq = 0;
+			} else {
 
-				for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+				for ( var i = 0; i < indices.length; i += 3 ) {
 
-					vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
-					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
+					addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
 
 				}
 
-				this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
-
 			}
 
-		}
+		} else {
 
-	}(),
+			for ( var i = 0; i < vertices.length / 3; i += 3 ) {
 
-	computeVertexNormals: function () {
+				addFace( i, i + 1, i + 2 );
 
-		if ( this.attributes[ "position" ] ) {
+			}
 
-			var i, il;
-			var j, jl;
+		}
 
-			var nVertexElements = this.attributes[ "position" ].array.length;
+		this.computeFaceNormals();
 
-			if ( this.attributes[ "normal" ] === undefined ) {
+		if ( geometry.boundingBox !== null ) {
 
-				this.attributes[ "normal" ] = {
+			this.boundingBox = geometry.boundingBox.clone();
 
-					itemSize: 3,
-					array: new Float32Array( nVertexElements )
+		}
 
-				};
+		if ( geometry.boundingSphere !== null ) {
 
-			} else {
+			this.boundingSphere = geometry.boundingSphere.clone();
 
-				// reset existing normals to zero
+		}
 
-				for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
+		return this;
 
-					this.attributes[ "normal" ].array[ i ] = 0;
+	},
 
-				}
+	center: function () {
 
-			}
+		this.computeBoundingBox();
 
-			var positions = this.attributes[ "position" ].array;
-			var normals = this.attributes[ "normal" ].array;
+		var offset = this.boundingBox.center().negate();
 
-			var vA, vB, vC, x, y, z,
+		this.applyMatrix( new THREE.Matrix4().setPosition( offset ) );
 
-			pA = new THREE.Vector3(),
-			pB = new THREE.Vector3(),
-			pC = new THREE.Vector3(),
+		return offset;
 
-			cb = new THREE.Vector3(),
-			ab = new THREE.Vector3();
+	},
 
-			// indexed elements
+	computeFaceNormals: function () {
 
-			if ( this.attributes[ "index" ] ) {
+		var cb = new THREE.Vector3(), ab = new THREE.Vector3();
 
-				var indices = this.attributes[ "index" ].array;
+		for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-				var offsets = this.offsets;
+			var face = this.faces[ f ];
 
-				for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+			var vA = this.vertices[ face.a ];
+			var vB = this.vertices[ face.b ];
+			var vC = this.vertices[ face.c ];
 
-					var start = offsets[ j ].start;
-					var count = offsets[ j ].count;
-					var index = offsets[ j ].index;
+			cb.subVectors( vC, vB );
+			ab.subVectors( vA, vB );
+			cb.cross( ab );
 
-					for ( i = start, il = start + count; i < il; i += 3 ) {
+			cb.normalize();
 
-						vA = index + indices[ i ];
-						vB = index + indices[ i + 1 ];
-						vC = index + indices[ i + 2 ];
+			face.normal.copy( cb );
 
-						x = positions[ vA * 3 ];
-						y = positions[ vA * 3 + 1 ];
-						z = positions[ vA * 3 + 2 ];
-						pA.set( x, y, z );
+		}
 
-						x = positions[ vB * 3 ];
-						y = positions[ vB * 3 + 1 ];
-						z = positions[ vB * 3 + 2 ];
-						pB.set( x, y, z );
+	},
 
-						x = positions[ vC * 3 ];
-						y = positions[ vC * 3 + 1 ];
-						z = positions[ vC * 3 + 2 ];
-						pC.set( x, y, z );
+	computeVertexNormals: function ( areaWeighted ) {
 
-						cb.subVectors( pC, pB );
-						ab.subVectors( pA, pB );
-						cb.cross( ab );
+		var v, vl, f, fl, face, vertices;
 
-						normals[ vA * 3 ]     += cb.x;
-						normals[ vA * 3 + 1 ] += cb.y;
-						normals[ vA * 3 + 2 ] += cb.z;
+		vertices = new Array( this.vertices.length );
 
-						normals[ vB * 3 ]     += cb.x;
-						normals[ vB * 3 + 1 ] += cb.y;
-						normals[ vB * 3 + 2 ] += cb.z;
+		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
 
-						normals[ vC * 3 ]     += cb.x;
-						normals[ vC * 3 + 1 ] += cb.y;
-						normals[ vC * 3 + 2 ] += cb.z;
+			vertices[ v ] = new THREE.Vector3();
 
-					}
+		}
 
-				}
+		if ( areaWeighted ) {
 
-			// non-indexed elements (unconnected triangle soup)
+			// vertex normals weighted by triangle areas
+			// http://www.iquilezles.org/www/articles/normals/normals.htm
 
-			} else {
+			var vA, vB, vC;
+			var cb = new THREE.Vector3(), ab = new THREE.Vector3();
 
-				for ( i = 0, il = positions.length; i < il; i += 9 ) {
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-					x = positions[ i ];
-					y = positions[ i + 1 ];
-					z = positions[ i + 2 ];
-					pA.set( x, y, z );
+				face = this.faces[ f ];
 
-					x = positions[ i + 3 ];
-					y = positions[ i + 4 ];
-					z = positions[ i + 5 ];
-					pB.set( x, y, z );
+				vA = this.vertices[ face.a ];
+				vB = this.vertices[ face.b ];
+				vC = this.vertices[ face.c ];
 
-					x = positions[ i + 6 ];
-					y = positions[ i + 7 ];
-					z = positions[ i + 8 ];
-					pC.set( x, y, z );
+				cb.subVectors( vC, vB );
+				ab.subVectors( vA, vB );
+				cb.cross( ab );
 
-					cb.subVectors( pC, pB );
-					ab.subVectors( pA, pB );
-					cb.cross( ab );
-
-					normals[ i ] 	 = cb.x;
-					normals[ i + 1 ] = cb.y;
-					normals[ i + 2 ] = cb.z;
+				vertices[ face.a ].add( cb );
+				vertices[ face.b ].add( cb );
+				vertices[ face.c ].add( cb );
 
-					normals[ i + 3 ] = cb.x;
-					normals[ i + 4 ] = cb.y;
-					normals[ i + 5 ] = cb.z;
+			}
 
-					normals[ i + 6 ] = cb.x;
-					normals[ i + 7 ] = cb.y;
-					normals[ i + 8 ] = cb.z;
+		} else {
 
-				}
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			}
+				face = this.faces[ f ];
 
-			this.normalizeNormals();
+				vertices[ face.a ].add( face.normal );
+				vertices[ face.b ].add( face.normal );
+				vertices[ face.c ].add( face.normal );
 
-			this.normalsNeedUpdate = true;
+			}
 
 		}
 
-	},
-
-	normalizeNormals: function () {
-
-		var normals = this.attributes[ "normal" ].array;
+		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
 
-		var x, y, z, n;
+			vertices[ v ].normalize();
 
-		for ( var i = 0, il = normals.length; i < il; i += 3 ) {
+		}
 
-			x = normals[ i ];
-			y = normals[ i + 1 ];
-			z = normals[ i + 2 ];
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			n = 1.0 / Math.sqrt( x * x + y * y + z * z );
+			face = this.faces[ f ];
 
-			normals[ i ] 	 *= n;
-			normals[ i + 1 ] *= n;
-			normals[ i + 2 ] *= n;
+			face.vertexNormals[ 0 ] = vertices[ face.a ].clone();
+			face.vertexNormals[ 1 ] = vertices[ face.b ].clone();
+			face.vertexNormals[ 2 ] = vertices[ face.c ].clone();
 
 		}
 
 	},
 
-	computeTangents: function () {
-
-		// based on http://www.terathon.com/code/tangent.html
-		// (per vertex tangents)
-
-		if ( this.attributes[ "index" ] === undefined ||
-			 this.attributes[ "position" ] === undefined ||
-			 this.attributes[ "normal" ] === undefined ||
-			 this.attributes[ "uv" ] === undefined ) {
-
-			console.warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" );
-			return;
+	computeMorphNormals: function () {
 
-		}
+		var i, il, f, fl, face;
 
-		var indices = this.attributes[ "index" ].array;
-		var positions = this.attributes[ "position" ].array;
-		var normals = this.attributes[ "normal" ].array;
-		var uvs = this.attributes[ "uv" ].array;
+		// save original normals
+		// - create temp variables on first access
+		//   otherwise just copy (for faster repeated calls)
 
-		var nVertices = positions.length / 3;
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-		if ( this.attributes[ "tangent" ] === undefined ) {
+			face = this.faces[ f ];
 
-			var nTangentElements = 4 * nVertices;
+			if ( ! face.__originalFaceNormal ) {
 
-			this.attributes[ "tangent" ] = {
+				face.__originalFaceNormal = face.normal.clone();
 
-				itemSize: 4,
-				array: new Float32Array( nTangentElements )
+			} else {
 
-			};
+				face.__originalFaceNormal.copy( face.normal );
 
-		}
+			}
 
-		var tangents = this.attributes[ "tangent" ].array;
+			if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
 
-		var tan1 = [], tan2 = [];
+			for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
 
-		for ( var k = 0; k < nVertices; k ++ ) {
+				if ( ! face.__originalVertexNormals[ i ] ) {
 
-			tan1[ k ] = new THREE.Vector3();
-			tan2[ k ] = new THREE.Vector3();
+					face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
 
-		}
+				} else {
 
-		var xA, yA, zA,
-			xB, yB, zB,
-			xC, yC, zC,
+					face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
 
-			uA, vA,
-			uB, vB,
-			uC, vC,
+				}
 
-			x1, x2, y1, y2, z1, z2,
-			s1, s2, t1, t2, r;
+			}
 
-		var sdir = new THREE.Vector3(), tdir = new THREE.Vector3();
+		}
 
-		function handleTriangle( a, b, c ) {
+		// use temp geometry to compute face and vertex normals for each morph
 
-			xA = positions[ a * 3 ];
-			yA = positions[ a * 3 + 1 ];
-			zA = positions[ a * 3 + 2 ];
+		var tmpGeo = new THREE.Geometry();
+		tmpGeo.faces = this.faces;
 
-			xB = positions[ b * 3 ];
-			yB = positions[ b * 3 + 1 ];
-			zB = positions[ b * 3 + 2 ];
+		for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
 
-			xC = positions[ c * 3 ];
-			yC = positions[ c * 3 + 1 ];
-			zC = positions[ c * 3 + 2 ];
+			// create on first access
 
-			uA = uvs[ a * 2 ];
-			vA = uvs[ a * 2 + 1 ];
+			if ( ! this.morphNormals[ i ] ) {
 
-			uB = uvs[ b * 2 ];
-			vB = uvs[ b * 2 + 1 ];
+				this.morphNormals[ i ] = {};
+				this.morphNormals[ i ].faceNormals = [];
+				this.morphNormals[ i ].vertexNormals = [];
 
-			uC = uvs[ c * 2 ];
-			vC = uvs[ c * 2 + 1 ];
+				var dstNormalsFace = this.morphNormals[ i ].faceNormals;
+				var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
 
-			x1 = xB - xA;
-			x2 = xC - xA;
+				var faceNormal, vertexNormals;
 
-			y1 = yB - yA;
-			y2 = yC - yA;
+				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			z1 = zB - zA;
-			z2 = zC - zA;
+					faceNormal = new THREE.Vector3();
+					vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
 
-			s1 = uB - uA;
-			s2 = uC - uA;
+					dstNormalsFace.push( faceNormal );
+					dstNormalsVertex.push( vertexNormals );
 
-			t1 = vB - vA;
-			t2 = vC - vA;
+				}
 
-			r = 1.0 / ( s1 * t2 - s2 * t1 );
+			}
 
-			sdir.set(
-				( t2 * x1 - t1 * x2 ) * r,
-				( t2 * y1 - t1 * y2 ) * r,
-				( t2 * z1 - t1 * z2 ) * r
-			);
+			var morphNormals = this.morphNormals[ i ];
 
-			tdir.set(
-				( s1 * x2 - s2 * x1 ) * r,
-				( s1 * y2 - s2 * y1 ) * r,
-				( s1 * z2 - s2 * z1 ) * r
-			);
+			// set vertices to morph target
 
-			tan1[ a ].add( sdir );
-			tan1[ b ].add( sdir );
-			tan1[ c ].add( sdir );
+			tmpGeo.vertices = this.morphTargets[ i ].vertices;
 
-			tan2[ a ].add( tdir );
-			tan2[ b ].add( tdir );
-			tan2[ c ].add( tdir );
+			// compute morph normals
 
-		}
+			tmpGeo.computeFaceNormals();
+			tmpGeo.computeVertexNormals();
 
-		var i, il;
-		var j, jl;
-		var iA, iB, iC;
+			// store morph normals
 
-		var offsets = this.offsets;
+			var faceNormal, vertexNormals;
 
-		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			var start = offsets[ j ].start;
-			var count = offsets[ j ].count;
-			var index = offsets[ j ].index;
+				face = this.faces[ f ];
 
-			for ( i = start, il = start + count; i < il; i += 3 ) {
+				faceNormal = morphNormals.faceNormals[ f ];
+				vertexNormals = morphNormals.vertexNormals[ f ];
 
-				iA = index + indices[ i ];
-				iB = index + indices[ i + 1 ];
-				iC = index + indices[ i + 2 ];
+				faceNormal.copy( face.normal );
 
-				handleTriangle( iA, iB, iC );
+				vertexNormals.a.copy( face.vertexNormals[ 0 ] );
+				vertexNormals.b.copy( face.vertexNormals[ 1 ] );
+				vertexNormals.c.copy( face.vertexNormals[ 2 ] );
 
 			}
 
 		}
 
-		var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
-		var n = new THREE.Vector3(), n2 = new THREE.Vector3();
-		var w, t, test;
+		// restore original normals
 
-		function handleVertex( v ) {
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			n.x = normals[ v * 3 ];
-			n.y = normals[ v * 3 + 1 ];
-			n.z = normals[ v * 3 + 2 ];
+			face = this.faces[ f ];
 
-			n2.copy( n );
+			face.normal = face.__originalFaceNormal;
+			face.vertexNormals = face.__originalVertexNormals;
 
-			t = tan1[ v ];
+		}
 
-			// Gram-Schmidt orthogonalize
+	},
 
-			tmp.copy( t );
-			tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
+	computeTangents: function () {
 
-			// Calculate handedness
+		// based on http://www.terathon.com/code/tangent.html
+		// tangents go to vertices
 
-			tmp2.crossVectors( n2, t );
-			test = tmp2.dot( tan2[ v ] );
-			w = ( test < 0.0 ) ? -1.0 : 1.0;
+		var f, fl, v, vl, i, vertexIndex,
+			face, uv, vA, vB, vC, uvA, uvB, uvC,
+			x1, x2, y1, y2, z1, z2,
+			s1, s2, t1, t2, r, t, test,
+			tan1 = [], tan2 = [],
+			sdir = new THREE.Vector3(), tdir = new THREE.Vector3(),
+			tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(),
+			n = new THREE.Vector3(), w;
 
-			tangents[ v * 4 ]     = tmp.x;
-			tangents[ v * 4 + 1 ] = tmp.y;
-			tangents[ v * 4 + 2 ] = tmp.z;
-			tangents[ v * 4 + 3 ] = w;
+		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+			tan1[ v ] = new THREE.Vector3();
+			tan2[ v ] = new THREE.Vector3();
 
 		}
 
-		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+		function handleTriangle( context, a, b, c, ua, ub, uc ) {
 
-			var start = offsets[ j ].start;
-			var count = offsets[ j ].count;
-			var index = offsets[ j ].index;
+			vA = context.vertices[ a ];
+			vB = context.vertices[ b ];
+			vC = context.vertices[ c ];
 
-			for ( i = start, il = start + count; i < il; i += 3 ) {
+			uvA = uv[ ua ];
+			uvB = uv[ ub ];
+			uvC = uv[ uc ];
 
-				iA = index + indices[ i ];
-				iB = index + indices[ i + 1 ];
-				iC = index + indices[ i + 2 ];
+			x1 = vB.x - vA.x;
+			x2 = vC.x - vA.x;
+			y1 = vB.y - vA.y;
+			y2 = vC.y - vA.y;
+			z1 = vB.z - vA.z;
+			z2 = vC.z - vA.z;
 
-				handleVertex( iA );
-				handleVertex( iB );
-				handleVertex( iC );
+			s1 = uvB.x - uvA.x;
+			s2 = uvC.x - uvA.x;
+			t1 = uvB.y - uvA.y;
+			t2 = uvC.y - uvA.y;
 
-			}
+			r = 1.0 / ( s1 * t2 - s2 * t1 );
+			sdir.set( ( t2 * x1 - t1 * x2 ) * r,
+					  ( t2 * y1 - t1 * y2 ) * r,
+					  ( t2 * z1 - t1 * z2 ) * r );
+			tdir.set( ( s1 * x2 - s2 * x1 ) * r,
+					  ( s1 * y2 - s2 * y1 ) * r,
+					  ( s1 * z2 - s2 * z1 ) * r );
+
+			tan1[ a ].add( sdir );
+			tan1[ b ].add( sdir );
+			tan1[ c ].add( sdir );
+
+			tan2[ a ].add( tdir );
+			tan2[ b ].add( tdir );
+			tan2[ c ].add( tdir );
 
 		}
 
-		this.hasTangents = true;
-		this.tangentsNeedUpdate = true;
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-	},
+			face = this.faces[ f ];
+			uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents
 
-	clone: function () {
+			handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
 
-		var geometry = new THREE.BufferGeometry();
+		}
 
-		var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ];
+		var faceIndex = [ 'a', 'b', 'c', 'd' ];
 
-		for ( var attr in this.attributes ) {
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
-			var sourceAttr = this.attributes[ attr ];
-			var sourceArray = sourceAttr.array;
+			face = this.faces[ f ];
+
+			for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) {
 
-			var attribute = {
+				n.copy( face.vertexNormals[ i ] );
 
-				itemSize: sourceAttr.itemSize,
-				numItems: sourceAttr.numItems,
-				array: null
+				vertexIndex = face[ faceIndex[ i ] ];
 
-			};
+				t = tan1[ vertexIndex ];
 
-			for ( var i = 0, il = types.length; i < il; i ++ ) {
+				// Gram-Schmidt orthogonalize
 
-				var type = types[ i ];
+				tmp.copy( t );
+				tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
 
-				if ( sourceArray instanceof type ) {
+				// Calculate handedness
 
-					attribute.array = new type( sourceArray );
-					break;
+				tmp2.crossVectors( face.vertexNormals[ i ], t );
+				test = tmp2.dot( tan2[ vertexIndex ] );
+				w = ( test < 0.0 ) ? - 1.0 : 1.0;
 
-				}
+				face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w );
 
 			}
 
-			geometry.attributes[ attr ] = attribute;
-
 		}
 
-		for ( var i = 0, il = this.offsets.length; i < il; i ++ ) {
+		this.hasTangents = true;
 
-			var offset = this.offsets[ i ];
+	},
 
-			geometry.offsets.push( {
+	computeLineDistances: function () {
 
-				start: offset.start,
-				index: offset.index,
-				count: offset.count
+		var d = 0;
+		var vertices = this.vertices;
 
-			} );
+		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
 
-		}
+			if ( i > 0 ) {
 
-		return geometry;
+				d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
 
-	},
+			}
 
-	dispose: function () {
+			this.lineDistances[ i ] = d;
 
-		this.dispatchEvent( { type: 'dispose' } );
+		}
 
-	}
+	},
 
-};
+	computeBoundingBox: function () {
 
-THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype );
+		if ( this.boundingBox === null ) {
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author mikael emtinger / http://gomo.se/
- * @author WestLangley / http://github.com/WestLangley
-*/
+			this.boundingBox = new THREE.Box3();
 
-THREE.Camera = function () {
+		}
 
-	THREE.Object3D.call( this );
+		this.boundingBox.setFromPoints( this.vertices );
 
-	this.matrixWorldInverse = new THREE.Matrix4();
+	},
 
-	this.projectionMatrix = new THREE.Matrix4();
-	this.projectionMatrixInverse = new THREE.Matrix4();
+	computeBoundingSphere: function () {
 
-};
+		if ( this.boundingSphere === null ) {
 
-THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
+			this.boundingSphere = new THREE.Sphere();
 
-THREE.Camera.prototype.lookAt = function () {
+		}
 
-	// This routine does not support cameras with rotated and/or translated parent(s)
+		this.boundingSphere.setFromPoints( this.vertices );
 
-	var m1 = new THREE.Matrix4();
+	},
 
-	return function ( vector ) {
+	merge: function ( geometry, matrix, materialIndexOffset ) {
 
-		m1.lookAt( this.position, vector, this.up );
+		if ( geometry instanceof THREE.Geometry === false ) {
 
-		this.quaternion.setFromRotationMatrix( m1 );
+			THREE.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
+			return;
 
-	};
+		}
 
-}();
+		var normalMatrix,
+		vertexOffset = this.vertices.length,
+		vertices1 = this.vertices,
+		vertices2 = geometry.vertices,
+		faces1 = this.faces,
+		faces2 = geometry.faces,
+		uvs1 = this.faceVertexUvs[ 0 ],
+		uvs2 = geometry.faceVertexUvs[ 0 ];
 
-THREE.Camera.prototype.clone = function (camera) {
+		if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
 
-	if ( camera === undefined ) camera = new THREE.Camera();
+		if ( matrix !== undefined ) {
 
-	THREE.Object3D.prototype.clone.call( this, camera );
+			normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
 
-	camera.matrixWorldInverse.copy( this.matrixWorldInverse );
-	camera.projectionMatrix.copy( this.projectionMatrix );
-	camera.projectionMatrixInverse.copy( this.projectionMatrixInverse );
+		}
 
-	return camera;
-};
+		// vertices
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+		for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
 
-THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+			var vertex = vertices2[ i ];
 
-	THREE.Camera.call( this );
+			var vertexCopy = vertex.clone();
 
-	this.left = left;
-	this.right = right;
-	this.top = top;
-	this.bottom = bottom;
+			if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
 
-	this.near = ( near !== undefined ) ? near : 0.1;
-	this.far = ( far !== undefined ) ? far : 2000;
+			vertices1.push( vertexCopy );
 
-	this.updateProjectionMatrix();
+		}
 
-};
+		// faces
 
-THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
+		for ( i = 0, il = faces2.length; i < il; i ++ ) {
 
-THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
+			var face = faces2[ i ], faceCopy, normal, color,
+			faceVertexNormals = face.vertexNormals,
+			faceVertexColors = face.vertexColors;
 
-	this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far );
+			faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
+			faceCopy.normal.copy( face.normal );
 
-};
+			if ( normalMatrix !== undefined ) {
 
-THREE.OrthographicCamera.prototype.clone = function () {
+				faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
 
-	var camera = new THREE.OrthographicCamera();
+			}
 
-	THREE.Camera.prototype.clone.call( this, camera );
+			for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
 
-	camera.left = this.left;
-	camera.right = this.right;
-	camera.top = this.top;
-	camera.bottom = this.bottom;
+				normal = faceVertexNormals[ j ].clone();
 
-	camera.near = this.near;
-	camera.far = this.far;
+				if ( normalMatrix !== undefined ) {
 
-	return camera;
-};
+					normal.applyMatrix3( normalMatrix ).normalize();
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author greggman / http://games.greggman.com/
- * @author zz85 / http://www.lab4games.net/zz85/blog
- */
+				}
 
-THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
+				faceCopy.vertexNormals.push( normal );
 
-	THREE.Camera.call( this );
+			}
 
-	this.fov = fov !== undefined ? fov : 50;
-	this.aspect = aspect !== undefined ? aspect : 1;
-	this.near = near !== undefined ? near : 0.1;
-	this.far = far !== undefined ? far : 2000;
+			faceCopy.color.copy( face.color );
 
-	this.updateProjectionMatrix();
+			for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
 
-};
+				color = faceVertexColors[ j ];
+				faceCopy.vertexColors.push( color.clone() );
 
-THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
+			}
 
+			faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
 
-/**
- * Uses Focal Length (in mm) to estimate and set FOV
- * 35mm (fullframe) camera is used if frame size is not specified;
- * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
- */
+			faces1.push( faceCopy );
 
-THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+		}
 
-	if ( frameHeight === undefined ) frameHeight = 24;
+		// uvs
 
-	this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
-	this.updateProjectionMatrix();
+		for ( i = 0, il = uvs2.length; i < il; i ++ ) {
 
-}
+			var uv = uvs2[ i ], uvCopy = [];
 
+			if ( uv === undefined ) {
 
-/**
- * Sets an offset in a larger frustum. This is useful for multi-window or
- * multi-monitor/multi-machine setups.
- *
- * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
- * the monitors are in grid like this
- *
- *   +---+---+---+
- *   | A | B | C |
- *   +---+---+---+
- *   | D | E | F |
- *   +---+---+---+
- *
- * then for each monitor you would call it like this
- *
- *   var w = 1920;
- *   var h = 1080;
- *   var fullWidth = w * 3;
- *   var fullHeight = h * 2;
- *
- *   --A--
- *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
- *   --B--
- *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
- *   --C--
- *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
- *   --D--
- *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
- *   --E--
- *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
- *   --F--
- *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
- *
- *   Note there is no reason monitors have to be the same size or in a grid.
- */
+				continue;
 
-THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
+			}
 
-	this.fullWidth = fullWidth;
-	this.fullHeight = fullHeight;
-	this.x = x;
-	this.y = y;
-	this.width = width;
-	this.height = height;
+			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
 
-	this.updateProjectionMatrix();
+				uvCopy.push( uv[ j ].clone() );
 
-};
+			}
 
+			uvs1.push( uvCopy );
 
-THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
+		}
 
-	if ( this.fullWidth ) {
+	},
 
-		var aspect = this.fullWidth / this.fullHeight;
-		var top = Math.tan( THREE.Math.degToRad( this.fov * 0.5 ) ) * this.near;
-		var bottom = -top;
-		var left = aspect * bottom;
-		var right = aspect * top;
-		var width = Math.abs( right - left );
-		var height = Math.abs( top - bottom );
+	mergeMesh: function ( mesh ) {
 
-		this.projectionMatrix.makeFrustum(
-			left + this.x * width / this.fullWidth,
-			left + ( this.x + this.width ) * width / this.fullWidth,
-			top - ( this.y + this.height ) * height / this.fullHeight,
-			top - this.y * height / this.fullHeight,
-			this.near,
-			this.far
-		);
+		if ( mesh instanceof THREE.Mesh === false ) {
 
-	} else {
+			THREE.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
+			return;
 
-		this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far );
+		}
 
-	}
+		mesh.matrixAutoUpdate && mesh.updateMatrix();
 
-};
+		this.merge( mesh.geometry, mesh.matrix );
 
-THREE.PerspectiveCamera.prototype.clone = function () {
+	},
 
-	var camera = new THREE.PerspectiveCamera();
+	/*
+	 * Checks for duplicate vertices with hashmap.
+	 * Duplicated vertices are removed
+	 * and faces' vertices are updated.
+	 */
 
-	THREE.Camera.prototype.clone.call( this, camera );
+	mergeVertices: function () {
 
-	camera.fov = this.fov;
-	camera.aspect = this.aspect;
-	camera.near = this.near;
-	camera.far = this.far;
+		var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique)
+		var unique = [], changes = [];
 
-	return camera;
-};
+		var v, key;
+		var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001
+		var precision = Math.pow( 10, precisionPoints );
+		var i, il, face;
+		var indices, j, jl;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+		for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
 
-THREE.Light = function ( hex ) {
+			v = this.vertices[ i ];
+			key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
 
-	THREE.Object3D.call( this );
+			if ( verticesMap[ key ] === undefined ) {
 
-	this.color = new THREE.Color( hex );
+				verticesMap[ key ] = i;
+				unique.push( this.vertices[ i ] );
+				changes[ i ] = unique.length - 1;
 
-};
+			} else {
 
-THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+				//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
+				changes[ i ] = changes[ verticesMap[ key ] ];
 
-THREE.Light.prototype.clone = function ( light ) {
+			}
 
-	if ( light === undefined ) light = new THREE.Light();
-
-	THREE.Object3D.prototype.clone.call( this, light );
-
-	light.color.copy( this.color );
-
-	return light;
+		};
 
-};
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		// if faces are completely degenerate after merging vertices, we
+		// have to remove them from the geometry.
+		var faceIndicesToRemove = [];
 
-THREE.AmbientLight = function ( hex ) {
+		for ( i = 0, il = this.faces.length; i < il; i ++ ) {
 
-	THREE.Light.call( this, hex );
+			face = this.faces[ i ];
 
-};
+			face.a = changes[ face.a ];
+			face.b = changes[ face.b ];
+			face.c = changes[ face.c ];
 
-THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+			indices = [ face.a, face.b, face.c ];
 
-THREE.AmbientLight.prototype.clone = function () {
+			var dupIndex = - 1;
 
-	var light = new THREE.AmbientLight();
+			// if any duplicate vertices are found in a Face3
+			// we have to remove the face as nothing can be saved
+			for ( var n = 0; n < 3; n ++ ) {
+				if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) {
 
-	THREE.Light.prototype.clone.call( this, light );
+					dupIndex = n;
+					faceIndicesToRemove.push( i );
+					break;
 
-	return light;
+				}
+			}
 
-};
+		}
 
-/**
- * @author MPanknin / http://www.redplant.de/
- * @author alteredq / http://alteredqualia.com/
- */
+		for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
+			var idx = faceIndicesToRemove[ i ];
 
-THREE.AreaLight = function ( hex, intensity ) {
+			this.faces.splice( idx, 1 );
 
-	THREE.Light.call( this, hex );
+			for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
 
-	this.normal = new THREE.Vector3( 0, -1, 0 );
-	this.right = new THREE.Vector3( 1, 0, 0 );
+				this.faceVertexUvs[ j ].splice( idx, 1 );
 
-	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+			}
 
-	this.width = 1.0;
-	this.height = 1.0;
+		}
 
-	this.constantAttenuation = 1.5;
-	this.linearAttenuation = 0.5;
-	this.quadraticAttenuation = 0.1;
+		// Use unique set of vertices
 
-};
+		var diff = this.vertices.length - unique.length;
+		this.vertices = unique;
+		return diff;
 
-THREE.AreaLight.prototype = Object.create( THREE.Light.prototype );
+	},
 
+	toJSON: function () {
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+		var output = {
+			metadata: {
+				version: 4.0,
+				type: 'BufferGeometry',
+				generator: 'BufferGeometryExporter'
+			},
+			uuid: this.uuid,
+			type: this.type
+		};
 
-THREE.DirectionalLight = function ( hex, intensity ) {
+		if ( this.name !== "" ) output.name = this.name;
 
-	THREE.Light.call( this, hex );
+		if ( this.parameters !== undefined ) {
 
-	this.position.set( 0, 1, 0 );
-	this.target = new THREE.Object3D();
+			var parameters = this.parameters;
 
-	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+			for ( var key in parameters ) {
 
-	this.castShadow = false;
-	this.onlyShadow = false;
+				if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ];
 
-	//
+			}
 
-	this.shadowCameraNear = 50;
-	this.shadowCameraFar = 5000;
+			return output;
 
-	this.shadowCameraLeft = -500;
-	this.shadowCameraRight = 500;
-	this.shadowCameraTop = 500;
-	this.shadowCameraBottom = -500;
+		}
 
-	this.shadowCameraVisible = false;
+		var vertices = [];
 
-	this.shadowBias = 0;
-	this.shadowDarkness = 0.5;
+		for ( var i = 0; i < this.vertices.length; i ++ ) {
 
-	this.shadowMapWidth = 512;
-	this.shadowMapHeight = 512;
+			var vertex = this.vertices[ i ];
+			vertices.push( vertex.x, vertex.y, vertex.z );
 
-	//
+		}
 
-	this.shadowCascade = false;
+		var faces = [];
+		var normals = [];
+		var normalsHash = {};
+		var colors = [];
+		var colorsHash = {};
+		var uvs = [];
+		var uvsHash = {};
 
-	this.shadowCascadeOffset = new THREE.Vector3( 0, 0, -1000 );
-	this.shadowCascadeCount = 2;
+		for ( var i = 0; i < this.faces.length; i ++ ) {
 
-	this.shadowCascadeBias = [ 0, 0, 0 ];
-	this.shadowCascadeWidth = [ 512, 512, 512 ];
-	this.shadowCascadeHeight = [ 512, 512, 512 ];
+			var face = this.faces[ i ];
 
-	this.shadowCascadeNearZ = [ -1.000, 0.990, 0.998 ];
-	this.shadowCascadeFarZ  = [  0.990, 0.998, 1.000 ];
+			var hasMaterial = false; // face.materialIndex !== undefined;
+			var hasFaceUv = false; // deprecated
+			var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
+			var hasFaceNormal = face.normal.length() > 0;
+			var hasFaceVertexNormal = face.vertexNormals.length > 0;
+			var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
+			var hasFaceVertexColor = face.vertexColors.length > 0;
 
-	this.shadowCascadeArray = [];
+			var faceType = 0;
 
-	//
+			faceType = setBit( faceType, 0, 0 );
+			faceType = setBit( faceType, 1, hasMaterial );
+			faceType = setBit( faceType, 2, hasFaceUv );
+			faceType = setBit( faceType, 3, hasFaceVertexUv );
+			faceType = setBit( faceType, 4, hasFaceNormal );
+			faceType = setBit( faceType, 5, hasFaceVertexNormal );
+			faceType = setBit( faceType, 6, hasFaceColor );
+			faceType = setBit( faceType, 7, hasFaceVertexColor );
 
-	this.shadowMap = null;
-	this.shadowMapSize = null;
-	this.shadowCamera = null;
-	this.shadowMatrix = null;
+			faces.push( faceType );
+			faces.push( face.a, face.b, face.c );
 
-};
 
-THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
+			/*
+			if ( hasMaterial ) {
 
-THREE.DirectionalLight.prototype.clone = function () {
+				faces.push( face.materialIndex );
 
-	var light = new THREE.DirectionalLight();
+			}
+			*/
 
-	THREE.Light.prototype.clone.call( this, light );
+			if ( hasFaceVertexUv ) {
 
-	light.target = this.target.clone();
+				var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
 
-	light.intensity = this.intensity;
+				faces.push(
+					getUvIndex( faceVertexUvs[ 0 ] ),
+					getUvIndex( faceVertexUvs[ 1 ] ),
+					getUvIndex( faceVertexUvs[ 2 ] )
+				);
 
-	light.castShadow = this.castShadow;
-	light.onlyShadow = this.onlyShadow;
+			}
 
-	return light;
+			if ( hasFaceNormal ) {
 
-};
+				faces.push( getNormalIndex( face.normal ) );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+			}
 
-THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) {
+			if ( hasFaceVertexNormal ) {
 
-	THREE.Light.call( this, skyColorHex );
+				var vertexNormals = face.vertexNormals;
 
-	this.position.set( 0, 100, 0 );
+				faces.push(
+					getNormalIndex( vertexNormals[ 0 ] ),
+					getNormalIndex( vertexNormals[ 1 ] ),
+					getNormalIndex( vertexNormals[ 2 ] )
+				);
 
-	this.groundColor = new THREE.Color( groundColorHex );
-	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+			}
 
-};
+			if ( hasFaceColor ) {
 
-THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+				faces.push( getColorIndex( face.color ) );
 
-THREE.HemisphereLight.prototype.clone = function () {
+			}
 
-	var light = new THREE.HemisphereLight();
+			if ( hasFaceVertexColor ) {
 
-	THREE.Light.prototype.clone.call( this, light );
+				var vertexColors = face.vertexColors;
 
-	light.groundColor.copy( this.groundColor );
-	light.intensity = this.intensity;
+				faces.push(
+					getColorIndex( vertexColors[ 0 ] ),
+					getColorIndex( vertexColors[ 1 ] ),
+					getColorIndex( vertexColors[ 2 ] )
+				);
 
-	return light;
+			}
 
-};
+		}
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		function setBit( value, position, enabled ) {
 
-THREE.PointLight = function ( hex, intensity, distance ) {
+			return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) );
 
-	THREE.Light.call( this, hex );
+		}
 
-	this.intensity = ( intensity !== undefined ) ? intensity : 1;
-	this.distance = ( distance !== undefined ) ? distance : 0;
+		function getNormalIndex( normal ) {
 
-};
+			var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
 
-THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+			if ( normalsHash[ hash ] !== undefined ) {
 
-THREE.PointLight.prototype.clone = function () {
+				return normalsHash[ hash ];
 
-	var light = new THREE.PointLight();
+			}
 
-	THREE.Light.prototype.clone.call( this, light );
+			normalsHash[ hash ] = normals.length / 3;
+			normals.push( normal.x, normal.y, normal.z );
 
-	light.intensity = this.intensity;
-	light.distance = this.distance;
+			return normalsHash[ hash ];
 
-	return light;
+		}
 
-};
+		function getColorIndex( color ) {
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+			var hash = color.r.toString() + color.g.toString() + color.b.toString();
 
-THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) {
+			if ( colorsHash[ hash ] !== undefined ) {
 
-	THREE.Light.call( this, hex );
+				return colorsHash[ hash ];
 
-	this.position.set( 0, 1, 0 );
-	this.target = new THREE.Object3D();
+			}
 
-	this.intensity = ( intensity !== undefined ) ? intensity : 1;
-	this.distance = ( distance !== undefined ) ? distance : 0;
-	this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
-	this.exponent = ( exponent !== undefined ) ? exponent : 10;
+			colorsHash[ hash ] = colors.length;
+			colors.push( color.getHex() );
 
-	this.castShadow = false;
-	this.onlyShadow = false;
+			return colorsHash[ hash ];
 
-	//
+		}
 
-	this.shadowCameraNear = 50;
-	this.shadowCameraFar = 5000;
-	this.shadowCameraFov = 50;
+		function getUvIndex( uv ) {
 
-	this.shadowCameraVisible = false;
+			var hash = uv.x.toString() + uv.y.toString();
 
-	this.shadowBias = 0;
-	this.shadowDarkness = 0.5;
+			if ( uvsHash[ hash ] !== undefined ) {
 
-	this.shadowMapWidth = 512;
-	this.shadowMapHeight = 512;
+				return uvsHash[ hash ];
 
-	//
+			}
 
-	this.shadowMap = null;
-	this.shadowMapSize = null;
-	this.shadowCamera = null;
-	this.shadowMatrix = null;
+			uvsHash[ hash ] = uvs.length / 2;
+			uvs.push( uv.x, uv.y );
 
-};
+			return uvsHash[ hash ];
 
-THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
+		}
 
-THREE.SpotLight.prototype.clone = function () {
+		output.data = {};
 
-	var light = new THREE.SpotLight();
+		output.data.vertices = vertices;
+		output.data.normals = normals;
+		if ( colors.length > 0 ) output.data.colors = colors;
+		if ( uvs.length > 0 ) output.data.uvs = [ uvs ]; // temporal backward compatibility
+		output.data.faces = faces;
 
-	THREE.Light.prototype.clone.call( this, light );
+		//
 
-	light.target = this.target.clone();
+		return output;
 
-	light.intensity = this.intensity;
-	light.distance = this.distance;
-	light.angle = this.angle;
-	light.exponent = this.exponent;
+	},
 
-	light.castShadow = this.castShadow;
-	light.onlyShadow = this.onlyShadow;
+	clone: function () {
 
-	return light;
+		var geometry = new THREE.Geometry();
 
-};
+		var vertices = this.vertices;
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
 
-THREE.Loader = function ( showStatus ) {
+			geometry.vertices.push( vertices[ i ].clone() );
 
-	this.showStatus = showStatus;
-	this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null;
+		}
 
-	this.onLoadStart = function () {};
-	this.onLoadProgress = function () {};
-	this.onLoadComplete = function () {};
+		var faces = this.faces;
 
-};
+		for ( var i = 0, il = faces.length; i < il; i ++ ) {
 
-THREE.Loader.prototype = {
+			geometry.faces.push( faces[ i ].clone() );
 
-	constructor: THREE.Loader,
+		}
 
-	crossOrigin: 'anonymous',
+		for ( var i = 0, il = this.faceVertexUvs.length; i < il; i ++ ) {
 
-	addStatusElement: function () {
+			var faceVertexUvs = this.faceVertexUvs[ i ];
 
-		var e = document.createElement( "div" );
-
-		e.style.position = "absolute";
-		e.style.right = "0px";
-		e.style.top = "0px";
-		e.style.fontSize = "0.8em";
-		e.style.textAlign = "left";
-		e.style.background = "rgba(0,0,0,0.25)";
-		e.style.color = "#fff";
-		e.style.width = "120px";
-		e.style.padding = "0.5em 0.5em 0.5em 0.5em";
-		e.style.zIndex = 1000;
+			if ( geometry.faceVertexUvs[ i ] === undefined ) {
 
-		e.innerHTML = "Loading ...";
+				geometry.faceVertexUvs[ i ] = [];
 
-		return e;
+			}
 
-	},
+			for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {
 
-	updateProgress: function ( progress ) {
+				var uvs = faceVertexUvs[ j ], uvsCopy = [];
 
-		var message = "Loaded ";
+				for ( var k = 0, kl = uvs.length; k < kl; k ++ ) {
 
-		if ( progress.total ) {
+					var uv = uvs[ k ];
 
-			message += ( 100 * progress.loaded / progress.total ).toFixed(0) + "%";
+					uvsCopy.push( uv.clone() );
 
+				}
 
-		} else {
+				geometry.faceVertexUvs[ i ].push( uvsCopy );
 
-			message += ( progress.loaded / 1000 ).toFixed(2) + " KB";
+			}
 
 		}
 
-		this.statusDomElement.innerHTML = message;
+		return geometry;
 
 	},
 
-	extractUrlBase: function ( url ) {
+	dispose: function () {
 
-		var parts = url.split( '/' );
-		parts.pop();
-		return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
+		this.dispatchEvent( { type: 'dispose' } );
 
-	},
+	}
 
-	initMaterials: function ( materials, texturePath ) {
+};
 
-		var array = [];
+THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );
 
-		for ( var i = 0; i < materials.length; ++ i ) {
+THREE.GeometryIdCount = 0;
 
-			array[ i ] = THREE.Loader.prototype.createMaterial( materials[ i ], texturePath );
+// File:src/cameras/Camera.js
 
-		}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-		return array;
+THREE.Camera = function () {
 
-	},
+	THREE.Object3D.call( this );
 
-	needsTangents: function ( materials ) {
+	this.type = 'Camera';
 
-		for( var i = 0, il = materials.length; i < il; i ++ ) {
+	this.matrixWorldInverse = new THREE.Matrix4();
+	this.projectionMatrix = new THREE.Matrix4();
 
-			var m = materials[ i ];
+};
 
-			if ( m instanceof THREE.ShaderMaterial ) return true;
+THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Camera.prototype.constructor = THREE.Camera;
 
-		}
+THREE.Camera.prototype.getWorldDirection = function () {
 
-		return false;
+	var quaternion = new THREE.Quaternion();
 
-	},
+	return function ( optionalTarget ) {
 
-	createMaterial: function ( m, texturePath ) {
+		var result = optionalTarget || new THREE.Vector3();
 
-		var _this = this;
+		this.getWorldQuaternion( quaternion );
 
-		function is_pow2( n ) {
+		return result.set( 0, 0, - 1 ).applyQuaternion( quaternion );
 
-			var l = Math.log( n ) / Math.LN2;
-			return Math.floor( l ) == l;
+	}
 
-		}
+}();
 
-		function nearest_pow2( n ) {
+THREE.Camera.prototype.lookAt = function () {
 
-			var l = Math.log( n ) / Math.LN2;
-			return Math.pow( 2, Math.round(  l ) );
+	// This routine does not support cameras with rotated and/or translated parent(s)
 
-		}
+	var m1 = new THREE.Matrix4();
 
-		function load_image( where, url ) {
+	return function ( vector ) {
 
-			var image = new Image();
+		m1.lookAt( this.position, vector, this.up );
 
-			image.onload = function () {
+		this.quaternion.setFromRotationMatrix( m1 );
 
-				if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) {
+	};
 
-					var width = nearest_pow2( this.width );
-					var height = nearest_pow2( this.height );
+}();
 
-					where.image.width = width;
-					where.image.height = height;
-					where.image.getContext( '2d' ).drawImage( this, 0, 0, width, height );
+THREE.Camera.prototype.clone = function ( camera ) {
 
-				} else {
+	if ( camera === undefined ) camera = new THREE.Camera();
 
-					where.image = this;
+	THREE.Object3D.prototype.clone.call( this, camera );
 
-				}
+	camera.matrixWorldInverse.copy( this.matrixWorldInverse );
+	camera.projectionMatrix.copy( this.projectionMatrix );
 
-				where.needsUpdate = true;
+	return camera;
+};
 
-			};
+// File:src/cameras/CubeCamera.js
 
-			image.crossOrigin = _this.crossOrigin;
-			image.src = url;
+/**
+ * Camera for rendering cube maps
+ *	- renders scene into axis-aligned cube
+ *
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		}
+THREE.CubeCamera = function ( near, far, cubeResolution ) {
 
-		function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
+	THREE.Object3D.call( this );
 
-			var isCompressed = /\.dds$/i.test( sourceFile );
-			var fullPath = texturePath + "/" + sourceFile;
+	this.type = 'CubeCamera';
 
-			if ( isCompressed ) {
+	var fov = 90, aspect = 1;
 
-				var texture = THREE.ImageUtils.loadCompressedTexture( fullPath );
+	var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraPX.up.set( 0, - 1, 0 );
+	cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
+	this.add( cameraPX );
 
-				where[ name ] = texture;
+	var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraNX.up.set( 0, - 1, 0 );
+	cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) );
+	this.add( cameraNX );
 
-			} else {
+	var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraPY.up.set( 0, 0, 1 );
+	cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
+	this.add( cameraPY );
 
-				var texture = document.createElement( 'canvas' );
+	var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraNY.up.set( 0, 0, - 1 );
+	cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) );
+	this.add( cameraNY );
 
-				where[ name ] = new THREE.Texture( texture );
+	var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraPZ.up.set( 0, - 1, 0 );
+	cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
+	this.add( cameraPZ );
 
-			}
+	var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	cameraNZ.up.set( 0, - 1, 0 );
+	cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) );
+	this.add( cameraNZ );
 
-			where[ name ].sourceFile = sourceFile;
+	this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
 
-			if( repeat ) {
+	this.updateCubeMap = function ( renderer, scene ) {
 
-				where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] );
+		var renderTarget = this.renderTarget;
+		var generateMipmaps = renderTarget.generateMipmaps;
 
-				if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping;
-				if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping;
+		renderTarget.generateMipmaps = false;
 
-			}
+		renderTarget.activeCubeFace = 0;
+		renderer.render( scene, cameraPX, renderTarget );
 
-			if ( offset ) {
+		renderTarget.activeCubeFace = 1;
+		renderer.render( scene, cameraNX, renderTarget );
 
-				where[ name ].offset.set( offset[ 0 ], offset[ 1 ] );
+		renderTarget.activeCubeFace = 2;
+		renderer.render( scene, cameraPY, renderTarget );
 
-			}
+		renderTarget.activeCubeFace = 3;
+		renderer.render( scene, cameraNY, renderTarget );
 
-			if ( wrap ) {
+		renderTarget.activeCubeFace = 4;
+		renderer.render( scene, cameraPZ, renderTarget );
 
-				var wrapMap = {
-					"repeat": THREE.RepeatWrapping,
-					"mirror": THREE.MirroredRepeatWrapping
-				}
+		renderTarget.generateMipmaps = generateMipmaps;
 
-				if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ];
-				if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ];
+		renderTarget.activeCubeFace = 5;
+		renderer.render( scene, cameraNZ, renderTarget );
 
-			}
+	};
 
-			if ( anisotropy ) {
+};
 
-				where[ name ].anisotropy = anisotropy;
+THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.CubeCamera.prototype.constructor = THREE.CubeCamera;
 
-			}
+// File:src/cameras/OrthographicCamera.js
 
-			if ( ! isCompressed ) {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-				load_image( where[ name ], fullPath );
+THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
 
-			}
+	THREE.Camera.call( this );
 
-		}
+	this.type = 'OrthographicCamera';
 
-		function rgb2hex( rgb ) {
+	this.zoom = 1;
 
-			return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255;
+	this.left = left;
+	this.right = right;
+	this.top = top;
+	this.bottom = bottom;
 
-		}
+	this.near = ( near !== undefined ) ? near : 0.1;
+	this.far = ( far !== undefined ) ? far : 2000;
 
-		// defaults
+	this.updateProjectionMatrix();
 
-		var mtype = "MeshLambertMaterial";
-		var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false };
+};
 
-		// parameters from model file
+THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera;
 
-		if ( m.shading ) {
+THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
 
-			var shading = m.shading.toLowerCase();
+	var dx = ( this.right - this.left ) / ( 2 * this.zoom );
+	var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
+	var cx = ( this.right + this.left ) / 2;
+	var cy = ( this.top + this.bottom ) / 2;
 
-			if ( shading === "phong" ) mtype = "MeshPhongMaterial";
-			else if ( shading === "basic" ) mtype = "MeshBasicMaterial";
+	this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far );
 
-		}
+};
 
-		if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) {
+THREE.OrthographicCamera.prototype.clone = function () {
 
-			mpars.blending = THREE[ m.blending ];
+	var camera = new THREE.OrthographicCamera();
 
-		}
+	THREE.Camera.prototype.clone.call( this, camera );
 
-		if ( m.transparent !== undefined || m.opacity < 1.0 ) {
+	camera.zoom = this.zoom;
 
-			mpars.transparent = m.transparent;
+	camera.left = this.left;
+	camera.right = this.right;
+	camera.top = this.top;
+	camera.bottom = this.bottom;
 
-		}
+	camera.near = this.near;
+	camera.far = this.far;
 
-		if ( m.depthTest !== undefined ) {
+	camera.projectionMatrix.copy( this.projectionMatrix );
 
-			mpars.depthTest = m.depthTest;
+	return camera;
+};
 
-		}
+// File:src/cameras/PerspectiveCamera.js
 
-		if ( m.depthWrite !== undefined ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author greggman / http://games.greggman.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
 
-			mpars.depthWrite = m.depthWrite;
+THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
 
-		}
+	THREE.Camera.call( this );
 
-		if ( m.visible !== undefined ) {
+	this.type = 'PerspectiveCamera';
 
-			mpars.visible = m.visible;
+	this.zoom = 1;
 
-		}
+	this.fov = fov !== undefined ? fov : 50;
+	this.aspect = aspect !== undefined ? aspect : 1;
+	this.near = near !== undefined ? near : 0.1;
+	this.far = far !== undefined ? far : 2000;
 
-		if ( m.flipSided !== undefined ) {
+	this.updateProjectionMatrix();
 
-			mpars.side = THREE.BackSide;
+};
 
-		}
+THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera;
 
-		if ( m.doubleSided !== undefined ) {
 
-			mpars.side = THREE.DoubleSide;
+/**
+ * Uses Focal Length (in mm) to estimate and set FOV
+ * 35mm (fullframe) camera is used if frame size is not specified;
+ * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
+ */
 
-		}
+THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
 
-		if ( m.wireframe !== undefined ) {
+	if ( frameHeight === undefined ) frameHeight = 24;
 
-			mpars.wireframe = m.wireframe;
+	this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+	this.updateProjectionMatrix();
 
-		}
+}
 
-		if ( m.vertexColors !== undefined ) {
 
-			if ( m.vertexColors === "face" ) {
+/**
+ * Sets an offset in a larger frustum. This is useful for multi-window or
+ * multi-monitor/multi-machine setups.
+ *
+ * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
+ * the monitors are in grid like this
+ *
+ *   +---+---+---+
+ *   | A | B | C |
+ *   +---+---+---+
+ *   | D | E | F |
+ *   +---+---+---+
+ *
+ * then for each monitor you would call it like this
+ *
+ *   var w = 1920;
+ *   var h = 1080;
+ *   var fullWidth = w * 3;
+ *   var fullHeight = h * 2;
+ *
+ *   --A--
+ *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
+ *   --B--
+ *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
+ *   --C--
+ *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
+ *   --D--
+ *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
+ *   --E--
+ *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
+ *   --F--
+ *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
+ *
+ *   Note there is no reason monitors have to be the same size or in a grid.
+ */
 
-				mpars.vertexColors = THREE.FaceColors;
+THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
 
-			} else if ( m.vertexColors ) {
+	this.fullWidth = fullWidth;
+	this.fullHeight = fullHeight;
+	this.x = x;
+	this.y = y;
+	this.width = width;
+	this.height = height;
 
-				mpars.vertexColors = THREE.VertexColors;
+	this.updateProjectionMatrix();
 
-			}
+};
 
-		}
 
-		// colors
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
 
-		if ( m.colorDiffuse ) {
+	var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) );
 
-			mpars.color = rgb2hex( m.colorDiffuse );
+	if ( this.fullWidth ) {
 
-		} else if ( m.DbgColor ) {
+		var aspect = this.fullWidth / this.fullHeight;
+		var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near;
+		var bottom = - top;
+		var left = aspect * bottom;
+		var right = aspect * top;
+		var width = Math.abs( right - left );
+		var height = Math.abs( top - bottom );
 
-			mpars.color = m.DbgColor;
+		this.projectionMatrix.makeFrustum(
+			left + this.x * width / this.fullWidth,
+			left + ( this.x + this.width ) * width / this.fullWidth,
+			top - ( this.y + this.height ) * height / this.fullHeight,
+			top - this.y * height / this.fullHeight,
+			this.near,
+			this.far
+		);
 
-		}
-
-		if ( m.colorSpecular ) {
-
-			mpars.specular = rgb2hex( m.colorSpecular );
-
-		}
-
-		if ( m.colorAmbient ) {
-
-			mpars.ambient = rgb2hex( m.colorAmbient );
-
-		}
+	} else {
 
-		// modifiers
+		this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far );
 
-		if ( m.transparency ) {
+	}
 
-			mpars.opacity = m.transparency;
+};
 
-		}
+THREE.PerspectiveCamera.prototype.clone = function () {
 
-		if ( m.specularCoef ) {
+	var camera = new THREE.PerspectiveCamera();
 
-			mpars.shininess = m.specularCoef;
+	THREE.Camera.prototype.clone.call( this, camera );
 
-		}
+	camera.zoom = this.zoom;
 
-		// textures
+	camera.fov = this.fov;
+	camera.aspect = this.aspect;
+	camera.near = this.near;
+	camera.far = this.far;
 
-		if ( m.mapDiffuse && texturePath ) {
+	camera.projectionMatrix.copy( this.projectionMatrix );
 
-			create_texture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
+	return camera;
 
-		}
+};
 
-		if ( m.mapLight && texturePath ) {
+// File:src/lights/Light.js
 
-			create_texture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		}
+THREE.Light = function ( color ) {
 
-		if ( m.mapBump && texturePath ) {
+	THREE.Object3D.call( this );
 
-			create_texture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
+	this.type = 'Light';
+	
+	this.color = new THREE.Color( color );
 
-		}
+};
 
-		if ( m.mapNormal && texturePath ) {
+THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Light.prototype.constructor = THREE.Light;
 
-			create_texture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
+THREE.Light.prototype.clone = function ( light ) {
 
-		}
+	if ( light === undefined ) light = new THREE.Light();
 
-		if ( m.mapSpecular && texturePath ) {
+	THREE.Object3D.prototype.clone.call( this, light );
 
-			create_texture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
+	light.color.copy( this.color );
 
-		}
+	return light;
 
-		//
+};
 
-		if ( m.mapBumpScale ) {
+// File:src/lights/AmbientLight.js
 
-			mpars.bumpScale = m.mapBumpScale;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		}
+THREE.AmbientLight = function ( color ) {
 
-		// special case for normal mapped material
+	THREE.Light.call( this, color );
 
-		if ( m.mapNormal ) {
+	this.type = 'AmbientLight';
 
-			var shader = THREE.ShaderLib[ "normalmap" ];
-			var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+};
 
-			uniforms[ "tNormal" ].value = mpars.normalMap;
+THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+THREE.AmbientLight.prototype.constructor = THREE.AmbientLight;
 
-			if ( m.mapNormalFactor ) {
+THREE.AmbientLight.prototype.clone = function () {
 
-				uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor );
+	var light = new THREE.AmbientLight();
 
-			}
+	THREE.Light.prototype.clone.call( this, light );
 
-			if ( mpars.map ) {
+	return light;
 
-				uniforms[ "tDiffuse" ].value = mpars.map;
-				uniforms[ "enableDiffuse" ].value = true;
+};
 
-			}
+// File:src/lights/AreaLight.js
 
-			if ( mpars.specularMap ) {
+/**
+ * @author MPanknin / http://www.redplant.de/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-				uniforms[ "tSpecular" ].value = mpars.specularMap;
-				uniforms[ "enableSpecular" ].value = true;
+THREE.AreaLight = function ( color, intensity ) {
 
-			}
+	THREE.Light.call( this, color );
 
-			if ( mpars.lightMap ) {
+	this.type = 'AreaLight';
 
-				uniforms[ "tAO" ].value = mpars.lightMap;
-				uniforms[ "enableAO" ].value = true;
+	this.normal = new THREE.Vector3( 0, - 1, 0 );
+	this.right = new THREE.Vector3( 1, 0, 0 );
 
-			}
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
 
-			// for the moment don't handle displacement texture
+	this.width = 1.0;
+	this.height = 1.0;
 
-			uniforms[ "uDiffuseColor" ].value.setHex( mpars.color );
-			uniforms[ "uSpecularColor" ].value.setHex( mpars.specular );
-			uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient );
+	this.constantAttenuation = 1.5;
+	this.linearAttenuation = 0.5;
+	this.quadraticAttenuation = 0.1;
 
-			uniforms[ "uShininess" ].value = mpars.shininess;
+};
 
-			if ( mpars.opacity !== undefined ) {
+THREE.AreaLight.prototype = Object.create( THREE.Light.prototype );
+THREE.AreaLight.prototype.constructor = THREE.AreaLight;
 
-				uniforms[ "uOpacity" ].value = mpars.opacity;
 
-			}
+// File:src/lights/DirectionalLight.js
 
-			var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
-			var material = new THREE.ShaderMaterial( parameters );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			if ( mpars.transparent ) {
+THREE.DirectionalLight = function ( color, intensity ) {
 
-				material.transparent = true;
+	THREE.Light.call( this, color );
 
-			}
+	this.type = 'DirectionalLight';
 
-		} else {
+	this.position.set( 0, 1, 0 );
+	this.target = new THREE.Object3D();
 
-			var material = new THREE[ mtype ]( mpars );
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
 
-		}
+	this.castShadow = false;
+	this.onlyShadow = false;
 
-		if ( m.DbgName !== undefined ) material.name = m.DbgName;
+	//
 
-		return material;
+	this.shadowCameraNear = 50;
+	this.shadowCameraFar = 5000;
 
-	}
+	this.shadowCameraLeft = - 500;
+	this.shadowCameraRight = 500;
+	this.shadowCameraTop = 500;
+	this.shadowCameraBottom = - 500;
 
-};
+	this.shadowCameraVisible = false;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+	this.shadowBias = 0;
+	this.shadowDarkness = 0.5;
 
-THREE.XHRLoader = function ( manager ) {
+	this.shadowMapWidth = 512;
+	this.shadowMapHeight = 512;
 
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+	//
 
-};
+	this.shadowCascade = false;
 
-THREE.XHRLoader.prototype = {
+	this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 );
+	this.shadowCascadeCount = 2;
 
-	constructor: THREE.XHRLoader,
+	this.shadowCascadeBias = [ 0, 0, 0 ];
+	this.shadowCascadeWidth = [ 512, 512, 512 ];
+	this.shadowCascadeHeight = [ 512, 512, 512 ];
 
-	load: function ( url, onLoad, onProgress, onError ) {
+	this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ];
+	this.shadowCascadeFarZ  = [ 0.990, 0.998, 1.000 ];
 
-		var scope = this;
-		var request = new XMLHttpRequest();
+	this.shadowCascadeArray = [];
 
-		if ( onLoad !== undefined ) {
+	//
 
-			request.addEventListener( 'load', function ( event ) {
+	this.shadowMap = null;
+	this.shadowMapSize = null;
+	this.shadowCamera = null;
+	this.shadowMatrix = null;
 
-				onLoad( event.target.responseText );
-				scope.manager.itemEnd( url );
+};
 
-			}, false );
+THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
+THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight;
 
-		}
+THREE.DirectionalLight.prototype.clone = function () {
 
-		if ( onProgress !== undefined ) {
+	var light = new THREE.DirectionalLight();
 
-			request.addEventListener( 'progress', function ( event ) {
+	THREE.Light.prototype.clone.call( this, light );
 
-				onProgress( event );
+	light.target = this.target.clone();
 
-			}, false );
+	light.intensity = this.intensity;
 
-		}
+	light.castShadow = this.castShadow;
+	light.onlyShadow = this.onlyShadow;
 
-		if ( onError !== undefined ) {
+	//
 
-			request.addEventListener( 'error', function ( event ) {
+	light.shadowCameraNear = this.shadowCameraNear;
+	light.shadowCameraFar = this.shadowCameraFar;
 
-				onError( event );
+	light.shadowCameraLeft = this.shadowCameraLeft;
+	light.shadowCameraRight = this.shadowCameraRight;
+	light.shadowCameraTop = this.shadowCameraTop;
+	light.shadowCameraBottom = this.shadowCameraBottom;
 
-			}, false );
+	light.shadowCameraVisible = this.shadowCameraVisible;
 
-		}
+	light.shadowBias = this.shadowBias;
+	light.shadowDarkness = this.shadowDarkness;
 
-		if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
+	light.shadowMapWidth = this.shadowMapWidth;
+	light.shadowMapHeight = this.shadowMapHeight;
 
-		request.open( 'GET', url, true );
-		request.send( null );
+	//
 
-		scope.manager.itemStart( url );
+	light.shadowCascade = this.shadowCascade;
 
-	},
+	light.shadowCascadeOffset.copy( this.shadowCascadeOffset );
+	light.shadowCascadeCount = this.shadowCascadeCount;
 
-	setCrossOrigin: function ( value ) {
+	light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 );
+	light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 );
+	light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 );
 
-		this.crossOrigin = value;
+	light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 );
+	light.shadowCascadeFarZ  = this.shadowCascadeFarZ.slice( 0 );
 
-	}
+	return light;
 
 };
 
+// File:src/lights/HemisphereLight.js
+
 /**
- * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.ImageLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.ImageLoader.prototype = {
+THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) {
 
-	constructor: THREE.ImageLoader,
+	THREE.Light.call( this, skyColor );
 
-	load: function ( url, onLoad, onProgress, onError ) {
+	this.type = 'HemisphereLight';
 
-		var scope = this;
-		var image = document.createElement( 'img' );
+	this.position.set( 0, 100, 0 );
 
-		if ( onLoad !== undefined ) {
+	this.groundColor = new THREE.Color( groundColor );
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
 
-			image.addEventListener( 'load', function ( event ) {
+};
 
-				scope.manager.itemEnd( url );
-				onLoad( this );
+THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight;
 
-			}, false );
+THREE.HemisphereLight.prototype.clone = function () {
 
-		}
+	var light = new THREE.HemisphereLight();
 
-		if ( onProgress !== undefined ) {
+	THREE.Light.prototype.clone.call( this, light );
 
-			image.addEventListener( 'progress', function ( event ) {
+	light.groundColor.copy( this.groundColor );
+	light.intensity = this.intensity;
 
-				onProgress( event );
+	return light;
 
-			}, false );
+};
 
-		}
+// File:src/lights/PointLight.js
 
-		if ( onError !== undefined ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			image.addEventListener( 'error', function ( event ) {
+THREE.PointLight = function ( color, intensity, distance, decay ) {
 
-				onError( event );
+	THREE.Light.call( this, color );
 
-			}, false );
+	this.type = 'PointLight';
 
-		}
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+	this.distance = ( distance !== undefined ) ? distance : 0;
+	this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.
 
-		if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
+};
 
-		image.src = url;
+THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+THREE.PointLight.prototype.constructor = THREE.PointLight;
 
-		scope.manager.itemStart( url );
+THREE.PointLight.prototype.clone = function () {
 
-		return image;
+	var light = new THREE.PointLight();
 
-	},
+	THREE.Light.prototype.clone.call( this, light );
 
-	setCrossOrigin: function ( value ) {
+	light.intensity = this.intensity;
+	light.distance = this.distance;
+	light.decay = this.decay;
 
-		this.crossOrigin = value;
+	return light;
 
-	}
+};
 
-}
+// File:src/lights/SpotLight.js
 
 /**
- * @author mrdoob / http://mrdoob.com/
  * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.JSONLoader = function ( showStatus ) {
+THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) {
 
-	THREE.Loader.call( this, showStatus );
+	THREE.Light.call( this, color );
 
-	this.withCredentials = false;
+	this.type = 'SpotLight';
 
-};
+	this.position.set( 0, 1, 0 );
+	this.target = new THREE.Object3D();
 
-THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype );
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+	this.distance = ( distance !== undefined ) ? distance : 0;
+	this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
+	this.exponent = ( exponent !== undefined ) ? exponent : 10;
+	this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.
 
-THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) {
+	this.castShadow = false;
+	this.onlyShadow = false;
 
-	var scope = this;
+	//
 
-	// todo: unify load API to for easier SceneLoader use
+	this.shadowCameraNear = 50;
+	this.shadowCameraFar = 5000;
+	this.shadowCameraFov = 50;
 
-	texturePath = texturePath && ( typeof texturePath === "string" ) ? texturePath : this.extractUrlBase( url );
+	this.shadowCameraVisible = false;
 
-	this.onLoadStart();
-	this.loadAjaxJSON( this, url, callback, texturePath );
+	this.shadowBias = 0;
+	this.shadowDarkness = 0.5;
 
-};
+	this.shadowMapWidth = 512;
+	this.shadowMapHeight = 512;
 
-THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) {
+	//
 
-	var xhr = new XMLHttpRequest();
+	this.shadowMap = null;
+	this.shadowMapSize = null;
+	this.shadowCamera = null;
+	this.shadowMatrix = null;
 
-	var length = 0;
+};
 
-	xhr.onreadystatechange = function () {
+THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
+THREE.SpotLight.prototype.constructor = THREE.SpotLight;
 
-		if ( xhr.readyState === xhr.DONE ) {
+THREE.SpotLight.prototype.clone = function () {
 
-			if ( xhr.status === 200 || xhr.status === 0 ) {
+	var light = new THREE.SpotLight();
 
-				if ( xhr.responseText ) {
+	THREE.Light.prototype.clone.call( this, light );
 
-					var json = JSON.parse( xhr.responseText );
-					var result = context.parse( json, texturePath );
-					callback( result.geometry, result.materials );
+	light.target = this.target.clone();
 
-				} else {
+	light.intensity = this.intensity;
+	light.distance = this.distance;
+	light.angle = this.angle;
+	light.exponent = this.exponent;
+	light.decay = this.decay;
 
-					console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there is empty" );
+	light.castShadow = this.castShadow;
+	light.onlyShadow = this.onlyShadow;
 
-				}
+	//
 
-				// in context of more complex asset initialization
-				// do not block on single failed file
-				// maybe should go even one more level up
+	light.shadowCameraNear = this.shadowCameraNear;
+	light.shadowCameraFar = this.shadowCameraFar;
+	light.shadowCameraFov = this.shadowCameraFov;
 
-				context.onLoadComplete();
+	light.shadowCameraVisible = this.shadowCameraVisible;
 
-			} else {
+	light.shadowBias = this.shadowBias;
+	light.shadowDarkness = this.shadowDarkness;
 
-				console.error( "THREE.JSONLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
+	light.shadowMapWidth = this.shadowMapWidth;
+	light.shadowMapHeight = this.shadowMapHeight;
 
-			}
+	return light;
 
-		} else if ( xhr.readyState === xhr.LOADING ) {
+};
 
-			if ( callbackProgress ) {
+// File:src/loaders/Cache.js
 
-				if ( length === 0 ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-					length = xhr.getResponseHeader( "Content-Length" );
+THREE.Cache = {
 
-				}
+	files: {},
 
-				callbackProgress( { total: length, loaded: xhr.responseText.length } );
+	add: function ( key, file ) {
 
-			}
+		// console.log( 'THREE.Cache', 'Adding key:', key );
 
-		} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
+		this.files[ key ] = file;
 
-			if ( callbackProgress !== undefined ) {
+	},
 
-				length = xhr.getResponseHeader( "Content-Length" );
+	get: function ( key ) {
 
-			}
+		// console.log( 'THREE.Cache', 'Checking key:', key );
 
-		}
+		return this.files[ key ];
 
-	};
+	},
 
-	xhr.open( "GET", url, true );
-	xhr.withCredentials = this.withCredentials;
-	xhr.send( null );
+	remove: function ( key ) {
 
-};
+		delete this.files[ key ];
 
-THREE.JSONLoader.prototype.parse = function ( json, texturePath ) {
+	},
 
-	var scope = this,
-	geometry = new THREE.Geometry(),
-	scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
+	clear: function () {
 
-	parseModel( scale );
+		this.files = {}
 
-	parseSkin();
-	parseMorphing( scale );
+	}
 
-	geometry.computeCentroids();
-	geometry.computeFaceNormals();
-	geometry.computeBoundingSphere();
+};
 
-	function parseModel( scale ) {
+// File:src/loaders/Loader.js
 
-		function isBitSet( value, position ) {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			return value & ( 1 << position );
+THREE.Loader = function ( showStatus ) {
 
-		}
+	this.showStatus = showStatus;
+	this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null;
 
-		var i, j, fi,
+	this.imageLoader = new THREE.ImageLoader();
 
-		offset, zLength,
+	this.onLoadStart = function () {};
+	this.onLoadProgress = function () {};
+	this.onLoadComplete = function () {};
 
-		colorIndex, normalIndex, uvIndex, materialIndex,
+};
 
-		type,
-		isQuad,
-		hasMaterial,
-		hasFaceVertexUv,
-		hasFaceNormal, hasFaceVertexNormal,
-		hasFaceColor, hasFaceVertexColor,
+THREE.Loader.prototype = {
 
-		vertex, face, faceA, faceB, color, hex, normal,
+	constructor: THREE.Loader,
 
-		uvLayer, uv, u, v,
+	crossOrigin: undefined,
 
-		faces = json.faces,
-		vertices = json.vertices,
-		normals = json.normals,
-		colors = json.colors,
+	addStatusElement: function () {
 
-		nUvLayers = 0;
+		var e = document.createElement( 'div' );
+
+		e.style.position = 'absolute';
+		e.style.right = '0px';
+		e.style.top = '0px';
+		e.style.fontSize = '0.8em';
+		e.style.textAlign = 'left';
+		e.style.background = 'rgba(0,0,0,0.25)';
+		e.style.color = '#fff';
+		e.style.width = '120px';
+		e.style.padding = '0.5em 0.5em 0.5em 0.5em';
+		e.style.zIndex = 1000;
 
-		if ( json.uvs !== undefined ) {
+		e.innerHTML = 'Loading ...';
 
-			// disregard empty arrays
+		return e;
 
-			for ( i = 0; i < json.uvs.length; i++ ) {
+	},
 
-				if ( json.uvs[ i ].length ) nUvLayers ++;
+	updateProgress: function ( progress ) {
 
-			}
+		var message = 'Loaded ';
 
-			for ( i = 0; i < nUvLayers; i++ ) {
+		if ( progress.total ) {
 
-				geometry.faceVertexUvs[ i ] = [];
+			message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%';
 
-			}
 
-		}
+		} else {
 
-		offset = 0;
-		zLength = vertices.length;
+			message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB';
 
-		while ( offset < zLength ) {
+		}
 
-			vertex = new THREE.Vector3();
+		this.statusDomElement.innerHTML = message;
 
-			vertex.x = vertices[ offset ++ ] * scale;
-			vertex.y = vertices[ offset ++ ] * scale;
-			vertex.z = vertices[ offset ++ ] * scale;
+	},
 
-			geometry.vertices.push( vertex );
+	extractUrlBase: function ( url ) {
 
-		}
+		var parts = url.split( '/' );
 
-		offset = 0;
-		zLength = faces.length;
+		if ( parts.length === 1 ) return './';
 
-		while ( offset < zLength ) {
+		parts.pop();
 
-			type = faces[ offset ++ ];
+		return parts.join( '/' ) + '/';
 
+	},
 
-			isQuad              = isBitSet( type, 0 );
-			hasMaterial         = isBitSet( type, 1 );
-			hasFaceVertexUv     = isBitSet( type, 3 );
-			hasFaceNormal       = isBitSet( type, 4 );
-			hasFaceVertexNormal = isBitSet( type, 5 );
-			hasFaceColor	    = isBitSet( type, 6 );
-			hasFaceVertexColor  = isBitSet( type, 7 );
+	initMaterials: function ( materials, texturePath ) {
 
-			// console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
+		var array = [];
 
-			if ( isQuad ) {
+		for ( var i = 0; i < materials.length; ++ i ) {
 
-				faceA = new THREE.Face3();
-				faceA.a = faces[ offset ];
-				faceA.b = faces[ offset + 1 ];
-				faceA.c = faces[ offset + 3 ];
+			array[ i ] = this.createMaterial( materials[ i ], texturePath );
 
-				faceB = new THREE.Face3();
-				faceB.a = faces[ offset + 1 ];
-				faceB.b = faces[ offset + 2 ];
-				faceB.c = faces[ offset + 3 ];
+		}
 
-				offset += 4;
+		return array;
 
-				if ( hasMaterial ) {
+	},
 
-					materialIndex = faces[ offset ++ ];
-					faceA.materialIndex = materialIndex;
-					faceB.materialIndex = materialIndex;
+	needsTangents: function ( materials ) {
 
-				}
+		for ( var i = 0, il = materials.length; i < il; i ++ ) {
 
-				// to get face <=> uv index correspondence
+			var m = materials[ i ];
 
-				fi = geometry.faces.length;
+			if ( m instanceof THREE.ShaderMaterial ) return true;
 
-				if ( hasFaceVertexUv ) {
+		}
 
-					for ( i = 0; i < nUvLayers; i++ ) {
+		return false;
 
-						uvLayer = json.uvs[ i ];
+	},
 
-						geometry.faceVertexUvs[ i ][ fi ] = [];
-						geometry.faceVertexUvs[ i ][ fi + 1 ] = []
+	createMaterial: function ( m, texturePath ) {
 
-						for ( j = 0; j < 4; j ++ ) {
+		var scope = this;
 
-							uvIndex = faces[ offset ++ ];
+		function nearest_pow2( n ) {
 
-							u = uvLayer[ uvIndex * 2 ];
-							v = uvLayer[ uvIndex * 2 + 1 ];
+			var l = Math.log( n ) / Math.LN2;
+			return Math.pow( 2, Math.round(  l ) );
 
-							uv = new THREE.Vector2( u, v );
+		}
 
-							if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
-							if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
+		function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
 
-						}
+			var fullPath = texturePath + sourceFile;
 
-					}
+			var texture;
 
-				}
+			var loader = THREE.Loader.Handlers.get( fullPath );
 
-				if ( hasFaceNormal ) {
+			if ( loader !== null ) {
 
-					normalIndex = faces[ offset ++ ] * 3;
+				texture = loader.load( fullPath );
 
-					faceA.normal.set(
-						normals[ normalIndex ++ ],
-						normals[ normalIndex ++ ],
-						normals[ normalIndex ]
-					);
+			} else {
 
-					faceB.normal.copy( faceA.normal );
+				texture = new THREE.Texture();
 
-				}
+				loader = scope.imageLoader;
+				loader.crossOrigin = scope.crossOrigin;
+				loader.load( fullPath, function ( image ) {
 
-				if ( hasFaceVertexNormal ) {
+					if ( THREE.Math.isPowerOfTwo( image.width ) === false ||
+						 THREE.Math.isPowerOfTwo( image.height ) === false ) {
 
-					for ( i = 0; i < 4; i++ ) {
+						var width = nearest_pow2( image.width );
+						var height = nearest_pow2( image.height );
 
-						normalIndex = faces[ offset ++ ] * 3;
+						var canvas = document.createElement( 'canvas' );
+						canvas.width = width;
+						canvas.height = height;
 
-						normal = new THREE.Vector3(
-							normals[ normalIndex ++ ],
-							normals[ normalIndex ++ ],
-							normals[ normalIndex ]
-						);
+						var context = canvas.getContext( '2d' );
+						context.drawImage( image, 0, 0, width, height );
 
+						texture.image = canvas;
 
-						if ( i !== 2 ) faceA.vertexNormals.push( normal );
-						if ( i !== 0 ) faceB.vertexNormals.push( normal );
+					} else {
+
+						texture.image = image;
 
 					}
 
-				}
+					texture.needsUpdate = true;
 
+				} );
 
-				if ( hasFaceColor ) {
+			}
 
-					colorIndex = faces[ offset ++ ];
-					hex = colors[ colorIndex ];
+			texture.sourceFile = sourceFile;
 
-					faceA.color.setHex( hex );
-					faceB.color.setHex( hex );
+			if ( repeat ) {
 
-				}
+				texture.repeat.set( repeat[ 0 ], repeat[ 1 ] );
 
+				if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+				if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
 
-				if ( hasFaceVertexColor ) {
+			}
 
-					for ( i = 0; i < 4; i++ ) {
+			if ( offset ) {
 
-						colorIndex = faces[ offset ++ ];
-						hex = colors[ colorIndex ];
+				texture.offset.set( offset[ 0 ], offset[ 1 ] );
 
-						if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
-						if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );
+			}
 
-					}
+			if ( wrap ) {
 
+				var wrapMap = {
+					'repeat': THREE.RepeatWrapping,
+					'mirror': THREE.MirroredRepeatWrapping
 				}
 
-				geometry.faces.push( faceA );
-				geometry.faces.push( faceB );
+				if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ];
+				if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ];
 
-			} else {
+			}
 
-				face = new THREE.Face3();
-				face.a = faces[ offset ++ ];
-				face.b = faces[ offset ++ ];
-				face.c = faces[ offset ++ ];
+			if ( anisotropy ) {
 
-				if ( hasMaterial ) {
+				texture.anisotropy = anisotropy;
 
-					materialIndex = faces[ offset ++ ];
-					face.materialIndex = materialIndex;
+			}
 
-				}
+			where[ name ] = texture;
 
-				// to get face <=> uv index correspondence
+		}
 
-				fi = geometry.faces.length;
+		function rgb2hex( rgb ) {
 
-				if ( hasFaceVertexUv ) {
+			return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255;
 
-					for ( i = 0; i < nUvLayers; i++ ) {
+		}
 
-						uvLayer = json.uvs[ i ];
+		// defaults
 
-						geometry.faceVertexUvs[ i ][ fi ] = [];
+		var mtype = 'MeshLambertMaterial';
+		var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false };
 
-						for ( j = 0; j < 3; j ++ ) {
-
-							uvIndex = faces[ offset ++ ];
-
-							u = uvLayer[ uvIndex * 2 ];
-							v = uvLayer[ uvIndex * 2 + 1 ];
-
-							uv = new THREE.Vector2( u, v );
-
-							geometry.faceVertexUvs[ i ][ fi ].push( uv );
-
-						}
-
-					}
-
-				}
-
-				if ( hasFaceNormal ) {
-
-					normalIndex = faces[ offset ++ ] * 3;
-
-					face.normal.set(
-						normals[ normalIndex ++ ],
-						normals[ normalIndex ++ ],
-						normals[ normalIndex ]
-					);
-
-				}
-
-				if ( hasFaceVertexNormal ) {
-
-					for ( i = 0; i < 3; i++ ) {
-
-						normalIndex = faces[ offset ++ ] * 3;
-
-						normal = new THREE.Vector3(
-							normals[ normalIndex ++ ],
-							normals[ normalIndex ++ ],
-							normals[ normalIndex ]
-						);
-
-						face.vertexNormals.push( normal );
-
-					}
-
-				}
-
-
-				if ( hasFaceColor ) {
-
-					colorIndex = faces[ offset ++ ];
-					face.color.setHex( colors[ colorIndex ] );
-
-				}
-
-
-				if ( hasFaceVertexColor ) {
-
-					for ( i = 0; i < 3; i++ ) {
-
-						colorIndex = faces[ offset ++ ];
-						face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );
-
-					}
+		// parameters from model file
 
-				}
+		if ( m.shading ) {
 
-				geometry.faces.push( face );
+			var shading = m.shading.toLowerCase();
 
-			}
+			if ( shading === 'phong' ) mtype = 'MeshPhongMaterial';
+			else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial';
 
 		}
 
-	};
-
-	function parseSkin() {
-
-		var i, l, x, y, z, w, a, b, c, d;
-
-		if ( json.skinWeights ) {
+		if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) {
 
-			for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) {
+			mpars.blending = THREE[ m.blending ];
 
-				x = json.skinWeights[ i     ];
-				y = json.skinWeights[ i + 1 ];
-				z = 0;
-				w = 0;
+		}
 
-				geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
+		if ( m.transparent !== undefined ) {
 
-			}
+			mpars.transparent = m.transparent;
 
 		}
 
-		if ( json.skinIndices ) {
+		if ( m.opacity !== undefined && m.opacity < 1.0 ) {
 
-			for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) {
+			mpars.transparent = true;
 
-				a = json.skinIndices[ i     ];
-				b = json.skinIndices[ i + 1 ];
-				c = 0;
-				d = 0;
+		}
 
-				geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
+		if ( m.depthTest !== undefined ) {
 
-			}
+			mpars.depthTest = m.depthTest;
 
 		}
 
-		geometry.bones = json.bones;
-		// could change this to json.animations[0] or remove completely
-		geometry.animation = json.animation;
-		geometry.animations = json.animations;
-	};
-
-	function parseMorphing( scale ) {
+		if ( m.depthWrite !== undefined ) {
 
-		if ( json.morphTargets !== undefined ) {
+			mpars.depthWrite = m.depthWrite;
 
-			var i, l, v, vl, dstVertices, srcVertices;
+		}
 
-			for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
+		if ( m.visible !== undefined ) {
 
-				geometry.morphTargets[ i ] = {};
-				geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
-				geometry.morphTargets[ i ].vertices = [];
+			mpars.visible = m.visible;
 
-				dstVertices = geometry.morphTargets[ i ].vertices;
-				srcVertices = json.morphTargets [ i ].vertices;
+		}
 
-				for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
+		if ( m.flipSided !== undefined ) {
 
-					var vertex = new THREE.Vector3();
-					vertex.x = srcVertices[ v ] * scale;
-					vertex.y = srcVertices[ v + 1 ] * scale;
-					vertex.z = srcVertices[ v + 2 ] * scale;
+			mpars.side = THREE.BackSide;
 
-					dstVertices.push( vertex );
+		}
 
-				}
+		if ( m.doubleSided !== undefined ) {
 
-			}
+			mpars.side = THREE.DoubleSide;
 
 		}
 
-		if ( json.morphColors !== undefined ) {
+		if ( m.wireframe !== undefined ) {
 
-			var i, l, c, cl, dstColors, srcColors, color;
+			mpars.wireframe = m.wireframe;
 
-			for ( i = 0, l = json.morphColors.length; i < l; i++ ) {
+		}
 
-				geometry.morphColors[ i ] = {};
-				geometry.morphColors[ i ].name = json.morphColors[ i ].name;
-				geometry.morphColors[ i ].colors = [];
+		if ( m.vertexColors !== undefined ) {
 
-				dstColors = geometry.morphColors[ i ].colors;
-				srcColors = json.morphColors [ i ].colors;
+			if ( m.vertexColors === 'face' ) {
 
-				for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) {
+				mpars.vertexColors = THREE.FaceColors;
 
-					color = new THREE.Color( 0xffaa00 );
-					color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );
-					dstColors.push( color );
+			} else if ( m.vertexColors ) {
 
-				}
+				mpars.vertexColors = THREE.VertexColors;
 
 			}
 
 		}
 
-	};
-
-	if ( json.materials === undefined ) {
-
-		return { geometry: geometry };
+		// colors
 
-	} else {
+		if ( m.colorDiffuse ) {
 
-		var materials = this.initMaterials( json.materials, texturePath );
+			mpars.color = rgb2hex( m.colorDiffuse );
 
-		if ( this.needsTangents( materials ) ) {
+		} else if ( m.DbgColor ) {
 
-			geometry.computeTangents();
+			mpars.color = m.DbgColor;
 
 		}
 
-		return { geometry: geometry, materials: materials };
-
-	}
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.LoadingManager = function ( onLoad, onProgress, onError ) {
-
-	var scope = this;
-
-	var loaded = 0, total = 0;
-
-	this.onLoad = onLoad;
-	this.onProgress = onProgress;
-	this.onError = onError;
-
-	this.itemStart = function ( url ) {
-
-		total ++;
-
-	};
-
-	this.itemEnd = function ( url ) {
-
-		loaded ++;
-
-		if ( scope.onProgress !== undefined ) {
+		if ( m.colorSpecular ) {
 
-			scope.onProgress( url, loaded, total );
+			mpars.specular = rgb2hex( m.colorSpecular );
 
 		}
 
-		if ( loaded === total && scope.onLoad !== undefined ) {
+		if ( m.colorEmissive ) {
 
-			scope.onLoad();
+			mpars.emissive = rgb2hex( m.colorEmissive );
 
 		}
 
-	};
-
-};
-
-THREE.DefaultLoadingManager = new THREE.LoadingManager();
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.BufferGeometryLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.BufferGeometryLoader.prototype = {
-
-	constructor: THREE.BufferGeometryLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader();
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( text ) {
+		// modifiers
 
-			onLoad( scope.parse( JSON.parse( text ) ) );
+		if ( m.transparency !== undefined ) {
 
-		} );
+			console.warn( 'THREE.Loader: transparency has been renamed to opacity' );
+			m.opacity = m.transparency;
 
-	},
+		}
 
-	setCrossOrigin: function ( value ) {
+		if ( m.opacity !== undefined ) {
 
-		this.crossOrigin = value;
+			mpars.opacity = m.opacity;
 
-	},
+		}
 
-	parse: function ( json ) {
+		if ( m.specularCoef ) {
 
-		var geometry = new THREE.BufferGeometry();
+			mpars.shininess = m.specularCoef;
 
-		var attributes = json.attributes;
-		var offsets = json.offsets;
-		var boundingSphere = json.boundingSphere;
+		}
 
-		for ( var key in attributes ) {
+		// textures
 
-			var attribute = attributes[ key ];
+		if ( m.mapDiffuse && texturePath ) {
 
-			geometry.attributes[ key ] = {
-				itemSize: attribute.itemSize,
-				array: new self[ attribute.type ]( attribute.array )
-			}
+			create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
 
 		}
 
-		if ( offsets !== undefined ) {
+		if ( m.mapLight && texturePath ) {
 
-			geometry.offsets = JSON.parse( JSON.stringify( offsets ) );
+			create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
 
 		}
 
-		if ( boundingSphere !== undefined ) {
+		if ( m.mapBump && texturePath ) {
 
-			geometry.boundingSphere = new THREE.Sphere(
-				new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ),
-				boundingSphere.radius
-			);
+			create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
 
 		}
 
-		return geometry;
-
-	}
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.GeometryLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
+		if ( m.mapNormal && texturePath ) {
 
-THREE.GeometryLoader.prototype = {
+			create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
 
-	constructor: THREE.GeometryLoader,
+		}
 
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader();
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( text ) {
-
-			onLoad( scope.parse( JSON.parse( text ) ) );
-
-		} );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	parse: function ( json ) {
-
-
-
-	}
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.MaterialLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.MaterialLoader.prototype = {
-
-	constructor: THREE.MaterialLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader();
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( text ) {
-
-			onLoad( scope.parse( JSON.parse( text ) ) );
-
-		} );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	parse: function ( json ) {
-
-		var material = new THREE[ json.type ];
-
-		if ( json.color !== undefined ) material.color.setHex( json.color );
-		if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient );
-		if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
-		if ( json.specular !== undefined ) material.specular.setHex( json.specular );
-		if ( json.shininess !== undefined ) material.shininess = json.shininess;
-		if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
-		if ( json.blending !== undefined ) material.blending = json.blending;
-		if ( json.opacity !== undefined ) material.opacity = json.opacity;
-		if ( json.transparent !== undefined ) material.transparent = json.transparent;
-		if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
-
-		if ( json.materials !== undefined ) {
-
-			for ( var i = 0, l = json.materials.length; i < l; i ++ ) {
-
-				material.materials.push( this.parse( json.materials[ i ] ) );
-
-			}
-
-		}
-
-		return material;
-
-	}
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.ObjectLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.ObjectLoader.prototype = {
-
-	constructor: THREE.ObjectLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader( scope.manager );
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( text ) {
-
-			onLoad( scope.parse( JSON.parse( text ) ) );
-
-		} );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	parse: function ( json ) {
-
-		var geometries = this.parseGeometries( json.geometries );
-		var materials = this.parseMaterials( json.materials );
-		var object = this.parseObject( json.object, geometries, materials );
-
-		return object;
-
-	},
-
-	parseGeometries: function ( json ) {
-
-		var geometries = {};
-
-		if ( json !== undefined ) {
-
-			var geometryLoader = new THREE.JSONLoader();
-			var bufferGeometryLoader = new THREE.BufferGeometryLoader();
-
-			for ( var i = 0, l = json.length; i < l; i ++ ) {
-
-				var geometry;
-				var data = json[ i ];
-
-				switch ( data.type ) {
-
-					case 'PlaneGeometry':
-
-						geometry = new THREE.PlaneGeometry(
-							data.width,
-							data.height,
-							data.widthSegments,
-							data.heightSegments
-						);
-
-						break;
-
-					case 'CircleGeometry':
-
-						geometry = new THREE.CircleGeometry(
-							data.radius,
-							data.segments
-						);
-
-						break;
-
-					case 'CubeGeometry':
-
-						geometry = new THREE.CubeGeometry(
-							data.width,
-							data.height,
-							data.depth,
-							data.widthSegments,
-							data.heightSegments,
-							data.depthSegments
-						);
-
-						break;
-
-					case 'CylinderGeometry':
-
-						geometry = new THREE.CylinderGeometry(
-							data.radiusTop,
-							data.radiusBottom,
-							data.height,
-							data.radiusSegments,
-							data.heightSegments,
-							data.openEnded
-						);
-
-						break;
-
-					case 'SphereGeometry':
-
-						geometry = new THREE.SphereGeometry(
-							data.radius,
-							data.widthSegments,
-							data.heightSegments,
-							data.phiStart,
-							data.phiLength,
-							data.thetaStart,
-							data.thetaLength
-						);
-
-						break;
-
-					case 'IcosahedronGeometry':
-
-						geometry = new THREE.IcosahedronGeometry(
-							data.radius,
-							data.detail
-						);
-
-						break;
-
-					case 'TorusGeometry':
-
-						geometry = new THREE.TorusGeometry(
-							data.radius,
-							data.tube,
-							data.radialSegments,
-							data.tubularSegments,
-							data.arc
-						);
-
-						break;
-
-					case 'TorusKnotGeometry':
-
-						geometry = new THREE.TorusKnotGeometry(
-							data.radius,
-							data.tube,
-							data.radialSegments,
-							data.tubularSegments,
-							data.p,
-							data.q,
-							data.heightScale
-						);
-
-						break;
-
-					case 'BufferGeometry':
-
-						geometry = bufferGeometryLoader.parse( data.data );
-
-						break;
-
-					case 'Geometry':
-
-						geometry = geometryLoader.parse( data.data ).geometry;
-
-						break;
-
-				}
-
-				geometry.uuid = data.uuid;
-
-				if ( data.name !== undefined ) geometry.name = data.name;
-
-				geometries[ data.uuid ] = geometry;
-
-			}
-
-		}
-
-		return geometries;
-
-	},
-
-	parseMaterials: function ( json ) {
-
-		var materials = {};
-
-		if ( json !== undefined ) {
-
-			var loader = new THREE.MaterialLoader();
-
-			for ( var i = 0, l = json.length; i < l; i ++ ) {
-
-				var data = json[ i ];
-				var material = loader.parse( data );
-
-				material.uuid = data.uuid;
-
-				if ( data.name !== undefined ) material.name = data.name;
-
-				materials[ data.uuid ] = material;
-
-			}
-
-		}
-
-		return materials;
-
-	},
-
-	parseObject: function () {
-
-		var matrix = new THREE.Matrix4();
-
-		return function ( data, geometries, materials ) {
-
-			var object;
-
-			switch ( data.type ) {
-
-				case 'Scene':
-
-					object = new THREE.Scene();
-
-					break;
-
-				case 'PerspectiveCamera':
-
-					object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
-
-					break;
-
-				case 'OrthographicCamera':
-
-					object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
-
-					break;
-
-				case 'AmbientLight':
-
-					object = new THREE.AmbientLight( data.color );
-
-					break;
-
-				case 'DirectionalLight':
-
-					object = new THREE.DirectionalLight( data.color, data.intensity );
-
-					break;
-
-				case 'PointLight':
-
-					object = new THREE.PointLight( data.color, data.intensity, data.distance );
-
-					break;
-
-				case 'SpotLight':
-
-					object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent );
-
-					break;
-
-				case 'HemisphereLight':
-
-					object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity );
-
-					break;
-
-				case 'Mesh':
-
-					var geometry = geometries[ data.geometry ];
-					var material = materials[ data.material ];
-
-					if ( geometry === undefined ) {
-
-						console.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry );
-
-					}
-
-					if ( material === undefined ) {
-
-						console.error( 'THREE.ObjectLoader: Undefined material ' + data.material );
-
-					}
-
-					object = new THREE.Mesh( geometry, material );
-
-					break;
-
-				default:
-
-					object = new THREE.Object3D();
-
-			}
-
-			object.uuid = data.uuid;
-
-			if ( data.name !== undefined ) object.name = data.name;
-			if ( data.matrix !== undefined ) {
-
-				matrix.fromArray( data.matrix );
-				matrix.decompose( object.position, object.quaternion, object.scale );
-
-			} else {
-
-				if ( data.position !== undefined ) object.position.fromArray( data.position );
-				if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
-				if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
-
-			}
-
-			if ( data.visible !== undefined ) object.visible = data.visible;
-			if ( data.userData !== undefined ) object.userData = data.userData;
-
-			if ( data.children !== undefined ) {
-
-				for ( var child in data.children ) {
-
-					object.add( this.parseObject( data.children[ child ], geometries, materials ) );
-
-				}
-
-			}
-
-			return object;
-
-		}
-
-	}()
-
-};
-
-/**
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.SceneLoader = function () {
-
-	this.onLoadStart = function () {};
-	this.onLoadProgress = function() {};
-	this.onLoadComplete = function () {};
-
-	this.callbackSync = function () {};
-	this.callbackProgress = function () {};
-
-	this.geometryHandlers = {};
-	this.hierarchyHandlers = {};
-
-	this.addGeometryHandler( "ascii", THREE.JSONLoader );
-
-};
-
-THREE.SceneLoader.prototype = {
-
-	constructor: THREE.SceneLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader( scope.manager );
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( text ) {
-
-			scope.parse( JSON.parse( text ), onLoad, url );
-
-		} );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	addGeometryHandler: function ( typeID, loaderClass ) {
-
-		this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass };
-
-	},
-
-	addHierarchyHandler: function ( typeID, loaderClass ) {
-
-		this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass };
-
-	},
-
-	parse: function ( json, callbackFinished, url ) {
-
-		var scope = this;
-
-		var urlBase = THREE.Loader.prototype.extractUrlBase( url );
-
-		var geometry, material, camera, fog,
-			texture, images, color,
-			light, hex, intensity,
-			counter_models, counter_textures,
-			total_models, total_textures,
-			result;
-
-		var target_array = [];
-
-		var data = json;
-
-		// async geometry loaders
-
-		for ( var typeID in this.geometryHandlers ) {
-
-			var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ];
-			this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
-
-		}
-
-		// async hierachy loaders
-
-		for ( var typeID in this.hierarchyHandlers ) {
-
-			var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ];
-			this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
-
-		}
-
-		counter_models = 0;
-		counter_textures = 0;
-
-		result = {
-
-			scene: new THREE.Scene(),
-			geometries: {},
-			face_materials: {},
-			materials: {},
-			textures: {},
-			objects: {},
-			cameras: {},
-			lights: {},
-			fogs: {},
-			empties: {},
-			groups: {}
-
-		};
-
-		if ( data.transform ) {
-
-			var position = data.transform.position,
-				rotation = data.transform.rotation,
-				scale = data.transform.scale;
-
-			if ( position ) {
-
-				result.scene.position.fromArray( position );
-
-			}
-
-			if ( rotation ) {
-
-				result.scene.rotation.fromArray( rotation );
-
-			}
-
-			if ( scale ) {
-
-				result.scene.scale.fromArray( scale );
-
-			}
-
-			if ( position || rotation || scale ) {
-
-				result.scene.updateMatrix();
-				result.scene.updateMatrixWorld();
-
-			}
-
-		}
-
-		function get_url( source_url, url_type ) {
-
-			if ( url_type == "relativeToHTML" ) {
-
-				return source_url;
-
-			} else {
-
-				return urlBase + "/" + source_url;
-
-			}
-
-		};
-
-		// toplevel loader function, delegates to handle_children
-
-		function handle_objects() {
-
-			handle_children( result.scene, data.objects );
-
-		}
-
-		// handle all the children from the loaded json and attach them to given parent
-
-		function handle_children( parent, children ) {
-
-			var mat, dst, pos, rot, scl, quat;
-
-			for ( var objID in children ) {
-
-				// check by id if child has already been handled,
-				// if not, create new object
-
-				var object = result.objects[ objID ];
-				var objJSON = children[ objID ];
-
-				if ( object === undefined ) {
-
-					// meshes
-
-					if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
-
-						if ( objJSON.loading === undefined ) {
-
-							var reservedTypes = {
-								"type": 1, "url": 1, "material": 1,
-								"position": 1, "rotation": 1, "scale" : 1,
-								"visible": 1, "children": 1, "userData": 1,
-								"skin": 1, "morph": 1, "mirroredLoop": 1, "duration": 1
-							};
-
-							var loaderParameters = {};
-
-							for ( var parType in objJSON ) {
-
-								if ( ! ( parType in reservedTypes ) ) {
-
-									loaderParameters[ parType ] = objJSON[ parType ];
-
-								}
-
-							}
-
-							material = result.materials[ objJSON.material ];
-
-							objJSON.loading = true;
-
-							var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ];
-
-							// ColladaLoader
-
-							if ( loader.options ) {
-
-								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
-
-							// UTF8Loader
-							// OBJLoader
-
-							} else {
-
-								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ), loaderParameters );
-
-							}
-
-						}
-
-					} else if ( objJSON.geometry !== undefined ) {
-
-						geometry = result.geometries[ objJSON.geometry ];
-
-						// geometry already loaded
-
-						if ( geometry ) {
-
-							var needsTangents = false;
-
-							material = result.materials[ objJSON.material ];
-							needsTangents = material instanceof THREE.ShaderMaterial;
-
-							pos = objJSON.position;
-							rot = objJSON.rotation;
-							scl = objJSON.scale;
-							mat = objJSON.matrix;
-							quat = objJSON.quaternion;
-
-							// use materials from the model file
-							// if there is no material specified in the object
-
-							if ( ! objJSON.material ) {
-
-								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
-
-							}
-
-							// use materials from the model file
-							// if there is just empty face material
-							// (must create new material as each model has its own face material)
-
-							if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) {
-
-								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
-
-							}
-
-							if ( material instanceof THREE.MeshFaceMaterial ) {
-
-								for ( var i = 0; i < material.materials.length; i ++ ) {
-
-									needsTangents = needsTangents || ( material.materials[ i ] instanceof THREE.ShaderMaterial );
-
-								}
-
-							}
-
-							if ( needsTangents ) {
-
-								geometry.computeTangents();
-
-							}
-
-							if ( objJSON.skin ) {
-
-								object = new THREE.SkinnedMesh( geometry, material );
-
-							} else if ( objJSON.morph ) {
-
-								object = new THREE.MorphAnimMesh( geometry, material );
-
-								if ( objJSON.duration !== undefined ) {
-
-									object.duration = objJSON.duration;
-
-								}
-
-								if ( objJSON.time !== undefined ) {
-
-									object.time = objJSON.time;
-
-								}
-
-								if ( objJSON.mirroredLoop !== undefined ) {
-
-									object.mirroredLoop = objJSON.mirroredLoop;
-
-								}
-
-								if ( material.morphNormals ) {
-
-									geometry.computeMorphNormals();
-
-								}
-
-							} else {
-
-								object = new THREE.Mesh( geometry, material );
-
-							}
-
-							object.name = objID;
-
-							if ( mat ) {
-
-								object.matrixAutoUpdate = false;
-								object.matrix.set(
-									mat[0],  mat[1],  mat[2],  mat[3],
-									mat[4],  mat[5],  mat[6],  mat[7],
-									mat[8],  mat[9],  mat[10], mat[11],
-									mat[12], mat[13], mat[14], mat[15]
-								);
-
-							} else {
-
-								object.position.fromArray( pos );
-
-								if ( quat ) {
-
-									object.quaternion.fromArray( quat );
-
-								} else {
-
-									object.rotation.fromArray( rot );
-
-								}
-
-								object.scale.fromArray( scl );
-
-							}
-
-							object.visible = objJSON.visible;
-							object.castShadow = objJSON.castShadow;
-							object.receiveShadow = objJSON.receiveShadow;
-
-							parent.add( object );
-
-							result.objects[ objID ] = object;
-
-						}
-
-					// lights
-
-					} else if ( objJSON.type === "DirectionalLight" || objJSON.type === "PointLight" || objJSON.type === "AmbientLight" ) {
-
-						hex = ( objJSON.color !== undefined ) ? objJSON.color : 0xffffff;
-						intensity = ( objJSON.intensity !== undefined ) ? objJSON.intensity : 1;
-
-						if ( objJSON.type === "DirectionalLight" ) {
-
-							pos = objJSON.direction;
-
-							light = new THREE.DirectionalLight( hex, intensity );
-							light.position.fromArray( pos );
-
-							if ( objJSON.target ) {
-
-								target_array.push( { "object": light, "targetName" : objJSON.target } );
-
-								// kill existing default target
-								// otherwise it gets added to scene when parent gets added
-
-								light.target = null;
-
-							}
-
-						} else if ( objJSON.type === "PointLight" ) {
-
-							pos = objJSON.position;
-							dst = objJSON.distance;
-
-							light = new THREE.PointLight( hex, intensity, dst );
-							light.position.fromArray( pos );
-
-						} else if ( objJSON.type === "AmbientLight" ) {
-
-							light = new THREE.AmbientLight( hex );
-
-						}
-
-						parent.add( light );
-
-						light.name = objID;
-						result.lights[ objID ] = light;
-						result.objects[ objID ] = light;
-
-					// cameras
-
-					} else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) {
-
-						pos = objJSON.position;
-						rot = objJSON.rotation;
-						quat = objJSON.quaternion;
-
-						if ( objJSON.type === "PerspectiveCamera" ) {
-
-							camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far );
-
-						} else if ( objJSON.type === "OrthographicCamera" ) {
-
-							camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far );
-
-						}
-
-						camera.name = objID;
-						camera.position.fromArray( pos );
-
-						if ( quat !== undefined ) {
-
-							camera.quaternion.fromArray( quat );
-
-						} else if ( rot !== undefined ) {
-
-							camera.rotation.fromArray( rot );
-
-						}
-
-						parent.add( camera );
-
-						result.cameras[ objID ] = camera;
-						result.objects[ objID ] = camera;
-
-					// pure Object3D
-
-					} else {
-
-						pos = objJSON.position;
-						rot = objJSON.rotation;
-						scl = objJSON.scale;
-						quat = objJSON.quaternion;
-
-						object = new THREE.Object3D();
-						object.name = objID;
-						object.position.fromArray( pos );
-
-						if ( quat ) {
-
-							object.quaternion.fromArray( quat );
-
-						} else {
-
-							object.rotation.fromArray( rot );
-
-						}
-
-						object.scale.fromArray( scl );
-						object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
-
-						parent.add( object );
-
-						result.objects[ objID ] = object;
-						result.empties[ objID ] = object;
-
-					}
-
-					if ( object ) {
-
-						if ( objJSON.userData !== undefined ) {
-
-							for ( var key in objJSON.userData ) {
-
-								var value = objJSON.userData[ key ];
-								object.userData[ key ] = value;
-
-							}
-
-						}
-
-						if ( objJSON.groups !== undefined ) {
-
-							for ( var i = 0; i < objJSON.groups.length; i ++ ) {
-
-								var groupID = objJSON.groups[ i ];
-
-								if ( result.groups[ groupID ] === undefined ) {
-
-									result.groups[ groupID ] = [];
-
-								}
-
-								result.groups[ groupID ].push( objID );
-
-							}
-
-						}
-
-					}
-
-				}
-
-				if ( object !== undefined && objJSON.children !== undefined ) {
-
-					handle_children( object, objJSON.children );
-
-				}
-
-			}
-
-		};
-
-		function handle_mesh( geo, mat, id ) {
-
-			result.geometries[ id ] = geo;
-			result.face_materials[ id ] = mat;
-			handle_objects();
-
-		};
-
-		function handle_hierarchy( node, id, parent, material, obj ) {
-
-			var p = obj.position;
-			var r = obj.rotation;
-			var q = obj.quaternion;
-			var s = obj.scale;
-
-			node.position.fromArray( p );
-
-			if ( q ) {
-
-				node.quaternion.fromArray( q );
-
-			} else {
-
-				node.rotation.fromArray( r );
-
-			}
-
-			node.scale.fromArray( s );
-
-			// override children materials
-			// if object material was specified in JSON explicitly
-
-			if ( material ) {
-
-				node.traverse( function ( child ) {
-
-					child.material = material;
-
-				} );
-
-			}
-
-			// override children visibility
-			// with root node visibility as specified in JSON
-
-			var visible = ( obj.visible !== undefined ) ? obj.visible : true;
-
-			node.traverse( function ( child ) {
-
-				child.visible = visible;
-
-			} );
-
-			parent.add( node );
-
-			node.name = id;
-
-			result.objects[ id ] = node;
-			handle_objects();
-
-		};
-
-		function create_callback_geometry( id ) {
-
-			return function ( geo, mat ) {
-
-				geo.name = id;
-
-				handle_mesh( geo, mat, id );
-
-				counter_models -= 1;
-
-				scope.onLoadComplete();
-
-				async_callback_gate();
-
-			}
-
-		};
-
-		function create_callback_hierachy( id, parent, material, obj ) {
-
-			return function ( event ) {
-
-				var result;
-
-				// loaders which use EventDispatcher
-
-				if ( event.content ) {
-
-					result = event.content;
-
-				// ColladaLoader
-
-				} else if ( event.dae ) {
-
-					result = event.scene;
-
-
-				// UTF8Loader
-
-				} else {
-
-					result = event;
-
-				}
-
-				handle_hierarchy( result, id, parent, material, obj );
-
-				counter_models -= 1;
-
-				scope.onLoadComplete();
-
-				async_callback_gate();
-
-			}
-
-		};
-
-		function create_callback_embed( id ) {
-
-			return function ( geo, mat ) {
-
-				geo.name = id;
-
-				result.geometries[ id ] = geo;
-				result.face_materials[ id ] = mat;
-
-			}
-
-		};
-
-		function async_callback_gate() {
-
-			var progress = {
-
-				totalModels : total_models,
-				totalTextures : total_textures,
-				loadedModels : total_models - counter_models,
-				loadedTextures : total_textures - counter_textures
-
-			};
-
-			scope.callbackProgress( progress, result );
-
-			scope.onLoadProgress();
-
-			if ( counter_models === 0 && counter_textures === 0 ) {
-
-				finalize();
-				callbackFinished( result );
-
-			}
-
-		};
-
-		function finalize() {
-
-			// take care of targets which could be asynchronously loaded objects
-
-			for ( var i = 0; i < target_array.length; i ++ ) {
-
-				var ta = target_array[ i ];
-
-				var target = result.objects[ ta.targetName ];
-
-				if ( target ) {
-
-					ta.object.target = target;
-
-				} else {
-
-					// if there was error and target of specified name doesn't exist in the scene file
-					// create instead dummy target
-					// (target must be added to scene explicitly as parent is already added)
-
-					ta.object.target = new THREE.Object3D();
-					result.scene.add( ta.object.target );
-
-				}
-
-				ta.object.target.userData.targetInverse = ta.object;
-
-			}
-
-		};
-
-		var callbackTexture = function ( count ) {
-
-			counter_textures -= count;
-			async_callback_gate();
-
-			scope.onLoadComplete();
-
-		};
-
-		// must use this instead of just directly calling callbackTexture
-		// because of closure in the calling context loop
-
-		var generateTextureCallback = function ( count ) {
-
-			return function () {
-
-				callbackTexture( count );
-
-			};
-
-		};
-
-		function traverse_json_hierarchy( objJSON, callback ) {
-
-			callback( objJSON );
-
-			if ( objJSON.children !== undefined ) {
-
-				for ( var objChildID in objJSON.children ) {
-
-					traverse_json_hierarchy( objJSON.children[ objChildID ], callback );
-
-				}
-
-			}
-
-		};
-
-		// first go synchronous elements
-
-		// fogs
-
-		var fogID, fogJSON;
-
-		for ( fogID in data.fogs ) {
-
-			fogJSON = data.fogs[ fogID ];
-
-			if ( fogJSON.type === "linear" ) {
-
-				fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far );
-
-			} else if ( fogJSON.type === "exp2" ) {
-
-				fog = new THREE.FogExp2( 0x000000, fogJSON.density );
-
-			}
-
-			color = fogJSON.color;
-			fog.color.setRGB( color[0], color[1], color[2] );
-
-			result.fogs[ fogID ] = fog;
-
-		}
-
-		// now come potentially asynchronous elements
-
-		// geometries
-
-		// count how many geometries will be loaded asynchronously
-
-		var geoID, geoJSON;
-
-		for ( geoID in data.geometries ) {
-
-			geoJSON = data.geometries[ geoID ];
-
-			if ( geoJSON.type in this.geometryHandlers ) {
-
-				counter_models += 1;
-
-				scope.onLoadStart();
-
-			}
-
-		}
-
-		// count how many hierarchies will be loaded asynchronously
-
-		for ( var objID in data.objects ) {
-
-			traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) {
-
-				if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
-
-					counter_models += 1;
-
-					scope.onLoadStart();
-
-				}
-
-			});
-
-		}
-
-		total_models = counter_models;
-
-		for ( geoID in data.geometries ) {
-
-			geoJSON = data.geometries[ geoID ];
-
-			if ( geoJSON.type === "cube" ) {
-
-				geometry = new THREE.CubeGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "plane" ) {
-
-				geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "sphere" ) {
-
-				geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "cylinder" ) {
-
-				geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "torus" ) {
-
-				geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "icosahedron" ) {
-
-				geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type in this.geometryHandlers ) {
-
-				var loaderParameters = {};
-
-				for ( var parType in geoJSON ) {
-
-					if ( parType !== "type" && parType !== "url" ) {
-
-						loaderParameters[ parType ] = geoJSON[ parType ];
-
-					}
-
-				}
-
-				var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ];
-				loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ), loaderParameters );
-
-			} else if ( geoJSON.type === "embedded" ) {
-
-				var modelJson = data.embeds[ geoJSON.id ],
-					texture_path = "";
-
-				// pass metadata along to jsonLoader so it knows the format version
-
-				modelJson.metadata = data.metadata;
-
-				if ( modelJson ) {
-
-					var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ];
-					var model = jsonLoader.parse( modelJson, texture_path );
-					create_callback_embed( geoID )( model.geometry, model.materials );
-
-				}
-
-			}
-
-		}
-
-		// textures
-
-		// count how many textures will be loaded asynchronously
-
-		var textureID, textureJSON;
-
-		for ( textureID in data.textures ) {
-
-			textureJSON = data.textures[ textureID ];
-
-			if ( textureJSON.url instanceof Array ) {
-
-				counter_textures += textureJSON.url.length;
-
-				for( var n = 0; n < textureJSON.url.length; n ++ ) {
-
-					scope.onLoadStart();
-
-				}
-
-			} else {
-
-				counter_textures += 1;
-
-				scope.onLoadStart();
-
-			}
-
-		}
-
-		total_textures = counter_textures;
-
-		for ( textureID in data.textures ) {
-
-			textureJSON = data.textures[ textureID ];
-
-			if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) {
-
-				textureJSON.mapping = new THREE[ textureJSON.mapping ]();
-
-			}
-
-			if ( textureJSON.url instanceof Array ) {
-
-				var count = textureJSON.url.length;
-				var url_array = [];
-
-				for( var i = 0; i < count; i ++ ) {
-
-					url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType );
-
-				}
-
-				var isCompressed = /\.dds$/i.test( url_array[ 0 ] );
-
-				if ( isCompressed ) {
-
-					texture = THREE.ImageUtils.loadCompressedTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
-
-				} else {
-
-					texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
-
-				}
-
-			} else {
-
-				var isCompressed = /\.dds$/i.test( textureJSON.url );
-				var fullUrl = get_url( textureJSON.url, data.urlBaseType );
-				var textureCallback = generateTextureCallback( 1 );
-
-				if ( isCompressed ) {
-
-					texture = THREE.ImageUtils.loadCompressedTexture( fullUrl, textureJSON.mapping, textureCallback );
-
-				} else {
-
-					texture = THREE.ImageUtils.loadTexture( fullUrl, textureJSON.mapping, textureCallback );
-
-				}
-
-				if ( THREE[ textureJSON.minFilter ] !== undefined )
-					texture.minFilter = THREE[ textureJSON.minFilter ];
-
-				if ( THREE[ textureJSON.magFilter ] !== undefined )
-					texture.magFilter = THREE[ textureJSON.magFilter ];
-
-				if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy;
-
-				if ( textureJSON.repeat ) {
-
-					texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] );
-
-					if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
-					if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
-
-				}
-
-				if ( textureJSON.offset ) {
-
-					texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] );
-
-				}
-
-				// handle wrap after repeat so that default repeat can be overriden
-
-				if ( textureJSON.wrap ) {
-
-					var wrapMap = {
-						"repeat": THREE.RepeatWrapping,
-						"mirror": THREE.MirroredRepeatWrapping
-					}
-
-					if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ];
-					if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ];
-
-				}
-
-			}
-
-			result.textures[ textureID ] = texture;
-
-		}
-
-		// materials
-
-		var matID, matJSON;
-		var parID;
-
-		for ( matID in data.materials ) {
-
-			matJSON = data.materials[ matID ];
-
-			for ( parID in matJSON.parameters ) {
-
-				if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" ) {
-
-					matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ];
-
-				} else if ( parID === "shading" ) {
-
-					matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading;
-
-				} else if ( parID === "side" ) {
-
-					if ( matJSON.parameters[ parID ] == "double" ) {
-
-						matJSON.parameters[ parID ] = THREE.DoubleSide;
-
-					} else if ( matJSON.parameters[ parID ] == "back" ) {
-
-						matJSON.parameters[ parID ] = THREE.BackSide;
-
-					} else {
-
-						matJSON.parameters[ parID ] = THREE.FrontSide;
-
-					}
-
-				} else if ( parID === "blending" ) {
-
-					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending;
-
-				} else if ( parID === "combine" ) {
-
-					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation;
-
-				} else if ( parID === "vertexColors" ) {
-
-					if ( matJSON.parameters[ parID ] == "face" ) {
-
-						matJSON.parameters[ parID ] = THREE.FaceColors;
-
-					// default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false
-
-					} else if ( matJSON.parameters[ parID ] ) {
-
-						matJSON.parameters[ parID ] = THREE.VertexColors;
-
-					}
-
-				} else if ( parID === "wrapRGB" ) {
-
-					var v3 = matJSON.parameters[ parID ];
-					matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] );
-
-				}
-
-			}
-
-			if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) {
-
-				matJSON.parameters.transparent = true;
-
-			}
-
-			if ( matJSON.parameters.normalMap ) {
-
-				var shader = THREE.ShaderLib[ "normalmap" ];
-				var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
-
-				var diffuse = matJSON.parameters.color;
-				var specular = matJSON.parameters.specular;
-				var ambient = matJSON.parameters.ambient;
-				var shininess = matJSON.parameters.shininess;
-
-				uniforms[ "tNormal" ].value = result.textures[ matJSON.parameters.normalMap ];
-
-				if ( matJSON.parameters.normalScale ) {
-
-					uniforms[ "uNormalScale" ].value.set( matJSON.parameters.normalScale[ 0 ], matJSON.parameters.normalScale[ 1 ] );
-
-				}
-
-				if ( matJSON.parameters.map ) {
-
-					uniforms[ "tDiffuse" ].value = matJSON.parameters.map;
-					uniforms[ "enableDiffuse" ].value = true;
-
-				}
-
-				if ( matJSON.parameters.envMap ) {
-
-					uniforms[ "tCube" ].value = matJSON.parameters.envMap;
-					uniforms[ "enableReflection" ].value = true;
-					uniforms[ "uReflectivity" ].value = matJSON.parameters.reflectivity;
-
-				}
-
-				if ( matJSON.parameters.lightMap ) {
-
-					uniforms[ "tAO" ].value = matJSON.parameters.lightMap;
-					uniforms[ "enableAO" ].value = true;
-
-				}
-
-				if ( matJSON.parameters.specularMap ) {
-
-					uniforms[ "tSpecular" ].value = result.textures[ matJSON.parameters.specularMap ];
-					uniforms[ "enableSpecular" ].value = true;
-
-				}
-
-				if ( matJSON.parameters.displacementMap ) {
-
-					uniforms[ "tDisplacement" ].value = result.textures[ matJSON.parameters.displacementMap ];
-					uniforms[ "enableDisplacement" ].value = true;
-
-					uniforms[ "uDisplacementBias" ].value = matJSON.parameters.displacementBias;
-					uniforms[ "uDisplacementScale" ].value = matJSON.parameters.displacementScale;
-
-				}
-
-				uniforms[ "uDiffuseColor" ].value.setHex( diffuse );
-				uniforms[ "uSpecularColor" ].value.setHex( specular );
-				uniforms[ "uAmbientColor" ].value.setHex( ambient );
-
-				uniforms[ "uShininess" ].value = shininess;
-
-				if ( matJSON.parameters.opacity ) {
-
-					uniforms[ "uOpacity" ].value = matJSON.parameters.opacity;
-
-				}
-
-				var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
-
-				material = new THREE.ShaderMaterial( parameters );
-
-			} else {
-
-				material = new THREE[ matJSON.type ]( matJSON.parameters );
-
-			}
-
-			material.name = matID;
-
-			result.materials[ matID ] = material;
-
-		}
-
-		// second pass through all materials to initialize MeshFaceMaterials
-		// that could be referring to other materials out of order
-
-		for ( matID in data.materials ) {
-
-			matJSON = data.materials[ matID ];
-
-			if ( matJSON.parameters.materials ) {
-
-				var materialArray = [];
-
-				for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) {
-
-					var label = matJSON.parameters.materials[ i ];
-					materialArray.push( result.materials[ label ] );
-
-				}
-
-				result.materials[ matID ].materials = materialArray;
-
-			}
-
-		}
-
-		// objects ( synchronous init of procedural primitives )
-
-		handle_objects();
-
-		// defaults
-
-		if ( result.cameras && data.defaults.camera ) {
-
-			result.currentCamera = result.cameras[ data.defaults.camera ];
-
-		}
-
-		if ( result.fogs && data.defaults.fog ) {
-
-			result.scene.fog = result.fogs[ data.defaults.fog ];
-
-		}
-
-		// synchronous callback
-
-		scope.callbackSync( result );
-
-		// just in case there are no async elements
-
-		async_callback_gate();
-
-	}
-
-}
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.TextureLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.TextureLoader.prototype = {
-
-	constructor: THREE.TextureLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.ImageLoader( scope.manager );
-		loader.setCrossOrigin( this.crossOrigin );
-		loader.load( url, function ( image ) {
-
-			var texture = new THREE.Texture( image );
-			texture.needsUpdate = true;
-
-			if ( onLoad !== undefined ) {
-
-				onLoad( texture );
-
-			}
-
-		} );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	}
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.Material = function () {
-
-	this.id = THREE.MaterialIdCount ++;
-	this.uuid = THREE.Math.generateUUID();
-
-	this.name = '';
-
-	this.side = THREE.FrontSide;
-
-	this.opacity = 1;
-	this.transparent = false;
-
-	this.blending = THREE.NormalBlending;
-
-	this.blendSrc = THREE.SrcAlphaFactor;
-	this.blendDst = THREE.OneMinusSrcAlphaFactor;
-	this.blendEquation = THREE.AddEquation;
-
-	this.depthTest = true;
-	this.depthWrite = true;
-
-	this.polygonOffset = false;
-	this.polygonOffsetFactor = 0;
-	this.polygonOffsetUnits = 0;
-
-	this.alphaTest = 0;
-
-	this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
-
-	this.visible = true;
-
-	this.needsUpdate = true;
-
-};
-
-THREE.Material.prototype = {
-
-	constructor: THREE.Material,
-
-	setValues: function ( values ) {
-
-		if ( values === undefined ) return;
-
-		for ( var key in values ) {
-
-			var newValue = values[ key ];
-
-			if ( newValue === undefined ) {
-
-				console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' );
-				continue;
-
-			}
-
-			if ( key in this ) {
-
-				var currentValue = this[ key ];
-
-				if ( currentValue instanceof THREE.Color ) {
-
-					currentValue.set( newValue );
-
-				} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
-
-					currentValue.copy( newValue );
-
-				} else if ( key == 'overdraw') {
-
-					// ensure overdraw is backwards-compatable with legacy boolean type
-					this[ key ] = Number(newValue);
-
-				} else {
-
-					this[ key ] = newValue;
-
-				}
-
-			}
-
-		}
-
-	},
-
-	clone: function ( material ) {
-
-		if ( material === undefined ) material = new THREE.Material();
-
-		material.name = this.name;
-
-		material.side = this.side;
-
-		material.opacity = this.opacity;
-		material.transparent = this.transparent;
-
-		material.blending = this.blending;
-
-		material.blendSrc = this.blendSrc;
-		material.blendDst = this.blendDst;
-		material.blendEquation = this.blendEquation;
-
-		material.depthTest = this.depthTest;
-		material.depthWrite = this.depthWrite;
-
-		material.polygonOffset = this.polygonOffset;
-		material.polygonOffsetFactor = this.polygonOffsetFactor;
-		material.polygonOffsetUnits = this.polygonOffsetUnits;
-
-		material.alphaTest = this.alphaTest;
-
-		material.overdraw = this.overdraw;
-
-		material.visible = this.visible;
-
-		return material;
-
-	},
-
-	dispose: function () {
-
-		this.dispatchEvent( { type: 'dispose' } );
-
-	}
-
-};
-
-THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );
-
-THREE.MaterialIdCount = 0;
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  opacity: <float>,
- *
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  linewidth: <float>,
- *  linecap: "round",
- *  linejoin: "round",
- *
- *  vertexColors: <bool>
- *
- *  fog: <bool>
- * }
- */
-
-THREE.LineBasicMaterial = function ( parameters ) {
-
-	THREE.Material.call( this );
-
-	this.color = new THREE.Color( 0xffffff );
-
-	this.linewidth = 1;
-	this.linecap = 'round';
-	this.linejoin = 'round';
-
-	this.vertexColors = false;
-
-	this.fog = true;
-
-	this.setValues( parameters );
-
-};
-
-THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
-
-THREE.LineBasicMaterial.prototype.clone = function () {
-
-	var material = new THREE.LineBasicMaterial();
-
-	THREE.Material.prototype.clone.call( this, material );
-
-	material.color.copy( this.color );
-
-	material.linewidth = this.linewidth;
-	material.linecap = this.linecap;
-	material.linejoin = this.linejoin;
-
-	material.vertexColors = this.vertexColors;
-
-	material.fog = this.fog;
-
-	return material;
-
-};
-
-/**
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  opacity: <float>,
- *
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  linewidth: <float>,
- *
- *  scale: <float>,
- *  dashSize: <float>,
- *  gapSize: <float>,
- *
- *  vertexColors: <bool>
- *
- *  fog: <bool>
- * }
- */
-
-THREE.LineDashedMaterial = function ( parameters ) {
-
-	THREE.Material.call( this );
-
-	this.color = new THREE.Color( 0xffffff );
-
-	this.linewidth = 1;
-
-	this.scale = 1;
-	this.dashSize = 3;
-	this.gapSize = 1;
-
-	this.vertexColors = false;
-
-	this.fog = true;
-
-	this.setValues( parameters );
-
-};
-
-THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
-
-THREE.LineDashedMaterial.prototype.clone = function () {
-
-	var material = new THREE.LineDashedMaterial();
-
-	THREE.Material.prototype.clone.call( this, material );
-
-	material.color.copy( this.color );
-
-	material.linewidth = this.linewidth;
-
-	material.scale = this.scale;
-	material.dashSize = this.dashSize;
-	material.gapSize = this.gapSize;
-
-	material.vertexColors = this.vertexColors;
-
-	material.fog = this.fog;
-
-	return material;
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  opacity: <float>,
- *  map: new THREE.Texture( <Image> ),
- *
- *  lightMap: new THREE.Texture( <Image> ),
- *
- *  specularMap: new THREE.Texture( <Image> ),
- *
- *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
- *  combine: THREE.Multiply,
- *  reflectivity: <float>,
- *  refractionRatio: <float>,
- *
- *  shading: THREE.SmoothShading,
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>,
- *
- *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
- *
- *  skinning: <bool>,
- *  morphTargets: <bool>,
- *
- *  fog: <bool>
- * }
- */
-
-THREE.MeshBasicMaterial = function ( parameters ) {
-
-	THREE.Material.call( this );
-
-	this.color = new THREE.Color( 0xffffff ); // emissive
-
-	this.map = null;
-
-	this.lightMap = null;
-
-	this.specularMap = null;
-
-	this.envMap = null;
-	this.combine = THREE.MultiplyOperation;
-	this.reflectivity = 1;
-	this.refractionRatio = 0.98;
-
-	this.fog = true;
-
-	this.shading = THREE.SmoothShading;
-
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
-	this.wireframeLinecap = 'round';
-	this.wireframeLinejoin = 'round';
-
-	this.vertexColors = THREE.NoColors;
-
-	this.skinning = false;
-	this.morphTargets = false;
-
-	this.setValues( parameters );
-
-};
-
-THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
-
-THREE.MeshBasicMaterial.prototype.clone = function () {
-
-	var material = new THREE.MeshBasicMaterial();
-
-	THREE.Material.prototype.clone.call( this, material );
-
-	material.color.copy( this.color );
-
-	material.map = this.map;
-
-	material.lightMap = this.lightMap;
-
-	material.specularMap = this.specularMap;
-
-	material.envMap = this.envMap;
-	material.combine = this.combine;
-	material.reflectivity = this.reflectivity;
-	material.refractionRatio = this.refractionRatio;
-
-	material.fog = this.fog;
-
-	material.shading = this.shading;
-
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
-	material.wireframeLinecap = this.wireframeLinecap;
-	material.wireframeLinejoin = this.wireframeLinejoin;
-
-	material.vertexColors = this.vertexColors;
-
-	material.skinning = this.skinning;
-	material.morphTargets = this.morphTargets;
-
-	return material;
-
-};
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  ambient: <hex>,
- *  emissive: <hex>,
- *  opacity: <float>,
- *
- *  map: new THREE.Texture( <Image> ),
- *
- *  lightMap: new THREE.Texture( <Image> ),
- *
- *  specularMap: new THREE.Texture( <Image> ),
- *
- *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
- *  combine: THREE.Multiply,
- *  reflectivity: <float>,
- *  refractionRatio: <float>,
- *
- *  shading: THREE.SmoothShading,
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>,
- *
- *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
- *
- *  skinning: <bool>,
- *  morphTargets: <bool>,
- *  morphNormals: <bool>,
- *
- *	fog: <bool>
- * }
- */
+		if ( m.mapSpecular && texturePath ) {
 
-THREE.MeshLambertMaterial = function ( parameters ) {
+			create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
 
-	THREE.Material.call( this );
+		}
 
-	this.color = new THREE.Color( 0xffffff ); // diffuse
-	this.ambient = new THREE.Color( 0xffffff );
-	this.emissive = new THREE.Color( 0x000000 );
+		if ( m.mapAlpha && texturePath ) {
 
-	this.wrapAround = false;
-	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
+			create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );
 
-	this.map = null;
+		}
 
-	this.lightMap = null;
+		//
 
-	this.specularMap = null;
+		if ( m.mapBumpScale ) {
 
-	this.envMap = null;
-	this.combine = THREE.MultiplyOperation;
-	this.reflectivity = 1;
-	this.refractionRatio = 0.98;
+			mpars.bumpScale = m.mapBumpScale;
 
-	this.fog = true;
+		}
 
-	this.shading = THREE.SmoothShading;
+		if ( m.mapNormalFactor ) {
 
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
-	this.wireframeLinecap = 'round';
-	this.wireframeLinejoin = 'round';
+			mpars.normalScale = new THREE.Vector2( m.mapNormalFactor, m.mapNormalFactor );
 
-	this.vertexColors = THREE.NoColors;
+		}
 
-	this.skinning = false;
-	this.morphTargets = false;
-	this.morphNormals = false;
+		var material = new THREE[ mtype ]( mpars );
 
-	this.setValues( parameters );
+		if ( m.DbgName !== undefined ) material.name = m.DbgName;
 
-};
+		return material;
 
-THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
+	}
 
-THREE.MeshLambertMaterial.prototype.clone = function () {
+};
 
-	var material = new THREE.MeshLambertMaterial();
+THREE.Loader.Handlers = {
 
-	THREE.Material.prototype.clone.call( this, material );
+	handlers: [],
 
-	material.color.copy( this.color );
-	material.ambient.copy( this.ambient );
-	material.emissive.copy( this.emissive );
+	add: function ( regex, loader ) {
 
-	material.wrapAround = this.wrapAround;
-	material.wrapRGB.copy( this.wrapRGB );
+		this.handlers.push( regex, loader );
 
-	material.map = this.map;
+	},
 
-	material.lightMap = this.lightMap;
+	get: function ( file ) {
 
-	material.specularMap = this.specularMap;
+		for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) {
 
-	material.envMap = this.envMap;
-	material.combine = this.combine;
-	material.reflectivity = this.reflectivity;
-	material.refractionRatio = this.refractionRatio;
+			var regex = this.handlers[ i ];
+			var loader  = this.handlers[ i + 1 ];
 
-	material.fog = this.fog;
+			if ( regex.test( file ) ) {
 
-	material.shading = this.shading;
+				return loader;
 
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
-	material.wireframeLinecap = this.wireframeLinecap;
-	material.wireframeLinejoin = this.wireframeLinejoin;
+			}
 
-	material.vertexColors = this.vertexColors;
+		}
 
-	material.skinning = this.skinning;
-	material.morphTargets = this.morphTargets;
-	material.morphNormals = this.morphNormals;
+		return null;
 
-	return material;
+	}
 
 };
 
+// File:src/loaders/XHRLoader.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  ambient: <hex>,
- *  emissive: <hex>,
- *  specular: <hex>,
- *  shininess: <float>,
- *  opacity: <float>,
- *
- *  map: new THREE.Texture( <Image> ),
- *
- *  lightMap: new THREE.Texture( <Image> ),
- *
- *  bumpMap: new THREE.Texture( <Image> ),
- *  bumpScale: <float>,
- *
- *  normalMap: new THREE.Texture( <Image> ),
- *  normalScale: <Vector2>,
- *
- *  specularMap: new THREE.Texture( <Image> ),
- *
- *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
- *  combine: THREE.Multiply,
- *  reflectivity: <float>,
- *  refractionRatio: <float>,
- *
- *  shading: THREE.SmoothShading,
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>,
- *
- *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
- *
- *  skinning: <bool>,
- *  morphTargets: <bool>,
- *  morphNormals: <bool>,
- *
- *	fog: <bool>
- * }
  */
 
-THREE.MeshPhongMaterial = function ( parameters ) {
+THREE.XHRLoader = function ( manager ) {
 
-	THREE.Material.call( this );
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-	this.color = new THREE.Color( 0xffffff ); // diffuse
-	this.ambient = new THREE.Color( 0xffffff );
-	this.emissive = new THREE.Color( 0x000000 );
-	this.specular = new THREE.Color( 0x111111 );
-	this.shininess = 30;
+};
 
-	this.metal = false;
-	this.perPixel = true;
+THREE.XHRLoader.prototype = {
 
-	this.wrapAround = false;
-	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
+	constructor: THREE.XHRLoader,
 
-	this.map = null;
+	load: function ( url, onLoad, onProgress, onError ) {
 
-	this.lightMap = null;
+		var scope = this;
 
-	this.bumpMap = null;
-	this.bumpScale = 1;
+		var cached = THREE.Cache.get( url );
 
-	this.normalMap = null;
-	this.normalScale = new THREE.Vector2( 1, 1 );
+		if ( cached !== undefined ) {
 
-	this.specularMap = null;
+			if ( onLoad ) onLoad( cached );
+			return;
 
-	this.envMap = null;
-	this.combine = THREE.MultiplyOperation;
-	this.reflectivity = 1;
-	this.refractionRatio = 0.98;
+		}
 
-	this.fog = true;
+		var request = new XMLHttpRequest();
+		request.open( 'GET', url, true );
 
-	this.shading = THREE.SmoothShading;
+		request.addEventListener( 'load', function ( event ) {
 
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
-	this.wireframeLinecap = 'round';
-	this.wireframeLinejoin = 'round';
+			THREE.Cache.add( url, this.response );
 
-	this.vertexColors = THREE.NoColors;
+			if ( onLoad ) onLoad( this.response );
 
-	this.skinning = false;
-	this.morphTargets = false;
-	this.morphNormals = false;
+			scope.manager.itemEnd( url );
 
-	this.setValues( parameters );
+		}, false );
 
-};
+		if ( onProgress !== undefined ) {
 
-THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
+			request.addEventListener( 'progress', function ( event ) {
 
-THREE.MeshPhongMaterial.prototype.clone = function () {
+				onProgress( event );
 
-	var material = new THREE.MeshPhongMaterial();
+			}, false );
 
-	THREE.Material.prototype.clone.call( this, material );
+		}
 
-	material.color.copy( this.color );
-	material.ambient.copy( this.ambient );
-	material.emissive.copy( this.emissive );
-	material.specular.copy( this.specular );
-	material.shininess = this.shininess;
+		if ( onError !== undefined ) {
 
-	material.metal = this.metal;
-	material.perPixel = this.perPixel;
+			request.addEventListener( 'error', function ( event ) {
 
-	material.wrapAround = this.wrapAround;
-	material.wrapRGB.copy( this.wrapRGB );
+				onError( event );
 
-	material.map = this.map;
+			}, false );
 
-	material.lightMap = this.lightMap;
+		}
 
-	material.bumpMap = this.bumpMap;
-	material.bumpScale = this.bumpScale;
+		if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
+		if ( this.responseType !== undefined ) request.responseType = this.responseType;
 
-	material.normalMap = this.normalMap;
-	material.normalScale.copy( this.normalScale );
+		request.send( null );
 
-	material.specularMap = this.specularMap;
+		scope.manager.itemStart( url );
 
-	material.envMap = this.envMap;
-	material.combine = this.combine;
-	material.reflectivity = this.reflectivity;
-	material.refractionRatio = this.refractionRatio;
+	},
 
-	material.fog = this.fog;
+	setResponseType: function ( value ) {
 
-	material.shading = this.shading;
+		this.responseType = value;
 
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
-	material.wireframeLinecap = this.wireframeLinecap;
-	material.wireframeLinejoin = this.wireframeLinejoin;
+	},
 
-	material.vertexColors = this.vertexColors;
+	setCrossOrigin: function ( value ) {
 
-	material.skinning = this.skinning;
-	material.morphTargets = this.morphTargets;
-	material.morphNormals = this.morphNormals;
+		this.crossOrigin = value;
 
-	return material;
+	}
 
 };
 
+// File:src/loaders/ImageLoader.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  opacity: <float>,
- *
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>
- * }
  */
 
-THREE.MeshDepthMaterial = function ( parameters ) {
-
-	THREE.Material.call( this );
-
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
+THREE.ImageLoader = function ( manager ) {
 
-	this.setValues( parameters );
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
 };
 
-THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
-
-THREE.MeshDepthMaterial.prototype.clone = function () {
-
-	var material = new THREE.MeshDepthMaterial();
-
-	THREE.Material.prototype.clone.call( this, material );
+THREE.ImageLoader.prototype = {
 
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
+	constructor: THREE.ImageLoader,
 
-	return material;
+	load: function ( url, onLoad, onProgress, onError ) {
 
-};
+		var scope = this;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- *
- * parameters = {
- *  opacity: <float>,
- *
- *  shading: THREE.FlatShading,
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>
- * }
- */
+		var cached = THREE.Cache.get( url );
 
-THREE.MeshNormalMaterial = function ( parameters ) {
+		if ( cached !== undefined ) {
 
-	THREE.Material.call( this, parameters );
+			onLoad( cached );
+			return;
 
-	this.shading = THREE.FlatShading;
+		}
 
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
+		var image = document.createElement( 'img' );
 
-	this.morphTargets = false;
+		image.addEventListener( 'load', function ( event ) {
 
-	this.setValues( parameters );
+			THREE.Cache.add( url, this );
 
-};
+			if ( onLoad ) onLoad( this );
+			
+			scope.manager.itemEnd( url );
 
-THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
+		}, false );
 
-THREE.MeshNormalMaterial.prototype.clone = function () {
+		if ( onProgress !== undefined ) {
 
-	var material = new THREE.MeshNormalMaterial();
+			image.addEventListener( 'progress', function ( event ) {
 
-	THREE.Material.prototype.clone.call( this, material );
+				onProgress( event );
 
-	material.shading = this.shading;
+			}, false );
 
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
+		}
 
-	return material;
+		if ( onError !== undefined ) {
 
-};
+			image.addEventListener( 'error', function ( event ) {
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+				onError( event );
 
-THREE.MeshFaceMaterial = function ( materials ) {
+			}, false );
 
-	this.materials = materials instanceof Array ? materials : [];
+		}
 
-};
+		if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
+
+		image.src = url;
+
+		scope.manager.itemStart( url );
 
-THREE.MeshFaceMaterial.prototype.clone = function () {
+		return image;
 
-	var material = new THREE.MeshFaceMaterial();
+	},
 
-	for ( var i = 0; i < this.materials.length; i ++ ) {
+	setCrossOrigin: function ( value ) {
 
-		material.materials.push( this.materials[ i ].clone() );
+		this.crossOrigin = value;
 
 	}
 
-	return material;
+}
 
-};
+// File:src/loaders/JSONLoader.js
 
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  opacity: <float>,
- *  map: new THREE.Texture( <Image> ),
- *
- *  size: <float>,
- *
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  vertexColors: <bool>,
- *
- *  fog: <bool>
- * }
  */
 
-THREE.ParticleSystemMaterial = function ( parameters ) {
+THREE.JSONLoader = function ( showStatus ) {
 
-	THREE.Material.call( this );
+	THREE.Loader.call( this, showStatus );
 
-	this.color = new THREE.Color( 0xffffff );
+	this.withCredentials = false;
 
-	this.map = null;
+};
 
-	this.size = 1;
-	this.sizeAttenuation = true;
+THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype );
+THREE.JSONLoader.prototype.constructor = THREE.JSONLoader;
 
-	this.vertexColors = false;
+THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) {
 
-	this.fog = true;
+	// todo: unify load API to for easier SceneLoader use
 
-	this.setValues( parameters );
+	texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url );
+
+	this.onLoadStart();
+	this.loadAjaxJSON( this, url, callback, texturePath );
 
 };
 
-THREE.ParticleSystemMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) {
 
-THREE.ParticleSystemMaterial.prototype.clone = function () {
+	var xhr = new XMLHttpRequest();
 
-	var material = new THREE.ParticleSystemMaterial();
+	var length = 0;
 
-	THREE.Material.prototype.clone.call( this, material );
+	xhr.onreadystatechange = function () {
 
-	material.color.copy( this.color );
+		if ( xhr.readyState === xhr.DONE ) {
 
-	material.map = this.map;
+			if ( xhr.status === 200 || xhr.status === 0 ) {
 
-	material.size = this.size;
-	material.sizeAttenuation = this.sizeAttenuation;
+				if ( xhr.responseText ) {
 
-	material.vertexColors = this.vertexColors;
+					var json = JSON.parse( xhr.responseText );
+					var metadata = json.metadata;
 
-	material.fog = this.fog;
+					if ( metadata !== undefined ) {
 
-	return material;
+						if ( metadata.type === 'object' ) {
 
-};
+							THREE.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
+							return;
 
-// backwards compatibility
+						}
 
-THREE.ParticleBasicMaterial = THREE.ParticleSystemMaterial;
+						if ( metadata.type === 'scene' ) {
 
-/**
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  fragmentShader: <string>,
- *  vertexShader: <string>,
- *
- *  uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
- *
- *  defines: { "label" : "value" },
- *
- *  shading: THREE.SmoothShading,
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  wireframe: <boolean>,
- *  wireframeLinewidth: <float>,
- *
- *  lights: <bool>,
- *
- *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
- *
- *  skinning: <bool>,
- *  morphTargets: <bool>,
- *  morphNormals: <bool>,
- *
- *	fog: <bool>
- * }
- */
+							THREE.error( 'THREE.JSONLoader: ' + url + ' seems to be a Scene. Use THREE.SceneLoader instead.' );
+							return;
 
-THREE.ShaderMaterial = function ( parameters ) {
+						}
 
-	THREE.Material.call( this );
+					}
 
-	this.fragmentShader = "void main() {}";
-	this.vertexShader = "void main() {}";
-	this.uniforms = {};
-	this.defines = {};
-	this.attributes = null;
+					var result = context.parse( json, texturePath );
+					callback( result.geometry, result.materials );
 
-	this.shading = THREE.SmoothShading;
+				} else {
 
-	this.linewidth = 1;
+					THREE.error( 'THREE.JSONLoader: ' + url + ' seems to be unreachable or the file is empty.' );
 
-	this.wireframe = false;
-	this.wireframeLinewidth = 1;
+				}
 
-	this.fog = false; // set to use scene fog
+				// in context of more complex asset initialization
+				// do not block on single failed file
+				// maybe should go even one more level up
 
-	this.lights = false; // set to use scene lights
+				context.onLoadComplete();
 
-	this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
+			} else {
 
-	this.skinning = false; // set to use skinning attribute streams
+				THREE.error( 'THREE.JSONLoader: Couldn\'t load ' + url + ' (' + xhr.status + ')' );
 
-	this.morphTargets = false; // set to use morph targets
-	this.morphNormals = false; // set to use morph normals
+			}
 
-	// When rendered geometry doesn't include these attributes but the material does,
-	// use these default values in WebGL. This avoids errors when buffer data is missing.
-	this.defaultAttributeValues = {
-		"color" : [ 1, 1, 1],
-		"uv" : [ 0, 0 ],
-		"uv2" : [ 0, 0 ]
-	};
+		} else if ( xhr.readyState === xhr.LOADING ) {
 
-	// By default, bind position to attribute index 0. In WebGL, attribute 0
-	// should always be used to avoid potentially expensive emulation.
-	this.index0AttributeName = "position";
+			if ( callbackProgress ) {
 
-	this.setValues( parameters );
+				if ( length === 0 ) {
 
-};
+					length = xhr.getResponseHeader( 'Content-Length' );
 
-THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+				}
 
-THREE.ShaderMaterial.prototype.clone = function () {
+				callbackProgress( { total: length, loaded: xhr.responseText.length } );
 
-	var material = new THREE.ShaderMaterial();
+			}
 
-	THREE.Material.prototype.clone.call( this, material );
+		} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
 
-	material.fragmentShader = this.fragmentShader;
-	material.vertexShader = this.vertexShader;
+			if ( callbackProgress !== undefined ) {
 
-	material.uniforms = THREE.UniformsUtils.clone( this.uniforms );
+				length = xhr.getResponseHeader( 'Content-Length' );
 
-	material.attributes = this.attributes;
-	material.defines = this.defines;
+			}
 
-	material.shading = this.shading;
+		}
 
-	material.wireframe = this.wireframe;
-	material.wireframeLinewidth = this.wireframeLinewidth;
+	};
 
-	material.fog = this.fog;
+	xhr.open( 'GET', url, true );
+	xhr.withCredentials = this.withCredentials;
+	xhr.send( null );
 
-	material.lights = this.lights;
+};
 
-	material.vertexColors = this.vertexColors;
+THREE.JSONLoader.prototype.parse = function ( json, texturePath ) {
 
-	material.skinning = this.skinning;
+	var geometry = new THREE.Geometry(),
+	scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
 
-	material.morphTargets = this.morphTargets;
-	material.morphNormals = this.morphNormals;
+	parseModel( scale );
 
-	return material;
+	parseSkin();
+	parseMorphing( scale );
 
-};
+	geometry.computeFaceNormals();
+	geometry.computeBoundingSphere();
 
-/**
- * @author alteredq / http://alteredqualia.com/
- *
- * parameters = {
- *  color: <hex>,
- *  opacity: <float>,
- *  map: new THREE.Texture( <Image> ),
- *
- *  blending: THREE.NormalBlending,
- *  depthTest: <bool>,
- *  depthWrite: <bool>,
- *
- *  useScreenCoordinates: <bool>,
- *  sizeAttenuation: <bool>,
- *  alignment: THREE.SpriteAlignment.center,
- *
- *	uvOffset: new THREE.Vector2(),
- *	uvScale: new THREE.Vector2(),
- *
- *  fog: <bool>
- * }
- */
+	function parseModel( scale ) {
 
-THREE.SpriteMaterial = function ( parameters ) {
+		function isBitSet( value, position ) {
 
-	THREE.Material.call( this );
+			return value & ( 1 << position );
 
-	// defaults
+		}
 
-	this.color = new THREE.Color( 0xffffff );
-	this.map = new THREE.Texture();
+		var i, j, fi,
 
-	this.useScreenCoordinates = true;
-	this.depthTest = !this.useScreenCoordinates;
-	this.sizeAttenuation = !this.useScreenCoordinates;
-	this.alignment = THREE.SpriteAlignment.center.clone();
+		offset, zLength,
 
-	this.fog = false;
+		colorIndex, normalIndex, uvIndex, materialIndex,
+
+		type,
+		isQuad,
+		hasMaterial,
+		hasFaceVertexUv,
+		hasFaceNormal, hasFaceVertexNormal,
+		hasFaceColor, hasFaceVertexColor,
 
-	this.uvOffset = new THREE.Vector2( 0, 0 );
-	this.uvScale  = new THREE.Vector2( 1, 1 );
+		vertex, face, faceA, faceB, hex, normal,
 
-	// set parameters
+		uvLayer, uv, u, v,
 
-	this.setValues( parameters );
+		faces = json.faces,
+		vertices = json.vertices,
+		normals = json.normals,
+		colors = json.colors,
 
-	// override coupled defaults if not specified explicitly by parameters
+		nUvLayers = 0;
 
-	parameters = parameters || {};
+		if ( json.uvs !== undefined ) {
 
-	if ( parameters.depthTest === undefined ) this.depthTest = !this.useScreenCoordinates;
-	if ( parameters.sizeAttenuation === undefined ) this.sizeAttenuation = !this.useScreenCoordinates;
+			// disregard empty arrays
 
-};
+			for ( i = 0; i < json.uvs.length; i ++ ) {
 
-THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+				if ( json.uvs[ i ].length ) nUvLayers ++;
 
-THREE.SpriteMaterial.prototype.clone = function () {
+			}
 
-	var material = new THREE.SpriteMaterial();
+			for ( i = 0; i < nUvLayers; i ++ ) {
 
-	THREE.Material.prototype.clone.call( this, material );
+				geometry.faceVertexUvs[ i ] = [];
 
-	material.color.copy( this.color );
-	material.map = this.map;
+			}
 
-	material.useScreenCoordinates = this.useScreenCoordinates;
-	material.sizeAttenuation = this.sizeAttenuation;
-	material.alignment.copy( this.alignment );
+		}
 
-	material.uvOffset.copy( this.uvOffset );
-	material.uvScale.copy( this.uvScale );
+		offset = 0;
+		zLength = vertices.length;
 
-	material.fog = this.fog;
+		while ( offset < zLength ) {
 
-	return material;
+			vertex = new THREE.Vector3();
 
-};
+			vertex.x = vertices[ offset ++ ] * scale;
+			vertex.y = vertices[ offset ++ ] * scale;
+			vertex.z = vertices[ offset ++ ] * scale;
 
-// Alignment enums
+			geometry.vertices.push( vertex );
 
-THREE.SpriteAlignment = {};
-THREE.SpriteAlignment.topLeft = new THREE.Vector2( 0.5, -0.5 );
-THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -0.5 );
-THREE.SpriteAlignment.topRight = new THREE.Vector2( -0.5, -0.5 );
-THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 0.5, 0 );
-THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
-THREE.SpriteAlignment.centerRight = new THREE.Vector2( -0.5, 0 );
-THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 0.5, 0.5 );
-THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 0.5 );
-THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -0.5, 0.5 );
+		}
 
-/**
- * @author mrdoob / http://mrdoob.com/
- *
- * parameters = {
- *  color: <hex>,
- *  program: <function>,
- *  opacity: <float>,
- *  blending: THREE.NormalBlending
- * }
- */
+		offset = 0;
+		zLength = faces.length;
 
-THREE.SpriteCanvasMaterial = function ( parameters ) {
+		while ( offset < zLength ) {
 
-	THREE.Material.call( this );
+			type = faces[ offset ++ ];
 
-	this.color = new THREE.Color( 0xffffff );
-	this.program = function ( context, color ) {};
 
-	this.setValues( parameters );
+			isQuad              = isBitSet( type, 0 );
+			hasMaterial         = isBitSet( type, 1 );
+			hasFaceVertexUv     = isBitSet( type, 3 );
+			hasFaceNormal       = isBitSet( type, 4 );
+			hasFaceVertexNormal = isBitSet( type, 5 );
+			hasFaceColor	     = isBitSet( type, 6 );
+			hasFaceVertexColor  = isBitSet( type, 7 );
 
-};
+			// console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
 
-THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
+			if ( isQuad ) {
 
-THREE.SpriteCanvasMaterial.prototype.clone = function () {
+				faceA = new THREE.Face3();
+				faceA.a = faces[ offset ];
+				faceA.b = faces[ offset + 1 ];
+				faceA.c = faces[ offset + 3 ];
 
-	var material = new THREE.SpriteCanvasMaterial();
+				faceB = new THREE.Face3();
+				faceB.a = faces[ offset + 1 ];
+				faceB.b = faces[ offset + 2 ];
+				faceB.c = faces[ offset + 3 ];
 
-	THREE.Material.prototype.clone.call( this, material );
+				offset += 4;
 
-	material.color.copy( this.color );
-	material.program = this.program;
+				if ( hasMaterial ) {
 
-	return material;
+					materialIndex = faces[ offset ++ ];
+					faceA.materialIndex = materialIndex;
+					faceB.materialIndex = materialIndex;
 
-};
+				}
 
-// backwards compatibility
+				// to get face <=> uv index correspondence
 
-THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial;
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author szimek / https://github.com/szimek/
- */
+				fi = geometry.faces.length;
 
-THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+				if ( hasFaceVertexUv ) {
 
-	this.id = THREE.TextureIdCount ++;
-	this.uuid = THREE.Math.generateUUID();
+					for ( i = 0; i < nUvLayers; i ++ ) {
 
-	this.name = '';
+						uvLayer = json.uvs[ i ];
 
-	this.image = image;
-	this.mipmaps = [];
+						geometry.faceVertexUvs[ i ][ fi ] = [];
+						geometry.faceVertexUvs[ i ][ fi + 1 ] = []
 
-	this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping();
+						for ( j = 0; j < 4; j ++ ) {
 
-	this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
-	this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
+							uvIndex = faces[ offset ++ ];
 
-	this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
-	this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
+							u = uvLayer[ uvIndex * 2 ];
+							v = uvLayer[ uvIndex * 2 + 1 ];
 
-	this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+							uv = new THREE.Vector2( u, v );
 
-	this.format = format !== undefined ? format : THREE.RGBAFormat;
-	this.type = type !== undefined ? type : THREE.UnsignedByteType;
+							if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
+							if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
 
-	this.offset = new THREE.Vector2( 0, 0 );
-	this.repeat = new THREE.Vector2( 1, 1 );
+						}
 
-	this.generateMipmaps = true;
-	this.premultiplyAlpha = false;
-	this.flipY = true;
-	this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+					}
 
-	this.needsUpdate = false;
-	this.onUpdate = null;
+				}
 
-};
+				if ( hasFaceNormal ) {
 
-THREE.Texture.prototype = {
+					normalIndex = faces[ offset ++ ] * 3;
 
-	constructor: THREE.Texture,
+					faceA.normal.set(
+						normals[ normalIndex ++ ],
+						normals[ normalIndex ++ ],
+						normals[ normalIndex ]
+					);
 
-	clone: function ( texture ) {
+					faceB.normal.copy( faceA.normal );
 
-		if ( texture === undefined ) texture = new THREE.Texture();
+				}
 
-		texture.image = this.image;
-		texture.mipmaps = this.mipmaps.slice(0);
+				if ( hasFaceVertexNormal ) {
 
-		texture.mapping = this.mapping;
+					for ( i = 0; i < 4; i ++ ) {
 
-		texture.wrapS = this.wrapS;
-		texture.wrapT = this.wrapT;
+						normalIndex = faces[ offset ++ ] * 3;
 
-		texture.magFilter = this.magFilter;
-		texture.minFilter = this.minFilter;
+						normal = new THREE.Vector3(
+							normals[ normalIndex ++ ],
+							normals[ normalIndex ++ ],
+							normals[ normalIndex ]
+						);
 
-		texture.anisotropy = this.anisotropy;
 
-		texture.format = this.format;
-		texture.type = this.type;
+						if ( i !== 2 ) faceA.vertexNormals.push( normal );
+						if ( i !== 0 ) faceB.vertexNormals.push( normal );
 
-		texture.offset.copy( this.offset );
-		texture.repeat.copy( this.repeat );
+					}
 
-		texture.generateMipmaps = this.generateMipmaps;
-		texture.premultiplyAlpha = this.premultiplyAlpha;
-		texture.flipY = this.flipY;
-		texture.unpackAlignment = this.unpackAlignment;
+				}
 
-		return texture;
 
-	},
+				if ( hasFaceColor ) {
 
-	dispose: function () {
+					colorIndex = faces[ offset ++ ];
+					hex = colors[ colorIndex ];
 
-		this.dispatchEvent( { type: 'dispose' } );
+					faceA.color.setHex( hex );
+					faceB.color.setHex( hex );
 
-	}
+				}
 
-};
 
-THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );
+				if ( hasFaceVertexColor ) {
 
-THREE.TextureIdCount = 0;
+					for ( i = 0; i < 4; i ++ ) {
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+						colorIndex = faces[ offset ++ ];
+						hex = colors[ colorIndex ];
 
-THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+						if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
+						if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );
 
-	THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+					}
 
-	this.image = { width: width, height: height };
-	this.mipmaps = mipmaps;
+				}
 
-	this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file
+				geometry.faces.push( faceA );
+				geometry.faces.push( faceB );
 
-};
+			} else {
 
-THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+				face = new THREE.Face3();
+				face.a = faces[ offset ++ ];
+				face.b = faces[ offset ++ ];
+				face.c = faces[ offset ++ ];
 
-THREE.CompressedTexture.prototype.clone = function () {
+				if ( hasMaterial ) {
 
-	var texture = new THREE.CompressedTexture();
+					materialIndex = faces[ offset ++ ];
+					face.materialIndex = materialIndex;
 
-	THREE.Texture.prototype.clone.call( this, texture );
+				}
 
-	return texture;
+				// to get face <=> uv index correspondence
 
-};
+				fi = geometry.faces.length;
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+				if ( hasFaceVertexUv ) {
 
-THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+					for ( i = 0; i < nUvLayers; i ++ ) {
 
-	THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+						uvLayer = json.uvs[ i ];
 
-	this.image = { data: data, width: width, height: height };
+						geometry.faceVertexUvs[ i ][ fi ] = [];
 
-};
+						for ( j = 0; j < 3; j ++ ) {
 
-THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
+							uvIndex = faces[ offset ++ ];
 
-THREE.DataTexture.prototype.clone = function () {
+							u = uvLayer[ uvIndex * 2 ];
+							v = uvLayer[ uvIndex * 2 + 1 ];
 
-	var texture = new THREE.DataTexture();
+							uv = new THREE.Vector2( u, v );
 
-	THREE.Texture.prototype.clone.call( this, texture );
+							geometry.faceVertexUvs[ i ][ fi ].push( uv );
 
-	return texture;
+						}
 
-};
+					}
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+				}
 
-THREE.ParticleSystem = function ( geometry, material ) {
+				if ( hasFaceNormal ) {
 
-	THREE.Object3D.call( this );
+					normalIndex = faces[ offset ++ ] * 3;
 
-	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
-	this.material = material !== undefined ? material : new THREE.ParticleSystemMaterial( { color: Math.random() * 0xffffff } );
+					face.normal.set(
+						normals[ normalIndex ++ ],
+						normals[ normalIndex ++ ],
+						normals[ normalIndex ]
+					);
 
-	this.sortParticles = false;
-	this.frustumCulled = false;
+				}
 
-};
+				if ( hasFaceVertexNormal ) {
 
-THREE.ParticleSystem.prototype = Object.create( THREE.Object3D.prototype );
+					for ( i = 0; i < 3; i ++ ) {
 
-THREE.ParticleSystem.prototype.clone = function ( object ) {
+						normalIndex = faces[ offset ++ ] * 3;
 
-	if ( object === undefined ) object = new THREE.ParticleSystem( this.geometry, this.material );
+						normal = new THREE.Vector3(
+							normals[ normalIndex ++ ],
+							normals[ normalIndex ++ ],
+							normals[ normalIndex ]
+						);
 
-	object.sortParticles = this.sortParticles;
+						face.vertexNormals.push( normal );
 
-	THREE.Object3D.prototype.clone.call( this, object );
+					}
 
-	return object;
+				}
 
-};
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+				if ( hasFaceColor ) {
 
-THREE.Line = function ( geometry, material, type ) {
+					colorIndex = faces[ offset ++ ];
+					face.color.setHex( colors[ colorIndex ] );
 
-	THREE.Object3D.call( this );
+				}
 
-	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
-	this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
 
-	this.type = ( type !== undefined ) ? type : THREE.LineStrip;
+				if ( hasFaceVertexColor ) {
 
-};
+					for ( i = 0; i < 3; i ++ ) {
 
-THREE.LineStrip = 0;
-THREE.LinePieces = 1;
+						colorIndex = faces[ offset ++ ];
+						face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );
 
-THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
+					}
 
-THREE.Line.prototype.clone = function ( object ) {
+				}
 
-	if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type );
+				geometry.faces.push( face );
 
-	THREE.Object3D.prototype.clone.call( this, object );
+			}
 
-	return object;
+		}
 
-};
+	};
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author mikael emtinger / http://gomo.se/
- * @author jonobr1 / http://jonobr1.com/
- */
+	function parseSkin() {
+		var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;
 
-THREE.Mesh = function ( geometry, material ) {
+		if ( json.skinWeights ) {
 
-	THREE.Object3D.call( this );
+			for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
 
-	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
-	this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );
+				var x =                               json.skinWeights[ i     ];
+				var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
+				var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
+				var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
 
-	this.updateMorphTargets();
+				geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
 
-};
+			}
 
-THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
+		}
 
-THREE.Mesh.prototype.updateMorphTargets = function () {
+		if ( json.skinIndices ) {
 
-	if ( this.geometry.morphTargets.length > 0 ) {
+			for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
 
-		this.morphTargetBase = -1;
-		this.morphTargetForcedOrder = [];
-		this.morphTargetInfluences = [];
-		this.morphTargetDictionary = {};
+				var a =                               json.skinIndices[ i     ];
+				var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
+				var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
+				var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
 
-		for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
+				geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
 
-			this.morphTargetInfluences.push( 0 );
-			this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
+			}
 
 		}
 
-	}
+		geometry.bones = json.bones;
 
-};
+		if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {
 
-THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
+			THREE.warn( 'THREE.JSONLoader: When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
+					geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );
 
-	if ( this.morphTargetDictionary[ name ] !== undefined ) {
+		}
 
-		return this.morphTargetDictionary[ name ];
 
-	}
+		// could change this to json.animations[0] or remove completely
 
-	console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." );
+		geometry.animation = json.animation;
+		geometry.animations = json.animations;
 
-	return 0;
+	};
 
-};
+	function parseMorphing( scale ) {
 
-THREE.Mesh.prototype.clone = function ( object ) {
+		if ( json.morphTargets !== undefined ) {
 
-	if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
+			var i, l, v, vl, dstVertices, srcVertices;
 
-	THREE.Object3D.prototype.clone.call( this, object );
+			for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
 
-	return object;
+				geometry.morphTargets[ i ] = {};
+				geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
+				geometry.morphTargets[ i ].vertices = [];
 
-};
+				dstVertices = geometry.morphTargets[ i ].vertices;
+				srcVertices = json.morphTargets [ i ].vertices;
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+				for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
 
-THREE.Bone = function( belongsToSkin ) {
+					var vertex = new THREE.Vector3();
+					vertex.x = srcVertices[ v ] * scale;
+					vertex.y = srcVertices[ v + 1 ] * scale;
+					vertex.z = srcVertices[ v + 2 ] * scale;
 
-	THREE.Object3D.call( this );
+					dstVertices.push( vertex );
 
-	this.skin = belongsToSkin;
-	this.skinMatrix = new THREE.Matrix4();
+				}
 
-};
+			}
 
-THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+		}
+
+		if ( json.morphColors !== undefined ) {
 
-THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
+			var i, l, c, cl, dstColors, srcColors, color;
 
-	// update local
+			for ( i = 0, l = json.morphColors.length; i < l; i ++ ) {
 
-	if ( this.matrixAutoUpdate ) {
+				geometry.morphColors[ i ] = {};
+				geometry.morphColors[ i ].name = json.morphColors[ i ].name;
+				geometry.morphColors[ i ].colors = [];
 
-		forceUpdate |= this.updateMatrix();
+				dstColors = geometry.morphColors[ i ].colors;
+				srcColors = json.morphColors [ i ].colors;
 
-	}
+				for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) {
 
-	// update skin matrix
+					color = new THREE.Color( 0xffaa00 );
+					color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );
+					dstColors.push( color );
 
-	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
+				}
 
-		if( parentSkinMatrix ) {
+			}
 
-			this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
+		}
 
-		} else {
+	};
 
-			this.skinMatrix.copy( this.matrix );
+	if ( json.materials === undefined || json.materials.length === 0 ) {
 
-		}
+		return { geometry: geometry };
 
-		this.matrixWorldNeedsUpdate = false;
-		forceUpdate = true;
+	} else {
 
-	}
+		var materials = this.initMaterials( json.materials, texturePath );
 
-	// update children
+		if ( this.needsTangents( materials ) ) {
 
-	var child, i, l = this.children.length;
+			geometry.computeTangents();
 
-	for ( i = 0; i < l; i ++ ) {
+		}
 
-		this.children[ i ].update( this.skinMatrix, forceUpdate );
+		return { geometry: geometry, materials: materials };
 
 	}
 
 };
 
+// File:src/loaders/LoadingManager.js
 
 /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-	//
-
-	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+THREE.LoadingManager = function ( onLoad, onProgress, onError ) {
 
-	// init bones
+	var scope = this;
 
-	this.identityMatrix = new THREE.Matrix4();
+	var loaded = 0, total = 0;
 
-	this.bones = [];
-	this.boneMatrices = [];
+	this.onLoad = onLoad;
+	this.onProgress = onProgress;
+	this.onError = onError;
 
-	var b, bone, gbone, p, q, s;
+	this.itemStart = function ( url ) {
 
-	if ( this.geometry && this.geometry.bones !== undefined ) {
+		total ++;
 
-		for ( b = 0; b < this.geometry.bones.length; b ++ ) {
+	};
 
-			gbone = this.geometry.bones[ b ];
+	this.itemEnd = function ( url ) {
 
-			p = gbone.pos;
-			q = gbone.rotq;
-			s = gbone.scl;
+		loaded ++;
 
-			bone = this.addBone();
+		if ( scope.onProgress !== undefined ) {
 
-			bone.name = gbone.name;
-			bone.position.set( p[0], p[1], p[2] );
-			bone.quaternion.set( q[0], q[1], q[2], q[3] );
+			scope.onProgress( url, loaded, total );
 
-			if ( s !== undefined ) {
+		}
 
-				bone.scale.set( s[0], s[1], s[2] );
+		if ( loaded === total && scope.onLoad !== undefined ) {
 
-			} else {
+			scope.onLoad();
 
-				bone.scale.set( 1, 1, 1 );
+		}
 
-			}
+	};
 
-		}
+};
 
-		for ( b = 0; b < this.bones.length; b ++ ) {
+THREE.DefaultLoadingManager = new THREE.LoadingManager();
 
-			gbone = this.geometry.bones[ b ];
-			bone = this.bones[ b ];
+// File:src/loaders/BufferGeometryLoader.js
 
-			if ( gbone.parent === -1 ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				this.add( bone );
+THREE.BufferGeometryLoader = function ( manager ) {
 
-			} else {
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-				this.bones[ gbone.parent ].add( bone );
+};
 
-			}
+THREE.BufferGeometryLoader.prototype = {
 
-		}
+	constructor: THREE.BufferGeometryLoader,
 
-		//
+	load: function ( url, onLoad, onProgress, onError ) {
 
-		var nBones = this.bones.length;
+		var scope = this;
 
-		if ( this.useVertexTexture ) {
+		var loader = new THREE.XHRLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
 
-			// layout (1 matrix = 4 pixels)
-			//	RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
-			//  with  8x8  pixel texture max   16 bones  (8 * 8  / 4)
-			//  	 16x16 pixel texture max   64 bones (16 * 16 / 4)
-			//  	 32x32 pixel texture max  256 bones (32 * 32 / 4)
-			//  	 64x64 pixel texture max 1024 bones (64 * 64 / 4)
-
-			var size;
-
-			if ( nBones > 256 )
-				size = 64;
-			else if ( nBones > 64 )
-				size = 32;
-			else if ( nBones > 16 )
-				size = 16;
-			else
-				size = 8;
-
-			this.boneTextureWidth = size;
-			this.boneTextureHeight = size;
-
-			this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
-			this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
-			this.boneTexture.minFilter = THREE.NearestFilter;
-			this.boneTexture.magFilter = THREE.NearestFilter;
-			this.boneTexture.generateMipmaps = false;
-			this.boneTexture.flipY = false;
+			onLoad( scope.parse( JSON.parse( text ) ) );
 
-		} else {
+		}, onProgress, onError );
 
-			this.boneMatrices = new Float32Array( 16 * nBones );
+	},
 
-		}
+	setCrossOrigin: function ( value ) {
 
-		this.pose();
+		this.crossOrigin = value;
 
-	}
+	},
 
-};
+	parse: function ( json ) {
 
-THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+		var geometry = new THREE.BufferGeometry();
 
-THREE.SkinnedMesh.prototype.addBone = function( bone ) {
+		var attributes = json.data.attributes;
 
-	if ( bone === undefined ) {
+		for ( var key in attributes ) {
 
-		bone = new THREE.Bone( this );
+			var attribute = attributes[ key ];
+			var typedArray = new self[ attribute.type ]( attribute.array );
 
-	}
+			geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) );
 
-	this.bones.push( bone );
+		}
 
-	return bone;
+		var offsets = json.data.offsets;
 
-};
+		if ( offsets !== undefined ) {
 
-THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
+			geometry.offsets = JSON.parse( JSON.stringify( offsets ) );
 
-	var offsetMatrix = new THREE.Matrix4();
+		}
 
-	return function ( force ) {
+		var boundingSphere = json.data.boundingSphere;
 
-		this.matrixAutoUpdate && this.updateMatrix();
+		if ( boundingSphere !== undefined ) {
 
-		// update matrixWorld
+			var center = new THREE.Vector3();
 
-		if ( this.matrixWorldNeedsUpdate || force ) {
+			if ( boundingSphere.center !== undefined ) {
 
-			if ( this.parent ) {
+				center.fromArray( boundingSphere.center );
 
-				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+			}
 
-			} else {
+			geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius );
 
-				this.matrixWorld.copy( this.matrix );
+		}
 
-			}
+		return geometry;
 
-			this.matrixWorldNeedsUpdate = false;
+	}
 
-			force = true;
+};
 
-		}
+// File:src/loaders/MaterialLoader.js
 
-		// update children
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+THREE.MaterialLoader = function ( manager ) {
 
-			var child = this.children[ i ];
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-			if ( child instanceof THREE.Bone ) {
+};
 
-				child.update( this.identityMatrix, false );
+THREE.MaterialLoader.prototype = {
 
-			} else {
+	constructor: THREE.MaterialLoader,
 
-				child.updateMatrixWorld( true );
+	load: function ( url, onLoad, onProgress, onError ) {
 
-			}
+		var scope = this;
 
-		}
+		var loader = new THREE.XHRLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
 
-		// make a snapshot of the bones' rest position
+			onLoad( scope.parse( JSON.parse( text ) ) );
 
-		if ( this.boneInverses == undefined ) {
+		}, onProgress, onError );
 
-			this.boneInverses = [];
+	},
 
-			for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+	setCrossOrigin: function ( value ) {
 
-				var inverse = new THREE.Matrix4();
+		this.crossOrigin = value;
 
-				inverse.getInverse( this.bones[ b ].skinMatrix );
+	},
 
-				this.boneInverses.push( inverse );
+	parse: function ( json ) {
 
-			}
+		var material = new THREE[ json.type ];
 
-		}
+		if ( json.color !== undefined ) material.color.setHex( json.color );
+		if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
+		if ( json.specular !== undefined ) material.specular.setHex( json.specular );
+		if ( json.shininess !== undefined ) material.shininess = json.shininess;
+		if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;
+		if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
+		if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
+		if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+		if ( json.shading !== undefined ) material.shading = json.shading;
+		if ( json.blending !== undefined ) material.blending = json.blending;
+		if ( json.side !== undefined ) material.side = json.side;
+		if ( json.opacity !== undefined ) material.opacity = json.opacity;
+		if ( json.transparent !== undefined ) material.transparent = json.transparent;
+		if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
 
-		// flatten bone matrices to array
+		// for PointCloudMaterial
+		if ( json.size !== undefined ) material.size = json.size;
+		if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
 
-		for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+		if ( json.materials !== undefined ) {
 
-			// compute the offset between the current and the original transform;
+			for ( var i = 0, l = json.materials.length; i < l; i ++ ) {
 
-			// TODO: we could get rid of this multiplication step if the skinMatrix
-			// was already representing the offset; however, this requires some
-			// major changes to the animation system
+				material.materials.push( this.parse( json.materials[ i ] ) );
 
-			offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );
-			offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+			}
 
 		}
 
-		if ( this.useVertexTexture ) {
-
-			this.boneTexture.needsUpdate = true;
+		return material;
 
-		}
+	}
 
-	};
+};
 
-}();
+// File:src/loaders/ObjectLoader.js
 
-THREE.SkinnedMesh.prototype.pose = function () {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	this.updateMatrixWorld( true );
+THREE.ObjectLoader = function ( manager ) {
 
-	this.normalizeSkinWeights();
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+	this.texturePath = '';
 
 };
 
-THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
-
-	if ( this.geometry instanceof THREE.Geometry ) {
-
-		for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
-
-			var sw = this.geometry.skinWeights[ i ];
+THREE.ObjectLoader.prototype = {
 
-			var scale = 1.0 / sw.lengthManhattan();
+	constructor: THREE.ObjectLoader,
 
-			if ( scale !== Infinity ) {
+	load: function ( url, onLoad, onProgress, onError ) {
 
-				sw.multiplyScalar( scale );
+		if ( this.texturePath === '' ) {
 
-			} else {
+			this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );
 
-				sw.set( 1 ); // this will be normalized by the shader anyway
+		}
 
-			}
+		var scope = this;
 
-		}
+		var loader = new THREE.XHRLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
 
-	} else {
+			scope.parse( JSON.parse( text ), onLoad );
 
-		// skinning weights assumed to be normalized for THREE.BufferGeometry
+		}, onProgress, onError );
 
-	}
+	},
 
-};
+	setTexturePath: function ( value ) {
 
-THREE.SkinnedMesh.prototype.clone = function ( object ) {
+		this.texturePath = value;
 
-	if ( object === undefined ) {
+	},
 
-		object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture );
+	setCrossOrigin: function ( value ) {
 
-	}
+		this.crossOrigin = value;
 
-	THREE.Mesh.prototype.clone.call( this, object );
+	},
 
-	return object;
+	parse: function ( json, onLoad ) {
 
-};
+		var geometries = this.parseGeometries( json.geometries );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+		var images = this.parseImages( json.images, function () {
 
-THREE.MorphAnimMesh = function ( geometry, material ) {
+			if ( onLoad !== undefined ) onLoad( object );
 
-	THREE.Mesh.call( this, geometry, material );
+		} );
+		var textures  = this.parseTextures( json.textures, images );
+		var materials = this.parseMaterials( json.materials, textures );
+		var object = this.parseObject( json.object, geometries, materials );
 
-	// API
+		if ( json.images === undefined || json.images.length === 0 ) {
 
-	this.duration = 1000; // milliseconds
-	this.mirroredLoop = false;
-	this.time = 0;
+			if ( onLoad !== undefined ) onLoad( object );
 
-	// internals
+		}
 
-	this.lastKeyframe = 0;
-	this.currentKeyframe = 0;
+		return object;
 
-	this.direction = 1;
-	this.directionBackwards = false;
+	},
 
-	this.setFrameRange( 0, this.geometry.morphTargets.length - 1 );
+	parseGeometries: function ( json ) {
 
-};
+		var geometries = {};
 
-THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype );
+		if ( json !== undefined ) {
 
-THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) {
+			var geometryLoader = new THREE.JSONLoader();
+			var bufferGeometryLoader = new THREE.BufferGeometryLoader();
 
-	this.startKeyframe = start;
-	this.endKeyframe = end;
+			for ( var i = 0, l = json.length; i < l; i ++ ) {
 
-	this.length = this.endKeyframe - this.startKeyframe + 1;
+				var geometry;
+				var data = json[ i ];
 
-};
+				switch ( data.type ) {
 
-THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
+					case 'PlaneGeometry':
+					case 'PlaneBufferGeometry':
 
-	this.direction = 1;
-	this.directionBackwards = false;
+						geometry = new THREE[ data.type ](
+							data.width,
+							data.height,
+							data.widthSegments,
+							data.heightSegments
+						);
 
-};
+						break;
 
-THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
+					case 'BoxGeometry':
+					case 'CubeGeometry': // backwards compatible
 
-	this.direction = -1;
-	this.directionBackwards = true;
+						geometry = new THREE.BoxGeometry(
+							data.width,
+							data.height,
+							data.depth,
+							data.widthSegments,
+							data.heightSegments,
+							data.depthSegments
+						);
 
-};
+						break;
 
-THREE.MorphAnimMesh.prototype.parseAnimations = function () {
+					case 'CircleGeometry':
 
-	var geometry = this.geometry;
+						geometry = new THREE.CircleGeometry(
+							data.radius,
+							data.segments
+						);
 
-	if ( ! geometry.animations ) geometry.animations = {};
+						break;
 
-	var firstAnimation, animations = geometry.animations;
+					case 'CylinderGeometry':
 
-	var pattern = /([a-z]+)(\d+)/;
+						geometry = new THREE.CylinderGeometry(
+							data.radiusTop,
+							data.radiusBottom,
+							data.height,
+							data.radialSegments,
+							data.heightSegments,
+							data.openEnded
+						);
 
-	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+						break;
 
-		var morph = geometry.morphTargets[ i ];
-		var parts = morph.name.match( pattern );
+					case 'SphereGeometry':
 
-		if ( parts && parts.length > 1 ) {
+						geometry = new THREE.SphereGeometry(
+							data.radius,
+							data.widthSegments,
+							data.heightSegments,
+							data.phiStart,
+							data.phiLength,
+							data.thetaStart,
+							data.thetaLength
+						);
 
-			var label = parts[ 1 ];
-			var num = parts[ 2 ];
+						break;
 
-			if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: -Infinity };
+					case 'IcosahedronGeometry':
 
-			var animation = animations[ label ];
+						geometry = new THREE.IcosahedronGeometry(
+							data.radius,
+							data.detail
+						);
 
-			if ( i < animation.start ) animation.start = i;
-			if ( i > animation.end ) animation.end = i;
+						break;
 
-			if ( ! firstAnimation ) firstAnimation = label;
+					case 'TorusGeometry':
 
-		}
+						geometry = new THREE.TorusGeometry(
+							data.radius,
+							data.tube,
+							data.radialSegments,
+							data.tubularSegments,
+							data.arc
+						);
 
-	}
+						break;
 
-	geometry.firstAnimation = firstAnimation;
+					case 'TorusKnotGeometry':
 
-};
+						geometry = new THREE.TorusKnotGeometry(
+							data.radius,
+							data.tube,
+							data.radialSegments,
+							data.tubularSegments,
+							data.p,
+							data.q,
+							data.heightScale
+						);
 
-THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) {
+						break;
 
-	if ( ! this.geometry.animations ) this.geometry.animations = {};
+					case 'BufferGeometry':
 
-	this.geometry.animations[ label ] = { start: start, end: end };
+						geometry = bufferGeometryLoader.parse( data );
 
-};
+						break;
 
-THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) {
+					case 'Geometry':
 
-	var animation = this.geometry.animations[ label ];
+						geometry = geometryLoader.parse( data.data ).geometry;
 
-	if ( animation ) {
+						break;
 
-		this.setFrameRange( animation.start, animation.end );
-		this.duration = 1000 * ( ( animation.end - animation.start ) / fps );
-		this.time = 0;
+				}
 
-	} else {
+				geometry.uuid = data.uuid;
 
-		console.warn( "animation[" + label + "] undefined" );
+				if ( data.name !== undefined ) geometry.name = data.name;
 
-	}
+				geometries[ data.uuid ] = geometry;
 
-};
+			}
 
-THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) {
+		}
 
-	var frameTime = this.duration / this.length;
+		return geometries;
 
-	this.time += this.direction * delta;
+	},
 
-	if ( this.mirroredLoop ) {
+	parseMaterials: function ( json, textures ) {
 
-		if ( this.time > this.duration || this.time < 0 ) {
+		var materials = {};
 
-			this.direction *= -1;
+		if ( json !== undefined ) {
 
-			if ( this.time > this.duration ) {
+			var getTexture = function ( name ) {
 
-				this.time = this.duration;
-				this.directionBackwards = true;
+				if ( textures[ name ] === undefined ) {
 
-			}
+					THREE.warn( 'THREE.ObjectLoader: Undefined texture', name );
 
-			if ( this.time < 0 ) {
+				}
 
-				this.time = 0;
-				this.directionBackwards = false;
+				return textures[ name ];
 
-			}
+			};
 
-		}
+			var loader = new THREE.MaterialLoader();
 
-	} else {
+			for ( var i = 0, l = json.length; i < l; i ++ ) {
 
-		this.time = this.time % this.duration;
+				var data = json[ i ];
+				var material = loader.parse( data );
 
-		if ( this.time < 0 ) this.time += this.duration;
+				material.uuid = data.uuid;
 
-	}
+				if ( data.name !== undefined ) material.name = data.name;
 
-	var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 );
+				if ( data.map !== undefined ) {
 
-	if ( keyframe !== this.currentKeyframe ) {
+					material.map = getTexture( data.map );
 
-		this.morphTargetInfluences[ this.lastKeyframe ] = 0;
-		this.morphTargetInfluences[ this.currentKeyframe ] = 1;
+				}
 
-		this.morphTargetInfluences[ keyframe ] = 0;
+				if ( data.bumpMap !== undefined ) {
 
-		this.lastKeyframe = this.currentKeyframe;
-		this.currentKeyframe = keyframe;
+					material.bumpMap = getTexture( data.bumpMap );
+					if ( data.bumpScale ) {
+						material.bumpScale = new THREE.Vector2( data.bumpScale, data.bumpScale );
+					}
 
-	}
+				}
 
-	var mix = ( this.time % frameTime ) / frameTime;
+				if ( data.alphaMap !== undefined ) {
 
-	if ( this.directionBackwards ) {
+					material.alphaMap = getTexture( data.alphaMap );
 
-		mix = 1 - mix;
+				}
 
-	}
+				if ( data.envMap !== undefined ) {
 
-	this.morphTargetInfluences[ this.currentKeyframe ] = mix;
-	this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix;
+					material.envMap = getTexture( data.envMap );
 
-};
+				}
 
-THREE.MorphAnimMesh.prototype.clone = function ( object ) {
+				if ( data.normalMap !== undefined ) {
 
-	if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material );
+					material.normalMap = getTexture( data.normalMap );
+					if ( data.normalScale ) {
+						material.normalScale = new THREE.Vector2( data.normalScale, data.normalScale );
+					}
 
-	object.duration = this.duration;
-	object.mirroredLoop = this.mirroredLoop;
-	object.time = this.time;
+				}
 
-	object.lastKeyframe = this.lastKeyframe;
-	object.currentKeyframe = this.currentKeyframe;
+				if ( data.lightMap !== undefined ) {
 
-	object.direction = this.direction;
-	object.directionBackwards = this.directionBackwards;
+					material.lightMap = getTexture( data.lightMap );
 
-	THREE.Mesh.prototype.clone.call( this, object );
+				}
 
-	return object;
+				if ( data.specularMap !== undefined ) {
 
-};
+					material.specularMap = getTexture( data.specularMap );
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
+				}
 
-THREE.LOD = function () {
+				materials[ data.uuid ] = material;
 
-	THREE.Object3D.call( this );
+			}
 
-	this.objects = [];
+		}
 
-};
+		return materials;
 
+	},
 
-THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
+	parseImages: function ( json, onLoad ) {
 
-THREE.LOD.prototype.addLevel = function ( object, distance ) {
+		var scope = this;
+		var images = {};
 
-	if ( distance === undefined ) distance = 0;
+		if ( json !== undefined && json.length > 0 ) {
 
-	distance = Math.abs( distance );
+			var manager = new THREE.LoadingManager( onLoad );
 
-	for ( var l = 0; l < this.objects.length; l ++ ) {
+			var loader = new THREE.ImageLoader( manager );
+			loader.setCrossOrigin( this.crossOrigin );
 
-		if ( distance < this.objects[ l ].distance ) {
+			var loadImage = function ( url ) {
 
-			break;
+				scope.manager.itemStart( url );
 
-		}
+				return loader.load( url, function () {
 
-	}
+					scope.manager.itemEnd( url );
 
-	this.objects.splice( l, 0, { distance: distance, object: object } );
-	this.add( object );
+				} );
 
-};
+			};
 
-THREE.LOD.prototype.getObjectForDistance = function ( distance ) {
+			for ( var i = 0, l = json.length; i < l; i ++ ) {
 
-	for ( var i = 1, l = this.objects.length; i < l; i ++ ) {
+				var image = json[ i ];
+				var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;
 
-		if ( distance < this.objects[ i ].distance ) {
+				images[ image.uuid ] = loadImage( path );
 
-			break;
+			}
 
 		}
 
-	}
+		return images;
 
-	return this.objects[ i - 1 ].object;
+	},
 
-};
+	parseTextures: function ( json, images ) {
 
-THREE.LOD.prototype.update = function () {
+		var textures = {};
 
-	var v1 = new THREE.Vector3();
-	var v2 = new THREE.Vector3();
+		if ( json !== undefined ) {
 
-	return function ( camera ) {
+			for ( var i = 0, l = json.length; i < l; i ++ ) {
 
-		if ( this.objects.length > 1 ) {
+				var data = json[ i ];
 
-			v1.getPositionFromMatrix( camera.matrixWorld );
-			v2.getPositionFromMatrix( this.matrixWorld );
+				if ( data.image === undefined ) {
 
-			var distance = v1.distanceTo( v2 );
+					THREE.warn( 'THREE.ObjectLoader: No "image" speficied for', data.uuid );
 
-			this.objects[ 0 ].object.visible = true;
+				}
 
-			for ( var i = 1, l = this.objects.length; i < l; i ++ ) {
+				if ( images[ data.image ] === undefined ) {
 
-				if ( distance >= this.objects[ i ].distance ) {
+					THREE.warn( 'THREE.ObjectLoader: Undefined image', data.image );
 
-					this.objects[ i - 1 ].object.visible = false;
-					this.objects[ i     ].object.visible = true;
+				}
 
-				} else {
+				var texture = new THREE.Texture( images[ data.image ] );
+				texture.needsUpdate = true;
 
-					break;
+				texture.uuid = data.uuid;
 
-				}
+				if ( data.name !== undefined ) texture.name = data.name;
+				if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] );
+				if ( data.minFilter !== undefined ) texture.minFilter = THREE[ data.minFilter ];
+				if ( data.magFilter !== undefined ) texture.magFilter = THREE[ data.magFilter ];
+				if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
+				if ( data.wrap instanceof Array ) {
 
-			}
+					texture.wrapS = THREE[ data.wrap[ 0 ] ];
+					texture.wrapT = THREE[ data.wrap[ 1 ] ];
 
-			for( ; i < l; i ++ ) {
+				}
 
-				this.objects[ i ].object.visible = false;
+				textures[ data.uuid ] = texture;
 
 			}
 
 		}
 
-	};
-
-}();
+		return textures;
 
-THREE.LOD.prototype.clone = function () {
+	},
 
-	// TODO
+	parseObject: function () {
 
-};
+		var matrix = new THREE.Matrix4();
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+		return function ( data, geometries, materials ) {
 
-THREE.Sprite = function ( material ) {
+			var object;
 
-	THREE.Object3D.call( this );
+			var getGeometry = function ( name ) {
 
-	this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
+				if ( geometries[ name ] === undefined ) {
 
-	this.rotation = 0;
+					THREE.warn( 'THREE.ObjectLoader: Undefined geometry', name );
 
-};
+				}
 
-THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+				return geometries[ name ];
 
-/*
- * Custom update matrix
- */
+			};
 
-THREE.Sprite.prototype.updateMatrix = function () {
+			var getMaterial = function ( name ) {
 
-	this.matrix.compose( this.position, this.quaternion, this.scale );
+				if ( materials[ name ] === undefined ) {
 
-	this.matrixWorldNeedsUpdate = true;
+					THREE.warn( 'THREE.ObjectLoader: Undefined material', name );
 
-};
+				}
 
-THREE.Sprite.prototype.clone = function ( object ) {
+				return materials[ name ];
 
-	if ( object === undefined ) object = new THREE.Sprite( this.material );
+			};
 
-	THREE.Object3D.prototype.clone.call( this, object );
+			switch ( data.type ) {
 
-	return object;
+				case 'Scene':
 
-};
+					object = new THREE.Scene();
 
-// Backwards compatibility
+					break;
 
-THREE.Particle = THREE.Sprite;
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+				case 'PerspectiveCamera':
 
-THREE.Scene = function () {
+					object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
 
-	THREE.Object3D.call( this );
+					break;
 
-	this.fog = null;
-	this.overrideMaterial = null;
+				case 'OrthographicCamera':
 
-	this.autoUpdate = true; // checked by the renderer
-	this.matrixAutoUpdate = false;
+					object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
 
-	this.__lights = [];
+					break;
 
-	this.__objectsAdded = [];
-	this.__objectsRemoved = [];
+				case 'AmbientLight':
 
-};
+					object = new THREE.AmbientLight( data.color );
 
-THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+					break;
 
-THREE.Scene.prototype.__addObject = function ( object ) {
+				case 'DirectionalLight':
 
-	if ( object instanceof THREE.Light ) {
+					object = new THREE.DirectionalLight( data.color, data.intensity );
 
-		if ( this.__lights.indexOf( object ) === - 1 ) {
+					break;
 
-			this.__lights.push( object );
+				case 'PointLight':
 
-		}
+					object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay );
 
-		if ( object.target && object.target.parent === undefined ) {
+					break;
 
-			this.add( object.target );
+				case 'SpotLight':
 
-		}
+					object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay );
 
-	} else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) {
+					break;
 
-		this.__objectsAdded.push( object );
+				case 'HemisphereLight':
 
-		// check if previously removed
+					object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity );
 
-		var i = this.__objectsRemoved.indexOf( object );
+					break;
 
-		if ( i !== -1 ) {
+				case 'Mesh':
 
-			this.__objectsRemoved.splice( i, 1 );
+					object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) );
 
-		}
+					break;
 
-	}
+				case 'Line':
 
-	for ( var c = 0; c < object.children.length; c ++ ) {
+					object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );
 
-		this.__addObject( object.children[ c ] );
+					break;
 
-	}
+				case 'PointCloud':
 
-};
+					object = new THREE.PointCloud( getGeometry( data.geometry ), getMaterial( data.material ) );
 
-THREE.Scene.prototype.__removeObject = function ( object ) {
+					break;
 
-	if ( object instanceof THREE.Light ) {
+				case 'Sprite':
 
-		var i = this.__lights.indexOf( object );
+					object = new THREE.Sprite( getMaterial( data.material ) );
 
-		if ( i !== -1 ) {
+					break;
 
-			this.__lights.splice( i, 1 );
+				case 'Group':
 
-		}
+					object = new THREE.Group();
 
-		if ( object.shadowCascadeArray ) {
+					break;
 
-			for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) {
+				default:
 
-				this.__removeObject( object.shadowCascadeArray[ x ] );
+					object = new THREE.Object3D();
 
 			}
 
-		}
-
-	} else if ( !( object instanceof THREE.Camera ) ) {
-
-		this.__objectsRemoved.push( object );
-
-		// check if previously added
-
-		var i = this.__objectsAdded.indexOf( object );
+			object.uuid = data.uuid;
 
-		if ( i !== -1 ) {
+			if ( data.name !== undefined ) object.name = data.name;
+			if ( data.matrix !== undefined ) {
 
-			this.__objectsAdded.splice( i, 1 );
+				matrix.fromArray( data.matrix );
+				matrix.decompose( object.position, object.quaternion, object.scale );
 
-		}
+			} else {
 
-	}
+				if ( data.position !== undefined ) object.position.fromArray( data.position );
+				if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
+				if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
 
-	for ( var c = 0; c < object.children.length; c ++ ) {
+			}
 
-		this.__removeObject( object.children[ c ] );
+			if ( data.visible !== undefined ) object.visible = data.visible;
+			if ( data.userData !== undefined ) object.userData = data.userData;
 
-	}
+			if ( data.children !== undefined ) {
 
-};
+				for ( var child in data.children ) {
 
-THREE.Scene.prototype.clone = function ( object ) {
+					object.add( this.parseObject( data.children[ child ], geometries, materials ) );
 
-	if ( object === undefined ) object = new THREE.Scene();
+				}
 
-	THREE.Object3D.prototype.clone.call(this, object);
+			}
 
-	if ( this.fog !== null ) object.fog = this.fog.clone();
-	if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone();
+			return object;
 
-	object.autoUpdate = this.autoUpdate;
-	object.matrixAutoUpdate = this.matrixAutoUpdate;
+		}
 
-	return object;
+	}()
 
 };
 
+// File:src/loaders/TextureLoader.js
+
 /**
  * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.Fog = function ( hex, near, far ) {
-
-	this.name = '';
-
-	this.color = new THREE.Color( hex );
+THREE.TextureLoader = function ( manager ) {
 
-	this.near = ( near !== undefined ) ? near : 1;
-	this.far = ( far !== undefined ) ? far : 1000;
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
 };
 
-THREE.Fog.prototype.clone = function () {
+THREE.TextureLoader.prototype = {
 
-	return new THREE.Fog( this.color.getHex(), this.near, this.far );
+	constructor: THREE.TextureLoader,
 
-};
+	load: function ( url, onLoad, onProgress, onError ) {
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+		var scope = this;
 
-THREE.FogExp2 = function ( hex, density ) {
+		var loader = new THREE.ImageLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( image ) {
 
-	this.name = '';
+			var texture = new THREE.Texture( image );
+			texture.needsUpdate = true;
 
-	this.color = new THREE.Color( hex );
-	this.density = ( density !== undefined ) ? density : 0.00025;
+			if ( onLoad !== undefined ) {
 
-};
+				onLoad( texture );
 
-THREE.FogExp2.prototype.clone = function () {
+			}
 
-	return new THREE.FogExp2( this.color.getHex(), this.density );
+		}, onProgress, onError );
 
-};
+	},
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+	setCrossOrigin: function ( value ) {
 
-THREE.CanvasRenderer = function ( parameters ) {
+		this.crossOrigin = value;
 
-	console.log( 'THREE.CanvasRenderer', THREE.REVISION );
+	}
 
-	var smoothstep = THREE.Math.smoothstep;
+};
 
-	parameters = parameters || {};
+// File:src/loaders/BinaryTextureLoader.js
 
-	var _this = this,
-	_renderData, _elements, _lights,
-	_projector = new THREE.Projector(),
+/**
+ * @author Nikos M. / https://github.com/foo123/
+ *
+ * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
+ */
 
-	_canvas = parameters.canvas !== undefined
-			? parameters.canvas
-			: document.createElement( 'canvas' ),
+THREE.DataTextureLoader = THREE.BinaryTextureLoader = function () {
 
-	_canvasWidth = _canvas.width,
-	_canvasHeight = _canvas.height,
-	_canvasWidthHalf = Math.floor( _canvasWidth / 2 ),
-	_canvasHeightHalf = Math.floor( _canvasHeight / 2 ),
+	// override in sub classes
+	this._parser = null;
 
-	_context = _canvas.getContext( '2d' ),
+};
 
-	_clearColor = new THREE.Color( 0x000000 ),
-	_clearAlpha = 0,
+THREE.BinaryTextureLoader.prototype = {
 
-	_contextGlobalAlpha = 1,
-	_contextGlobalCompositeOperation = 0,
-	_contextStrokeStyle = null,
-	_contextFillStyle = null,
-	_contextLineWidth = null,
-	_contextLineCap = null,
-	_contextLineJoin = null,
-	_contextDashSize = null,
-	_contextGapSize = 0,
+	constructor: THREE.BinaryTextureLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
 
-	_camera,
+		var scope = this;
 
-	_v1, _v2, _v3, _v4,
-	_v5 = new THREE.RenderableVertex(),
-	_v6 = new THREE.RenderableVertex(),
+		var texture = new THREE.DataTexture( );
 
-	_v1x, _v1y, _v2x, _v2y, _v3x, _v3y,
-	_v4x, _v4y, _v5x, _v5y, _v6x, _v6y,
+		var loader = new THREE.XHRLoader();
+		loader.setResponseType( 'arraybuffer' );
 
-	_color = new THREE.Color(),
-	_color1 = new THREE.Color(),
-	_color2 = new THREE.Color(),
-	_color3 = new THREE.Color(),
-	_color4 = new THREE.Color(),
+		loader.load( url, function ( buffer ) {
 
-	_diffuseColor = new THREE.Color(),
-	_emissiveColor = new THREE.Color(),
+			var texData = scope._parser( buffer );
 
-	_lightColor = new THREE.Color(),
+			if ( !texData ) return;
 
-	_patterns = {}, _imagedatas = {},
+			if ( undefined !== texData.image ) {
 
-	_near, _far,
+				texture.image = texData.image;
 
-	_image, _uvs,
-	_uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y,
+			} else if ( undefined !== texData.data ) {
 
-	_clipBox = new THREE.Box2(),
-	_clearBox = new THREE.Box2(),
-	_elemBox = new THREE.Box2(),
+				texture.image.width = texData.width;
+				texture.image.height = texData.height;
+				texture.image.data = texData.data;
 
-	_ambientLight = new THREE.Color(),
-	_directionalLights = new THREE.Color(),
-	_pointLights = new THREE.Color(),
+			}
 
-	_vector3 = new THREE.Vector3(), // Needed for PointLight
+			texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping;
+			texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping;
 
-	_pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData,
-	_gradientMap, _gradientMapContext, _gradientMapQuality = 16;
+			texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter;
+			texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter;
 
-	_pixelMap = document.createElement( 'canvas' );
-	_pixelMap.width = _pixelMap.height = 2;
+			texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;
 
-	_pixelMapContext = _pixelMap.getContext( '2d' );
-	_pixelMapContext.fillStyle = 'rgba(0,0,0,1)';
-	_pixelMapContext.fillRect( 0, 0, 2, 2 );
+			if ( undefined !== texData.format ) {
 
-	_pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 );
-	_pixelMapData = _pixelMapImage.data;
+				texture.format = texData.format;
 
-	_gradientMap = document.createElement( 'canvas' );
-	_gradientMap.width = _gradientMap.height = _gradientMapQuality;
+			}
+			if ( undefined !== texData.type ) {
 
-	_gradientMapContext = _gradientMap.getContext( '2d' );
-	_gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 );
-	_gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality );
+				texture.type = texData.type;
 
-	_gradientMapQuality --; // Fix UVs
+			}
 
-	// dash+gap fallbacks for Firefox and everything else
+			if ( undefined !== texData.mipmaps ) {
 
-	if ( _context.setLineDash === undefined ) {
+				texture.mipmaps = texData.mipmaps;
 
-		if ( _context.mozDash !== undefined ) {
+			}
 
-			_context.setLineDash = function ( values ) {
+			if ( 1 === texData.mipmapCount ) {
 
-				_context.mozDash = values[ 0 ] !== null ? values : null;
+				texture.minFilter = THREE.LinearFilter;
 
 			}
 
-		} else {
+			texture.needsUpdate = true;
 
-			_context.setLineDash = function () {}
+			if ( onLoad ) onLoad( texture, texData );
 
-		}
+		}, onProgress, onError );
 
-	}
 
-	this.domElement = _canvas;
+		return texture;
 
-	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
-				? parameters.devicePixelRatio
-				: self.devicePixelRatio !== undefined
-					? self.devicePixelRatio
-					: 1;
+	}
 
-	this.autoClear = true;
-	this.sortObjects = true;
-	this.sortElements = true;
+};
 
-	this.info = {
+// File:src/loaders/CompressedTextureLoader.js
 
-		render: {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * Abstract Base class to block based textures loader (dds, pvr, ...)
+ */
 
-			vertices: 0,
-			faces: 0
+THREE.CompressedTextureLoader = function () {
 
-		}
+	// override in sub classes
+	this._parser = null;
 
-	}
+};
 
-	// WebGLRenderer compatibility
 
-	this.supportsVertexTextures = function () {};
-	this.setFaceCulling = function () {};
+THREE.CompressedTextureLoader.prototype = {
 
-	this.setSize = function ( width, height, updateStyle ) {
+	constructor: THREE.CompressedTextureLoader,
 
-		_canvasWidth = width * this.devicePixelRatio;
-		_canvasHeight = height * this.devicePixelRatio;
+	load: function ( url, onLoad, onError ) {
 
-		_canvasWidthHalf = Math.floor( _canvasWidth / 2 );
-		_canvasHeightHalf = Math.floor( _canvasHeight / 2 );
+		var scope = this;
 
-		_canvas.width = _canvasWidth;
-		_canvas.height = _canvasHeight;
+		var images = [];
 
-		if ( this.devicePixelRatio !== 1 && updateStyle !== false ) {
+		var texture = new THREE.CompressedTexture();
+		texture.image = images;
 
-			_canvas.style.width = width + 'px';
-			_canvas.style.height = height + 'px';
+		var loader = new THREE.XHRLoader();
+		loader.setResponseType( 'arraybuffer' );
 
-		}
+		if ( url instanceof Array ) {
 
-		_clipBox.set(
-			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
-			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
-		);
+			var loaded = 0;
 
-		_clearBox.set(
-			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
-			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
-		);
+			var loadTexture = function ( i ) {
 
-		_contextGlobalAlpha = 1;
-		_contextGlobalCompositeOperation = 0;
-		_contextStrokeStyle = null;
-		_contextFillStyle = null;
-		_contextLineWidth = null;
-		_contextLineCap = null;
-		_contextLineJoin = null;
+				loader.load( url[ i ], function ( buffer ) {
 
-	};
+					var texDatas = scope._parser( buffer, true );
 
-	this.setClearColor = function ( color, alpha ) {
+					images[ i ] = {
+						width: texDatas.width,
+						height: texDatas.height,
+						format: texDatas.format,
+						mipmaps: texDatas.mipmaps
+					};
 
-		_clearColor.set( color );
-		_clearAlpha = alpha !== undefined ? alpha : 1;
+					loaded += 1;
 
-		_clearBox.set(
-			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
-			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
-		);
+					if ( loaded === 6 ) {
 
-	};
+						if (texDatas.mipmapCount == 1)
+ 							texture.minFilter = THREE.LinearFilter;
 
-	this.setClearColorHex = function ( hex, alpha ) {
+						texture.format = texDatas.format;
+						texture.needsUpdate = true;
 
-		console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' );
-		this.setClearColor( hex, alpha );
+						if ( onLoad ) onLoad( texture );
 
-	};
+					}
 
-	this.getMaxAnisotropy = function () {
+				} );
 
-		return 0;
+			};
 
-	};
+			for ( var i = 0, il = url.length; i < il; ++ i ) {
 
-	this.clear = function () {
+				loadTexture( i );
 
-		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+			}
 
-		if ( _clearBox.empty() === false ) {
+		} else {
 
-			_clearBox.intersect( _clipBox );
-			_clearBox.expandByScalar( 2 );
+			// compressed cubemap texture stored in a single DDS file
 
-			if ( _clearAlpha < 1 ) {
+			loader.load( url, function ( buffer ) {
 
-				_context.clearRect(
-					_clearBox.min.x | 0,
-					_clearBox.min.y | 0,
-					( _clearBox.max.x - _clearBox.min.x ) | 0,
-					( _clearBox.max.y - _clearBox.min.y ) | 0
-				);
+				var texDatas = scope._parser( buffer, true );
 
-			}
+				if ( texDatas.isCubemap ) {
 
-			if ( _clearAlpha > 0 ) {
+					var faces = texDatas.mipmaps.length / texDatas.mipmapCount;
 
-				setBlending( THREE.NormalBlending );
-				setOpacity( 1 );
+					for ( var f = 0; f < faces; f ++ ) {
 
-				setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' );
+						images[ f ] = { mipmaps : [] };
 
-				_context.fillRect(
-					_clearBox.min.x | 0,
-					_clearBox.min.y | 0,
-					( _clearBox.max.x - _clearBox.min.x ) | 0,
-					( _clearBox.max.y - _clearBox.min.y ) | 0
-				);
+						for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {
 
-			}
+							images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
+							images[ f ].format = texDatas.format;
+							images[ f ].width = texDatas.width;
+							images[ f ].height = texDatas.height;
 
-			_clearBox.makeEmpty();
+						}
 
-		}
+					}
 
+				} else {
 
-	};
+					texture.image.width = texDatas.width;
+					texture.image.height = texDatas.height;
+					texture.mipmaps = texDatas.mipmaps;
 
-	this.render = function ( scene, camera ) {
+				}
 
-		if ( camera instanceof THREE.Camera === false ) {
+				if ( texDatas.mipmapCount === 1 ) {
 
-			console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' );
-			return;
+					texture.minFilter = THREE.LinearFilter;
 
-		}
+				}
 
-		if ( this.autoClear === true ) this.clear();
+				texture.format = texDatas.format;
+				texture.needsUpdate = true;
 
-		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+				if ( onLoad ) onLoad( texture );
 
-		_this.info.render.vertices = 0;
-		_this.info.render.faces = 0;
+			} );
 
-		_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
-		_elements = _renderData.elements;
-		_lights = _renderData.lights;
-		_camera = camera;
+		}
 
-		/* DEBUG
-		setFillStyle( 'rgba( 0, 255, 255, 0.5 )' );
-		_context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y );
-		*/
+		return texture;
 
-		calculateLights();
+	}
 
-		for ( var e = 0, el = _elements.length; e < el; e++ ) {
+};
 
-			var element = _elements[ e ];
+// File:src/materials/Material.js
 
-			var material = element.material;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			if ( material === undefined || material.visible === false ) continue;
+THREE.Material = function () {
 
-			_elemBox.makeEmpty();
+	Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } );
 
-			if ( element instanceof THREE.RenderableSprite ) {
+	this.uuid = THREE.Math.generateUUID();
 
-				_v1 = element;
-				_v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf;
+	this.name = '';
+	this.type = 'Material';
 
-				renderSprite( _v1, element, material );
+	this.side = THREE.FrontSide;
 
-			} else if ( element instanceof THREE.RenderableLine ) {
+	this.opacity = 1;
+	this.transparent = false;
 
-				_v1 = element.v1; _v2 = element.v2;
+	this.blending = THREE.NormalBlending;
 
-				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
-				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
+	this.blendSrc = THREE.SrcAlphaFactor;
+	this.blendDst = THREE.OneMinusSrcAlphaFactor;
+	this.blendEquation = THREE.AddEquation;
+	this.blendSrcAlpha = null;
+	this.blendDstAlpha = null;
+	this.blendEquationAlpha = null;
 
-				_elemBox.setFromPoints( [
-					_v1.positionScreen,
-					_v2.positionScreen
-				] );
+	this.depthTest = true;
+	this.depthWrite = true;
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+	this.colorWrite = true;
 
-					renderLine( _v1, _v2, element, material );
+	this.polygonOffset = false;
+	this.polygonOffsetFactor = 0;
+	this.polygonOffsetUnits = 0;
 
-				}
+	this.alphaTest = 0;
 
-			} else if ( element instanceof THREE.RenderableFace3 ) {
+	this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
 
-				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
+	this.visible = true;
 
-				if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
-				if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
-				if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;
+	this._needsUpdate = true;
 
-				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
-				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
-				_v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf;
+};
 
-				if ( material.overdraw > 0 ) {
+THREE.Material.prototype = {
 
-					expand( _v1.positionScreen, _v2.positionScreen, material.overdraw );
-					expand( _v2.positionScreen, _v3.positionScreen, material.overdraw );
-					expand( _v3.positionScreen, _v1.positionScreen, material.overdraw );
+	constructor: THREE.Material,
 
-				}
+	get needsUpdate () {
 
-				_elemBox.setFromPoints( [
-					_v1.positionScreen,
-					_v2.positionScreen,
-					_v3.positionScreen
-				] );
+		return this._needsUpdate;
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+	},
 
-					renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material );
+	set needsUpdate ( value ) {
 
-				}
+		if ( value === true ) this.update();
 
-			}
+		this._needsUpdate = value;
 
-			/* DEBUG
-			setLineWidth( 1 );
-			setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' );
-			_context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y );
-			*/
+	},
 
-			_clearBox.union( _elemBox );
+	setValues: function ( values ) {
 
-		}
+		if ( values === undefined ) return;
 
-		/* DEBUG
-		setLineWidth( 1 );
-		setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' );
-		_context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y );
-		*/
+		for ( var key in values ) {
 
-		_context.setTransform( 1, 0, 0, 1, 0, 0 );
+			var newValue = values[ key ];
 
-	};
+			if ( newValue === undefined ) {
 
-	//
+				THREE.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+				continue;
 
-	function calculateLights() {
+			}
 
-		_ambientLight.setRGB( 0, 0, 0 );
-		_directionalLights.setRGB( 0, 0, 0 );
-		_pointLights.setRGB( 0, 0, 0 );
+			if ( key in this ) {
 
-		for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+				var currentValue = this[ key ];
 
-			var light = _lights[ l ];
-			var lightColor = light.color;
+				if ( currentValue instanceof THREE.Color ) {
 
-			if ( light instanceof THREE.AmbientLight ) {
+					currentValue.set( newValue );
 
-				_ambientLight.add( lightColor );
+				} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
 
-			} else if ( light instanceof THREE.DirectionalLight ) {
+					currentValue.copy( newValue );
 
-				// for sprites
+				} else if ( key == 'overdraw' ) {
 
-				_directionalLights.add( lightColor );
+					// ensure overdraw is backwards-compatable with legacy boolean type
+					this[ key ] = Number( newValue );
 
-			} else if ( light instanceof THREE.PointLight ) {
+				} else {
 
-				// for sprites
+					this[ key ] = newValue;
 
-				_pointLights.add( lightColor );
+				}
 
 			}
 
 		}
 
-	}
+	},
 
-	function calculateLight( position, normal, color ) {
+	toJSON: function () {
 
-		for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+		var output = {
+			metadata: {
+				version: 4.2,
+				type: 'material',
+				generator: 'MaterialExporter'
+			},
+			uuid: this.uuid,
+			type: this.type
+		};
 
-			var light = _lights[ l ];
+		if ( this.name !== "" ) output.name = this.name;
 
-			_lightColor.copy( light.color );
+		if ( this instanceof THREE.MeshBasicMaterial ) {
 
-			if ( light instanceof THREE.DirectionalLight ) {
+			output.color = this.color.getHex();
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
 
-				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize();
+		} else if ( this instanceof THREE.MeshLambertMaterial ) {
 
-				var amount = normal.dot( lightPosition );
+			output.color = this.color.getHex();
+			output.emissive = this.emissive.getHex();
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.shading !== THREE.SmoothShading ) output.shading = this.shading;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
 
-				if ( amount <= 0 ) continue;
+		} else if ( this instanceof THREE.MeshPhongMaterial ) {
 
-				amount *= light.intensity;
+			output.color = this.color.getHex();
+			output.emissive = this.emissive.getHex();
+			output.specular = this.specular.getHex();
+			output.shininess = this.shininess;
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.shading !== THREE.SmoothShading ) output.shading = this.shading;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
 
-				color.add( _lightColor.multiplyScalar( amount ) );
+		} else if ( this instanceof THREE.MeshNormalMaterial ) {
 
-			} else if ( light instanceof THREE.PointLight ) {
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
 
-				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld );
+		} else if ( this instanceof THREE.MeshDepthMaterial ) {
 
-				var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
 
-				if ( amount <= 0 ) continue;
+		} else if ( this instanceof THREE.PointCloudMaterial ) {
 
-				amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
+			output.size  = this.size;
+			output.sizeAttenuation = this.sizeAttenuation;
+			output.color = this.color.getHex();
 
-				if ( amount == 0 ) continue;
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
 
-				amount *= light.intensity;
+		} else if ( this instanceof THREE.ShaderMaterial ) {
 
-				color.add( _lightColor.multiplyScalar( amount ) );
+			output.uniforms = this.uniforms;
+			output.vertexShader = this.vertexShader;
+			output.fragmentShader = this.fragmentShader;
 
-			}
+		} else if ( this instanceof THREE.SpriteMaterial ) {
+
+			output.color = this.color.getHex();
 
 		}
 
-	}
+		if ( this.opacity < 1 ) output.opacity = this.opacity;
+		if ( this.transparent !== false ) output.transparent = this.transparent;
+		if ( this.wireframe !== false ) output.wireframe = this.wireframe;
 
-	function renderSprite( v1, element, material ) {
+		return output;
 
-		setOpacity( material.opacity );
-		setBlending( material.blending );
+	},
 
-		var width, height, scaleX, scaleY,
-		bitmap, bitmapWidth, bitmapHeight;
+	clone: function ( material ) {
 
-		if ( material instanceof THREE.SpriteMaterial ||
-			 material instanceof THREE.ParticleSystemMaterial ) { // Backwards compatibility
+		if ( material === undefined ) material = new THREE.Material();
 
-			if ( material.map.image !== undefined ) {
+		material.name = this.name;
 
-				bitmap = material.map.image;
-				bitmapWidth = bitmap.width >> 1;
-				bitmapHeight = bitmap.height >> 1;
+		material.side = this.side;
 
-				scaleX = element.scale.x * _canvasWidthHalf;
-				scaleY = element.scale.y * _canvasHeightHalf;
+		material.opacity = this.opacity;
+		material.transparent = this.transparent;
 
-				width = scaleX * bitmapWidth;
-				height = scaleY * bitmapHeight;
+		material.blending = this.blending;
 
-				// TODO: Rotations break this...
+		material.blendSrc = this.blendSrc;
+		material.blendDst = this.blendDst;
+		material.blendEquation = this.blendEquation;
+		material.blendSrcAlpha = this.blendSrcAlpha;
+		material.blendDstAlpha = this.blendDstAlpha;
+		material.blendEquationAlpha = this.blendEquationAlpha;
 
-				_elemBox.min.set( v1.x - width, v1.y - height );
-				_elemBox.max.set( v1.x + width, v1.y + height );
+		material.depthTest = this.depthTest;
+		material.depthWrite = this.depthWrite;
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+		material.polygonOffset = this.polygonOffset;
+		material.polygonOffsetFactor = this.polygonOffsetFactor;
+		material.polygonOffsetUnits = this.polygonOffsetUnits;
 
-					_elemBox.makeEmpty();
-					return;
+		material.alphaTest = this.alphaTest;
 
-				}
+		material.overdraw = this.overdraw;
 
-				_context.save();
-				_context.translate( v1.x, v1.y );
-				_context.rotate( - element.rotation );
-				_context.scale( scaleX, - scaleY );
+		material.visible = this.visible;
 
-				_context.translate( - bitmapWidth, - bitmapHeight );
-				_context.drawImage( bitmap, 0, 0 );
-				_context.restore();
+		return material;
 
-			} else {
+	},
 
-				scaleX = element.object.scale.x;
-				scaleY = element.object.scale.y;
+	update: function () {
 
-				// TODO: Be able to disable this
+		this.dispatchEvent( { type: 'update' } );
 
-				scaleX *= element.scale.x * _canvasWidthHalf;
-				scaleY *= element.scale.y * _canvasHeightHalf;
+	},
 
-				_elemBox.min.set( v1.x - scaleX, v1.y - scaleY );
-				_elemBox.max.set( v1.x + scaleX, v1.y + scaleY );
+	dispose: function () {
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+		this.dispatchEvent( { type: 'dispose' } );
 
-					_elemBox.makeEmpty();
-					return;
+	}
 
-				}
+};
 
-				setFillStyle( material.color.getStyle() );
+THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );
 
-				_context.save();
-				_context.translate( v1.x, v1.y );
-				_context.rotate( - element.rotation );
-				_context.scale( scaleX, scaleY );
-				_context.fillRect( -1, -1, 2, 2 );
-				_context.restore();
+THREE.MaterialIdCount = 0;
 
-			}
+// File:src/materials/LineBasicMaterial.js
 
-			/* DEBUG
-			setStrokeStyle( 'rgb(255,255,0)' );
-			_context.beginPath();
-			_context.moveTo( v1.x - 10, v1.y );
-			_context.lineTo( v1.x + 10, v1.y );
-			_context.moveTo( v1.x, v1.y - 10 );
-			_context.lineTo( v1.x, v1.y + 10 );
-			_context.stroke();
-			*/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  linewidth: <float>,
+ *  linecap: "round",
+ *  linejoin: "round",
+ *
+ *  vertexColors: <bool>
+ *
+ *  fog: <bool>
+ * }
+ */
 
-		} else if ( material instanceof THREE.SpriteCanvasMaterial ) {
+THREE.LineBasicMaterial = function ( parameters ) {
 
-			width = element.scale.x * _canvasWidthHalf;
-			height = element.scale.y * _canvasHeightHalf;
+	THREE.Material.call( this );
 
-			_elemBox.min.set( v1.x - width, v1.y - height );
-			_elemBox.max.set( v1.x + width, v1.y + height );
+	this.type = 'LineBasicMaterial';
 
-			if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+	this.color = new THREE.Color( 0xffffff );
 
-				_elemBox.makeEmpty();
-				return;
+	this.linewidth = 1;
+	this.linecap = 'round';
+	this.linejoin = 'round';
 
-			}
+	this.vertexColors = THREE.NoColors;
 
-			setStrokeStyle( material.color.getStyle() );
-			setFillStyle( material.color.getStyle() );
+	this.fog = true;
 
-			_context.save();
-			_context.translate( v1.x, v1.y );
-			_context.rotate( - element.rotation );
-			_context.scale( width, height );
+	this.setValues( parameters );
 
-			material.program( _context );
+};
 
-			_context.restore();
+THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial;
 
-		}
+THREE.LineBasicMaterial.prototype.clone = function () {
 
-	}
+	var material = new THREE.LineBasicMaterial();
 
-	function renderLine( v1, v2, element, material ) {
+	THREE.Material.prototype.clone.call( this, material );
 
-		setOpacity( material.opacity );
-		setBlending( material.blending );
+	material.color.copy( this.color );
 
-		_context.beginPath();
-		_context.moveTo( v1.positionScreen.x, v1.positionScreen.y );
-		_context.lineTo( v2.positionScreen.x, v2.positionScreen.y );
+	material.linewidth = this.linewidth;
+	material.linecap = this.linecap;
+	material.linejoin = this.linejoin;
 
-		if ( material instanceof THREE.LineBasicMaterial ) {
+	material.vertexColors = this.vertexColors;
 
-			setLineWidth( material.linewidth );
-			setLineCap( material.linecap );
-			setLineJoin( material.linejoin );
+	material.fog = this.fog;
 
-			if ( material.vertexColors !== THREE.VertexColors ) {
+	return material;
 
-				setStrokeStyle( material.color.getStyle() );
+};
 
-			} else {
+// File:src/materials/LineDashedMaterial.js
 
-				var colorStyle1 = element.vertexColors[0].getStyle();
-				var colorStyle2 = element.vertexColors[1].getStyle();
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  linewidth: <float>,
+ *
+ *  scale: <float>,
+ *  dashSize: <float>,
+ *  gapSize: <float>,
+ *
+ *  vertexColors: <bool>
+ *
+ *  fog: <bool>
+ * }
+ */
 
-				if ( colorStyle1 === colorStyle2 ) {
+THREE.LineDashedMaterial = function ( parameters ) {
 
-					setStrokeStyle( colorStyle1 );
+	THREE.Material.call( this );
 
-				} else {
+	this.type = 'LineDashedMaterial';
 
-					try {
+	this.color = new THREE.Color( 0xffffff );
 
-						var grad = _context.createLinearGradient(
-							v1.positionScreen.x,
-							v1.positionScreen.y,
-							v2.positionScreen.x,
-							v2.positionScreen.y
-						);
-						grad.addColorStop( 0, colorStyle1 );
-						grad.addColorStop( 1, colorStyle2 );
+	this.linewidth = 1;
 
-					} catch ( exception ) {
+	this.scale = 1;
+	this.dashSize = 3;
+	this.gapSize = 1;
 
-						grad = colorStyle1;
+	this.vertexColors = false;
 
-					}
+	this.fog = true;
 
-					setStrokeStyle( grad );
+	this.setValues( parameters );
 
-				}
+};
 
-			}
+THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;
 
-			_context.stroke();
-			_elemBox.expandByScalar( material.linewidth * 2 );
+THREE.LineDashedMaterial.prototype.clone = function () {
 
-		} else if ( material instanceof THREE.LineDashedMaterial ) {
+	var material = new THREE.LineDashedMaterial();
 
-			setLineWidth( material.linewidth );
-			setLineCap( material.linecap );
-			setLineJoin( material.linejoin );
-			setStrokeStyle( material.color.getStyle() );
-			setDashAndGap( material.dashSize, material.gapSize );
+	THREE.Material.prototype.clone.call( this, material );
 
-			_context.stroke();
+	material.color.copy( this.color );
 
-			_elemBox.expandByScalar( material.linewidth * 2 );
+	material.linewidth = this.linewidth;
 
-			setDashAndGap( null, null );
+	material.scale = this.scale;
+	material.dashSize = this.dashSize;
+	material.gapSize = this.gapSize;
 
-		}
+	material.vertexColors = this.vertexColors;
 
-	}
+	material.fog = this.fog;
 
-	function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) {
+	return material;
 
-		_this.info.render.vertices += 3;
-		_this.info.render.faces ++;
+};
 
-		setOpacity( material.opacity );
-		setBlending( material.blending );
+// File:src/materials/MeshBasicMaterial.js
 
-		_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
-		_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
-		_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  lightMap: new THREE.Texture( <Image> ),
+ *
+ *  specularMap: new THREE.Texture( <Image> ),
+ *
+ *  alphaMap: new THREE.Texture( <Image> ),
+ *
+ *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ *  combine: THREE.Multiply,
+ *  reflectivity: <float>,
+ *  refractionRatio: <float>,
+ *
+ *  shading: THREE.SmoothShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>,
+ *
+ *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ *  skinning: <bool>,
+ *  morphTargets: <bool>,
+ *
+ *  fog: <bool>
+ * }
+ */
 
-		drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y );
+THREE.MeshBasicMaterial = function ( parameters ) {
 
-		if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) {
+	THREE.Material.call( this );
 
-			_diffuseColor.copy( material.color );
-			_emissiveColor.copy( material.emissive );
+	this.type = 'MeshBasicMaterial';
 
-			if ( material.vertexColors === THREE.FaceColors ) {
+	this.color = new THREE.Color( 0xffffff ); // emissive
 
-				_diffuseColor.multiply( element.color );
+	this.map = null;
 
-			}
+	this.lightMap = null;
 
-			if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) {
+	this.specularMap = null;
 
-				_color1.copy( _ambientLight );
-				_color2.copy( _ambientLight );
-				_color3.copy( _ambientLight );
+	this.alphaMap = null;
 
-				calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
-				calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
-				calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color3 );
+	this.envMap = null;
+	this.combine = THREE.MultiplyOperation;
+	this.reflectivity = 1;
+	this.refractionRatio = 0.98;
 
-				_color1.multiply( _diffuseColor ).add( _emissiveColor );
-				_color2.multiply( _diffuseColor ).add( _emissiveColor );
-				_color3.multiply( _diffuseColor ).add( _emissiveColor );
-				_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+	this.fog = true;
 
-				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+	this.shading = THREE.SmoothShading;
 
-				clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+	this.wireframeLinecap = 'round';
+	this.wireframeLinejoin = 'round';
 
-			} else {
+	this.vertexColors = THREE.NoColors;
 
-				_color.copy( _ambientLight );
+	this.skinning = false;
+	this.morphTargets = false;
 
-				calculateLight( element.centroidModel, element.normalModel, _color );
+	this.setValues( parameters );
 
-				_color.multiply( _diffuseColor ).add( _emissiveColor );
+};
 
-				material.wireframe === true
-					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
-					: fillPath( _color );
+THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial;
 
-			}
+THREE.MeshBasicMaterial.prototype.clone = function () {
 
-		} else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
+	var material = new THREE.MeshBasicMaterial();
 
-			if ( material.map !== null ) {
+	THREE.Material.prototype.clone.call( this, material );
 
-				if ( material.map.mapping instanceof THREE.UVMapping ) {
+	material.color.copy( this.color );
 
-					_uvs = element.uvs[ 0 ];
-					patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map );
+	material.map = this.map;
 
-				}
+	material.lightMap = this.lightMap;
 
+	material.specularMap = this.specularMap;
 
-			} else if ( material.envMap !== null ) {
+	material.alphaMap = this.alphaMap;
 
-				if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) {
+	material.envMap = this.envMap;
+	material.combine = this.combine;
+	material.reflectivity = this.reflectivity;
+	material.refractionRatio = this.refractionRatio;
 
-					_vector3.copy( element.vertexNormalsModelView[ uv1 ] );
-					_uv1x = 0.5 * _vector3.x + 0.5;
-					_uv1y = 0.5 * _vector3.y + 0.5;
+	material.fog = this.fog;
 
-					_vector3.copy( element.vertexNormalsModelView[ uv2 ] );
-					_uv2x = 0.5 * _vector3.x + 0.5;
-					_uv2y = 0.5 * _vector3.y + 0.5;
+	material.shading = this.shading;
 
-					_vector3.copy( element.vertexNormalsModelView[ uv3 ] );
-					_uv3x = 0.5 * _vector3.x + 0.5;
-					_uv3y = 0.5 * _vector3.y + 0.5;
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+	material.wireframeLinecap = this.wireframeLinecap;
+	material.wireframeLinejoin = this.wireframeLinejoin;
 
-					patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
+	material.vertexColors = this.vertexColors;
 
-				}/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) {
+	material.skinning = this.skinning;
+	material.morphTargets = this.morphTargets;
 
+	return material;
 
+};
 
-				}*/
+// File:src/materials/MeshLambertMaterial.js
 
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  emissive: <hex>,
+ *  opacity: <float>,
+ *
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  lightMap: new THREE.Texture( <Image> ),
+ *
+ *  specularMap: new THREE.Texture( <Image> ),
+ *
+ *  alphaMap: new THREE.Texture( <Image> ),
+ *
+ *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ *  combine: THREE.Multiply,
+ *  reflectivity: <float>,
+ *  refractionRatio: <float>,
+ *
+ *  shading: THREE.SmoothShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>,
+ *
+ *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ *  skinning: <bool>,
+ *  morphTargets: <bool>,
+ *  morphNormals: <bool>,
+ *
+ *	fog: <bool>
+ * }
+ */
 
-			} else {
+THREE.MeshLambertMaterial = function ( parameters ) {
 
-				_color.copy( material.color );
+	THREE.Material.call( this );
 
-				if ( material.vertexColors === THREE.FaceColors ) {
+	this.type = 'MeshLambertMaterial';
 
-					_color.multiply( element.color );
+	this.color = new THREE.Color( 0xffffff ); // diffuse
+	this.emissive = new THREE.Color( 0x000000 );
 
-				}
+	this.wrapAround = false;
+	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
 
-				material.wireframe === true
-					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
-					: fillPath( _color );
+	this.map = null;
 
-			}
+	this.lightMap = null;
 
-		} else if ( material instanceof THREE.MeshDepthMaterial ) {
+	this.specularMap = null;
 
-			_near = _camera.near;
-			_far = _camera.far;
+	this.alphaMap = null;
 
-			_color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _near, _far );
-			_color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z * v2.positionScreen.w, _near, _far );
-			_color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far );
-			_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+	this.envMap = null;
+	this.combine = THREE.MultiplyOperation;
+	this.reflectivity = 1;
+	this.refractionRatio = 0.98;
 
-			_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+	this.fog = true;
 
-			clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+	this.shading = THREE.SmoothShading;
 
-		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+	this.wireframeLinecap = 'round';
+	this.wireframeLinejoin = 'round';
 
-			var normal;
+	this.vertexColors = THREE.NoColors;
 
-			if ( material.shading == THREE.FlatShading ) {
+	this.skinning = false;
+	this.morphTargets = false;
+	this.morphNormals = false;
 
-				normal = element.normalModelView;
+	this.setValues( parameters );
 
-				_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+};
 
-				material.wireframe === true
-					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
-					: fillPath( _color );
+THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial;
 
-			} else if ( material.shading == THREE.SmoothShading ) {
+THREE.MeshLambertMaterial.prototype.clone = function () {
 
-				normal = element.vertexNormalsModelView[ uv1 ];
-				_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+	var material = new THREE.MeshLambertMaterial();
 
-				normal = element.vertexNormalsModelView[ uv2 ];
-				_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+	THREE.Material.prototype.clone.call( this, material );
 
-				normal = element.vertexNormalsModelView[ uv3 ];
-				_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+	material.color.copy( this.color );
+	material.emissive.copy( this.emissive );
 
-				_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+	material.wrapAround = this.wrapAround;
+	material.wrapRGB.copy( this.wrapRGB );
 
-				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+	material.map = this.map;
 
-				clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+	material.lightMap = this.lightMap;
 
-			}
+	material.specularMap = this.specularMap;
 
-		}
+	material.alphaMap = this.alphaMap;
 
-	}
+	material.envMap = this.envMap;
+	material.combine = this.combine;
+	material.reflectivity = this.reflectivity;
+	material.refractionRatio = this.refractionRatio;
 
-	//
+	material.fog = this.fog;
 
-	function drawTriangle( x0, y0, x1, y1, x2, y2 ) {
+	material.shading = this.shading;
 
-		_context.beginPath();
-		_context.moveTo( x0, y0 );
-		_context.lineTo( x1, y1 );
-		_context.lineTo( x2, y2 );
-		_context.closePath();
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+	material.wireframeLinecap = this.wireframeLinecap;
+	material.wireframeLinejoin = this.wireframeLinejoin;
 
-	}
+	material.vertexColors = this.vertexColors;
 
-	function strokePath( color, linewidth, linecap, linejoin ) {
+	material.skinning = this.skinning;
+	material.morphTargets = this.morphTargets;
+	material.morphNormals = this.morphNormals;
 
-		setLineWidth( linewidth );
-		setLineCap( linecap );
-		setLineJoin( linejoin );
-		setStrokeStyle( color.getStyle() );
+	return material;
 
-		_context.stroke();
+};
 
-		_elemBox.expandByScalar( linewidth * 2 );
+// File:src/materials/MeshPhongMaterial.js
 
-	}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  emissive: <hex>,
+ *  specular: <hex>,
+ *  shininess: <float>,
+ *  opacity: <float>,
+ *
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  lightMap: new THREE.Texture( <Image> ),
+ *
+ *  bumpMap: new THREE.Texture( <Image> ),
+ *  bumpScale: <float>,
+ *
+ *  normalMap: new THREE.Texture( <Image> ),
+ *  normalScale: <Vector2>,
+ *
+ *  specularMap: new THREE.Texture( <Image> ),
+ *
+ *  alphaMap: new THREE.Texture( <Image> ),
+ *
+ *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ *  combine: THREE.Multiply,
+ *  reflectivity: <float>,
+ *  refractionRatio: <float>,
+ *
+ *  shading: THREE.SmoothShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>,
+ *
+ *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ *  skinning: <bool>,
+ *  morphTargets: <bool>,
+ *  morphNormals: <bool>,
+ *
+ *	fog: <bool>
+ * }
+ */
 
-	function fillPath( color ) {
+THREE.MeshPhongMaterial = function ( parameters ) {
 
-		setFillStyle( color.getStyle() );
-		_context.fill();
+	THREE.Material.call( this );
 
-	}
+	this.type = 'MeshPhongMaterial';
 
-	function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) {
+	this.color = new THREE.Color( 0xffffff ); // diffuse
+	this.emissive = new THREE.Color( 0x000000 );
+	this.specular = new THREE.Color( 0x111111 );
+	this.shininess = 30;
 
-		if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return;
+	this.metal = false;
 
-		if ( texture.needsUpdate === true ) {
+	this.wrapAround = false;
+	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
 
-			var repeatX = texture.wrapS == THREE.RepeatWrapping;
-			var repeatY = texture.wrapT == THREE.RepeatWrapping;
+	this.map = null;
 
-			_patterns[ texture.id ] = _context.createPattern(
-				texture.image, repeatX === true && repeatY === true
-					? 'repeat'
-					: repeatX === true && repeatY === false
-						? 'repeat-x'
-						: repeatX === false && repeatY === true
-							? 'repeat-y'
-							: 'no-repeat'
-			);
+	this.lightMap = null;
 
-			texture.needsUpdate = false;
+	this.bumpMap = null;
+	this.bumpScale = 1;
 
-		}
+	this.normalMap = null;
+	this.normalScale = new THREE.Vector2( 1, 1 );
 
-		_patterns[ texture.id ] === undefined
-			? setFillStyle( 'rgba(0,0,0,1)' )
-			: setFillStyle( _patterns[ texture.id ] );
+	this.specularMap = null;
 
-		// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+	this.alphaMap = null;
 
-		var a, b, c, d, e, f, det, idet,
-		offsetX = texture.offset.x / texture.repeat.x,
-		offsetY = texture.offset.y / texture.repeat.y,
-		width = texture.image.width * texture.repeat.x,
-		height = texture.image.height * texture.repeat.y;
+	this.envMap = null;
+	this.combine = THREE.MultiplyOperation;
+	this.reflectivity = 1;
+	this.refractionRatio = 0.98;
 
-		u0 = ( u0 + offsetX ) * width;
-		v0 = ( 1.0 - v0 + offsetY ) * height;
+	this.fog = true;
 
-		u1 = ( u1 + offsetX ) * width;
-		v1 = ( 1.0 - v1 + offsetY ) * height;
+	this.shading = THREE.SmoothShading;
 
-		u2 = ( u2 + offsetX ) * width;
-		v2 = ( 1.0 - v2 + offsetY ) * height;
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+	this.wireframeLinecap = 'round';
+	this.wireframeLinejoin = 'round';
 
-		x1 -= x0; y1 -= y0;
-		x2 -= x0; y2 -= y0;
+	this.vertexColors = THREE.NoColors;
 
-		u1 -= u0; v1 -= v0;
-		u2 -= u0; v2 -= v0;
+	this.skinning = false;
+	this.morphTargets = false;
+	this.morphNormals = false;
 
-		det = u1 * v2 - u2 * v1;
+	this.setValues( parameters );
 
-		if ( det === 0 ) {
+};
 
-			if ( _imagedatas[ texture.id ] === undefined ) {
+THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial;
 
-				var canvas = document.createElement( 'canvas' )
-				canvas.width = texture.image.width;
-				canvas.height = texture.image.height;
+THREE.MeshPhongMaterial.prototype.clone = function () {
 
-				var context = canvas.getContext( '2d' );
-				context.drawImage( texture.image, 0, 0 );
+	var material = new THREE.MeshPhongMaterial();
 
-				_imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data;
+	THREE.Material.prototype.clone.call( this, material );
 
-			}
+	material.color.copy( this.color );
+	material.emissive.copy( this.emissive );
+	material.specular.copy( this.specular );
+	material.shininess = this.shininess;
 
-			var data = _imagedatas[ texture.id ];
-			var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4;
+	material.metal = this.metal;
 
-			_color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 );
-			fillPath( _color );
+	material.wrapAround = this.wrapAround;
+	material.wrapRGB.copy( this.wrapRGB );
 
-			return;
+	material.map = this.map;
 
-		}
+	material.lightMap = this.lightMap;
 
-		idet = 1 / det;
+	material.bumpMap = this.bumpMap;
+	material.bumpScale = this.bumpScale;
 
-		a = ( v2 * x1 - v1 * x2 ) * idet;
-		b = ( v2 * y1 - v1 * y2 ) * idet;
-		c = ( u1 * x2 - u2 * x1 ) * idet;
-		d = ( u1 * y2 - u2 * y1 ) * idet;
+	material.normalMap = this.normalMap;
+	material.normalScale.copy( this.normalScale );
 
-		e = x0 - a * u0 - c * v0;
-		f = y0 - b * u0 - d * v0;
+	material.specularMap = this.specularMap;
 
-		_context.save();
-		_context.transform( a, b, c, d, e, f );
-		_context.fill();
-		_context.restore();
+	material.alphaMap = this.alphaMap;
 
-	}
+	material.envMap = this.envMap;
+	material.combine = this.combine;
+	material.reflectivity = this.reflectivity;
+	material.refractionRatio = this.refractionRatio;
 
-	function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) {
+	material.fog = this.fog;
 
-		// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+	material.shading = this.shading;
 
-		var a, b, c, d, e, f, det, idet,
-		width = image.width - 1,
-		height = image.height - 1;
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+	material.wireframeLinecap = this.wireframeLinecap;
+	material.wireframeLinejoin = this.wireframeLinejoin;
 
-		u0 *= width; v0 *= height;
-		u1 *= width; v1 *= height;
-		u2 *= width; v2 *= height;
+	material.vertexColors = this.vertexColors;
 
-		x1 -= x0; y1 -= y0;
-		x2 -= x0; y2 -= y0;
+	material.skinning = this.skinning;
+	material.morphTargets = this.morphTargets;
+	material.morphNormals = this.morphNormals;
 
-		u1 -= u0; v1 -= v0;
-		u2 -= u0; v2 -= v0;
+	return material;
 
-		det = u1 * v2 - u2 * v1;
+};
 
-		idet = 1 / det;
+// File:src/materials/MeshDepthMaterial.js
 
-		a = ( v2 * x1 - v1 * x2 ) * idet;
-		b = ( v2 * y1 - v1 * y2 ) * idet;
-		c = ( u1 * x2 - u2 * x1 ) * idet;
-		d = ( u1 * y2 - u2 * y1 ) * idet;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  opacity: <float>,
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>
+ * }
+ */
 
-		e = x0 - a * u0 - c * v0;
-		f = y0 - b * u0 - d * v0;
+THREE.MeshDepthMaterial = function ( parameters ) {
 
-		_context.save();
-		_context.transform( a, b, c, d, e, f );
-		_context.clip();
-		_context.drawImage( image, 0, 0 );
-		_context.restore();
+	THREE.Material.call( this );
 
-	}
+	this.type = 'MeshDepthMaterial';
 
-	function getGradientTexture( color1, color2, color3, color4 ) {
+	this.morphTargets = false;
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
 
-		// http://mrdoob.com/blog/post/710
+	this.setValues( parameters );
 
-		_pixelMapData[ 0 ] = ( color1.r * 255 ) | 0;
-		_pixelMapData[ 1 ] = ( color1.g * 255 ) | 0;
-		_pixelMapData[ 2 ] = ( color1.b * 255 ) | 0;
+};
 
-		_pixelMapData[ 4 ] = ( color2.r * 255 ) | 0;
-		_pixelMapData[ 5 ] = ( color2.g * 255 ) | 0;
-		_pixelMapData[ 6 ] = ( color2.b * 255 ) | 0;
+THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial;
 
-		_pixelMapData[ 8 ] = ( color3.r * 255 ) | 0;
-		_pixelMapData[ 9 ] = ( color3.g * 255 ) | 0;
-		_pixelMapData[ 10 ] = ( color3.b * 255 ) | 0;
+THREE.MeshDepthMaterial.prototype.clone = function () {
 
-		_pixelMapData[ 12 ] = ( color4.r * 255 ) | 0;
-		_pixelMapData[ 13 ] = ( color4.g * 255 ) | 0;
-		_pixelMapData[ 14 ] = ( color4.b * 255 ) | 0;
+	var material = new THREE.MeshDepthMaterial();
 
-		_pixelMapContext.putImageData( _pixelMapImage, 0, 0 );
-		_gradientMapContext.drawImage( _pixelMap, 0, 0 );
+	THREE.Material.prototype.clone.call( this, material );
 
-		return _gradientMap;
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
 
-	}
+	return material;
 
-	// Hide anti-alias gaps
+};
 
-	function expand( v1, v2, pixels ) {
+// File:src/materials/MeshNormalMaterial.js
 
-		var x = v2.x - v1.x, y = v2.y - v1.y,
-		det = x * x + y * y, idet;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ *  opacity: <float>,
+ *
+ *  shading: THREE.FlatShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>
+ * }
+ */
 
-		if ( det === 0 ) return;
+THREE.MeshNormalMaterial = function ( parameters ) {
 
-		idet = pixels / Math.sqrt( det );
+	THREE.Material.call( this, parameters );
 
-		x *= idet; y *= idet;
+	this.type = 'MeshNormalMaterial';
 
-		v2.x += x; v2.y += y;
-		v1.x -= x; v1.y -= y;
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
 
-	}
+	this.morphTargets = false;
 
-	// Context cached methods.
+	this.setValues( parameters );
 
-	function setOpacity( value ) {
+};
 
-		if ( _contextGlobalAlpha !== value ) {
+THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial;
 
-			_context.globalAlpha = value;
-			_contextGlobalAlpha = value;
+THREE.MeshNormalMaterial.prototype.clone = function () {
 
-		}
+	var material = new THREE.MeshNormalMaterial();
 
-	}
+	THREE.Material.prototype.clone.call( this, material );
 
-	function setBlending( value ) {
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
 
-		if ( _contextGlobalCompositeOperation !== value ) {
+	return material;
 
-			if ( value === THREE.NormalBlending ) {
+};
 
-				_context.globalCompositeOperation = 'source-over';
+// File:src/materials/MeshFaceMaterial.js
 
-			} else if ( value === THREE.AdditiveBlending ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				_context.globalCompositeOperation = 'lighter';
+THREE.MeshFaceMaterial = function ( materials ) {
 
-			} else if ( value === THREE.SubtractiveBlending ) {
+	this.uuid = THREE.Math.generateUUID();
 
-				_context.globalCompositeOperation = 'darker';
+	this.type = 'MeshFaceMaterial';
+	
+	this.materials = materials instanceof Array ? materials : [];
 
-			}
+};
 
-			_contextGlobalCompositeOperation = value;
+THREE.MeshFaceMaterial.prototype = {
 
-		}
+	constructor: THREE.MeshFaceMaterial,
 
-	}
+	toJSON: function () {
 
-	function setLineWidth( value ) {
+		var output = {
+			metadata: {
+				version: 4.2,
+				type: 'material',
+				generator: 'MaterialExporter'
+			},
+			uuid: this.uuid,
+			type: this.type,
+			materials: []
+		};
 
-		if ( _contextLineWidth !== value ) {
+		for ( var i = 0, l = this.materials.length; i < l; i ++ ) {
 
-			_context.lineWidth = value;
-			_contextLineWidth = value;
+			output.materials.push( this.materials[ i ].toJSON() );
 
 		}
 
-	}
-
-	function setLineCap( value ) {
+		return output;
 
-		// "butt", "round", "square"
-
-		if ( _contextLineCap !== value ) {
+	},
 
-			_context.lineCap = value;
-			_contextLineCap = value;
+	clone: function () {
 
-		}
+		var material = new THREE.MeshFaceMaterial();
 
-	}
+		for ( var i = 0; i < this.materials.length; i ++ ) {
 
-	function setLineJoin( value ) {
+			material.materials.push( this.materials[ i ].clone() );
 
-		// "round", "bevel", "miter"
+		}
 
-		if ( _contextLineJoin !== value ) {
+		return material;
 
-			_context.lineJoin = value;
-			_contextLineJoin = value;
+	}
 
-		}
+};
 
-	}
+// File:src/materials/PointCloudMaterial.js
 
-	function setStrokeStyle( value ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  size: <float>,
+ *  sizeAttenuation: <bool>,
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  vertexColors: <bool>,
+ *
+ *  fog: <bool>
+ * }
+ */
 
-		if ( _contextStrokeStyle !== value ) {
+THREE.PointCloudMaterial = function ( parameters ) {
 
-			_context.strokeStyle = value;
-			_contextStrokeStyle = value;
+	THREE.Material.call( this );
 
-		}
+	this.type = 'PointCloudMaterial';
 
-	}
+	this.color = new THREE.Color( 0xffffff );
 
-	function setFillStyle( value ) {
+	this.map = null;
 
-		if ( _contextFillStyle !== value ) {
+	this.size = 1;
+	this.sizeAttenuation = true;
 
-			_context.fillStyle = value;
-			_contextFillStyle = value;
+	this.vertexColors = THREE.NoColors;
 
-		}
+	this.fog = true;
 
-	}
+	this.setValues( parameters );
 
-	function setDashAndGap( dashSizeValue, gapSizeValue ) {
+};
 
-		if ( _contextDashSize !== dashSizeValue || _contextGapSize !== gapSizeValue ) {
+THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.PointCloudMaterial.prototype.constructor = THREE.PointCloudMaterial;
 
-			_context.setLineDash( [ dashSizeValue, gapSizeValue ] );
-			_contextDashSize = dashSizeValue;
-			_contextGapSize = gapSizeValue;
+THREE.PointCloudMaterial.prototype.clone = function () {
 
-		}
+	var material = new THREE.PointCloudMaterial();
 
-	}
+	THREE.Material.prototype.clone.call( this, material );
 
-};
+	material.color.copy( this.color );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- * @author mikael emtinger / http://gomo.se/
- */
+	material.map = this.map;
 
-THREE.ShaderChunk = {
+	material.size = this.size;
+	material.sizeAttenuation = this.sizeAttenuation;
 
-	// FOG
+	material.vertexColors = this.vertexColors;
 
-	fog_pars_fragment: [
+	material.fog = this.fog;
 
-		"#ifdef USE_FOG",
+	return material;
 
-			"uniform vec3 fogColor;",
+};
 
-			"#ifdef FOG_EXP2",
+// backwards compatibility
 
-				"uniform float fogDensity;",
+THREE.ParticleBasicMaterial = function ( parameters ) {
 
-			"#else",
+	THREE.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' );
+	return new THREE.PointCloudMaterial( parameters );
 
-				"uniform float fogNear;",
-				"uniform float fogFar;",
+};
 
-			"#endif",
+THREE.ParticleSystemMaterial = function ( parameters ) {
 
-		"#endif"
+	THREE.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' );
+	return new THREE.PointCloudMaterial( parameters );
 
-	].join("\n"),
+};
 
-	fog_fragment: [
+// File:src/materials/ShaderMaterial.js
 
-		"#ifdef USE_FOG",
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  defines: { "label" : "value" },
+ *  uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
+ *
+ *  fragmentShader: <string>,
+ *  vertexShader: <string>,
+ *
+ *  shading: THREE.SmoothShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>,
+ *
+ *  lights: <bool>,
+ *
+ *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ *  skinning: <bool>,
+ *  morphTargets: <bool>,
+ *  morphNormals: <bool>,
+ *
+ *	fog: <bool>
+ * }
+ */
 
-			"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+THREE.ShaderMaterial = function ( parameters ) {
 
-			"#ifdef FOG_EXP2",
+	THREE.Material.call( this );
 
-				"const float LOG2 = 1.442695;",
-				"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
-				"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
+	this.type = 'ShaderMaterial';
 
-			"#else",
+	this.defines = {};
+	this.uniforms = {};
+	this.attributes = null;
 
-				"float fogFactor = smoothstep( fogNear, fogFar, depth );",
+	this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
+	this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';
 
-			"#endif",
+	this.shading = THREE.SmoothShading;
 
-			"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
+	this.linewidth = 1;
 
-		"#endif"
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
 
-	].join("\n"),
+	this.fog = false; // set to use scene fog
 
-	// ENVIRONMENT MAP
+	this.lights = false; // set to use scene lights
 
-	envmap_pars_fragment: [
+	this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
 
-		"#ifdef USE_ENVMAP",
+	this.skinning = false; // set to use skinning attribute streams
 
-			"uniform float reflectivity;",
-			"uniform samplerCube envMap;",
-			"uniform float flipEnvMap;",
-			"uniform int combine;",
+	this.morphTargets = false; // set to use morph targets
+	this.morphNormals = false; // set to use morph normals
 
-			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+	// When rendered geometry doesn't include these attributes but the material does,
+	// use these default values in WebGL. This avoids errors when buffer data is missing.
+	this.defaultAttributeValues = {
+		'color': [ 1, 1, 1 ],
+		'uv': [ 0, 0 ],
+		'uv2': [ 0, 0 ]
+	};
 
-				"uniform bool useRefract;",
-				"uniform float refractionRatio;",
+	this.index0AttributeName = undefined;
 
-			"#else",
+	this.setValues( parameters );
 
-				"varying vec3 vReflect;",
+};
 
-			"#endif",
+THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial;
 
-		"#endif"
+THREE.ShaderMaterial.prototype.clone = function () {
 
-	].join("\n"),
+	var material = new THREE.ShaderMaterial();
 
-	envmap_fragment: [
+	THREE.Material.prototype.clone.call( this, material );
 
-		"#ifdef USE_ENVMAP",
+	material.fragmentShader = this.fragmentShader;
+	material.vertexShader = this.vertexShader;
 
-			"vec3 reflectVec;",
+	material.uniforms = THREE.UniformsUtils.clone( this.uniforms );
 
-			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+	material.attributes = this.attributes;
+	material.defines = this.defines;
 
-				"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+	material.shading = this.shading;
 
-				"if ( useRefract ) {",
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
 
-					"reflectVec = refract( cameraToVertex, normal, refractionRatio );",
+	material.fog = this.fog;
 
-				"} else { ",
+	material.lights = this.lights;
 
-					"reflectVec = reflect( cameraToVertex, normal );",
+	material.vertexColors = this.vertexColors;
 
-				"}",
+	material.skinning = this.skinning;
 
-			"#else",
+	material.morphTargets = this.morphTargets;
+	material.morphNormals = this.morphNormals;
 
-				"reflectVec = vReflect;",
+	return material;
 
-			"#endif",
+};
 
-			"#ifdef DOUBLE_SIDED",
+// File:src/materials/RawShaderMaterial.js
 
-				"float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
-				"vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			"#else",
+THREE.RawShaderMaterial = function ( parameters ) {
 
-				"vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+	THREE.ShaderMaterial.call( this, parameters );
 
-			"#endif",
+	this.type = 'RawShaderMaterial';
 
-			"#ifdef GAMMA_INPUT",
+};
 
-				"cubeColor.xyz *= cubeColor.xyz;",
+THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
+THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial;
 
-			"#endif",
+THREE.RawShaderMaterial.prototype.clone = function () {
 
-			"if ( combine == 1 ) {",
+	var material = new THREE.RawShaderMaterial();
 
-				"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );",
+	THREE.ShaderMaterial.prototype.clone.call( this, material );
 
-			"} else if ( combine == 2 ) {",
+	return material;
 
-				"gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;",
+};
 
-			"} else {",
+// File:src/materials/SpriteMaterial.js
 
-				"gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );",
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *	uvOffset: new THREE.Vector2(),
+ *	uvScale: new THREE.Vector2(),
+ *
+ *  fog: <bool>
+ * }
+ */
 
-			"}",
+THREE.SpriteMaterial = function ( parameters ) {
 
-		"#endif"
+	THREE.Material.call( this );
 
-	].join("\n"),
+	this.type = 'SpriteMaterial';
 
-	envmap_pars_vertex: [
+	this.color = new THREE.Color( 0xffffff );
+	this.map = null;
 
-		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+	this.rotation = 0;
 
-			"varying vec3 vReflect;",
+	this.fog = false;
 
-			"uniform float refractionRatio;",
-			"uniform bool useRefract;",
+	// set parameters
 
-		"#endif"
+	this.setValues( parameters );
 
-	].join("\n"),
+};
 
-	worldpos_vertex : [
+THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial;
 
-		"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )",
+THREE.SpriteMaterial.prototype.clone = function () {
 
-			"#ifdef USE_SKINNING",
+	var material = new THREE.SpriteMaterial();
 
-				"vec4 worldPosition = modelMatrix * skinned;",
+	THREE.Material.prototype.clone.call( this, material );
 
-			"#endif",
+	material.color.copy( this.color );
+	material.map = this.map;
 
-			"#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+	material.rotation = this.rotation;
 
-				"vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );",
+	material.fog = this.fog;
 
-			"#endif",
+	return material;
 
-			"#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+};
 
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+// File:src/textures/Texture.js
 
-			"#endif",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
 
-		"#endif"
+THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
 
-	].join("\n"),
+	Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );
 
-	envmap_vertex : [
+	this.uuid = THREE.Math.generateUUID();
 
-		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+	this.name = '';
+	this.sourceFile = '';
 
-			"vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;",
-			"worldNormal = normalize( worldNormal );",
+	this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
+	this.mipmaps = [];
 
-			"vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );",
+	this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;
 
-			"if ( useRefract ) {",
+	this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
+	this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
 
-				"vReflect = refract( cameraToVertex, worldNormal, refractionRatio );",
+	this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
+	this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
 
-			"} else {",
+	this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
 
-				"vReflect = reflect( cameraToVertex, worldNormal );",
+	this.format = format !== undefined ? format : THREE.RGBAFormat;
+	this.type = type !== undefined ? type : THREE.UnsignedByteType;
 
-			"}",
+	this.offset = new THREE.Vector2( 0, 0 );
+	this.repeat = new THREE.Vector2( 1, 1 );
 
-		"#endif"
+	this.generateMipmaps = true;
+	this.premultiplyAlpha = false;
+	this.flipY = true;
+	this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
 
-	].join("\n"),
+	this._needsUpdate = false;
+	this.onUpdate = null;
 
-	// COLOR MAP (particles)
+};
 
-	map_particle_pars_fragment: [
+THREE.Texture.DEFAULT_IMAGE = undefined;
+THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;
 
-		"#ifdef USE_MAP",
+THREE.Texture.prototype = {
 
-			"uniform sampler2D map;",
+	constructor: THREE.Texture,
 
-		"#endif"
+	get needsUpdate () {
 
-	].join("\n"),
+		return this._needsUpdate;
 
+	},
 
-	map_particle_fragment: [
+	set needsUpdate ( value ) {
 
-		"#ifdef USE_MAP",
+		if ( value === true ) this.update();
 
-			"gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );",
+		this._needsUpdate = value;
 
-		"#endif"
+	},
 
-	].join("\n"),
+	clone: function ( texture ) {
 
-	// COLOR MAP (triangles)
+		if ( texture === undefined ) texture = new THREE.Texture();
 
-	map_pars_vertex: [
+		texture.image = this.image;
+		texture.mipmaps = this.mipmaps.slice( 0 );
 
-		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+		texture.mapping = this.mapping;
 
-			"varying vec2 vUv;",
-			"uniform vec4 offsetRepeat;",
+		texture.wrapS = this.wrapS;
+		texture.wrapT = this.wrapT;
 
-		"#endif"
+		texture.magFilter = this.magFilter;
+		texture.minFilter = this.minFilter;
 
-	].join("\n"),
+		texture.anisotropy = this.anisotropy;
 
-	map_pars_fragment: [
+		texture.format = this.format;
+		texture.type = this.type;
 
-		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+		texture.offset.copy( this.offset );
+		texture.repeat.copy( this.repeat );
 
-			"varying vec2 vUv;",
+		texture.generateMipmaps = this.generateMipmaps;
+		texture.premultiplyAlpha = this.premultiplyAlpha;
+		texture.flipY = this.flipY;
+		texture.unpackAlignment = this.unpackAlignment;
 
-		"#endif",
+		return texture;
 
-		"#ifdef USE_MAP",
+	},
 
-			"uniform sampler2D map;",
+	update: function () {
 
-		"#endif"
+		this.dispatchEvent( { type: 'update' } );
 
-	].join("\n"),
+	},
 
-	map_vertex: [
+	dispose: function () {
 
-		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+		this.dispatchEvent( { type: 'dispose' } );
 
-			"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
+	}
 
-		"#endif"
+};
 
-	].join("\n"),
+THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );
 
-	map_fragment: [
+THREE.TextureIdCount = 0;
 
-		"#ifdef USE_MAP",
+// File:src/textures/CubeTexture.js
 
-			"vec4 texelColor = texture2D( map, vUv );",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			"#ifdef GAMMA_INPUT",
+THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
 
-				"texelColor.xyz *= texelColor.xyz;",
+	mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping;
+	
+	THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
 
-			"#endif",
+	this.images = images;
 
-			"gl_FragColor = gl_FragColor * texelColor;",
+};
 
-		"#endif"
+THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CubeTexture.prototype.constructor = THREE.CubeTexture;
 
-	].join("\n"),
+THREE.CubeTexture.clone = function ( texture ) {
 
-	// LIGHT MAP
+	if ( texture === undefined ) texture = new THREE.CubeTexture();
 
-	lightmap_pars_fragment: [
+	THREE.Texture.prototype.clone.call( this, texture );
 
-		"#ifdef USE_LIGHTMAP",
+	texture.images = this.images;
 
-			"varying vec2 vUv2;",
-			"uniform sampler2D lightMap;",
+	return texture;
 
-		"#endif"
+};
 
-	].join("\n"),
+// File:src/textures/CompressedTexture.js
 
-	lightmap_pars_vertex: [
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		"#ifdef USE_LIGHTMAP",
+THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
 
-			"varying vec2 vUv2;",
+	THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
 
-		"#endif"
+	this.image = { width: width, height: height };
+	this.mipmaps = mipmaps;
 
-	].join("\n"),
+	// no flipping for cube textures
+	// (also flipping doesn't work for compressed textures )
 
-	lightmap_fragment: [
+	this.flipY = false;
 
-		"#ifdef USE_LIGHTMAP",
+	// can't generate mipmaps for compressed textures
+	// mips must be embedded in DDS files
 
-			"gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );",
+	this.generateMipmaps = false;
 
-		"#endif"
+};
 
-	].join("\n"),
+THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture;
 
-	lightmap_vertex: [
+THREE.CompressedTexture.prototype.clone = function () {
 
-		"#ifdef USE_LIGHTMAP",
+	var texture = new THREE.CompressedTexture();
 
-			"vUv2 = uv2;",
+	THREE.Texture.prototype.clone.call( this, texture );
 
-		"#endif"
+	return texture;
 
-	].join("\n"),
+};
 
-	// BUMP MAP
+// File:src/textures/DataTexture.js
 
-	bumpmap_pars_fragment: [
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		"#ifdef USE_BUMPMAP",
+THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
 
-			"uniform sampler2D bumpMap;",
-			"uniform float bumpScale;",
+	THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
 
-			// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen
-			//	http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html
+	this.image = { data: data, width: width, height: height };
 
-			// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
+};
 
-			"vec2 dHdxy_fwd() {",
+THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.DataTexture.prototype.constructor = THREE.DataTexture;
 
-				"vec2 dSTdx = dFdx( vUv );",
-				"vec2 dSTdy = dFdy( vUv );",
+THREE.DataTexture.prototype.clone = function () {
 
-				"float Hll = bumpScale * texture2D( bumpMap, vUv ).x;",
-				"float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;",
-				"float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;",
+	var texture = new THREE.DataTexture();
 
-				"return vec2( dBx, dBy );",
+	THREE.Texture.prototype.clone.call( this, texture );
 
-			"}",
+	return texture;
 
-			"vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {",
+};
 
-				"vec3 vSigmaX = dFdx( surf_pos );",
-				"vec3 vSigmaY = dFdy( surf_pos );",
-				"vec3 vN = surf_norm;",		// normalized
+// File:src/textures/VideoTexture.js
 
-				"vec3 R1 = cross( vSigmaY, vN );",
-				"vec3 R2 = cross( vN, vSigmaX );",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				"float fDet = dot( vSigmaX, R1 );",
+THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
 
-				"vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );",
-				"return normalize( abs( fDet ) * surf_norm - vGrad );",
+	THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
 
-			"}",
+	this.generateMipmaps = false;
 
-		"#endif"
+	var scope = this;
 
-	].join("\n"),
+	var update = function () {
 
-	// NORMAL MAP
+		requestAnimationFrame( update );
 
-	normalmap_pars_fragment: [
+		if ( video.readyState === video.HAVE_ENOUGH_DATA ) {
 
-		"#ifdef USE_NORMALMAP",
+			scope.needsUpdate = true;
 
-			"uniform sampler2D normalMap;",
-			"uniform vec2 normalScale;",
+		}
 
-			// Per-Pixel Tangent Space Normal Mapping
-			// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+	};
 
-			"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {",
+	update();
 
-				"vec3 q0 = dFdx( eye_pos.xyz );",
-				"vec3 q1 = dFdy( eye_pos.xyz );",
-				"vec2 st0 = dFdx( vUv.st );",
-				"vec2 st1 = dFdy( vUv.st );",
+};
 
-				"vec3 S = normalize(  q0 * st1.t - q1 * st0.t );",
-				"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
-				"vec3 N = normalize( surf_norm );",
+THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;
 
-				"vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;",
-				"mapN.xy = normalScale * mapN.xy;",
-				"mat3 tsn = mat3( S, T, N );",
-				"return normalize( tsn * mapN );",
+// File:src/objects/Group.js
 
-			"}",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		"#endif"
+THREE.Group = function () {
 
-	].join("\n"),
+	THREE.Object3D.call( this );
 
-	// SPECULAR MAP
+	this.type = 'Group';
 
-	specularmap_pars_fragment: [
+};
 
-		"#ifdef USE_SPECULARMAP",
+THREE.Group.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Group.prototype.constructor = THREE.Group;
 
-			"uniform sampler2D specularMap;",
+// File:src/objects/PointCloud.js
 
-		"#endif"
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	].join("\n"),
+THREE.PointCloud = function ( geometry, material ) {
 
-	specularmap_fragment: [
+	THREE.Object3D.call( this );
 
-		"float specularStrength;",
+	this.type = 'PointCloud';
 
-		"#ifdef USE_SPECULARMAP",
+	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+	this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } );
 
-			"vec4 texelSpecular = texture2D( specularMap, vUv );",
-			"specularStrength = texelSpecular.r;",
+};
 
-		"#else",
+THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype );
+THREE.PointCloud.prototype.constructor = THREE.PointCloud;
 
-			"specularStrength = 1.0;",
+THREE.PointCloud.prototype.raycast = ( function () {
 
-		"#endif"
+	var inverseMatrix = new THREE.Matrix4();
+	var ray = new THREE.Ray();
 
-	].join("\n"),
+	return function ( raycaster, intersects ) {
 
-	// LIGHTS LAMBERT
+		var object = this;
+		var geometry = object.geometry;
+		var threshold = raycaster.params.PointCloud.threshold;
 
-	lights_lambert_pars_vertex: [
+		inverseMatrix.getInverse( this.matrixWorld );
+		ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
 
-		"uniform vec3 ambient;",
-		"uniform vec3 diffuse;",
-		"uniform vec3 emissive;",
+		if ( geometry.boundingBox !== null ) {
 
-		"uniform vec3 ambientLightColor;",
+			if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {
 
-		"#if MAX_DIR_LIGHTS > 0",
+				return;
 
-			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
-			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+			}
 
-		"#endif",
+		}
 
-		"#if MAX_HEMI_LIGHTS > 0",
+		var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
+		var position = new THREE.Vector3();
 
-			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
-			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
-			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+		var testPoint = function ( point, index ) {
 
-		"#endif",
+			var rayPointDistance = ray.distanceToPoint( point );
 
-		"#if MAX_POINT_LIGHTS > 0",
+			if ( rayPointDistance < localThreshold ) {
 
-			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
-			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
-			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+				var intersectPoint = ray.closestPointToPoint( point );
+				intersectPoint.applyMatrix4( object.matrixWorld );
 
-		"#endif",
+				var distance = raycaster.ray.origin.distanceTo( intersectPoint );
 
-		"#if MAX_SPOT_LIGHTS > 0",
+				intersects.push( {
 
-			"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
-			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
-			"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
+					distance: distance,
+					distanceToRay: rayPointDistance,
+					point: intersectPoint.clone(),
+					index: index,
+					face: null,
+					object: object
 
-		"#endif",
+				} );
 
-		"#ifdef WRAP_AROUND",
+			}
 
-			"uniform vec3 wrapRGB;",
+		};
 
-		"#endif"
+		if ( geometry instanceof THREE.BufferGeometry ) {
 
-	].join("\n"),
+			var attributes = geometry.attributes;
+			var positions = attributes.position.array;
 
-	lights_lambert_vertex: [
+			if ( attributes.index !== undefined ) {
 
-		"vLightFront = vec3( 0.0 );",
+				var indices = attributes.index.array;
+				var offsets = geometry.offsets;
 
-		"#ifdef DOUBLE_SIDED",
+				if ( offsets.length === 0 ) {
 
-			"vLightBack = vec3( 0.0 );",
+					var offset = {
+						start: 0,
+						count: indices.length,
+						index: 0
+					};
 
-		"#endif",
+					offsets = [ offset ];
 
-		"transformedNormal = normalize( transformedNormal );",
+				}
 
-		"#if MAX_DIR_LIGHTS > 0",
+				for ( var oi = 0, ol = offsets.length; oi < ol; ++ oi ) {
 
-		"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+					var start = offsets[ oi ].start;
+					var count = offsets[ oi ].count;
+					var index = offsets[ oi ].index;
 
-			"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
-			"vec3 dirVector = normalize( lDirection.xyz );",
+					for ( var i = start, il = start + count; i < il; i ++ ) {
 
-			"float dotProduct = dot( transformedNormal, dirVector );",
-			"vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+						var a = index + indices[ i ];
 
-			"#ifdef DOUBLE_SIDED",
+						position.fromArray( positions, a * 3 );
 
-				"vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+						testPoint( position, a );
 
-				"#ifdef WRAP_AROUND",
+					}
 
-					"vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+				}
 
-				"#endif",
+			} else {
 
-			"#endif",
+				var pointCount = positions.length / 3;
 
-			"#ifdef WRAP_AROUND",
+				for ( var i = 0; i < pointCount; i ++ ) {
 
-				"vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
-				"directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );",
+					position.set(
+						positions[ 3 * i ],
+						positions[ 3 * i + 1 ],
+						positions[ 3 * i + 2 ]
+					);
 
-				"#ifdef DOUBLE_SIDED",
+					testPoint( position, i );
 
-					"directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );",
+				}
 
-				"#endif",
+			}
 
-			"#endif",
+		} else {
 
-			"vLightFront += directionalLightColor[ i ] * directionalLightWeighting;",
+			var vertices = this.geometry.vertices;
 
-			"#ifdef DOUBLE_SIDED",
+			for ( var i = 0; i < vertices.length; i ++ ) {
 
-				"vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;",
+				testPoint( vertices[ i ], i );
 
-			"#endif",
+			}
 
-		"}",
+		}
 
-		"#endif",
+	};
 
-		"#if MAX_POINT_LIGHTS > 0",
+}() );
 
-			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+THREE.PointCloud.prototype.clone = function ( object ) {
 
-				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
-				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+	if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material );
 
-				"float lDistance = 1.0;",
-				"if ( pointLightDistance[ i ] > 0.0 )",
-					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+	THREE.Object3D.prototype.clone.call( this, object );
 
-				"lVector = normalize( lVector );",
-				"float dotProduct = dot( transformedNormal, lVector );",
+	return object;
 
-				"vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+};
 
-				"#ifdef DOUBLE_SIDED",
+// Backwards compatibility
 
-					"vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+THREE.ParticleSystem = function ( geometry, material ) {
 
-					"#ifdef WRAP_AROUND",
+	THREE.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' );
+	return new THREE.PointCloud( geometry, material );
 
-						"vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+};
 
-					"#endif",
+// File:src/objects/Line.js
 
-				"#endif",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				"#ifdef WRAP_AROUND",
+THREE.Line = function ( geometry, material, mode ) {
 
-					"vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
-					"pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );",
+	THREE.Object3D.call( this );
 
-					"#ifdef DOUBLE_SIDED",
+	this.type = 'Line';
 
-						"pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );",
+	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+	this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
 
-					"#endif",
+	this.mode = mode !== undefined ? mode : THREE.LineStrip;
 
-				"#endif",
+};
 
-				"vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;",
+THREE.LineStrip = 0;
+THREE.LinePieces = 1;
 
-				"#ifdef DOUBLE_SIDED",
+THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Line.prototype.constructor = THREE.Line;
 
-					"vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;",
+THREE.Line.prototype.raycast = ( function () {
 
-				"#endif",
+	var inverseMatrix = new THREE.Matrix4();
+	var ray = new THREE.Ray();
+	var sphere = new THREE.Sphere();
 
-			"}",
+	return function ( raycaster, intersects ) {
 
-		"#endif",
+		var precision = raycaster.linePrecision;
+		var precisionSq = precision * precision;
 
-		"#if MAX_SPOT_LIGHTS > 0",
+		var geometry = this.geometry;
 
-			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+		if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
 
-				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
-				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+		// Checking boundingSphere distance to ray
 
-				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );",
+		sphere.copy( geometry.boundingSphere );
+		sphere.applyMatrix4( this.matrixWorld );
 
-				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+		if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
 
-					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+			return;
 
-					"float lDistance = 1.0;",
-					"if ( spotLightDistance[ i ] > 0.0 )",
-						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+		}
 
-					"lVector = normalize( lVector );",
+		inverseMatrix.getInverse( this.matrixWorld );
+		ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
 
-					"float dotProduct = dot( transformedNormal, lVector );",
-					"vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+		var vStart = new THREE.Vector3();
+		var vEnd = new THREE.Vector3();
+		var interSegment = new THREE.Vector3();
+		var interRay = new THREE.Vector3();
+		var step = this.mode === THREE.LineStrip ? 1 : 2;
 
-					"#ifdef DOUBLE_SIDED",
+		if ( geometry instanceof THREE.BufferGeometry ) {
 
-						"vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+			var attributes = geometry.attributes;
 
-						"#ifdef WRAP_AROUND",
+			if ( attributes.index !== undefined ) {
 
-							"vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+				var indices = attributes.index.array;
+				var positions = attributes.position.array;
+				var offsets = geometry.offsets;
 
-						"#endif",
+				if ( offsets.length === 0 ) {
 
-					"#endif",
+					offsets = [ { start: 0, count: indices.length, index: 0 } ];
 
-					"#ifdef WRAP_AROUND",
+				}
 
-						"vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
-						"spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );",
+				for ( var oi = 0; oi < offsets.length; oi ++) {
 
-						"#ifdef DOUBLE_SIDED",
+					var start = offsets[ oi ].start;
+					var count = offsets[ oi ].count;
+					var index = offsets[ oi ].index;
 
-							"spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );",
+					for ( var i = start; i < start + count - 1; i += step ) {
 
-						"#endif",
+						var a = index + indices[ i ];
+						var b = index + indices[ i + 1 ];
 
-					"#endif",
+						vStart.fromArray( positions, a * 3 );
+						vEnd.fromArray( positions, b * 3 );
 
-					"vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;",
+						var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
 
-					"#ifdef DOUBLE_SIDED",
+						if ( distSq > precisionSq ) continue;
 
-						"vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;",
+						var distance = ray.origin.distanceTo( interRay );
 
-					"#endif",
+						if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
-				"}",
+						intersects.push( {
 
-			"}",
+							distance: distance,
+							// What do we want? intersection point on the ray or on the segment??
+							// point: raycaster.ray.at( distance ),
+							point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+							index: i,
+							offsetIndex: oi,
+							face: null,
+							faceIndex: null,
+							object: this
 
-		"#endif",
+						} );
 
-		"#if MAX_HEMI_LIGHTS > 0",
+					}
 
-			"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+				}
 
-				"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
-				"vec3 lVector = normalize( lDirection.xyz );",
+			} else {
 
-				"float dotProduct = dot( transformedNormal, lVector );",
+				var positions = attributes.position.array;
 
-				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
-				"float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;",
+				for ( var i = 0; i < positions.length / 3 - 1; i += step ) {
 
-				"vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+					vStart.fromArray( positions, 3 * i );
+					vEnd.fromArray( positions, 3 * i + 3 );
 
-				"#ifdef DOUBLE_SIDED",
+					var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
 
-					"vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );",
+					if ( distSq > precisionSq ) continue;
 
-				"#endif",
+					var distance = ray.origin.distanceTo( interRay );
 
-			"}",
+					if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
-		"#endif",
+					intersects.push( {
 
-		"vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
+						distance: distance,
+						// What do we want? intersection point on the ray or on the segment??
+						// point: raycaster.ray.at( distance ),
+						point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+						index: i,
+						face: null,
+						faceIndex: null,
+						object: this
 
-		"#ifdef DOUBLE_SIDED",
+					} );
 
-			"vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;",
+				}
 
-		"#endif"
+			}
 
-	].join("\n"),
+		} else if ( geometry instanceof THREE.Geometry ) {
 
-	// LIGHTS PHONG
+			var vertices = geometry.vertices;
+			var nbVertices = vertices.length;
 
-	lights_phong_pars_vertex: [
+			for ( var i = 0; i < nbVertices - 1; i += step ) {
 
-		"#ifndef PHONG_PER_PIXEL",
+				var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
 
-		"#if MAX_POINT_LIGHTS > 0",
+				if ( distSq > precisionSq ) continue;
 
-			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
-			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+				var distance = ray.origin.distanceTo( interRay );
 
-			"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+				if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
-		"#endif",
+				intersects.push( {
 
-		"#if MAX_SPOT_LIGHTS > 0",
+					distance: distance,
+					// What do we want? intersection point on the ray or on the segment??
+					// point: raycaster.ray.at( distance ),
+					point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+					index: i,
+					face: null,
+					faceIndex: null,
+					object: this
 
-			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+				} );
 
-			"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
+			}
 
-		"#endif",
+		}
 
-		"#endif",
+	};
 
-		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+}() );
 
-			"varying vec3 vWorldPosition;",
+THREE.Line.prototype.clone = function ( object ) {
 
-		"#endif"
+	if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.mode );
 
-	].join("\n"),
+	THREE.Object3D.prototype.clone.call( this, object );
 
+	return object;
 
-	lights_phong_vertex: [
+};
 
-		"#ifndef PHONG_PER_PIXEL",
+// File:src/objects/Mesh.js
 
-		"#if MAX_POINT_LIGHTS > 0",
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author jonobr1 / http://jonobr1.com/
+ */
 
-			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+THREE.Mesh = function ( geometry, material ) {
 
-				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
-				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+	THREE.Object3D.call( this );
 
-				"float lDistance = 1.0;",
-				"if ( pointLightDistance[ i ] > 0.0 )",
-					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+	this.type = 'Mesh';
+	
+	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+	this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );
 
-				"vPointLight[ i ] = vec4( lVector, lDistance );",
+	this.updateMorphTargets();
 
-			"}",
+};
 
-		"#endif",
+THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Mesh.prototype.constructor = THREE.Mesh;
 
-		"#if MAX_SPOT_LIGHTS > 0",
+THREE.Mesh.prototype.updateMorphTargets = function () {
 
-			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+	if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {
 
-				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
-				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+		this.morphTargetBase = - 1;
+		this.morphTargetForcedOrder = [];
+		this.morphTargetInfluences = [];
+		this.morphTargetDictionary = {};
 
-				"float lDistance = 1.0;",
-				"if ( spotLightDistance[ i ] > 0.0 )",
-					"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+		for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
 
-				"vSpotLight[ i ] = vec4( lVector, lDistance );",
+			this.morphTargetInfluences.push( 0 );
+			this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
 
-			"}",
+		}
 
-		"#endif",
+	}
 
-		"#endif",
+};
 
-		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
 
-			"vWorldPosition = worldPosition.xyz;",
+	if ( this.morphTargetDictionary[ name ] !== undefined ) {
 
-		"#endif"
+		return this.morphTargetDictionary[ name ];
 
-	].join("\n"),
+	}
 
-	lights_phong_pars_fragment: [
+	THREE.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );
 
-		"uniform vec3 ambientLightColor;",
+	return 0;
 
-		"#if MAX_DIR_LIGHTS > 0",
+};
 
-			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
-			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
 
-		"#endif",
+THREE.Mesh.prototype.raycast = ( function () {
 
-		"#if MAX_HEMI_LIGHTS > 0",
+	var inverseMatrix = new THREE.Matrix4();
+	var ray = new THREE.Ray();
+	var sphere = new THREE.Sphere();
 
-			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
-			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
-			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+	var vA = new THREE.Vector3();
+	var vB = new THREE.Vector3();
+	var vC = new THREE.Vector3();
 
-		"#endif",
+	return function ( raycaster, intersects ) {
 
-		"#if MAX_POINT_LIGHTS > 0",
+		var geometry = this.geometry;
 
-			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+		// Checking boundingSphere distance to ray
 
-			"#ifdef PHONG_PER_PIXEL",
+		if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
 
-				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
-				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+		sphere.copy( geometry.boundingSphere );
+		sphere.applyMatrix4( this.matrixWorld );
 
-			"#else",
+		if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
 
-				"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+			return;
 
-			"#endif",
+		}
 
-		"#endif",
+		// Check boundingBox before continuing
 
-		"#if MAX_SPOT_LIGHTS > 0",
+		inverseMatrix.getInverse( this.matrixWorld );
+		ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
 
-			"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
-			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
-			"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
-			"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
+		if ( geometry.boundingBox !== null ) {
 
-			"#ifdef PHONG_PER_PIXEL",
+			if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {
 
-				"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+				return;
 
-			"#else",
+			}
 
-				"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
+		}
 
-			"#endif",
+		if ( geometry instanceof THREE.BufferGeometry ) {
 
-		"#endif",
+			var material = this.material;
 
-		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+			if ( material === undefined ) return;
 
-			"varying vec3 vWorldPosition;",
+			var attributes = geometry.attributes;
 
-		"#endif",
+			var a, b, c;
+			var precision = raycaster.precision;
 
-		"#ifdef WRAP_AROUND",
+			if ( attributes.index !== undefined ) {
 
-			"uniform vec3 wrapRGB;",
+				var indices = attributes.index.array;
+				var positions = attributes.position.array;
+				var offsets = geometry.offsets;
 
-		"#endif",
+				if ( offsets.length === 0 ) {
 
-		"varying vec3 vViewPosition;",
-		"varying vec3 vNormal;"
+					offsets = [ { start: 0, count: indices.length, index: 0 } ];
 
-	].join("\n"),
+				}
 
-	lights_phong_fragment: [
+				for ( var oi = 0, ol = offsets.length; oi < ol; ++ oi ) {
 
-		"vec3 normal = normalize( vNormal );",
-		"vec3 viewPosition = normalize( vViewPosition );",
+					var start = offsets[ oi ].start;
+					var count = offsets[ oi ].count;
+					var index = offsets[ oi ].index;
 
-		"#ifdef DOUBLE_SIDED",
+					for ( var i = start, il = start + count; i < il; i += 3 ) {
 
-			"normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
+						a = index + indices[ i ];
+						b = index + indices[ i + 1 ];
+						c = index + indices[ i + 2 ];
 
-		"#endif",
+						vA.fromArray( positions, a * 3 );
+						vB.fromArray( positions, b * 3 );
+						vC.fromArray( positions, c * 3 );
 
-		"#ifdef USE_NORMALMAP",
+						if ( material.side === THREE.BackSide ) {
 
-			"normal = perturbNormal2Arb( -vViewPosition, normal );",
+							var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );
 
-		"#elif defined( USE_BUMPMAP )",
+						} else {
 
-			"normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
+							var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
 
-		"#endif",
+						}
 
-		"#if MAX_POINT_LIGHTS > 0",
+						if ( intersectionPoint === null ) continue;
 
-			"vec3 pointDiffuse  = vec3( 0.0 );",
-			"vec3 pointSpecular = vec3( 0.0 );",
+						intersectionPoint.applyMatrix4( this.matrixWorld );
 
-			"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+						var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
-				"#ifdef PHONG_PER_PIXEL",
+						if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
 
-					"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
-					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+						intersects.push( {
 
-					"float lDistance = 1.0;",
-					"if ( pointLightDistance[ i ] > 0.0 )",
-						"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+							distance: distance,
+							point: intersectionPoint,
+							face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ),
+							faceIndex: null,
+							object: this
 
-					"lVector = normalize( lVector );",
+						} );
 
-				"#else",
+					}
 
-					"vec3 lVector = normalize( vPointLight[ i ].xyz );",
-					"float lDistance = vPointLight[ i ].w;",
+				}
 
-				"#endif",
+			} else {
 
-				// diffuse
+				var positions = attributes.position.array;
 
-				"float dotProduct = dot( normal, lVector );",
+				for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) {
 
-				"#ifdef WRAP_AROUND",
+					a = i;
+					b = i + 1;
+					c = i + 2;
 
-					"float pointDiffuseWeightFull = max( dotProduct, 0.0 );",
-					"float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+					vA.fromArray( positions, j );
+					vB.fromArray( positions, j + 3 );
+					vC.fromArray( positions, j + 6 );
 
-					"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+					if ( material.side === THREE.BackSide ) {
 
-				"#else",
+						var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );
 
-					"float pointDiffuseWeight = max( dotProduct, 0.0 );",
+					} else {
 
-				"#endif",
+						var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
 
-				"pointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;",
+					}
 
-				// specular
+					if ( intersectionPoint === null ) continue;
 
-				"vec3 pointHalfVector = normalize( lVector + viewPosition );",
-				"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
-				"float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
+					intersectionPoint.applyMatrix4( this.matrixWorld );
 
-				"#ifdef PHYSICALLY_BASED_SHADING",
+					var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
-					// 2.0 => 2.0001 is hack to work around ANGLE bug
+					if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
 
-					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+					intersects.push( {
 
-					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );",
-					"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;",
+						distance: distance,
+						point: intersectionPoint,
+						face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ),
+						faceIndex: null,
+						object: this
 
-				"#else",
+					} );
 
-					"pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;",
+				}
 
-				"#endif",
+			}
 
-			"}",
+		} else if ( geometry instanceof THREE.Geometry ) {
 
-		"#endif",
+			var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial;
+			var objectMaterials = isFaceMaterial === true ? this.material.materials : null;
 
-		"#if MAX_SPOT_LIGHTS > 0",
+			var a, b, c;
+			var precision = raycaster.precision;
 
-			"vec3 spotDiffuse  = vec3( 0.0 );",
-			"vec3 spotSpecular = vec3( 0.0 );",
+			var vertices = geometry.vertices;
 
-			"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+			for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
 
-				"#ifdef PHONG_PER_PIXEL",
+				var face = geometry.faces[ f ];
 
-					"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
-					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+				var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material;
 
-					"float lDistance = 1.0;",
-					"if ( spotLightDistance[ i ] > 0.0 )",
-						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+				if ( material === undefined ) continue;
 
-					"lVector = normalize( lVector );",
+				a = vertices[ face.a ];
+				b = vertices[ face.b ];
+				c = vertices[ face.c ];
 
-				"#else",
+				if ( material.morphTargets === true ) {
 
-					"vec3 lVector = normalize( vSpotLight[ i ].xyz );",
-					"float lDistance = vSpotLight[ i ].w;",
+					var morphTargets = geometry.morphTargets;
+					var morphInfluences = this.morphTargetInfluences;
 
-				"#endif",
+					vA.set( 0, 0, 0 );
+					vB.set( 0, 0, 0 );
+					vC.set( 0, 0, 0 );
 
-				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+					for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
 
-				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+						var influence = morphInfluences[ t ];
 
-					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+						if ( influence === 0 ) continue;
 
-					// diffuse
+						var targets = morphTargets[ t ].vertices;
 
-					"float dotProduct = dot( normal, lVector );",
+						vA.x += ( targets[ face.a ].x - a.x ) * influence;
+						vA.y += ( targets[ face.a ].y - a.y ) * influence;
+						vA.z += ( targets[ face.a ].z - a.z ) * influence;
 
-					"#ifdef WRAP_AROUND",
+						vB.x += ( targets[ face.b ].x - b.x ) * influence;
+						vB.y += ( targets[ face.b ].y - b.y ) * influence;
+						vB.z += ( targets[ face.b ].z - b.z ) * influence;
 
-						"float spotDiffuseWeightFull = max( dotProduct, 0.0 );",
-						"float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+						vC.x += ( targets[ face.c ].x - c.x ) * influence;
+						vC.y += ( targets[ face.c ].y - c.y ) * influence;
+						vC.z += ( targets[ face.c ].z - c.z ) * influence;
 
-						"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+					}
 
-					"#else",
+					vA.add( a );
+					vB.add( b );
+					vC.add( c );
 
-						"float spotDiffuseWeight = max( dotProduct, 0.0 );",
+					a = vA;
+					b = vB;
+					c = vC;
 
-					"#endif",
+				}
 
-					"spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;",
+				if ( material.side === THREE.BackSide ) {
 
-					// specular
+					var intersectionPoint = ray.intersectTriangle( c, b, a, true );
 
-					"vec3 spotHalfVector = normalize( lVector + viewPosition );",
-					"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
-					"float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );",
+				} else {
 
-					"#ifdef PHYSICALLY_BASED_SHADING",
+					var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide );
 
-						// 2.0 => 2.0001 is hack to work around ANGLE bug
+				}
 
-						"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+				if ( intersectionPoint === null ) continue;
 
-						"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );",
-						"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;",
+				intersectionPoint.applyMatrix4( this.matrixWorld );
 
-					"#else",
+				var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
-						"spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;",
+				if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;
 
-					"#endif",
+				intersects.push( {
 
-				"}",
+					distance: distance,
+					point: intersectionPoint,
+					face: face,
+					faceIndex: f,
+					object: this
 
-			"}",
+				} );
 
-		"#endif",
+			}
 
-		"#if MAX_DIR_LIGHTS > 0",
+		}
 
-			"vec3 dirDiffuse  = vec3( 0.0 );",
-			"vec3 dirSpecular = vec3( 0.0 );" ,
+	};
 
-			"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+}() );
 
-				"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
-				"vec3 dirVector = normalize( lDirection.xyz );",
+THREE.Mesh.prototype.clone = function ( object, recursive ) {
 
-				// diffuse
+	if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
 
-				"float dotProduct = dot( normal, dirVector );",
+	THREE.Object3D.prototype.clone.call( this, object, recursive );
 
-				"#ifdef WRAP_AROUND",
+	return object;
 
-					"float dirDiffuseWeightFull = max( dotProduct, 0.0 );",
-					"float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+};
 
-					"vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );",
+// File:src/objects/Bone.js
 
-				"#else",
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
 
-					"float dirDiffuseWeight = max( dotProduct, 0.0 );",
+THREE.Bone = function ( skin ) {
 
-				"#endif",
+	THREE.Object3D.call( this );
 
-				"dirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;",
+	this.type = 'Bone';
 
-				// specular
+	this.skin = skin;
 
-				"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
-				"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
-				"float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );",
+};
 
-				"#ifdef PHYSICALLY_BASED_SHADING",
+THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Bone.prototype.constructor = THREE.Bone;
 
-					/*
-					// fresnel term from skin shader
-					"const float F0 = 0.128;",
+// File:src/objects/Skeleton.js
 
-					"float base = 1.0 - dot( viewPosition, dirHalfVector );",
-					"float exponential = pow( base, 5.0 );",
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author michael guerrero / http://realitymeltdown.com
+ * @author ikerr / http://verold.com
+ */
 
-					"float fresnel = exponential + F0 * ( 1.0 - exponential );",
-					*/
+THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) {
 
-					/*
-					// fresnel term from fresnel shader
-					"const float mFresnelBias = 0.08;",
-					"const float mFresnelScale = 0.3;",
-					"const float mFresnelPower = 5.0;",
+	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
 
-					"float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );",
-					*/
+	this.identityMatrix = new THREE.Matrix4();
 
-					// 2.0 => 2.0001 is hack to work around ANGLE bug
+	// copy the bone array
 
-					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+	bones = bones || [];
 
-					//"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;",
+	this.bones = bones.slice( 0 );
 
-					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
-					"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
+	// create a bone texture or an array of floats
 
-				"#else",
+	if ( this.useVertexTexture ) {
 
-					"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;",
+		// layout (1 matrix = 4 pixels)
+		//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+		//  with  8x8  pixel texture max   16 bones  (8 * 8  / 4)
+		//       16x16 pixel texture max   64 bones (16 * 16 / 4)
+		//       32x32 pixel texture max  256 bones (32 * 32 / 4)
+		//       64x64 pixel texture max 1024 bones (64 * 64 / 4)
 
-				"#endif",
+		var size;
 
-			"}",
+		if ( this.bones.length > 256 )
+			size = 64;
+		else if ( this.bones.length > 64 )
+			size = 32;
+		else if ( this.bones.length > 16 )
+			size = 16;
+		else
+			size = 8;
 
-		"#endif",
+		this.boneTextureWidth = size;
+		this.boneTextureHeight = size;
 
-		"#if MAX_HEMI_LIGHTS > 0",
+		this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
+		this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
+		this.boneTexture.minFilter = THREE.NearestFilter;
+		this.boneTexture.magFilter = THREE.NearestFilter;
+		this.boneTexture.generateMipmaps = false;
+		this.boneTexture.flipY = false;
 
-			"vec3 hemiDiffuse  = vec3( 0.0 );",
-			"vec3 hemiSpecular = vec3( 0.0 );" ,
+	} else {
 
-			"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+		this.boneMatrices = new Float32Array( 16 * this.bones.length );
 
-				"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
-				"vec3 lVector = normalize( lDirection.xyz );",
+	}
 
-				// diffuse
+	// use the supplied bone inverses or calculate the inverses
 
-				"float dotProduct = dot( normal, lVector );",
-				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+	if ( boneInverses === undefined ) {
 
-				"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+		this.calculateInverses();
 
-				"hemiDiffuse += diffuse * hemiColor;",
+	} else {
 
-				// specular (sky light)
+		if ( this.bones.length === boneInverses.length ) {
 
-				"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
-				"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
-				"float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
+			this.boneInverses = boneInverses.slice( 0 );
 
-				// specular (ground light)
+		} else {
 
-				"vec3 lVectorGround = -lVector;",
+			THREE.warn( 'THREE.Skeleton bonInverses is the wrong length.' );
 
-				"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
-				"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
-				"float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
+			this.boneInverses = [];
 
-				"#ifdef PHYSICALLY_BASED_SHADING",
+			for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-					"float dotProductGround = dot( normal, lVectorGround );",
+				this.boneInverses.push( new THREE.Matrix4() );
 
-					// 2.0 => 2.0001 is hack to work around ANGLE bug
+			}
 
-					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+		}
 
-					"vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
-					"vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
-					"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
+	}
 
-				"#else",
+};
 
-					"hemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
+THREE.Skeleton.prototype.calculateInverses = function () {
 
-				"#endif",
+	this.boneInverses = [];
 
-			"}",
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-		"#endif",
+		var inverse = new THREE.Matrix4();
 
-		"vec3 totalDiffuse = vec3( 0.0 );",
-		"vec3 totalSpecular = vec3( 0.0 );",
+		if ( this.bones[ b ] ) {
 
-		"#if MAX_DIR_LIGHTS > 0",
+			inverse.getInverse( this.bones[ b ].matrixWorld );
 
-			"totalDiffuse += dirDiffuse;",
-			"totalSpecular += dirSpecular;",
+		}
 
-		"#endif",
+		this.boneInverses.push( inverse );
 
-		"#if MAX_HEMI_LIGHTS > 0",
+	}
 
-			"totalDiffuse += hemiDiffuse;",
-			"totalSpecular += hemiSpecular;",
+};
 
-		"#endif",
+THREE.Skeleton.prototype.pose = function () {
 
-		"#if MAX_POINT_LIGHTS > 0",
+	var bone;
 
-			"totalDiffuse += pointDiffuse;",
-			"totalSpecular += pointSpecular;",
+	// recover the bind-time world matrices
 
-		"#endif",
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-		"#if MAX_SPOT_LIGHTS > 0",
+		bone = this.bones[ b ];
 
-			"totalDiffuse += spotDiffuse;",
-			"totalSpecular += spotSpecular;",
+		if ( bone ) {
 
-		"#endif",
+			bone.matrixWorld.getInverse( this.boneInverses[ b ] );
 
-		"#ifdef METAL",
+		}
 
-			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );",
+	}
 
-		"#else",
+	// compute the local matrices, positions, rotations and scales
 
-			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;",
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-		"#endif"
+		bone = this.bones[ b ];
 
-	].join("\n"),
+		if ( bone ) {
 
-	// VERTEX COLORS
+			if ( bone.parent ) {
 
-	color_pars_fragment: [
+				bone.matrix.getInverse( bone.parent.matrixWorld );
+				bone.matrix.multiply( bone.matrixWorld );
 
-		"#ifdef USE_COLOR",
+			} else {
 
-			"varying vec3 vColor;",
+				bone.matrix.copy( bone.matrixWorld );
 
-		"#endif"
+			}
 
-	].join("\n"),
+			bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
 
+		}
 
-	color_fragment: [
+	}
 
-		"#ifdef USE_COLOR",
+};
 
-			"gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );",
+THREE.Skeleton.prototype.update = ( function () {
 
-		"#endif"
+	var offsetMatrix = new THREE.Matrix4();
+	
+	return function () {
 
-	].join("\n"),
+		// flatten bone matrices to array
 
-	color_pars_vertex: [
+		for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-		"#ifdef USE_COLOR",
+			// compute the offset between the current and the original transform
 
-			"varying vec3 vColor;",
+			var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
 
-		"#endif"
+			offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
+			offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
 
-	].join("\n"),
+		}
 
+		if ( this.useVertexTexture ) {
 
-	color_vertex: [
+			this.boneTexture.needsUpdate = true;
 
-		"#ifdef USE_COLOR",
+		}
+		
+	};
 
-			"#ifdef GAMMA_INPUT",
+} )();
 
-				"vColor = color * color;",
 
-			"#else",
+// File:src/objects/SkinnedMesh.js
 
-				"vColor = color;",
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
 
-			"#endif",
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
-		"#endif"
+	THREE.Mesh.call( this, geometry, material );
 
-	].join("\n"),
+	this.type = 'SkinnedMesh';
 
-	// SKINNING
+	this.bindMode = "attached";
+	this.bindMatrix = new THREE.Matrix4();
+	this.bindMatrixInverse = new THREE.Matrix4();
 
-	skinning_pars_vertex: [
+	// init bones
 
-		"#ifdef USE_SKINNING",
+	// TODO: remove bone creation as there is no reason (other than
+	// convenience) for THREE.SkinnedMesh to do this.
 
-			"#ifdef BONE_TEXTURE",
+	var bones = [];
 
-				"uniform sampler2D boneTexture;",
-				"uniform int boneTextureWidth;",
-				"uniform int boneTextureHeight;",
+	if ( this.geometry && this.geometry.bones !== undefined ) {
 
-				"mat4 getBoneMatrix( const in float i ) {",
+		var bone, gbone, p, q, s;
 
-					"float j = i * 4.0;",
-					"float x = mod( j, float( boneTextureWidth ) );",
-					"float y = floor( j / float( boneTextureWidth ) );",
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
 
-					"float dx = 1.0 / float( boneTextureWidth );",
-					"float dy = 1.0 / float( boneTextureHeight );",
+			gbone = this.geometry.bones[ b ];
 
-					"y = dy * ( y + 0.5 );",
+			p = gbone.pos;
+			q = gbone.rotq;
+			s = gbone.scl;
 
-					"vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );",
-					"vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );",
-					"vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );",
-					"vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );",
+			bone = new THREE.Bone( this );
+			bones.push( bone );
 
-					"mat4 bone = mat4( v1, v2, v3, v4 );",
+			bone.name = gbone.name;
+			bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] );
+			bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] );
 
-					"return bone;",
+			if ( s !== undefined ) {
 
-				"}",
+				bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] );
 
-			"#else",
+			} else {
 
-				"uniform mat4 boneGlobalMatrices[ MAX_BONES ];",
+				bone.scale.set( 1, 1, 1 );
 
-				"mat4 getBoneMatrix( const in float i ) {",
+			}
 
-					"mat4 bone = boneGlobalMatrices[ int(i) ];",
-					"return bone;",
+		}
 
-				"}",
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
 
-			"#endif",
+			gbone = this.geometry.bones[ b ];
 
-		"#endif"
+			if ( gbone.parent !== - 1 ) {
 
-	].join("\n"),
+				bones[ gbone.parent ].add( bones[ b ] );
 
-	skinbase_vertex: [
+			} else {
 
-		"#ifdef USE_SKINNING",
+				this.add( bones[ b ] );
 
-			"mat4 boneMatX = getBoneMatrix( skinIndex.x );",
-			"mat4 boneMatY = getBoneMatrix( skinIndex.y );",
+			}
 
-		"#endif"
+		}
 
-	].join("\n"),
+	}
 
-	skinning_vertex: [
+	this.normalizeSkinWeights();
 
-		"#ifdef USE_SKINNING",
+	this.updateMatrixWorld( true );
+	this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) );
 
-			"#ifdef USE_MORPHTARGETS",
+};
 
-			"vec4 skinVertex = vec4( morphed, 1.0 );",
 
-			"#else",
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh;
 
-			"vec4 skinVertex = vec4( position, 1.0 );",
+THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) {
 
-			"#endif",
+	this.skeleton = skeleton;
 
-			"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
-			"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+	if ( bindMatrix === undefined ) {
 
-		"#endif"
+		this.updateMatrixWorld( true );
 
-	].join("\n"),
+		bindMatrix = this.matrixWorld;
 
-	// MORPHING
+	}
 
-	morphtarget_pars_vertex: [
+	this.bindMatrix.copy( bindMatrix );
+	this.bindMatrixInverse.getInverse( bindMatrix );
 
-		"#ifdef USE_MORPHTARGETS",
+};
 
-			"#ifndef USE_MORPHNORMALS",
+THREE.SkinnedMesh.prototype.pose = function () {
 
-			"uniform float morphTargetInfluences[ 8 ];",
+	this.skeleton.pose();
 
-			"#else",
+};
 
-			"uniform float morphTargetInfluences[ 4 ];",
+THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
 
-			"#endif",
+	if ( this.geometry instanceof THREE.Geometry ) {
 
-		"#endif"
+		for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
 
-	].join("\n"),
+			var sw = this.geometry.skinWeights[ i ];
 
-	morphtarget_vertex: [
+			var scale = 1.0 / sw.lengthManhattan();
 
-		"#ifdef USE_MORPHTARGETS",
+			if ( scale !== Infinity ) {
 
-			"vec3 morphed = vec3( 0.0 );",
-			"morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];",
-			"morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];",
-			"morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];",
-			"morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];",
+				sw.multiplyScalar( scale );
 
-			"#ifndef USE_MORPHNORMALS",
+			} else {
 
-			"morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];",
-			"morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];",
-			"morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];",
-			"morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];",
+				sw.set( 1 ); // this will be normalized by the shader anyway
 
-			"#endif",
+			}
 
-			"morphed += position;",
+		}
 
-		"#endif"
+	} else {
 
-	].join("\n"),
+		// skinning weights assumed to be normalized for THREE.BufferGeometry
 
-	default_vertex : [
+	}
 
-		"vec4 mvPosition;",
+};
 
-		"#ifdef USE_SKINNING",
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) {
 
-			"mvPosition = modelViewMatrix * skinned;",
+	THREE.Mesh.prototype.updateMatrixWorld.call( this, true );
 
-		"#endif",
+	if ( this.bindMode === "attached" ) {
 
-		"#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )",
+		this.bindMatrixInverse.getInverse( this.matrixWorld );
 
-			"mvPosition = modelViewMatrix * vec4( morphed, 1.0 );",
+	} else if ( this.bindMode === "detached" ) {
 
-		"#endif",
+		this.bindMatrixInverse.getInverse( this.bindMatrix );
 
-		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )",
+	} else {
 
-			"mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+		THREE.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode );
 
-		"#endif",
+	}
 
-		"gl_Position = projectionMatrix * mvPosition;"
+};
 
-	].join("\n"),
+THREE.SkinnedMesh.prototype.clone = function( object ) {
 
-	morphnormal_vertex: [
+	if ( object === undefined ) {
 
-		"#ifdef USE_MORPHNORMALS",
+		object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture );
 
-			"vec3 morphedNormal = vec3( 0.0 );",
+	}
 
-			"morphedNormal +=  ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];",
-			"morphedNormal +=  ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];",
-			"morphedNormal +=  ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];",
-			"morphedNormal +=  ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];",
+	THREE.Mesh.prototype.clone.call( this, object );
 
-			"morphedNormal += normal;",
+	return object;
 
-		"#endif"
+};
 
-	].join("\n"),
 
-	skinnormal_vertex: [
+// File:src/objects/MorphAnimMesh.js
 
-		"#ifdef USE_SKINNING",
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			"mat4 skinMatrix = skinWeight.x * boneMatX;",
-			"skinMatrix 	+= skinWeight.y * boneMatY;",
+THREE.MorphAnimMesh = function ( geometry, material ) {
 
-			"#ifdef USE_MORPHNORMALS",
+	THREE.Mesh.call( this, geometry, material );
 
-			"vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );",
+	this.type = 'MorphAnimMesh';
 
-			"#else",
+	// API
 
-			"vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );",
+	this.duration = 1000; // milliseconds
+	this.mirroredLoop = false;
+	this.time = 0;
 
-			"#endif",
+	// internals
 
-		"#endif"
+	this.lastKeyframe = 0;
+	this.currentKeyframe = 0;
 
-	].join("\n"),
+	this.direction = 1;
+	this.directionBackwards = false;
 
-	defaultnormal_vertex: [
+	this.setFrameRange( 0, this.geometry.morphTargets.length - 1 );
 
-		"vec3 objectNormal;",
+};
 
-		"#ifdef USE_SKINNING",
+THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.MorphAnimMesh.prototype.constructor = THREE.MorphAnimMesh;
 
-			"objectNormal = skinnedNormal.xyz;",
+THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) {
 
-		"#endif",
+	this.startKeyframe = start;
+	this.endKeyframe = end;
 
-		"#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )",
+	this.length = this.endKeyframe - this.startKeyframe + 1;
 
-			"objectNormal = morphedNormal;",
+};
 
-		"#endif",
+THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
 
-		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )",
+	this.direction = 1;
+	this.directionBackwards = false;
 
-			"objectNormal = normal;",
+};
 
-		"#endif",
+THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
 
-		"#ifdef FLIP_SIDED",
+	this.direction = - 1;
+	this.directionBackwards = true;
 
-			"objectNormal = -objectNormal;",
+};
 
-		"#endif",
+THREE.MorphAnimMesh.prototype.parseAnimations = function () {
 
-		"vec3 transformedNormal = normalMatrix * objectNormal;"
+	var geometry = this.geometry;
 
-	].join("\n"),
+	if ( ! geometry.animations ) geometry.animations = {};
 
-	// SHADOW MAP
+	var firstAnimation, animations = geometry.animations;
 
-	// based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples
-	//  http://spidergl.org/example.php?id=6
-	// 	http://fabiensanglard.net/shadowmapping
+	var pattern = /([a-z]+)_?(\d+)/;
 
-	shadowmap_pars_fragment: [
+	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
 
-		"#ifdef USE_SHADOWMAP",
+		var morph = geometry.morphTargets[ i ];
+		var parts = morph.name.match( pattern );
 
-			"uniform sampler2D shadowMap[ MAX_SHADOWS ];",
-			"uniform vec2 shadowMapSize[ MAX_SHADOWS ];",
+		if ( parts && parts.length > 1 ) {
 
-			"uniform float shadowDarkness[ MAX_SHADOWS ];",
-			"uniform float shadowBias[ MAX_SHADOWS ];",
+			var label = parts[ 1 ];
 
-			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
+			if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity };
 
-			"float unpackDepth( const in vec4 rgba_depth ) {",
+			var animation = animations[ label ];
 
-				"const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
-				"float depth = dot( rgba_depth, bit_shift );",
-				"return depth;",
+			if ( i < animation.start ) animation.start = i;
+			if ( i > animation.end ) animation.end = i;
 
-			"}",
+			if ( ! firstAnimation ) firstAnimation = label;
 
-		"#endif"
+		}
 
-	].join("\n"),
+	}
 
-	shadowmap_fragment: [
+	geometry.firstAnimation = firstAnimation;
 
-		"#ifdef USE_SHADOWMAP",
+};
 
-			"#ifdef SHADOWMAP_DEBUG",
+THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) {
 
-				"vec3 frustumColors[3];",
-				"frustumColors[0] = vec3( 1.0, 0.5, 0.0 );",
-				"frustumColors[1] = vec3( 0.0, 1.0, 0.8 );",
-				"frustumColors[2] = vec3( 0.0, 0.5, 1.0 );",
+	if ( ! this.geometry.animations ) this.geometry.animations = {};
 
-			"#endif",
+	this.geometry.animations[ label ] = { start: start, end: end };
 
-			"#ifdef SHADOWMAP_CASCADE",
+};
 
-				"int inFrustumCount = 0;",
+THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) {
 
-			"#endif",
+	var animation = this.geometry.animations[ label ];
 
-			"float fDepth;",
-			"vec3 shadowColor = vec3( 1.0 );",
+	if ( animation ) {
 
-			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+		this.setFrameRange( animation.start, animation.end );
+		this.duration = 1000 * ( ( animation.end - animation.start ) / fps );
+		this.time = 0;
 
-				"vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;",
+	} else {
 
-				// "if ( something && something )" 		 breaks ATI OpenGL shader compiler
-				// "if ( all( something, something ) )"  using this instead
+		THREE.warn( 'THREE.MorphAnimMesh: animation[' + label + '] undefined in .playAnimation()' );
 
-				"bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );",
-				"bool inFrustum = all( inFrustumVec );",
+	}
 
-				// don't shadow pixels outside of light frustum
-				// use just first frustum (for cascades)
-				// don't shadow pixels behind far plane of light frustum
+};
 
-				"#ifdef SHADOWMAP_CASCADE",
+THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) {
 
-					"inFrustumCount += int( inFrustum );",
-					"bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );",
+	var frameTime = this.duration / this.length;
 
-				"#else",
+	this.time += this.direction * delta;
 
-					"bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );",
+	if ( this.mirroredLoop ) {
 
-				"#endif",
+		if ( this.time > this.duration || this.time < 0 ) {
 
-				"bool frustumTest = all( frustumTestVec );",
+			this.direction *= - 1;
 
-				"if ( frustumTest ) {",
+			if ( this.time > this.duration ) {
 
-					"shadowCoord.z += shadowBias[ i ];",
+				this.time = this.duration;
+				this.directionBackwards = true;
 
-					"#if defined( SHADOWMAP_TYPE_PCF )",
+			}
 
-						// Percentage-close filtering
-						// (9 pixel kernel)
-						// http://fabiensanglard.net/shadowmappingPCF/
+			if ( this.time < 0 ) {
 
-						"float shadow = 0.0;",
+				this.time = 0;
+				this.directionBackwards = false;
 
-						/*
-						// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
-						// must enroll loop manually
+			}
 
-						"for ( float y = -1.25; y <= 1.25; y += 1.25 )",
-							"for ( float x = -1.25; x <= 1.25; x += 1.25 ) {",
+		}
 
-								"vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );",
+	} else {
 
-								// doesn't seem to produce any noticeable visual difference compared to simple "texture2D" lookup
-								//"vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );",
+		this.time = this.time % this.duration;
 
-								"float fDepth = unpackDepth( rgbaDepth );",
+		if ( this.time < 0 ) this.time += this.duration;
 
-								"if ( fDepth < shadowCoord.z )",
-									"shadow += 1.0;",
+	}
 
-						"}",
+	var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 );
 
-						"shadow /= 9.0;",
+	if ( keyframe !== this.currentKeyframe ) {
 
-						*/
+		this.morphTargetInfluences[ this.lastKeyframe ] = 0;
+		this.morphTargetInfluences[ this.currentKeyframe ] = 1;
 
-						"const float shadowDelta = 1.0 / 9.0;",
+		this.morphTargetInfluences[ keyframe ] = 0;
 
-						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
-						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+		this.lastKeyframe = this.currentKeyframe;
+		this.currentKeyframe = keyframe;
 
-						"float dx0 = -1.25 * xPixelOffset;",
-						"float dy0 = -1.25 * yPixelOffset;",
-						"float dx1 = 1.25 * xPixelOffset;",
-						"float dy1 = 1.25 * yPixelOffset;",
+	}
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	var mix = ( this.time % frameTime ) / frameTime;
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	if ( this.directionBackwards ) {
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+		mix = 1 - mix;
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	}
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	this.morphTargetInfluences[ this.currentKeyframe ] = mix;
+	this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix;
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+};
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) {
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	var influences = this.morphTargetInfluences;
 
-						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
-						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+	for ( var i = 0, l = influences.length; i < l; i ++ ) {
 
-						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+		influences[ i ] = 0;
 
-					"#elif defined( SHADOWMAP_TYPE_PCF_SOFT )",
+	}
 
-						// Percentage-close filtering
-						// (9 pixel kernel)
-						// http://fabiensanglard.net/shadowmappingPCF/
+	if ( a > -1 ) influences[ a ] = 1 - t;
+	if ( b > -1 ) influences[ b ] = t;
 
-						"float shadow = 0.0;",
+};
 
-						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
-						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+THREE.MorphAnimMesh.prototype.clone = function ( object ) {
 
-						"float dx0 = -1.0 * xPixelOffset;",
-						"float dy0 = -1.0 * yPixelOffset;",
-						"float dx1 = 1.0 * xPixelOffset;",
-						"float dy1 = 1.0 * yPixelOffset;",
+	if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material );
 
-						"mat3 shadowKernel;",
-						"mat3 depthKernel;",
+	object.duration = this.duration;
+	object.mirroredLoop = this.mirroredLoop;
+	object.time = this.time;
 
-						"depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
-						"depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
-						"depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
-						"depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
-						"depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
-						"depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
-						"depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
-						"depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
-						"depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
+	object.lastKeyframe = this.lastKeyframe;
+	object.currentKeyframe = this.currentKeyframe;
 
-						"vec3 shadowZ = vec3( shadowCoord.z );",
-						"shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));",
-						"shadowKernel[0] *= vec3(0.25);",
+	object.direction = this.direction;
+	object.directionBackwards = this.directionBackwards;
 
-						"shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));",
-						"shadowKernel[1] *= vec3(0.25);",
+	THREE.Mesh.prototype.clone.call( this, object );
 
-						"shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));",
-						"shadowKernel[2] *= vec3(0.25);",
+	return object;
 
-						"vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );",
+};
 
-						"shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );",
-						"shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );",
+// File:src/objects/LOD.js
 
-						"vec4 shadowValues;",
-						"shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );",
-						"shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );",
-						"shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );",
-						"shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );",
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-						"shadow = dot( shadowValues, vec4( 1.0 ) );",
+THREE.LOD = function () {
 
-						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+	THREE.Object3D.call( this );
 
-					"#else",
+	this.objects = [];
 
-						"vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );",
-						"float fDepth = unpackDepth( rgbaDepth );",
+};
 
-						"if ( fDepth < shadowCoord.z )",
 
-							// spot with multiple shadows is darker
+THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LOD.prototype.constructor = THREE.LOD;
 
-							"shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );",
+THREE.LOD.prototype.addLevel = function ( object, distance ) {
 
-							// spot with multiple shadows has the same color as single shadow spot
+	if ( distance === undefined ) distance = 0;
 
-							//"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );",
+	distance = Math.abs( distance );
 
-					"#endif",
+	for ( var l = 0; l < this.objects.length; l ++ ) {
 
-				"}",
+		if ( distance < this.objects[ l ].distance ) {
 
+			break;
 
-				"#ifdef SHADOWMAP_DEBUG",
+		}
 
-					"#ifdef SHADOWMAP_CASCADE",
+	}
 
-						"if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];",
+	this.objects.splice( l, 0, { distance: distance, object: object } );
+	this.add( object );
 
-					"#else",
+};
 
-						"if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];",
+THREE.LOD.prototype.getObjectForDistance = function ( distance ) {
 
-					"#endif",
+	for ( var i = 1, l = this.objects.length; i < l; i ++ ) {
 
-				"#endif",
+		if ( distance < this.objects[ i ].distance ) {
 
-			"}",
+			break;
 
-			"#ifdef GAMMA_OUTPUT",
+		}
 
-				"shadowColor *= shadowColor;",
+	}
 
-			"#endif",
+	return this.objects[ i - 1 ].object;
 
-			"gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;",
+};
 
-		"#endif"
+THREE.LOD.prototype.raycast = ( function () {
 
-	].join("\n"),
+	var matrixPosition = new THREE.Vector3();
 
-	shadowmap_pars_vertex: [
+	return function ( raycaster, intersects ) {
 
-		"#ifdef USE_SHADOWMAP",
+		matrixPosition.setFromMatrixPosition( this.matrixWorld );
 
-			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
-			"uniform mat4 shadowMatrix[ MAX_SHADOWS ];",
+		var distance = raycaster.ray.origin.distanceTo( matrixPosition );
 
-		"#endif"
+		this.getObjectForDistance( distance ).raycast( raycaster, intersects );
 
-	].join("\n"),
+	};
 
-	shadowmap_vertex: [
+}() );
 
-		"#ifdef USE_SHADOWMAP",
+THREE.LOD.prototype.update = function () {
 
-			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+	var v1 = new THREE.Vector3();
+	var v2 = new THREE.Vector3();
 
-				"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+	return function ( camera ) {
 
-			"}",
+		if ( this.objects.length > 1 ) {
 
-		"#endif"
+			v1.setFromMatrixPosition( camera.matrixWorld );
+			v2.setFromMatrixPosition( this.matrixWorld );
 
-	].join("\n"),
+			var distance = v1.distanceTo( v2 );
 
-	// ALPHATEST
+			this.objects[ 0 ].object.visible = true;
 
-	alphatest_fragment: [
+			for ( var i = 1, l = this.objects.length; i < l; i ++ ) {
 
-		"#ifdef ALPHATEST",
+				if ( distance >= this.objects[ i ].distance ) {
 
-			"if ( gl_FragColor.a < ALPHATEST ) discard;",
+					this.objects[ i - 1 ].object.visible = false;
+					this.objects[ i     ].object.visible = true;
 
-		"#endif"
+				} else {
 
-	].join("\n"),
+					break;
 
-	// LINEAR SPACE
+				}
 
-	linear_to_gamma_fragment: [
+			}
 
-		"#ifdef GAMMA_OUTPUT",
+			for ( ; i < l; i ++ ) {
 
-			"gl_FragColor.xyz = sqrt( gl_FragColor.xyz );",
+				this.objects[ i ].object.visible = false;
 
-		"#endif"
+			}
 
-	].join("\n")
+		}
 
+	};
 
-};
+}();
 
-THREE.UniformsUtils = {
+THREE.LOD.prototype.clone = function ( object ) {
 
-	merge: function ( uniforms ) {
+	if ( object === undefined ) object = new THREE.LOD();
 
-		var u, p, tmp, merged = {};
+	THREE.Object3D.prototype.clone.call( this, object );
 
-		for ( u = 0; u < uniforms.length; u ++ ) {
+	for ( var i = 0, l = this.objects.length; i < l; i ++ ) {
+		var x = this.objects[ i ].object.clone();
+		x.visible = i === 0;
+		object.addLevel( x, this.objects[ i ].distance );
+	}
 
-			tmp = this.clone( uniforms[ u ] );
+	return object;
 
-			for ( p in tmp ) {
+};
 
-				merged[ p ] = tmp[ p ];
+// File:src/objects/Sprite.js
 
-			}
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		}
+THREE.Sprite = ( function () {
 
-		return merged;
+	var indices = new Uint16Array( [ 0, 1, 2,  0, 2, 3 ] );
+	var vertices = new Float32Array( [ - 0.5, - 0.5, 0,   0.5, - 0.5, 0,   0.5, 0.5, 0,   - 0.5, 0.5, 0 ] );
+	var uvs = new Float32Array( [ 0, 0,   1, 0,   1, 1,   0, 1 ] );
 
-	},
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
-	clone: function ( uniforms_src ) {
+	return function ( material ) {
 
-		var u, p, parameter, parameter_src, uniforms_dst = {};
+		THREE.Object3D.call( this );
 
-		for ( u in uniforms_src ) {
+		this.type = 'Sprite';
 
-			uniforms_dst[ u ] = {};
+		this.geometry = geometry;
+		this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
 
-			for ( p in uniforms_src[ u ] ) {
+	};
 
-				parameter_src = uniforms_src[ u ][ p ];
+} )();
 
-				if ( parameter_src instanceof THREE.Color ||
-					 parameter_src instanceof THREE.Vector2 ||
-					 parameter_src instanceof THREE.Vector3 ||
-					 parameter_src instanceof THREE.Vector4 ||
-					 parameter_src instanceof THREE.Matrix4 ||
-					 parameter_src instanceof THREE.Texture ) {
+THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Sprite.prototype.constructor = THREE.Sprite;
 
-					uniforms_dst[ u ][ p ] = parameter_src.clone();
+THREE.Sprite.prototype.raycast = ( function () {
 
-				} else if ( parameter_src instanceof Array ) {
+	var matrixPosition = new THREE.Vector3();
 
-					uniforms_dst[ u ][ p ] = parameter_src.slice();
+	return function ( raycaster, intersects ) {
 
-				} else {
+		matrixPosition.setFromMatrixPosition( this.matrixWorld );
 
-					uniforms_dst[ u ][ p ] = parameter_src;
+		var distance = raycaster.ray.distanceToPoint( matrixPosition );
 
-				}
+		if ( distance > this.scale.x ) {
 
-			}
+			return;
 
 		}
 
-		return uniforms_dst;
+		intersects.push( {
 
-	}
+			distance: distance,
+			point: this.position,
+			face: null,
+			object: this
 
-};
+		} );
 
-THREE.UniformsLib = {
+	};
 
-	common: {
+}() );
 
-		"diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
-		"opacity" : { type: "f", value: 1.0 },
+THREE.Sprite.prototype.clone = function ( object ) {
 
-		"map" : { type: "t", value: null },
-		"offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+	if ( object === undefined ) object = new THREE.Sprite( this.material );
 
-		"lightMap" : { type: "t", value: null },
-		"specularMap" : { type: "t", value: null },
+	THREE.Object3D.prototype.clone.call( this, object );
 
-		"envMap" : { type: "t", value: null },
-		"flipEnvMap" : { type: "f", value: -1 },
-		"useRefract" : { type: "i", value: 0 },
-		"reflectivity" : { type: "f", value: 1.0 },
-		"refractionRatio" : { type: "f", value: 0.98 },
-		"combine" : { type: "i", value: 0 },
+	return object;
 
-		"morphTargetInfluences" : { type: "f", value: 0 }
+};
 
-	},
+// Backwards compatibility
 
-	bump: {
+THREE.Particle = THREE.Sprite;
 
-		"bumpMap" : { type: "t", value: null },
-		"bumpScale" : { type: "f", value: 1 }
+// File:src/objects/LensFlare.js
 
-	},
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	normalmap: {
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
 
-		"normalMap" : { type: "t", value: null },
-		"normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }
-	},
+	THREE.Object3D.call( this );
 
-	fog : {
+	this.lensFlares = [];
 
-		"fogDensity" : { type: "f", value: 0.00025 },
-		"fogNear" : { type: "f", value: 1 },
-		"fogFar" : { type: "f", value: 2000 },
-		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+	this.positionScreen = new THREE.Vector3();
+	this.customUpdateCallback = undefined;
 
-	},
+	if ( texture !== undefined ) {
 
-	lights: {
+		this.add( texture, size, distance, blending, color );
 
-		"ambientLightColor" : { type: "fv", value: [] },
+	}
 
-		"directionalLightDirection" : { type: "fv", value: [] },
-		"directionalLightColor" : { type: "fv", value: [] },
+};
 
-		"hemisphereLightDirection" : { type: "fv", value: [] },
-		"hemisphereLightSkyColor" : { type: "fv", value: [] },
-		"hemisphereLightGroundColor" : { type: "fv", value: [] },
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LensFlare.prototype.constructor = THREE.LensFlare;
 
-		"pointLightColor" : { type: "fv", value: [] },
-		"pointLightPosition" : { type: "fv", value: [] },
-		"pointLightDistance" : { type: "fv1", value: [] },
 
-		"spotLightColor" : { type: "fv", value: [] },
-		"spotLightPosition" : { type: "fv", value: [] },
-		"spotLightDirection" : { type: "fv", value: [] },
-		"spotLightDistance" : { type: "fv1", value: [] },
-		"spotLightAngleCos" : { type: "fv1", value: [] },
-		"spotLightExponent" : { type: "fv1", value: [] }
+/*
+ * Add: adds another flare
+ */
 
-	},
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
 
-	particle: {
+	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;
 
-		"psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
-		"opacity" : { type: "f", value: 1.0 },
-		"size" : { type: "f", value: 1.0 },
-		"scale" : { type: "f", value: 1.0 },
-		"map" : { type: "t", value: null },
+	distance = Math.min( distance, Math.max( 0, distance ) );
 
-		"fogDensity" : { type: "f", value: 0.00025 },
-		"fogNear" : { type: "f", value: 1 },
-		"fogFar" : { type: "f", value: 2000 },
-		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+	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
+	} );
 
-	},
+};
 
-	shadowmap: {
+/*
+ * 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.
+ */
 
-		"shadowMap": { type: "tv", value: [] },
-		"shadowMapSize": { type: "v2v", value: [] },
+THREE.LensFlare.prototype.updateLensFlares = function () {
 
-		"shadowBias" : { type: "fv1", value: [] },
-		"shadowDarkness": { type: "fv1", value: [] },
+	var f, fl = this.lensFlares.length;
+	var flare;
+	var vecX = - this.positionScreen.x * 2;
+	var vecY = - this.positionScreen.y * 2;
 
-		"shadowMatrix" : { type: "m4v", value: [] }
+	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;
 
-THREE.ShaderLib = {
+		flare.wantedRotation = flare.x * Math.PI * 0.25;
+		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
 
-	'basic': {
+	}
 
-		uniforms: THREE.UniformsUtils.merge( [
+};
 
-			THREE.UniformsLib[ "common" ],
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "shadowmap" ]
 
-		] ),
+// File:src/scenes/Scene.js
 
-		vertexShader: [
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			THREE.ShaderChunk[ "map_pars_vertex" ],
-			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
-			THREE.ShaderChunk[ "envmap_pars_vertex" ],
-			THREE.ShaderChunk[ "color_pars_vertex" ],
-			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
-			THREE.ShaderChunk[ "skinning_pars_vertex" ],
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+THREE.Scene = function () {
 
-			"void main() {",
+	THREE.Object3D.call( this );
 
-				THREE.ShaderChunk[ "map_vertex" ],
-				THREE.ShaderChunk[ "lightmap_vertex" ],
-				THREE.ShaderChunk[ "color_vertex" ],
-				THREE.ShaderChunk[ "skinbase_vertex" ],
+	this.type = 'Scene';
 
-				"#ifdef USE_ENVMAP",
+	this.fog = null;
+	this.overrideMaterial = null;
 
-				THREE.ShaderChunk[ "morphnormal_vertex" ],
-				THREE.ShaderChunk[ "skinnormal_vertex" ],
-				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+	this.autoUpdate = true; // checked by the renderer
 
-				"#endif",
+};
 
-				THREE.ShaderChunk[ "morphtarget_vertex" ],
-				THREE.ShaderChunk[ "skinning_vertex" ],
-				THREE.ShaderChunk[ "default_vertex" ],
+THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Scene.prototype.constructor = THREE.Scene;
 
-				THREE.ShaderChunk[ "worldpos_vertex" ],
-				THREE.ShaderChunk[ "envmap_vertex" ],
-				THREE.ShaderChunk[ "shadowmap_vertex" ],
+THREE.Scene.prototype.clone = function ( object ) {
 
-			"}"
+	if ( object === undefined ) object = new THREE.Scene();
 
-		].join("\n"),
+	THREE.Object3D.prototype.clone.call( this, object );
 
-		fragmentShader: [
+	if ( this.fog !== null ) object.fog = this.fog.clone();
+	if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone();
 
-			"uniform vec3 diffuse;",
-			"uniform float opacity;",
+	object.autoUpdate = this.autoUpdate;
+	object.matrixAutoUpdate = this.matrixAutoUpdate;
 
-			THREE.ShaderChunk[ "color_pars_fragment" ],
-			THREE.ShaderChunk[ "map_pars_fragment" ],
-			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
-			THREE.ShaderChunk[ "envmap_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
-			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+	return object;
 
-			"void main() {",
+};
 
-				"gl_FragColor = vec4( diffuse, opacity );",
+// File:src/scenes/Fog.js
 
-				THREE.ShaderChunk[ "map_fragment" ],
-				THREE.ShaderChunk[ "alphatest_fragment" ],
-				THREE.ShaderChunk[ "specularmap_fragment" ],
-				THREE.ShaderChunk[ "lightmap_fragment" ],
-				THREE.ShaderChunk[ "color_fragment" ],
-				THREE.ShaderChunk[ "envmap_fragment" ],
-				THREE.ShaderChunk[ "shadowmap_fragment" ],
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+THREE.Fog = function ( color, near, far ) {
 
-				THREE.ShaderChunk[ "fog_fragment" ],
+	this.name = '';
 
-			"}"
+	this.color = new THREE.Color( color );
 
-		].join("\n")
+	this.near = ( near !== undefined ) ? near : 1;
+	this.far = ( far !== undefined ) ? far : 1000;
 
-	},
+};
 
-	'lambert': {
+THREE.Fog.prototype.clone = function () {
 
-		uniforms: THREE.UniformsUtils.merge( [
+	return new THREE.Fog( this.color.getHex(), this.near, this.far );
 
-			THREE.UniformsLib[ "common" ],
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-			THREE.UniformsLib[ "shadowmap" ],
+};
 
-			{
-				"ambient"  : { type: "c", value: new THREE.Color( 0xffffff ) },
-				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
-				"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
-			}
+// File:src/scenes/FogExp2.js
 
-		] ),
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		vertexShader: [
+THREE.FogExp2 = function ( color, density ) {
 
-			"#define LAMBERT",
+	this.name = '';
 
-			"varying vec3 vLightFront;",
+	this.color = new THREE.Color( color );
+	this.density = ( density !== undefined ) ? density : 0.00025;
 
-			"#ifdef DOUBLE_SIDED",
+};
 
-				"varying vec3 vLightBack;",
+THREE.FogExp2.prototype.clone = function () {
 
-			"#endif",
+	return new THREE.FogExp2( this.color.getHex(), this.density );
 
-			THREE.ShaderChunk[ "map_pars_vertex" ],
-			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
-			THREE.ShaderChunk[ "envmap_pars_vertex" ],
-			THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
-			THREE.ShaderChunk[ "color_pars_vertex" ],
-			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
-			THREE.ShaderChunk[ "skinning_pars_vertex" ],
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+};
 
-			"void main() {",
+// File:src/renderers/shaders/ShaderChunk.js
 
-				THREE.ShaderChunk[ "map_vertex" ],
-				THREE.ShaderChunk[ "lightmap_vertex" ],
-				THREE.ShaderChunk[ "color_vertex" ],
+THREE.ShaderChunk = {};
 
-				THREE.ShaderChunk[ "morphnormal_vertex" ],
-				THREE.ShaderChunk[ "skinbase_vertex" ],
-				THREE.ShaderChunk[ "skinnormal_vertex" ],
-				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+// File:src/renderers/shaders/ShaderChunk/common.glsl
 
-				THREE.ShaderChunk[ "morphtarget_vertex" ],
-				THREE.ShaderChunk[ "skinning_vertex" ],
-				THREE.ShaderChunk[ "default_vertex" ],
+THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\nfloat square( in float a ) { return a*a; }\nvec2  square( in vec2 a )  { return vec2( a.x*a.x, a.y*a.y ); }\nvec3  square( in vec3 a )  { return vec3( a.x*a.x, a.y*a.y, a.z*a.z ); }\nvec4  square( in vec4 a )  { return vec4( a.x*a.x, a.y*a.y, a.z*a.z, a.w*a.w ); }\nfloat saturate( in float a ) { return clamp( a, 0.0, 1.0 ); }\nvec2  saturate( in vec2 a )  { return clamp( a, 0.0, 1.0 ); }\nvec3  saturate( in vec3 a )  { return clamp( a, 0.0, 1.0 ); }\nvec4  saturate( in vec4 a )  { return clamp( a, 0.0, 1.0 ); }\nfloat average( in float a ) { return a; }\nfloat average( in vec2 a )  { return ( a.x + a.y) * 0.5; }\nfloat average( in vec3 a )  { return ( a.x + a.y + a.z) / 3.0; }\nfloat average( in vec4 a )  { return ( a.x + a.y + a.z + a.w) * 0.25; }\nfloat whiteCompliment( in float a ) { return saturate( 1.0 - a ); }\nvec2  whiteCompliment( in vec2 a )  { return saturate( vec2(1.0) - a ); }\nvec3  whiteCompliment( in vec3 a )  { return saturate( vec3(1.0) - a ); }\nvec4  whiteCompliment( in vec4 a )  { return saturate( vec4(1.0) - a ); }\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n	return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n}\n// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n	return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal) {\n	float distance = dot( planeNormal, point-pointOnPlane );\n	return point - distance * planeNormal;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n	return sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n	return pointOnLine + lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) );\n}\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n	if ( decayExponent > 0.0 ) {\n	  return pow( saturate( 1.0 - lightDistance / cutoffDistance ), decayExponent );\n	}\n	return 1.0;\n}\n\nvec3 inputToLinear( in vec3 a ) {\n#ifdef GAMMA_INPUT\n	return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n#else\n	return a;\n#endif\n}\nvec3 linearToOutput( in vec3 a ) {\n#ifdef GAMMA_OUTPUT\n	return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n#else\n	return a;\n#endif\n}\n";
 
-				THREE.ShaderChunk[ "worldpos_vertex" ],
-				THREE.ShaderChunk[ "envmap_vertex" ],
-				THREE.ShaderChunk[ "lights_lambert_vertex" ],
-				THREE.ShaderChunk[ "shadowmap_vertex" ],
+// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl
 
-			"}"
+THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n	if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n";
 
-		].join("\n"),
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
 
-		fragmentShader: [
+THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n	vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n	vec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n	float dotProduct = dot( transformedNormal, dirVector );\n	vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n	#ifdef DOUBLE_SIDED\n\n		vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n		#ifdef WRAP_AROUND\n\n			vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n		#endif\n\n	#endif\n\n	#ifdef WRAP_AROUND\n\n		vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n		directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n		#ifdef DOUBLE_SIDED\n\n			directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n		#endif\n\n	#endif\n\n	vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n	#ifdef DOUBLE_SIDED\n\n		vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n	#endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n		vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n		vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n		float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n		lVector = normalize( lVector );\n		float dotProduct = dot( transformedNormal, lVector );\n\n		vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n		#ifdef DOUBLE_SIDED\n\n			vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n			#ifdef WRAP_AROUND\n\n				vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n			#endif\n\n		#endif\n\n		#ifdef WRAP_AROUND\n\n			vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n			pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n			#ifdef DOUBLE_SIDED\n\n				pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n			#endif\n\n		#endif\n\n		vLightFront += pointLightColor[ i ] * pointLightWeighting * attenuation;\n\n		#ifdef DOUBLE_SIDED\n\n			vLightBack += pointLightColor[ i ] * pointLightWeightingBack * attenuation;\n\n		#endif\n\n	}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n		vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n		vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n		float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n		if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n			spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n			float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n			lVector = normalize( lVector );\n\n			float dotProduct = dot( transformedNormal, lVector );\n			vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n			#ifdef DOUBLE_SIDED\n\n				vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n				#ifdef WRAP_AROUND\n\n					vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n				#endif\n\n			#endif\n\n			#ifdef WRAP_AROUND\n\n				vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n				spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n				#ifdef DOUBLE_SIDED\n\n					spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n				#endif\n\n			#endif\n\n			vLightFront += spotLightColor[ i ] * spotLightWeighting * attenuation * spotEffect;\n\n			#ifdef DOUBLE_SIDED\n\n				vLightBack += spotLightColor[ i ] * spotLightWeightingBack * attenuation * spotEffect;\n\n			#endif\n\n		}\n\n	}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n		vec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n		float dotProduct = dot( transformedNormal, lVector );\n\n		float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n		float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n		vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n		#ifdef DOUBLE_SIDED\n\n			vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n		#endif\n\n	}\n\n#endif\n\nvLightFront += ambientLightColor;\n\n#ifdef DOUBLE_SIDED\n\n	vLightBack += ambientLightColor;\n\n#endif\n";
 
-			"uniform float opacity;",
+// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl
 
-			"varying vec3 vLightFront;",
+THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n	uniform vec4 offsetRepeat;\n	uniform sampler2D map;\n\n#endif\n";
 
-			"#ifdef DOUBLE_SIDED",
+// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl
 
-				"varying vec3 vLightBack;",
+THREE.ShaderChunk[ 'default_vertex'] = "#ifdef USE_SKINNING\n\n	vec4 mvPosition = modelViewMatrix * skinned;\n\n#elif defined( USE_MORPHTARGETS )\n\n	vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#else\n\n	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n";
 
-			"#endif",
+// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl
 
-			THREE.ShaderChunk[ "color_pars_fragment" ],
-			THREE.ShaderChunk[ "map_pars_fragment" ],
-			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
-			THREE.ShaderChunk[ "envmap_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
-			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n	varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n	uniform sampler2D map;\n\n#endif";
 
-			"void main() {",
+// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl
 
-				"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
+THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n	mat4 skinMatrix = mat4( 0.0 );\n	skinMatrix += skinWeight.x * boneMatX;\n	skinMatrix += skinWeight.y * boneMatY;\n	skinMatrix += skinWeight.z * boneMatZ;\n	skinMatrix += skinWeight.w * boneMatW;\n	skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\n	#ifdef USE_MORPHNORMALS\n\n	vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n	#else\n\n	vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n	#endif\n\n#endif\n";
 
-				THREE.ShaderChunk[ "map_fragment" ],
-				THREE.ShaderChunk[ "alphatest_fragment" ],
-				THREE.ShaderChunk[ "specularmap_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl
 
-				"#ifdef DOUBLE_SIDED",
+THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		varying float vFragDepth;\n\n	#endif\n\n	uniform float logDepthBufFC;\n\n#endif";
 
-					//"float isFront = float( gl_FrontFacing );",
-					//"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;",
+// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl
 
-					"if ( gl_FrontFacing )",
-						"gl_FragColor.xyz *= vLightFront;",
-					"else",
-						"gl_FragColor.xyz *= vLightBack;",
+THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n	varying vec2 vUv2;\n\n#endif";
 
-				"#else",
+// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
 
-					"gl_FragColor.xyz *= vLightFront;",
+THREE.ShaderChunk[ 'lights_phong_fragment'] = "#ifndef FLAT_SHADED\n\n	vec3 normal = normalize( vNormal );\n\n	#ifdef DOUBLE_SIDED\n\n		normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n	#endif\n\n#else\n\n	vec3 fdx = dFdx( vViewPosition );\n	vec3 fdy = dFdy( vViewPosition );\n	vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef USE_NORMALMAP\n\n	normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n	normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n		vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n		vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n		float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n		lVector = normalize( lVector );\n\n		// diffuse\n\n		float dotProduct = dot( normal, lVector );\n\n		#ifdef WRAP_AROUND\n\n			float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n			float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n			vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n		#else\n\n			float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n		#endif\n\n		totalDiffuseLight += pointLightColor[ i ] * pointDiffuseWeight * attenuation;\n\n				// specular\n\n		vec3 pointHalfVector = normalize( lVector + viewPosition );\n		float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n		float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n		float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n		vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n		totalSpecularLight += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * attenuation * specularNormalization;\n\n	}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n		vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n		vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n		float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n		lVector = normalize( lVector );\n\n		float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n		if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n			spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n			// diffuse\n\n			float dotProduct = dot( normal, lVector );\n\n			#ifdef WRAP_AROUND\n\n				float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n				float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n				vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n			#else\n\n				float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n			#endif\n\n			totalDiffuseLight += spotLightColor[ i ] * spotDiffuseWeight * attenuation * spotEffect;\n\n			// specular\n\n			vec3 spotHalfVector = normalize( lVector + viewPosition );\n			float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n			float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n			float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n			vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n			totalSpecularLight += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * attenuation * specularNormalization * spotEffect;\n\n		}\n\n	}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n	for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n		vec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n		// diffuse\n\n		float dotProduct = dot( normal, dirVector );\n\n		#ifdef WRAP_AROUND\n\n			float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n			float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n			vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n		#else\n\n			float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n		#endif\n\n		totalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;\n\n		// specular\n\n		vec3 dirHalfVector = normalize( dirVector + viewPosition );\n		float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n		float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n		/*\n		// fresnel term from skin shader\n		const float F0 = 0.128;\n\n		float base = 1.0 - dot( viewPosition, dirHalfVector );\n		float exponential = pow( base, 5.0 );\n\n		float fresnel = exponential + F0 * ( 1.0 - exponential );\n		*/\n\n		/*\n		// fresnel term from fresnel shader\n		const float mFresnelBias = 0.08;\n		const float mFresnelScale = 0.3;\n		const float mFresnelPower = 5.0;\n\n		float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n		*/\n\n		float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n		// 		dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n		vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n		totalSpecularLight += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n	}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n		vec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n		// diffuse\n\n		float dotProduct = dot( normal, lVector );\n		float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n		vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n		totalDiffuseLight += hemiColor;\n\n		// specular (sky light)\n\n		vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n		float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n		float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n		// specular (ground light)\n\n		vec3 lVectorGround = -lVector;\n\n		vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n		float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n		float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n		float dotProductGround = dot( normal, lVectorGround );\n\n		float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n		vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n		vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n		totalSpecularLight += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n	}\n\n#endif\n\n#ifdef METAL\n\n	outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) * specular + totalSpecularLight + emissive;\n\n#else\n\n	outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) + totalSpecularLight + emissive;\n\n#endif\n";
 
-				"#endif",
+// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl
 
-				THREE.ShaderChunk[ "lightmap_fragment" ],
-				THREE.ShaderChunk[ "color_fragment" ],
-				THREE.ShaderChunk[ "envmap_fragment" ],
-				THREE.ShaderChunk[ "shadowmap_fragment" ],
+THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n	uniform vec3 fogColor;\n\n	#ifdef FOG_EXP2\n\n		uniform float fogDensity;\n\n	#else\n\n		uniform float fogNear;\n		uniform float fogFar;\n	#endif\n\n#endif";
 
-				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl
 
-				THREE.ShaderChunk[ "fog_fragment" ],
+THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n	vec3 morphedNormal = vec3( 0.0 );\n\n	morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n	morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n	morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n	morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n	morphedNormal += normal;\n\n#endif";
 
-			"}"
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl
 
-		].join("\n")
+THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n	uniform float reflectivity;\n	#ifdef ENVMAP_TYPE_CUBE\n		uniform samplerCube envMap;\n	#else\n		uniform sampler2D envMap;\n	#endif\n	uniform float flipEnvMap;\n\n	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n		uniform float refractionRatio;\n\n	#else\n\n		varying vec3 vReflect;\n\n	#endif\n\n#endif\n";
 
-	},
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl
 
-	'phong': {
+THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n	gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
 
-		uniforms: THREE.UniformsUtils.merge( [
+// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl
 
-			THREE.UniformsLib[ "common" ],
-			THREE.UniformsLib[ "bump" ],
-			THREE.UniformsLib[ "normalmap" ],
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-			THREE.UniformsLib[ "shadowmap" ],
+THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n	uniform sampler2D normalMap;\n	uniform vec2 normalScale;\n\n	// Per-Pixel Tangent Space Normal Mapping\n	// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n	vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n		vec3 q0 = dFdx( eye_pos.xyz );\n		vec3 q1 = dFdy( eye_pos.xyz );\n		vec2 st0 = dFdx( vUv.st );\n		vec2 st1 = dFdy( vUv.st );\n\n		vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n		vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n		vec3 N = normalize( surf_norm );\n\n		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n		mapN.xy = normalScale * mapN.xy;\n		mat3 tsn = mat3( S, T, N );\n		return normalize( tsn * mapN );\n\n	}\n\n#endif\n";
 
-			{
-				"ambient"  : { type: "c", value: new THREE.Color( 0xffffff ) },
-				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
-				"specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
-				"shininess": { type: "f", value: 30 },
-				"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
-			}
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl
 
-		] ),
+THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n	varying vec3 vWorldPosition;\n\n#endif\n";
 
-		vertexShader: [
+// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl
 
-			"#define PHONG",
+THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n	varying vec2 vUv2;\n	uniform sampler2D lightMap;\n\n#endif";
 
-			"varying vec3 vViewPosition;",
-			"varying vec3 vNormal;",
+// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl
 
-			THREE.ShaderChunk[ "map_pars_vertex" ],
-			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
-			THREE.ShaderChunk[ "envmap_pars_vertex" ],
-			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
-			THREE.ShaderChunk[ "color_pars_vertex" ],
-			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
-			THREE.ShaderChunk[ "skinning_pars_vertex" ],
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n	for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n		vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n	}\n\n#endif";
 
-			"void main() {",
+// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl
 
-				THREE.ShaderChunk[ "map_vertex" ],
-				THREE.ShaderChunk[ "lightmap_vertex" ],
-				THREE.ShaderChunk[ "color_vertex" ],
+THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n	vWorldPosition = worldPosition.xyz;\n\n#endif";
 
-				THREE.ShaderChunk[ "morphnormal_vertex" ],
-				THREE.ShaderChunk[ "skinbase_vertex" ],
-				THREE.ShaderChunk[ "skinnormal_vertex" ],
-				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl
 
-				"vNormal = normalize( transformedNormal );",
+THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n	vec4 texelColor = texture2D( map, vUv );\n\n	texelColor.xyz = inputToLinear( texelColor.xyz );\n\n	diffuseColor *= texelColor;\n\n#endif";
 
-				THREE.ShaderChunk[ "morphtarget_vertex" ],
-				THREE.ShaderChunk[ "skinning_vertex" ],
-				THREE.ShaderChunk[ "default_vertex" ],
+// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl
 
-				"vViewPosition = -mvPosition.xyz;",
+THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n	vUv2 = uv2;\n\n#endif";
 
-				THREE.ShaderChunk[ "worldpos_vertex" ],
-				THREE.ShaderChunk[ "envmap_vertex" ],
-				THREE.ShaderChunk[ "lights_phong_vertex" ],
-				THREE.ShaderChunk[ "shadowmap_vertex" ],
+// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl
 
-			"}"
+THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n	diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n";
 
-		].join("\n"),
+// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl
 
-		fragmentShader: [
+THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n	varying vec3 vColor;\n\n#endif\n";
 
-			"uniform vec3 diffuse;",
-			"uniform float opacity;",
+// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl
 
-			"uniform vec3 ambient;",
-			"uniform vec3 emissive;",
-			"uniform vec3 specular;",
-			"uniform float shininess;",
+THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n	vColor.xyz = inputToLinear( color.xyz );\n\n#endif";
 
-			THREE.ShaderChunk[ "color_pars_fragment" ],
-			THREE.ShaderChunk[ "map_pars_fragment" ],
-			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
-			THREE.ShaderChunk[ "envmap_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-			THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
-			THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
-			THREE.ShaderChunk[ "normalmap_pars_fragment" ],
-			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl
 
-			"void main() {",
+THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n	#ifdef USE_MORPHTARGETS\n\n	vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n	#else\n\n	vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n	#endif\n\n	vec4 skinned = vec4( 0.0 );\n	skinned += boneMatX * skinVertex * skinWeight.x;\n	skinned += boneMatY * skinVertex * skinWeight.y;\n	skinned += boneMatZ * skinVertex * skinWeight.z;\n	skinned += boneMatW * skinVertex * skinWeight.w;\n	skinned  = bindMatrixInverse * skinned;\n\n#endif\n";
 
-				"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl
 
-				THREE.ShaderChunk[ "map_fragment" ],
-				THREE.ShaderChunk[ "alphatest_fragment" ],
-				THREE.ShaderChunk[ "specularmap_fragment" ],
+THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n	varying vec3 vReflect;\n\n	uniform float refractionRatio;\n\n#endif\n";
 
-				THREE.ShaderChunk[ "lights_phong_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl
 
-				THREE.ShaderChunk[ "lightmap_fragment" ],
-				THREE.ShaderChunk[ "color_fragment" ],
-				THREE.ShaderChunk[ "envmap_fragment" ],
-				THREE.ShaderChunk[ "shadowmap_fragment" ],
+THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n	outgoingLight = linearToOutput( outgoingLight );\n";
 
-				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl
 
-				THREE.ShaderChunk[ "fog_fragment" ],
+THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n	varying vec3 vColor;\n\n#endif";
 
-			"}"
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
 
-		].join("\n")
+THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n	uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n	uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n	uniform vec3 wrapRGB;\n\n#endif\n";
 
-	},
+// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl
 
-	'particle_basic': {
+THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n	varying vec2 vUv;\n	uniform vec4 offsetRepeat;\n\n#endif\n";
 
-		uniforms:  THREE.UniformsUtils.merge( [
+// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl
 
-			THREE.UniformsLib[ "particle" ],
-			THREE.UniformsLib[ "shadowmap" ]
+THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n		vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n		// Transforming Normal Vectors with the Inverse Transformation\n		vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n		#ifdef ENVMAP_MODE_REFLECTION\n\n			vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n		#else\n\n			vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n		#endif\n\n	#else\n\n		vec3 reflectVec = vReflect;\n\n	#endif\n\n	#ifdef DOUBLE_SIDED\n		float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n	#else\n		float flipNormal = 1.0;\n	#endif\n\n	#ifdef ENVMAP_TYPE_CUBE\n		vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n	#elif defined( ENVMAP_TYPE_EQUIREC )\n		vec2 sampleUV;\n		sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n		sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n		vec4 envColor = texture2D( envMap, sampleUV );\n\n	#elif defined( ENVMAP_TYPE_SPHERE )\n		vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n		vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n	#endif\n\n	envColor.xyz = inputToLinear( envColor.xyz );\n\n	#ifdef ENVMAP_BLENDING_MULTIPLY\n\n		outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n	#elif defined( ENVMAP_BLENDING_MIX )\n\n		outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n	#elif defined( ENVMAP_BLENDING_ADD )\n\n		outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n	#endif\n\n#endif\n";
 
-		] ),
+// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl
 
-		vertexShader: [
+THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n	uniform sampler2D specularMap;\n\n#endif";
 
-			"uniform float size;",
-			"uniform float scale;",
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl
 
-			THREE.ShaderChunk[ "color_pars_vertex" ],
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n	gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n		gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n	#endif\n\n#endif";
 
-			"void main() {",
+// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl
 
-				THREE.ShaderChunk[ "color_vertex" ],
+THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n	#ifndef USE_MORPHNORMALS\n\n	uniform float morphTargetInfluences[ 8 ];\n\n	#else\n\n	uniform float morphTargetInfluences[ 4 ];\n\n	#endif\n\n#endif";
 
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl
 
-				"#ifdef USE_SIZEATTENUATION",
-					"gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
-				"#else",
-					"gl_PointSize = size;",
-				"#endif",
+THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n	vec4 texelSpecular = texture2D( specularMap, vUv );\n	specularStrength = texelSpecular.r;\n\n#else\n\n	specularStrength = 1.0;\n\n#endif";
 
-				"gl_Position = projectionMatrix * mvPosition;",
+// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl
 
-				THREE.ShaderChunk[ "worldpos_vertex" ],
-				THREE.ShaderChunk[ "shadowmap_vertex" ],
+THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n	#else\n\n		float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n	#endif\n\n	#ifdef FOG_EXP2\n\n		float fogFactor = exp2( - square( fogDensity ) * square( depth ) * LOG2 );\n		fogFactor = whiteCompliment( fogFactor );\n\n	#else\n\n		float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n	#endif\n	\n	outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif";
 
-			"}"
+// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl
 
-		].join("\n"),
+THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n	uniform sampler2D bumpMap;\n	uniform float bumpScale;\n\n	// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n	// http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n	// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n	vec2 dHdxy_fwd() {\n\n		vec2 dSTdx = dFdx( vUv );\n		vec2 dSTdy = dFdy( vUv );\n\n		float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n		float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n		float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n		return vec2( dBx, dBy );\n\n	}\n\n	vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n		vec3 vSigmaX = dFdx( surf_pos );\n		vec3 vSigmaY = dFdy( surf_pos );\n		vec3 vN = surf_norm;		// normalized\n\n		vec3 R1 = cross( vSigmaY, vN );\n		vec3 R2 = cross( vN, vSigmaX );\n\n		float fDet = dot( vSigmaX, R1 );\n\n		vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n		return normalize( abs( fDet ) * surf_norm - vGrad );\n\n	}\n\n#endif\n";
 
-		fragmentShader: [
+// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl
 
-			"uniform vec3 psColor;",
-			"uniform float opacity;",
+THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef USE_SKINNING\n\n	vec3 objectNormal = skinnedNormal.xyz;\n\n#elif defined( USE_MORPHNORMALS )\n\n	vec3 objectNormal = morphedNormal;\n\n#else\n\n	vec3 objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n	objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n";
 
-			THREE.ShaderChunk[ "color_pars_fragment" ],
-			THREE.ShaderChunk[ "map_particle_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
 
-			"void main() {",
+THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n	uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n	uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n	varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n	uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n	varying vec3 vNormal;\n\n#endif\n";
 
-				"gl_FragColor = vec4( psColor, opacity );",
+// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl
 
-				THREE.ShaderChunk[ "map_particle_fragment" ],
-				THREE.ShaderChunk[ "alphatest_fragment" ],
-				THREE.ShaderChunk[ "color_fragment" ],
-				THREE.ShaderChunk[ "shadowmap_fragment" ],
-				THREE.ShaderChunk[ "fog_fragment" ],
+THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n	mat4 boneMatX = getBoneMatrix( skinIndex.x );\n	mat4 boneMatY = getBoneMatrix( skinIndex.y );\n	mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n	mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";
 
-			"}"
+// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl
 
-		].join("\n")
+THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n	vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
 
-	},
+// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl
 
-	'dashed': {
+THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n	outgoingLight *= diffuseColor.xyz * texture2D( lightMap, vUv2 ).xyz;\n\n#endif";
 
-		uniforms: THREE.UniformsUtils.merge( [
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl
 
-			THREE.UniformsLib[ "common" ],
-			THREE.UniformsLib[ "fog" ],
+THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n	varying vec4 vShadowCoord[ MAX_SHADOWS ];\n	uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif";
 
-			{
-				"scale":     { type: "f", value: 1 },
-				"dashSize":  { type: "f", value: 1 },
-				"totalSize": { type: "f", value: 2 }
-			}
+// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl
 
-		] ),
+THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n	diffuseColor.rgb *= vColor;\n\n#endif";
 
-		vertexShader: [
+// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl
 
-			"uniform float scale;",
-			"attribute float lineDistance;",
+THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n	vec3 morphed = vec3( 0.0 );\n	morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n	morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n	morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n	morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n	#ifndef USE_MORPHNORMALS\n\n	morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n	morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n	morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n	morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n	#endif\n\n	morphed += position;\n\n#endif";
 
-			"varying float vLineDistance;",
+// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl
 
-			THREE.ShaderChunk[ "color_pars_vertex" ],
+THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n	vec3 worldNormal = transformDirection( objectNormal, modelMatrix );\n\n	vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n	#ifdef ENVMAP_MODE_REFLECTION\n\n		vReflect = reflect( cameraToVertex, worldNormal );\n\n	#else\n\n		vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n	#endif\n\n#endif\n";
 
-			"void main() {",
+// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl
 
-				THREE.ShaderChunk[ "color_vertex" ],
+THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n	#ifdef SHADOWMAP_DEBUG\n\n		vec3 frustumColors[3];\n		frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n		frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n		frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n	#endif\n\n	#ifdef SHADOWMAP_CASCADE\n\n		int inFrustumCount = 0;\n\n	#endif\n\n	float fDepth;\n	vec3 shadowColor = vec3( 1.0 );\n\n	for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n		vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n				// if ( something && something ) breaks ATI OpenGL shader compiler\n				// if ( all( something, something ) ) using this instead\n\n		bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n		bool inFrustum = all( inFrustumVec );\n\n				// don't shadow pixels outside of light frustum\n				// use just first frustum (for cascades)\n				// don't shadow pixels behind far plane of light frustum\n\n		#ifdef SHADOWMAP_CASCADE\n\n			inFrustumCount += int( inFrustum );\n			bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n		#else\n\n			bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n		#endif\n\n		bool frustumTest = all( frustumTestVec );\n\n		if ( frustumTest ) {\n\n			shadowCoord.z += shadowBias[ i ];\n\n			#if defined( SHADOWMAP_TYPE_PCF )\n\n						// Percentage-close filtering\n						// (9 pixel kernel)\n						// http://fabiensanglard.net/shadowmappingPCF/\n\n				float shadow = 0.0;\n\n		/*\n						// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n						// must enroll loop manually\n\n				for ( float y = -1.25; y <= 1.25; y += 1.25 )\n					for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n						vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n								// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n								//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n						float fDepth = unpackDepth( rgbaDepth );\n\n						if ( fDepth < shadowCoord.z )\n							shadow += 1.0;\n\n				}\n\n				shadow /= 9.0;\n\n		*/\n\n				const float shadowDelta = 1.0 / 9.0;\n\n				float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n				float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n				float dx0 = -1.25 * xPixelOffset;\n				float dy0 = -1.25 * yPixelOffset;\n				float dx1 = 1.25 * xPixelOffset;\n				float dy1 = 1.25 * yPixelOffset;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n				shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n			#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n						// Percentage-close filtering\n						// (9 pixel kernel)\n						// http://fabiensanglard.net/shadowmappingPCF/\n\n				float shadow = 0.0;\n\n				float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n				float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n				float dx0 = -1.0 * xPixelOffset;\n				float dy0 = -1.0 * yPixelOffset;\n				float dx1 = 1.0 * xPixelOffset;\n				float dy1 = 1.0 * yPixelOffset;\n\n				mat3 shadowKernel;\n				mat3 depthKernel;\n\n				depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n				depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n				depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n				depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n				depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n				depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n				depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n				depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n				depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n				vec3 shadowZ = vec3( shadowCoord.z );\n				shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n				shadowKernel[0] *= vec3(0.25);\n\n				shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n				shadowKernel[1] *= vec3(0.25);\n\n				shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n				shadowKernel[2] *= vec3(0.25);\n\n				vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n				shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n				shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n				vec4 shadowValues;\n				shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n				shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n				shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n				shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n				shadow = dot( shadowValues, vec4( 1.0 ) );\n\n				shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n			#else\n\n				vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n				float fDepth = unpackDepth( rgbaDepth );\n\n				if ( fDepth < shadowCoord.z )\n\n		// spot with multiple shadows is darker\n\n					shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n		// spot with multiple shadows has the same color as single shadow spot\n\n		// 					shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n			#endif\n\n		}\n\n\n		#ifdef SHADOWMAP_DEBUG\n\n			#ifdef SHADOWMAP_CASCADE\n\n				if ( inFrustum && inFrustumCount == 1 ) outgoingLight *= frustumColors[ i ];\n\n			#else\n\n				if ( inFrustum ) outgoingLight *= frustumColors[ i ];\n\n			#endif\n\n		#endif\n\n	}\n\n	// NOTE: I am unsure if this is correct in linear space.  -bhouston, Dec 29, 2014\n	shadowColor = inputToLinear( shadowColor );\n\n	outgoingLight = outgoingLight * shadowColor;\n\n#endif\n";
 
-				"vLineDistance = scale * lineDistance;",
+// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl
 
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-				"gl_Position = projectionMatrix * mvPosition;",
+THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n	#ifdef USE_SKINNING\n\n		vec4 worldPosition = modelMatrix * skinned;\n\n	#elif defined( USE_MORPHTARGETS )\n\n		vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n	#else\n\n		vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n	#endif\n\n#endif\n";
 
-			"}"
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl
 
-		].join("\n"),
+THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n	uniform sampler2D shadowMap[ MAX_SHADOWS ];\n	uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n	uniform float shadowDarkness[ MAX_SHADOWS ];\n	uniform float shadowBias[ MAX_SHADOWS ];\n\n	varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n	float unpackDepth( const in vec4 rgba_depth ) {\n\n		const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n		float depth = dot( rgba_depth, bit_shift );\n		return depth;\n\n	}\n\n#endif";
 
-		fragmentShader: [
+// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl
 
-			"uniform vec3 diffuse;",
-			"uniform float opacity;",
+THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n	uniform mat4 bindMatrix;\n	uniform mat4 bindMatrixInverse;\n\n	#ifdef BONE_TEXTURE\n\n		uniform sampler2D boneTexture;\n		uniform int boneTextureWidth;\n		uniform int boneTextureHeight;\n\n		mat4 getBoneMatrix( const in float i ) {\n\n			float j = i * 4.0;\n			float x = mod( j, float( boneTextureWidth ) );\n			float y = floor( j / float( boneTextureWidth ) );\n\n			float dx = 1.0 / float( boneTextureWidth );\n			float dy = 1.0 / float( boneTextureHeight );\n\n			y = dy * ( y + 0.5 );\n\n			vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n			vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n			vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n			vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n			mat4 bone = mat4( v1, v2, v3, v4 );\n\n			return bone;\n\n		}\n\n	#else\n\n		uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n		mat4 getBoneMatrix( const in float i ) {\n\n			mat4 bone = boneGlobalMatrices[ int(i) ];\n			return bone;\n\n		}\n\n	#endif\n\n#endif\n";
 
-			"uniform float dashSize;",
-			"uniform float totalSize;",
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl
 
-			"varying float vLineDistance;",
+THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n	uniform float logDepthBufFC;\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		#extension GL_EXT_frag_depth : enable\n		varying float vFragDepth;\n\n	#endif\n\n#endif";
 
-			THREE.ShaderChunk[ "color_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
+// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl
 
-			"void main() {",
+THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n	diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";
 
-				"if ( mod( vLineDistance, totalSize ) > dashSize ) {",
+// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl
 
-					"discard;",
+THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n	uniform sampler2D alphaMap;\n\n#endif\n";
 
-				"}",
+// File:src/renderers/shaders/UniformsUtils.js
 
-				"gl_FragColor = vec4( diffuse, opacity );",
+/**
+ * Uniform Utilities
+ */
 
-				THREE.ShaderChunk[ "color_fragment" ],
-				THREE.ShaderChunk[ "fog_fragment" ],
+THREE.UniformsUtils = {
 
-			"}"
+	merge: function ( uniforms ) {
 
-		].join("\n")
+		var merged = {};
 
-	},
+		for ( var u = 0; u < uniforms.length; u ++ ) {
 
-	'depth': {
+			var tmp = this.clone( uniforms[ u ] );
 
-		uniforms: {
+			for ( var p in tmp ) {
 
-			"mNear": { type: "f", value: 1.0 },
-			"mFar" : { type: "f", value: 2000.0 },
-			"opacity" : { type: "f", value: 1.0 }
+				merged[ p ] = tmp[ p ];
 
-		},
+			}
 
-		vertexShader: [
+		}
 
-			"void main() {",
+		return merged;
 
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+	},
 
-			"}"
+	clone: function ( uniforms_src ) {
 
-		].join("\n"),
+		var uniforms_dst = {};
 
-		fragmentShader: [
+		for ( var u in uniforms_src ) {
 
-			"uniform float mNear;",
-			"uniform float mFar;",
-			"uniform float opacity;",
+			uniforms_dst[ u ] = {};
 
-			"void main() {",
+			for ( var p in uniforms_src[ u ] ) {
 
-				"float depth = gl_FragCoord.z / gl_FragCoord.w;",
-				"float color = 1.0 - smoothstep( mNear, mFar, depth );",
-				"gl_FragColor = vec4( vec3( color ), opacity );",
+				var parameter_src = uniforms_src[ u ][ p ];
 
-			"}"
+				if ( parameter_src instanceof THREE.Color ||
+					 parameter_src instanceof THREE.Vector2 ||
+					 parameter_src instanceof THREE.Vector3 ||
+					 parameter_src instanceof THREE.Vector4 ||
+					 parameter_src instanceof THREE.Matrix4 ||
+					 parameter_src instanceof THREE.Texture ) {
 
-		].join("\n")
+					uniforms_dst[ u ][ p ] = parameter_src.clone();
 
-	},
+				} else if ( parameter_src instanceof Array ) {
 
-	'normal': {
+					uniforms_dst[ u ][ p ] = parameter_src.slice();
 
-		uniforms: {
+				} else {
 
-			"opacity" : { type: "f", value: 1.0 }
+					uniforms_dst[ u ][ p ] = parameter_src;
 
-		},
+				}
 
-		vertexShader: [
+			}
 
-			"varying vec3 vNormal;",
+		}
 
-			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+		return uniforms_dst;
 
-			"void main() {",
+	}
 
-				"vNormal = normalize( normalMatrix * normal );",
+};
 
-				THREE.ShaderChunk[ "morphtarget_vertex" ],
-				THREE.ShaderChunk[ "default_vertex" ],
+// File:src/renderers/shaders/UniformsLib.js
 
-			"}"
+/**
+ * Uniforms library for shared webgl shaders
+ */
 
-		].join("\n"),
+THREE.UniformsLib = {
 
-		fragmentShader: [
+	common: {
 
-			"uniform float opacity;",
-			"varying vec3 vNormal;",
+		"diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+		"opacity" : { type: "f", value: 1.0 },
 
-			"void main() {",
+		"map" : { type: "t", value: null },
+		"offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
 
-				"gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
+		"lightMap" : { type: "t", value: null },
+		"specularMap" : { type: "t", value: null },
+		"alphaMap" : { type: "t", value: null },
 
-			"}"
+		"envMap" : { type: "t", value: null },
+		"flipEnvMap" : { type: "f", value: - 1 },
+		"reflectivity" : { type: "f", value: 1.0 },
+		"refractionRatio" : { type: "f", value: 0.98 },
 
-		].join("\n")
+		"morphTargetInfluences" : { type: "f", value: 0 }
 
 	},
 
-	/* -------------------------------------------------------------------------
-	//	Normal map shader
-	//		- Blinn-Phong
-	//		- normal + diffuse + specular + AO + displacement + reflection + shadow maps
-	//		- point and directional lights (use with "lights: true" material option)
-	 ------------------------------------------------------------------------- */
+	bump: {
 
-	'normalmap' : {
+		"bumpMap" : { type: "t", value: null },
+		"bumpScale" : { type: "f", value: 1 }
 
-		uniforms: THREE.UniformsUtils.merge( [
+	},
 
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-			THREE.UniformsLib[ "shadowmap" ],
+	normalmap: {
 
-			{
+		"normalMap" : { type: "t", value: null },
+		"normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }
+	},
+
+	fog : {
 
-			"enableAO"		  : { type: "i", value: 0 },
-			"enableDiffuse"	  : { type: "i", value: 0 },
-			"enableSpecular"  : { type: "i", value: 0 },
-			"enableReflection": { type: "i", value: 0 },
-			"enableDisplacement": { type: "i", value: 0 },
+		"fogDensity" : { type: "f", value: 0.00025 },
+		"fogNear" : { type: "f", value: 1 },
+		"fogFar" : { type: "f", value: 2000 },
+		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
 
-			"tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture
-			"tDiffuse"	   : { type: "t", value: null },
-			"tCube"		   : { type: "t", value: null },
-			"tNormal"	   : { type: "t", value: null },
-			"tSpecular"	   : { type: "t", value: null },
-			"tAO"		   : { type: "t", value: null },
+	},
 
-			"uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) },
+	lights: {
 
-			"uDisplacementBias": { type: "f", value: 0.0 },
-			"uDisplacementScale": { type: "f", value: 1.0 },
+		"ambientLightColor" : { type: "fv", value: [] },
 
-			"uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) },
-			"uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) },
-			"uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) },
-			"uShininess": { type: "f", value: 30 },
-			"uOpacity": { type: "f", value: 1 },
+		"directionalLightDirection" : { type: "fv", value: [] },
+		"directionalLightColor" : { type: "fv", value: [] },
 
-			"useRefract": { type: "i", value: 0 },
-			"uRefractionRatio": { type: "f", value: 0.98 },
-			"uReflectivity": { type: "f", value: 0.5 },
+		"hemisphereLightDirection" : { type: "fv", value: [] },
+		"hemisphereLightSkyColor" : { type: "fv", value: [] },
+		"hemisphereLightGroundColor" : { type: "fv", value: [] },
 
-			"uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) },
-			"uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) },
+		"pointLightColor" : { type: "fv", value: [] },
+		"pointLightPosition" : { type: "fv", value: [] },
+		"pointLightDistance" : { type: "fv1", value: [] },
+		"pointLightDecay" : { type: "fv1", value: [] },
 
-			"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
+		"spotLightColor" : { type: "fv", value: [] },
+		"spotLightPosition" : { type: "fv", value: [] },
+		"spotLightDirection" : { type: "fv", value: [] },
+		"spotLightDistance" : { type: "fv1", value: [] },
+		"spotLightAngleCos" : { type: "fv1", value: [] },
+		"spotLightExponent" : { type: "fv1", value: [] },
+		"spotLightDecay" : { type: "fv1", value: [] }
 
-			}
+	},
 
-		] ),
+	particle: {
 
-		fragmentShader: [
+		"psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+		"opacity" : { type: "f", value: 1.0 },
+		"size" : { type: "f", value: 1.0 },
+		"scale" : { type: "f", value: 1.0 },
+		"map" : { type: "t", value: null },
+		"offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
 
-			"uniform vec3 uAmbientColor;",
-			"uniform vec3 uDiffuseColor;",
-			"uniform vec3 uSpecularColor;",
-			"uniform float uShininess;",
-			"uniform float uOpacity;",
+		"fogDensity" : { type: "f", value: 0.00025 },
+		"fogNear" : { type: "f", value: 1 },
+		"fogFar" : { type: "f", value: 2000 },
+		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
 
-			"uniform bool enableDiffuse;",
-			"uniform bool enableSpecular;",
-			"uniform bool enableAO;",
-			"uniform bool enableReflection;",
+	},
 
-			"uniform sampler2D tDiffuse;",
-			"uniform sampler2D tNormal;",
-			"uniform sampler2D tSpecular;",
-			"uniform sampler2D tAO;",
+	shadowmap: {
 
-			"uniform samplerCube tCube;",
+		"shadowMap": { type: "tv", value: [] },
+		"shadowMapSize": { type: "v2v", value: [] },
 
-			"uniform vec2 uNormalScale;",
+		"shadowBias" : { type: "fv1", value: [] },
+		"shadowDarkness": { type: "fv1", value: [] },
 
-			"uniform bool useRefract;",
-			"uniform float uRefractionRatio;",
-			"uniform float uReflectivity;",
+		"shadowMatrix" : { type: "m4v", value: [] }
 
-			"varying vec3 vTangent;",
-			"varying vec3 vBinormal;",
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
+	}
 
-			"uniform vec3 ambientLightColor;",
+};
 
-			"#if MAX_DIR_LIGHTS > 0",
+// File:src/renderers/shaders/ShaderLib.js
 
-				"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
-				"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+/**
+ * Webgl Shader Library for three.js
+ *
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
 
-			"#endif",
 
-			"#if MAX_HEMI_LIGHTS > 0",
+THREE.ShaderLib = {
 
-				"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
-				"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
-				"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+	'basic': {
 
-			"#endif",
+		uniforms: THREE.UniformsUtils.merge( [
 
-			"#if MAX_POINT_LIGHTS > 0",
+			THREE.UniformsLib[ "common" ],
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "shadowmap" ]
 
-				"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
-				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
-				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+		] ),
 
-			"#endif",
+		vertexShader: [
 
-			"#if MAX_SPOT_LIGHTS > 0",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "map_pars_vertex" ],
+			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
+			THREE.ShaderChunk[ "envmap_pars_vertex" ],
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-				"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
-				"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
-				"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
-				"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
-				"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
-				"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+			"void main() {",
 
-			"#endif",
+				THREE.ShaderChunk[ "map_vertex" ],
+				THREE.ShaderChunk[ "lightmap_vertex" ],
+				THREE.ShaderChunk[ "color_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
 
-			"#ifdef WRAP_AROUND",
+			"	#ifdef USE_ENVMAP",
 
-				"uniform vec3 wrapRGB;",
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
 
-			"#endif",
+			"	#endif",
 
-			"varying vec3 vWorldPosition;",
-			"varying vec3 vViewPosition;",
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "envmap_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
 
-			"void main() {",
+			"}"
 
-				"gl_FragColor = vec4( vec3( 1.0 ), uOpacity );",
+		].join("\n"),
 
-				"vec3 specularTex = vec3( 1.0 );",
+		fragmentShader: [
 
-				"vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
-				"normalTex.xy *= uNormalScale;",
-				"normalTex = normalize( normalTex );",
+			"uniform vec3 diffuse;",
+			"uniform float opacity;",
 
-				"if( enableDiffuse ) {",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "map_pars_fragment" ],
+			THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-					"#ifdef GAMMA_INPUT",
+			"void main() {",
 
-						"vec4 texelColor = texture2D( tDiffuse, vUv );",
-						"texelColor.xyz *= texelColor.xyz;",
+			"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"	vec4 diffuseColor = vec4( diffuse, opacity );",
 
-						"gl_FragColor = gl_FragColor * texelColor;",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "map_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "alphamap_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
+				THREE.ShaderChunk[ "specularmap_fragment" ],
 
-					"#else",
+			"	outgoingLight = diffuseColor.rgb;", // simple shader
 
-						"gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );",
+				THREE.ShaderChunk[ "lightmap_fragment" ],		// TODO: Light map on an otherwise unlit surface doesn't make sense.
+				THREE.ShaderChunk[ "envmap_fragment" ],
+				THREE.ShaderChunk[ "shadowmap_fragment" ],		// TODO: Shadows on an otherwise unlit surface doesn't make sense.
 
-					"#endif",
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
 
-				"}",
+				THREE.ShaderChunk[ "fog_fragment" ],
 
-				"if( enableAO ) {",
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-					"#ifdef GAMMA_INPUT",
+			"}"
 
-						"vec4 aoColor = texture2D( tAO, vUv );",
-						"aoColor.xyz *= aoColor.xyz;",
+		].join("\n")
 
-						"gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;",
+	},
 
-					"#else",
+	'lambert': {
 
-						"gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;",
+		uniforms: THREE.UniformsUtils.merge( [
 
-					"#endif",
+			THREE.UniformsLib[ "common" ],
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ],
 
-				"}",
+			{
+				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+				"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
+			}
 
-				"if( enableSpecular )",
-					"specularTex = texture2D( tSpecular, vUv ).xyz;",
+		] ),
 
-				"mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );",
-				"vec3 finalNormal = tsb * normalTex;",
+		vertexShader: [
 
-				"#ifdef FLIP_SIDED",
+			"#define LAMBERT",
 
-					"finalNormal = -finalNormal;",
+			"varying vec3 vLightFront;",
 
-				"#endif",
+			"#ifdef DOUBLE_SIDED",
 
-				"vec3 normal = normalize( finalNormal );",
-				"vec3 viewPosition = normalize( vViewPosition );",
+			"	varying vec3 vLightBack;",
 
-				// point lights
+			"#endif",
 
-				"#if MAX_POINT_LIGHTS > 0",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "map_pars_vertex" ],
+			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
+			THREE.ShaderChunk[ "envmap_pars_vertex" ],
+			THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-					"vec3 pointDiffuse = vec3( 0.0 );",
-					"vec3 pointSpecular = vec3( 0.0 );",
+			"void main() {",
 
-					"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+				THREE.ShaderChunk[ "map_vertex" ],
+				THREE.ShaderChunk[ "lightmap_vertex" ],
+				THREE.ShaderChunk[ "color_vertex" ],
 
-						"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
-						"vec3 pointVector = lPosition.xyz + vViewPosition.xyz;",
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
 
-						"float pointDistance = 1.0;",
-						"if ( pointLightDistance[ i ] > 0.0 )",
-							"pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );",
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-						"pointVector = normalize( pointVector );",
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "envmap_vertex" ],
+				THREE.ShaderChunk[ "lights_lambert_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
 
-						// diffuse
+			"}"
 
-						"#ifdef WRAP_AROUND",
+		].join("\n"),
 
-							"float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );",
-							"float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );",
+		fragmentShader: [
 
-							"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+			"uniform vec3 diffuse;",
+			"uniform vec3 emissive;",
+			"uniform float opacity;",
 
-						"#else",
+			"varying vec3 vLightFront;",
 
-							"float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
+			"#ifdef DOUBLE_SIDED",
 
-						"#endif",
+			"	varying vec3 vLightBack;",
 
-						"pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;",
+			"#endif",
 
-						// specular
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "map_pars_fragment" ],
+			THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-						"vec3 pointHalfVector = normalize( pointVector + viewPosition );",
-						"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
-						"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );",
+			"void main() {",
 
-						"#ifdef PHYSICALLY_BASED_SHADING",
+			"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"	vec4 diffuseColor = vec4( diffuse, opacity );",
 
-							// 2.0 => 2.0001 is hack to work around ANGLE bug
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "map_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "alphamap_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
+				THREE.ShaderChunk[ "specularmap_fragment" ],
 
-							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+			"	#ifdef DOUBLE_SIDED",
 
-							"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );",
-							"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;",
+					//"float isFront = float( gl_FrontFacing );",
+					//"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;",
 
-						"#else",
+			"		if ( gl_FrontFacing )",
+			"			outgoingLight += diffuseColor.rgb * vLightFront + emissive;",
+			"		else",
+			"			outgoingLight += diffuseColor.rgb * vLightBack + emissive;",
 
-							"pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;",
+			"	#else",
 
-						"#endif",
+			"		outgoingLight += diffuseColor.rgb * vLightFront + emissive;",
 
-					"}",
+			"	#endif",
 
-				"#endif",
+				THREE.ShaderChunk[ "lightmap_fragment" ],
+				THREE.ShaderChunk[ "envmap_fragment" ],
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
 
-				// spot lights
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
 
-				"#if MAX_SPOT_LIGHTS > 0",
+				THREE.ShaderChunk[ "fog_fragment" ],
 
-					"vec3 spotDiffuse = vec3( 0.0 );",
-					"vec3 spotSpecular = vec3( 0.0 );",
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-					"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+			"}"
 
-						"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
-						"vec3 spotVector = lPosition.xyz + vViewPosition.xyz;",
+		].join("\n")
 
-						"float spotDistance = 1.0;",
-						"if ( spotLightDistance[ i ] > 0.0 )",
-							"spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );",
+	},
 
-						"spotVector = normalize( spotVector );",
+	'phong': {
 
-						"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+		uniforms: THREE.UniformsUtils.merge( [
 
-						"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+			THREE.UniformsLib[ "common" ],
+			THREE.UniformsLib[ "bump" ],
+			THREE.UniformsLib[ "normalmap" ],
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ],
 
-							"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+			{
+				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+				"specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
+				"shininess": { type: "f", value: 30 },
+				"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
+			}
 
-							// diffuse
+		] ),
 
-							"#ifdef WRAP_AROUND",
+		vertexShader: [
 
-								"float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );",
-								"float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );",
+			"#define PHONG",
 
-								"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+			"varying vec3 vViewPosition;",
 
-							"#else",
+			"#ifndef FLAT_SHADED",
 
-								"float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );",
+			"	varying vec3 vNormal;",
 
-							"#endif",
+			"#endif",
 
-							"spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "map_pars_vertex" ],
+			THREE.ShaderChunk[ "lightmap_pars_vertex" ],
+			THREE.ShaderChunk[ "envmap_pars_vertex" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-							// specular
+			"void main() {",
 
-							"vec3 spotHalfVector = normalize( spotVector + viewPosition );",
-							"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
-							"float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );",
+				THREE.ShaderChunk[ "map_vertex" ],
+				THREE.ShaderChunk[ "lightmap_vertex" ],
+				THREE.ShaderChunk[ "color_vertex" ],
 
-							"#ifdef PHYSICALLY_BASED_SHADING",
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
 
-								// 2.0 => 2.0001 is hack to work around ANGLE bug
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
 
-								"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+			"	vNormal = normalize( transformedNormal );",
 
-								"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );",
-								"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;",
+			"#endif",
 
-							"#else",
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-								"spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;",
+			"	vViewPosition = -mvPosition.xyz;",
 
-							"#endif",
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "envmap_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
 
-						"}",
+			"}"
 
-					"}",
+		].join("\n"),
 
-				"#endif",
+		fragmentShader: [
 
-				// directional lights
+			"#define PHONG",
 
-				"#if MAX_DIR_LIGHTS > 0",
+			"uniform vec3 diffuse;",
+			"uniform vec3 emissive;",
+			"uniform vec3 specular;",
+			"uniform float shininess;",
+			"uniform float opacity;",
 
-					"vec3 dirDiffuse = vec3( 0.0 );",
-					"vec3 dirSpecular = vec3( 0.0 );",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "map_pars_fragment" ],
+			THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
+			THREE.ShaderChunk[ "normalmap_pars_fragment" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-					"for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {",
+			"void main() {",
 
-						"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
-						"vec3 dirVector = normalize( lDirection.xyz );",
+			"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"	vec4 diffuseColor = vec4( diffuse, opacity );",
 
-						// diffuse
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "map_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "alphamap_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
+				THREE.ShaderChunk[ "specularmap_fragment" ],
 
-						"#ifdef WRAP_AROUND",
+				THREE.ShaderChunk[ "lights_phong_fragment" ],
 
-							"float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );",
-							"float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
+				THREE.ShaderChunk[ "lightmap_fragment" ],
+				THREE.ShaderChunk[ "envmap_fragment" ],
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
 
-							"vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );",
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
 
-						"#else",
+				THREE.ShaderChunk[ "fog_fragment" ],
 
-							"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-						"#endif",
+			"}"
 
-						"dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;",
+		].join("\n")
 
-						// specular
+	},
 
-						"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
-						"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
-						"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );",
+	'particle_basic': {
 
-						"#ifdef PHYSICALLY_BASED_SHADING",
+		uniforms: THREE.UniformsUtils.merge( [
 
-							// 2.0 => 2.0001 is hack to work around ANGLE bug
+			THREE.UniformsLib[ "particle" ],
+			THREE.UniformsLib[ "shadowmap" ]
 
-							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+		] ),
 
-							"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
-							"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
+		vertexShader: [
 
-						"#else",
+			"uniform float size;",
+			"uniform float scale;",
 
-							"dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-						"#endif",
+			"void main() {",
 
-					"}",
+				THREE.ShaderChunk[ "color_vertex" ],
 
-				"#endif",
+			"	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
 
-				// hemisphere lights
+			"	#ifdef USE_SIZEATTENUATION",
+			"		gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
+			"	#else",
+			"		gl_PointSize = size;",
+			"	#endif",
 
-				"#if MAX_HEMI_LIGHTS > 0",
+			"	gl_Position = projectionMatrix * mvPosition;",
 
-					"vec3 hemiDiffuse  = vec3( 0.0 );",
-					"vec3 hemiSpecular = vec3( 0.0 );" ,
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
 
-					"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+			"}"
 
-						"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
-						"vec3 lVector = normalize( lDirection.xyz );",
+		].join("\n"),
 
-						// diffuse
+		fragmentShader: [
 
-						"float dotProduct = dot( normal, lVector );",
-						"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+			"uniform vec3 psColor;",
+			"uniform float opacity;",
 
-						"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "map_particle_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-						"hemiDiffuse += uDiffuseColor * hemiColor;",
+			"void main() {",
 
-						// specular (sky light)
+			"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"	vec4 diffuseColor = vec4( psColor, opacity );",
 
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "map_particle_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
 
-						"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
-						"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
-						"float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );",
+			"	outgoingLight = diffuseColor.rgb;", // simple shader
 
-						// specular (ground light)
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
+				THREE.ShaderChunk[ "fog_fragment" ],
 
-						"vec3 lVectorGround = -lVector;",
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-						"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
-						"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
-						"float hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );",
+			"}"
 
-						"#ifdef PHYSICALLY_BASED_SHADING",
+		].join("\n")
 
-							"float dotProductGround = dot( normal, lVectorGround );",
+	},
 
-							// 2.0 => 2.0001 is hack to work around ANGLE bug
+	'dashed': {
 
-							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+		uniforms: THREE.UniformsUtils.merge( [
 
-							"vec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
-							"vec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
-							"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
+			THREE.UniformsLib[ "common" ],
+			THREE.UniformsLib[ "fog" ],
 
-						"#else",
+			{
+				"scale"    : { type: "f", value: 1 },
+				"dashSize" : { type: "f", value: 1 },
+				"totalSize": { type: "f", value: 2 }
+			}
 
-							"hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
+		] ),
 
-						"#endif",
+		vertexShader: [
 
-					"}",
+			"uniform float scale;",
+			"attribute float lineDistance;",
 
-				"#endif",
+			"varying float vLineDistance;",
 
-				// all lights contribution summation
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-				"vec3 totalDiffuse = vec3( 0.0 );",
-				"vec3 totalSpecular = vec3( 0.0 );",
+			"void main() {",
 
-				"#if MAX_DIR_LIGHTS > 0",
+				THREE.ShaderChunk[ "color_vertex" ],
 
-					"totalDiffuse += dirDiffuse;",
-					"totalSpecular += dirSpecular;",
+			"	vLineDistance = scale * lineDistance;",
 
-				"#endif",
+			"	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+			"	gl_Position = projectionMatrix * mvPosition;",
 
-				"#if MAX_HEMI_LIGHTS > 0",
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-					"totalDiffuse += hemiDiffuse;",
-					"totalSpecular += hemiSpecular;",
+			"}"
 
-				"#endif",
+		].join("\n"),
 
-				"#if MAX_POINT_LIGHTS > 0",
+		fragmentShader: [
 
-					"totalDiffuse += pointDiffuse;",
-					"totalSpecular += pointSpecular;",
+			"uniform vec3 diffuse;",
+			"uniform float opacity;",
 
-				"#endif",
+			"uniform float dashSize;",
+			"uniform float totalSize;",
 
-				"#if MAX_SPOT_LIGHTS > 0",
+			"varying float vLineDistance;",
 
-					"totalDiffuse += spotDiffuse;",
-					"totalSpecular += spotSpecular;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-				"#endif",
+			"void main() {",
 
-				"#ifdef METAL",
+			"	if ( mod( vLineDistance, totalSize ) > dashSize ) {",
 
-					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );",
+			"		discard;",
 
-				"#else",
+			"	}",
 
-					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;",
+			"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"	vec4 diffuseColor = vec4( diffuse, opacity );",
 
-				"#endif",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
 
-				"if ( enableReflection ) {",
+			"	outgoingLight = diffuseColor.rgb;", // simple shader
 
-					"vec3 vReflect;",
-					"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+				THREE.ShaderChunk[ "fog_fragment" ],
 
-					"if ( useRefract ) {",
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-						"vReflect = refract( cameraToVertex, normal, uRefractionRatio );",
+			"}"
 
-					"} else {",
+		].join("\n")
 
-						"vReflect = reflect( cameraToVertex, normal );",
+	},
 
-					"}",
+	'depth': {
 
-					"vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
+		uniforms: {
 
-					"#ifdef GAMMA_INPUT",
+			"mNear": { type: "f", value: 1.0 },
+			"mFar" : { type: "f", value: 2000.0 },
+			"opacity" : { type: "f", value: 1.0 }
 
-						"cubeColor.xyz *= cubeColor.xyz;",
+		},
 
-					"#endif",
+		vertexShader: [
 
-					"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-				"}",
+			"void main() {",
 
-				THREE.ShaderChunk[ "shadowmap_fragment" ],
-				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
-				THREE.ShaderChunk[ "fog_fragment" ],
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
 			"}"
 
 		].join("\n"),
 
-		vertexShader: [
-
-			"attribute vec4 tangent;",
+		fragmentShader: [
 
-			"uniform vec2 uOffset;",
-			"uniform vec2 uRepeat;",
+			"uniform float mNear;",
+			"uniform float mFar;",
+			"uniform float opacity;",
 
-			"uniform bool enableDisplacement;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-			"#ifdef VERTEX_TEXTURES",
+			"void main() {",
 
-				"uniform sampler2D tDisplacement;",
-				"uniform float uDisplacementScale;",
-				"uniform float uDisplacementBias;",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
 
-			"#endif",
+			"	#ifdef USE_LOGDEPTHBUF_EXT",
 
-			"varying vec3 vTangent;",
-			"varying vec3 vBinormal;",
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
+			"		float depth = gl_FragDepthEXT / gl_FragCoord.w;",
 
-			"varying vec3 vWorldPosition;",
-			"varying vec3 vViewPosition;",
+			"	#else",
 
-			THREE.ShaderChunk[ "skinning_pars_vertex" ],
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			"		float depth = gl_FragCoord.z / gl_FragCoord.w;",
 
-			"void main() {",
+			"	#endif",
 
-				THREE.ShaderChunk[ "skinbase_vertex" ],
-				THREE.ShaderChunk[ "skinnormal_vertex" ],
+			"	float color = 1.0 - smoothstep( mNear, mFar, depth );",
+			"	gl_FragColor = vec4( vec3( color ), opacity );",   // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
 
-				// normal, tangent and binormal vectors
+			"}"
 
-				"#ifdef USE_SKINNING",
+		].join("\n")
 
-					"vNormal = normalize( normalMatrix * skinnedNormal.xyz );",
+	},
 
-					"vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );",
-					"vTangent = normalize( normalMatrix * skinnedTangent.xyz );",
+	'normal': {
 
-				"#else",
+		uniforms: {
 
-					"vNormal = normalize( normalMatrix * normal );",
-					"vTangent = normalize( normalMatrix * tangent.xyz );",
+			"opacity" : { type: "f", value: 1.0 }
 
-				"#endif",
+		},
 
-				"vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );",
+		vertexShader: [
 
-				"vUv = uv * uRepeat + uOffset;",
+			"varying vec3 vNormal;",
 
-				// displacement mapping
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-				"vec3 displacedPosition;",
+			"void main() {",
 
-				"#ifdef VERTEX_TEXTURES",
+			"	vNormal = normalize( normalMatrix * normal );",
 
-					"if ( enableDisplacement ) {",
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-						"vec3 dv = texture2D( tDisplacement, uv ).xyz;",
-						"float df = uDisplacementScale * dv.x + uDisplacementBias;",
-						"displacedPosition = position + normalize( normal ) * df;",
+			"}"
 
-					"} else {",
+		].join("\n"),
 
-						"#ifdef USE_SKINNING",
+		fragmentShader: [
 
-							"vec4 skinVertex = vec4( position, 1.0 );",
+			"uniform float opacity;",
+			"varying vec3 vNormal;",
 
-							"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
-							"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-							"displacedPosition  = skinned.xyz;",
+			"void main() {",
 
-						"#else",
+			"	gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
 
-							"displacedPosition = position;",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
 
-						"#endif",
+			"}"
 
-					"}",
+		].join("\n")
 
-				"#else",
+	},
 
-					"#ifdef USE_SKINNING",
+	/* -------------------------------------------------------------------------
+	//	Cube map shader
+	 ------------------------------------------------------------------------- */
 
-						"vec4 skinVertex = vec4( position, 1.0 );",
+	'cube': {
 
-						"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
-						"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+		uniforms: { "tCube": { type: "t", value: null },
+					"tFlip": { type: "f", value: - 1 } },
 
-						"displacedPosition  = skinned.xyz;",
+		vertexShader: [
 
-					"#else",
+			"varying vec3 vWorldPosition;",
 
-						"displacedPosition = position;",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
-					"#endif",
+			"void main() {",
 
-				"#endif",
+			"	vWorldPosition = transformDirection( position, modelMatrix );",
 
-				//
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
 
-				"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
-				"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
-				"gl_Position = projectionMatrix * mvPosition;",
+			"}"
 
-				//
+		].join("\n"),
 
-				"vWorldPosition = worldPosition.xyz;",
-				"vViewPosition = -mvPosition.xyz;",
+		fragmentShader: [
 
-				// shadows
+			"uniform samplerCube tCube;",
+			"uniform float tFlip;",
 
-				"#ifdef USE_SHADOWMAP",
+			"varying vec3 vWorldPosition;",
 
-					"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
 
-						"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+			"void main() {",
 
-					"}",
+			"	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
 
-				"#endif",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
 
 			"}"
 
@@ -19509,21 +17692,25 @@ THREE.ShaderLib = {
 	//	Cube map shader
 	 ------------------------------------------------------------------------- */
 
-	'cube': {
+	'equirect': {
 
-		uniforms: { "tCube": { type: "t", value: null },
-					"tFlip": { type: "f", value: -1 } },
+		uniforms: { "tEquirect": { type: "t", value: null },
+					"tFlip": { type: "f", value: - 1 } },
 
 		vertexShader: [
 
 			"varying vec3 vWorldPosition;",
 
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
 			"void main() {",
 
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-				"vWorldPosition = worldPosition.xyz;",
+			"	vWorldPosition = transformDirection( position, modelMatrix );",
 
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
 			"}"
 
@@ -19531,14 +17718,24 @@ THREE.ShaderLib = {
 
 		fragmentShader: [
 
-			"uniform samplerCube tCube;",
+			"uniform sampler2D tEquirect;",
 			"uniform float tFlip;",
 
 			"varying vec3 vWorldPosition;",
 
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
 			"void main() {",
 
-				"gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+				// "	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+				"vec3 direction = normalize( vWorldPosition );",
+				"vec2 sampleUV;",
+				"sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );",
+				"sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
+				"gl_FragColor = texture2D( tEquirect, sampleUV );",
+
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
 
 			"}"
 
@@ -19546,13 +17743,17 @@ THREE.ShaderLib = {
 
 	},
 
-	// Depth encoding into RGBA texture
-	// 	based on SpiderGL shadow map example
-	// 		http://spidergl.org/example.php?id=6
-	// 	originally from
-	//		http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
-	// 	see also here:
-	//		http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
+	/* Depth encoding into RGBA texture
+	 *
+	 * based on SpiderGL shadow map example
+	 * http://spidergl.org/example.php?id=6
+	 *
+	 * originally from
+	 * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
+	 *
+	 * see also
+	 * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
+	 */
 
 	'depthRGBA': {
 
@@ -19560,8 +17761,10 @@ THREE.ShaderLib = {
 
 		vertexShader: [
 
+			THREE.ShaderChunk[ "common" ],
 			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
 			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
 
 			"void main() {",
 
@@ -19569,6 +17772,7 @@ THREE.ShaderLib = {
 				THREE.ShaderChunk[ "morphtarget_vertex" ],
 				THREE.ShaderChunk[ "skinning_vertex" ],
 				THREE.ShaderChunk[ "default_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
 
 			"}"
 
@@ -19576,19 +17780,32 @@ THREE.ShaderLib = {
 
 		fragmentShader: [
 
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
 			"vec4 pack_depth( const in float depth ) {",
 
-				"const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
-				"const vec4 bit_mask  = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
-				"vec4 res = fract( depth * bit_shift );",
-				"res -= res.xxyz * bit_mask;",
-				"return res;",
+			"	const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
+			"	const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
+			"	vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // "	vec4 res = fract( depth * bit_shift );",
+			"	res -= res.xxyz * bit_mask;",
+			"	return res;",
 
 			"}",
 
 			"void main() {",
 
-				"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+			"	#ifdef USE_LOGDEPTHBUF_EXT",
+
+			"		gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );",
+
+			"	#else",
+
+			"		gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+
+			"	#endif",
 
 				//"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );",
 				//"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );",
@@ -19603,6 +17820,8 @@ THREE.ShaderLib = {
 
 };
 
+// File:src/renderers/WebGLRenderer.js
+
 /**
  * @author supereggbert / http://www.paulbrunt.co.uk/
  * @author mrdoob / http://mrdoob.com/
@@ -19617,27 +17836,38 @@ THREE.WebGLRenderer = function ( parameters ) {
 	parameters = parameters || {};
 
 	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
+	_context = parameters.context !== undefined ? parameters.context : null,
+
+	pixelRatio = 1,
 
 	_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
 
-	_alpha = parameters.alpha !== undefined ? parameters.alpha : true,
-	_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
-	_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+	_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
+	_depth = parameters.depth !== undefined ? parameters.depth : true,
 	_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+	_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+	_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
 	_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+	_logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false,
 
 	_clearColor = new THREE.Color( 0x000000 ),
 	_clearAlpha = 0;
 
+	var lights = [];
+
+	var _webglObjects = {};
+	var _webglObjectsImmediate = [];
+
+	var opaqueObjects = [];
+	var transparentObjects = [];
+
+	var sprites = [];
+	var lensFlares = [];
+
 	// public properties
 
 	this.domElement = _canvas;
 	this.context = null;
-	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
-				? parameters.devicePixelRatio
-				: self.devicePixelRatio !== undefined
-					? self.devicePixelRatio
-					: 1;
 
 	// clearing
 
@@ -19649,18 +17879,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 	// scene graph
 
 	this.sortObjects = true;
-	this.autoUpdateObjects = true;
 
 	// physically based shading
 
+	this.gammaFactor = 2.0;	// for backwards compatibility
 	this.gammaInput = false;
 	this.gammaOutput = false;
-	this.physicallyBasedShading = false;
 
 	// shadow map
 
 	this.shadowMapEnabled = false;
-	this.shadowMapAutoUpdate = true;
 	this.shadowMapType = THREE.PCFShadowMap;
 	this.shadowMapCullFace = THREE.CullFaceFront;
 	this.shadowMapDebug = false;
@@ -19675,11 +17903,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.autoScaleCubemaps = true;
 
-	// custom render plugins
-
-	this.renderPluginsPre = [];
-	this.renderPluginsPost = [];
-
 	// info
 
 	this.info = {
@@ -19708,39 +17931,17 @@ THREE.WebGLRenderer = function ( parameters ) {
 	var _this = this,
 
 	_programs = [],
-	_programs_counter = 0,
 
 	// internal state cache
 
 	_currentProgram = null,
 	_currentFramebuffer = null,
-	_currentMaterialId = -1,
-	_currentGeometryGroupHash = null,
+	_currentMaterialId = - 1,
+	_currentGeometryProgram = '',
 	_currentCamera = null,
-	_geometryGroupCounter = 0,
 
 	_usedTextureUnits = 0,
 
-	// GL state cache
-
-	_oldDoubleSided = -1,
-	_oldFlipSided = -1,
-
-	_oldBlending = -1,
-
-	_oldBlendEquation = -1,
-	_oldBlendSrc = -1,
-	_oldBlendDst = -1,
-
-	_oldDepthTest = -1,
-	_oldDepthWrite = -1,
-
-	_oldPolygonOffset = null,
-	_oldPolygonOffsetFactor = null,
-	_oldPolygonOffsetUnits = null,
-
-	_oldLineWidth = null,
-
 	_viewportX = 0,
 	_viewportY = 0,
 	_viewportWidth = _canvas.width,
@@ -19748,8 +17949,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 	_currentWidth = 0,
 	_currentHeight = 0,
 
-	_enabledAttributes = {},
-
 	// frustum
 
 	_frustum = new THREE.Frustum(),
@@ -19757,7 +17956,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 	 // camera matrices cache
 
 	_projScreenMatrix = new THREE.Matrix4(),
-	_projScreenMatrixPS = new THREE.Matrix4(),
 
 	_vector3 = new THREE.Vector3(),
 
@@ -19770,10 +17968,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 	_lights = {
 
 		ambient: [ 0, 0, 0 ],
-		directional: { length: 0, colors: new Array(), positions: new Array() },
-		point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() },
-		spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), anglesCos: new Array(), exponents: new Array() },
-		hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() }
+		directional: { length: 0, colors:[], positions: [] },
+		point: { length: 0, colors: [], positions: [], distances: [], decays: [] },
+		spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] },
+		hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
 
 	};
 
@@ -19781,17 +17979,135 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	var _gl;
 
-	var _glExtensionTextureFloat;
-	var _glExtensionTextureFloatLinear;
-	var _glExtensionStandardDerivatives;
-	var _glExtensionTextureFilterAnisotropic;
-	var _glExtensionCompressedTextureS3TC;
+	try {
+
+		var attributes = {
+			alpha: _alpha,
+			depth: _depth,
+			stencil: _stencil,
+			antialias: _antialias,
+			premultipliedAlpha: _premultipliedAlpha,
+			preserveDrawingBuffer: _preserveDrawingBuffer
+		};
+
+		_gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
+
+		if ( _gl === null ) {
+
+			if ( _canvas.getContext( 'webgl') !== null ) {
+
+				throw 'Error creating WebGL context with your selected attributes.';
+
+			} else {
+
+				throw 'Error creating WebGL context.';
+
+			}
+
+		}
+
+		_canvas.addEventListener( 'webglcontextlost', function ( event ) {
+
+			event.preventDefault();
+
+			resetGLState();
+			setDefaultGLState();
+
+			_webglObjects = {};
+
+		}, false);
+
+	} catch ( error ) {
+
+		THREE.error( 'THREE.WebGLRenderer: ' + error );
+
+	}
+
+	var state = new THREE.WebGLState( _gl, paramThreeToGL );
+
+	if ( _gl.getShaderPrecisionFormat === undefined ) {
+
+		_gl.getShaderPrecisionFormat = function () {
+
+			return {
+				'rangeMin': 1,
+				'rangeMax': 1,
+				'precision': 1
+			};
+
+		}
+
+	}
+
+	var extensions = new THREE.WebGLExtensions( _gl );
+
+	extensions.get( 'OES_texture_float' );
+	extensions.get( 'OES_texture_float_linear' );
+	extensions.get( 'OES_texture_half_float' );
+	extensions.get( 'OES_texture_half_float_linear' );
+	extensions.get( 'OES_standard_derivatives' );
+
+	if ( _logarithmicDepthBuffer ) {
+
+		extensions.get( 'EXT_frag_depth' );
+
+	}
+
+	//
+
+	var glClearColor = function ( r, g, b, a ) {
+
+		if ( _premultipliedAlpha === true ) {
+
+			r *= a; g *= a; b *= a;
+
+		}
+
+		_gl.clearColor( r, g, b, a );
+
+	};
+
+	var setDefaultGLState = function () {
+
+		_gl.clearColor( 0, 0, 0, 1 );
+		_gl.clearDepth( 1 );
+		_gl.clearStencil( 0 );
+
+		_gl.enable( _gl.DEPTH_TEST );
+		_gl.depthFunc( _gl.LEQUAL );
+
+		_gl.frontFace( _gl.CCW );
+		_gl.cullFace( _gl.BACK );
+		_gl.enable( _gl.CULL_FACE );
+
+		_gl.enable( _gl.BLEND );
+		_gl.blendEquation( _gl.FUNC_ADD );
+		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
+
+		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+		glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	var resetGLState = function () {
 
-	initGL();
+		_currentProgram = null;
+		_currentCamera = null;
+
+		_currentGeometryProgram = '';
+		_currentMaterialId = - 1;
+
+		_lightsNeedUpdate = true;
+
+		state.reset();
+
+	};
 
 	setDefaultGLState();
 
 	this.context = _gl;
+	this.state = state;
 
 	// GPU capabilities
 
@@ -19800,59 +18116,84 @@ THREE.WebGLRenderer = function ( parameters ) {
 	var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE );
 	var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE );
 
-	var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
-
-	var _supportsVertexTextures = ( _maxVertexTextures > 0 );
-	var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat;
-
-	var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : [];
+	var _supportsVertexTextures = _maxVertexTextures > 0;
+	var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' );
 
 	//
 
 	var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT );
 	var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT );
-	var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT );
 
 	var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT );
 	var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT );
-	var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT );
 
-	var _vertexShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_INT );
-	var _vertexShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_INT );
-	var _vertexShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_INT );
+	var getCompressedTextureFormats = ( function () {
+
+		var array;
+
+		return function () {
+
+			if ( array !== undefined ) {
+
+				return array;
+
+			}
+
+			array = [];
+
+			if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) {
+
+				var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS );
+
+				for ( var i = 0; i < formats.length; i ++ ) {
+
+					array.push( formats[ i ] );
+
+				}
+
+			}
+
+			return array;
+
+		};
 
-	var _fragmentShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_INT );
-	var _fragmentShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_INT );
-	var _fragmentShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_INT );
+	} )();
 
 	// clamp precision to maximum available
 
 	var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0;
 	var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0;
 
-	if ( _precision === "highp" && ! highpAvailable ) {
+	if ( _precision === 'highp' && ! highpAvailable ) {
 
 		if ( mediumpAvailable ) {
 
-			_precision = "mediump";
-			console.warn( "WebGLRenderer: highp not supported, using mediump" );
+			_precision = 'mediump';
+			THREE.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' );
 
 		} else {
 
-			_precision = "lowp";
-			console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" );
+			_precision = 'lowp';
+			THREE.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' );
 
 		}
 
 	}
 
-	if ( _precision === "mediump" && ! mediumpAvailable ) {
+	if ( _precision === 'mediump' && ! mediumpAvailable ) {
 
-		_precision = "lowp";
-		console.warn( "WebGLRenderer: mediump not supported, using lowp" );
+		_precision = 'lowp';
+		THREE.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' );
 
 	}
 
+	// 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 () {
@@ -19861,6 +18202,12 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
+	this.forceContextLoss = function () {
+
+		extensions.get( 'WEBGL_lose_context' ).loseContext();
+
+	};
+
 	this.supportsVertexTextures = function () {
 
 		return _supportsVertexTextures;
@@ -19869,57 +18216,103 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.supportsFloatTextures = function () {
 
-		return _glExtensionTextureFloat;
+		return extensions.get( 'OES_texture_float' );
+
+	};
+
+	this.supportsHalfFloatTextures = function () {
+
+		return extensions.get( 'OES_texture_half_float' );
 
 	};
 
 	this.supportsStandardDerivatives = function () {
 
-		return _glExtensionStandardDerivatives;
+		return extensions.get( 'OES_standard_derivatives' );
 
 	};
 
 	this.supportsCompressedTextureS3TC = function () {
 
-		return _glExtensionCompressedTextureS3TC;
+		return extensions.get( 'WEBGL_compressed_texture_s3tc' );
 
 	};
 
-	this.getMaxAnisotropy  = function () {
+	this.supportsCompressedTexturePVRTC = function () {
 
-		return _maxAnisotropy;
+		return extensions.get( 'WEBGL_compressed_texture_pvrtc' );
 
 	};
 
+	this.supportsBlendMinMax = function () {
+
+		return extensions.get( 'EXT_blend_minmax' );
+
+	};
+
+	this.getMaxAnisotropy = ( function () {
+
+		var value;
+
+		return function () {
+
+			if ( value !== undefined ) {
+
+				return value;
+
+			}
+
+			var extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+			value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
+
+			return value;
+
+		}
+
+	} )();
+
 	this.getPrecision = function () {
 
 		return _precision;
 
 	};
 
+	this.getPixelRatio = function () {
+
+		return pixelRatio;
+
+	};
+
+	this.setPixelRatio = function ( value ) {
+
+		pixelRatio = value;
+
+	};
+
 	this.setSize = function ( width, height, updateStyle ) {
 
-		_canvas.width = width * this.devicePixelRatio;
-		_canvas.height = height * this.devicePixelRatio;
+		_canvas.width = width * pixelRatio;
+		_canvas.height = height * pixelRatio;
 
-		if ( this.devicePixelRatio !== 1 && updateStyle !== false ) {
+		if ( updateStyle !== false ) {
 
 			_canvas.style.width = width + 'px';
 			_canvas.style.height = height + 'px';
 
 		}
 
-		this.setViewport( 0, 0, _canvas.width, _canvas.height );
+		this.setViewport( 0, 0, width, height );
 
 	};
 
 	this.setViewport = function ( x, y, width, height ) {
 
-		_viewportX = x !== undefined ? x : 0;
-		_viewportY = y !== undefined ? y : 0;
+		_viewportX = x * pixelRatio;
+		_viewportY = y * pixelRatio;
 
-		_viewportWidth = width !== undefined ? width : _canvas.width;
-		_viewportHeight = height !== undefined ? height : _canvas.height;
+		_viewportWidth = width * pixelRatio;
+		_viewportHeight = height * pixelRatio;
 
 		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
 
@@ -19927,7 +18320,12 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.setScissor = function ( x, y, width, height ) {
 
-		_gl.scissor( x, y, width, height );
+		_gl.scissor(
+			x * pixelRatio,
+			y * pixelRatio,
+			width * pixelRatio,
+			height * pixelRatio
+		);
 
 	};
 
@@ -19939,31 +18337,33 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	// Clearing
 
+	this.getClearColor = function () {
+
+		return _clearColor;
+
+	};
+
 	this.setClearColor = function ( color, alpha ) {
 
 		_clearColor.set( color );
+
 		_clearAlpha = alpha !== undefined ? alpha : 1;
 
-		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+		glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
 
 	};
 
-	this.setClearColorHex = function ( hex, alpha ) {
+	this.getClearAlpha = function () {
 
-		console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' );
-		this.setClearColor( hex, alpha );
+		return _clearAlpha;
 
 	};
 
-	this.getClearColor = function () {
-
-		return _clearColor;
-
-	};
+	this.setClearAlpha = function ( alpha ) {
 
-	this.getClearAlpha = function () {
+		_clearAlpha = alpha;
 
-		return _clearAlpha;
+		glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
 
 	};
 
@@ -19979,48 +18379,34 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
-	this.clearTarget = function ( renderTarget, color, depth, stencil ) {
+	this.clearColor = function () {
 
-		this.setRenderTarget( renderTarget );
-		this.clear( color, depth, stencil );
+		_gl.clear( _gl.COLOR_BUFFER_BIT );
 
 	};
 
-	// Plugins
-
-	this.addPostPlugin = function ( plugin ) {
+	this.clearDepth = function () {
 
-		plugin.init( this );
-		this.renderPluginsPost.push( plugin );
+		_gl.clear( _gl.DEPTH_BUFFER_BIT );
 
 	};
 
-	this.addPrePlugin = function ( plugin ) {
+	this.clearStencil = function () {
 
-		plugin.init( this );
-		this.renderPluginsPre.push( plugin );
+		_gl.clear( _gl.STENCIL_BUFFER_BIT );
 
 	};
 
-	// Rendering
-
-	this.updateShadowMap = function ( scene, camera ) {
-
-		_currentProgram = null;
-		_oldBlending = -1;
-		_oldDepthTest = -1;
-		_oldDepthWrite = -1;
-		_currentGeometryGroupHash = -1;
-		_currentMaterialId = -1;
-		_lightsNeedUpdate = true;
-		_oldDoubleSided = -1;
-		_oldFlipSided = -1;
+	this.clearTarget = function ( renderTarget, color, depth, stencil ) {
 
-		this.shadowMapPlugin.update( scene, camera );
+		this.setRenderTarget( renderTarget );
+		this.clear( color, depth, stencil );
 
 	};
 
-	// Internal functions
+	// Reset
+
+	this.resetGLState = resetGLState;
 
 	// Buffer allocation
 
@@ -20058,13 +18444,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 		geometryGroup.__webglFaceBuffer = _gl.createBuffer();
 		geometryGroup.__webglLineBuffer = _gl.createBuffer();
 
-		var m, ml;
+		var numMorphTargets = geometryGroup.numMorphTargets;
 
-		if ( geometryGroup.numMorphTargets ) {
+		if ( numMorphTargets ) {
 
 			geometryGroup.__webglMorphTargetsBuffers = [];
 
-			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+			for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) {
 
 				geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
 
@@ -20072,11 +18458,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( geometryGroup.numMorphNormals ) {
+		var numMorphNormals = geometryGroup.numMorphNormals;
+
+		if ( numMorphNormals ) {
 
 			geometryGroup.__webglMorphNormalsBuffers = [];
 
-			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+			for ( var m = 0, ml = numMorphNormals; m < ml; m ++ ) {
 
 				geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
 
@@ -20090,6 +18478,20 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	// Events
 
+	var onObjectRemoved = function ( event ) {
+
+		var object = event.target;
+
+		object.traverse( function ( child ) {
+
+			child.removeEventListener( 'remove', onObjectRemoved );
+
+			removeObject( child );
+
+		} );
+
+	};
+
 	var onGeometryDispose = function ( event ) {
 
 		var geometry = event.target;
@@ -20131,38 +18533,57 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		material.removeEventListener( 'dispose', onMaterialDispose );
 
-		deallocateMaterial( material );
+		deallocateMaterial( material );
+
+	};
+
+	// Buffer deallocation
+
+	var deleteBuffers = function ( geometry ) {
+
+		var buffers = [
+			'__webglVertexBuffer',
+			'__webglNormalBuffer',
+			'__webglTangentBuffer',
+			'__webglColorBuffer',
+			'__webglUVBuffer',
+			'__webglUV2Buffer',
+
+			'__webglSkinIndicesBuffer',
+			'__webglSkinWeightsBuffer',
+
+			'__webglFaceBuffer',
+			'__webglLineBuffer',
+
+			'__webglLineDistanceBuffer'
+		];
+
+		for ( var i = 0, l = buffers.length; i < l; i ++ ) {
 
-	};
+			var name = buffers[ i ];
 
-	// Buffer deallocation
+			if ( geometry[ name ] !== undefined ) {
 
-	var deleteBuffers = function ( geometry ) {
+				_gl.deleteBuffer( geometry[ name ] );
 
-		if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer );
-		if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer );
-		if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer );
-		if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer );
-		if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer );
-		if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer );
+				delete geometry[ name ];
 
-		if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer );
-		if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer );
+			}
 
-		if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer );
-		if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer );
+		}
 
-		if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
 		// custom attributes
 
 		if ( geometry.__webglCustomAttributesList !== undefined ) {
 
-			for ( var id in geometry.__webglCustomAttributesList ) {
+			for ( var name in geometry.__webglCustomAttributesList ) {
 
-				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
+				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer );
 
 			}
 
+			delete geometry.__webglCustomAttributesList;
+
 		}
 
 		_this.info.memory.geometries --;
@@ -20171,17 +18592,19 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	var deallocateGeometry = function ( geometry ) {
 
-		geometry.__webglInit = undefined;
+		delete geometry.__webglInit;
 
 		if ( geometry instanceof THREE.BufferGeometry ) {
 
-			var attributes = geometry.attributes;
+			for ( var name in geometry.attributes ) {
 
-			for ( var key in attributes ) {
+				var attribute = geometry.attributes[ name ];
 
-				if ( attributes[ key ].buffer !== undefined ) {
+				if ( attribute.buffer !== undefined ) {
 
-					_gl.deleteBuffer( attributes[ key ].buffer );
+					_gl.deleteBuffer( attribute.buffer );
+
+					delete attribute.buffer;
 
 				}
 
@@ -20191,11 +18614,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		} else {
 
-			if ( geometry.geometryGroups !== undefined ) {
+			var geometryGroupsList = geometryGroups[ geometry.id ];
+
+			if ( geometryGroupsList !== undefined ) {
 
-				for ( var g in geometry.geometryGroups ) {
+				for ( var i = 0, l = geometryGroupsList.length; i < l; i ++ ) {
 
-					var geometryGroup = geometry.geometryGroups[ g ];
+					var geometryGroup = geometryGroupsList[ i ];
 
 					if ( geometryGroup.numMorphTargets !== undefined ) {
 
@@ -20205,6 +18630,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
+						delete geometryGroup.__webglMorphTargetsBuffers;
+
 					}
 
 					if ( geometryGroup.numMorphNormals !== undefined ) {
@@ -20215,12 +18642,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
+						delete geometryGroup.__webglMorphNormalsBuffers;
+
 					}
 
 					deleteBuffers( geometryGroup );
 
 				}
 
+				delete geometryGroups[ geometry.id ];
+
 			} else {
 
 				deleteBuffers( geometry );
@@ -20229,6 +18660,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
+		// TOFIX: Workaround for deleted geometry being currently bound
+
+		_currentGeometryProgram = '';
+
 	};
 
 	var deallocateTexture = function ( texture ) {
@@ -20239,25 +18674,31 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.deleteTexture( texture.image.__webglTextureCube );
 
+			delete texture.image.__webglTextureCube;
+
 		} else {
 
 			// 2D texture
 
-			if ( ! texture.__webglInit ) return;
+			if ( texture.__webglInit === undefined ) return;
 
-			texture.__webglInit = false;
 			_gl.deleteTexture( texture.__webglTexture );
 
+			delete texture.__webglTexture;
+			delete texture.__webglInit;
+
 		}
 
 	};
 
 	var deallocateRenderTarget = function ( renderTarget ) {
 
-		if ( !renderTarget || ! renderTarget.__webglTexture ) return;
+		if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return;
 
 		_gl.deleteTexture( renderTarget.__webglTexture );
 
+		delete renderTarget.__webglTexture;
+
 		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
 
 			for ( var i = 0; i < 6; i ++ ) {
@@ -20274,11 +18715,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
+		delete renderTarget.__webglFramebuffer;
+		delete renderTarget.__webglRenderbuffer;
+
 	};
 
 	var deallocateMaterial = function ( material ) {
 
-		var program = material.program;
+		var program = material.program.program;
 
 		if ( program === undefined ) return;
 
@@ -20341,12 +18785,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	// Buffer initialization
 
-	function initCustomAttributes ( geometry, object ) {
-
-		var nvertices = geometry.vertices.length;
+	function initCustomAttributes ( object ) {
 
+		var geometry = object.geometry;
 		var material = object.material;
 
+		var nvertices = geometry.vertices.length;
+
 		if ( material.attributes ) {
 
 			if ( geometry.__webglCustomAttributesList === undefined ) {
@@ -20355,27 +18800,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			for ( var a in material.attributes ) {
+			for ( var name in material.attributes ) {
 
-				var attribute = material.attributes[ a ];
+				var attribute = material.attributes[ name ];
 
-				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+				if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) {
 
 					attribute.__webglInitialized = true;
 
-					var size = 1;		// "f" and "i"
+					var size = 1;   // "f" and "i"
 
-					if ( attribute.type === "v2" ) size = 2;
-					else if ( attribute.type === "v3" ) size = 3;
-					else if ( attribute.type === "v4" ) size = 4;
-					else if ( attribute.type === "c"  ) size = 3;
+					if ( attribute.type === 'v2' ) size = 2;
+					else if ( attribute.type === 'v3' ) size = 3;
+					else if ( attribute.type === 'v4' ) size = 4;
+					else if ( attribute.type === 'c'  ) size = 3;
 
 					attribute.size = size;
 
 					attribute.array = new Float32Array( nvertices * size );
 
 					attribute.buffer = _gl.createBuffer();
-					attribute.buffer.belongsToAttribute = a;
+					attribute.buffer.belongsToAttribute = name;
 
 					attribute.needsUpdate = true;
 
@@ -20396,11 +18841,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 		geometry.__vertexArray = new Float32Array( nvertices * 3 );
 		geometry.__colorArray = new Float32Array( nvertices * 3 );
 
-		geometry.__sortArray = [];
-
 		geometry.__webglParticleCount = nvertices;
 
-		initCustomAttributes ( geometry, object );
+		initCustomAttributes( object );
 
 	};
 
@@ -20414,7 +18857,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		geometry.__webglLineCount = nvertices;
 
-		initCustomAttributes ( geometry, object );
+		initCustomAttributes( object );
 
 	};
 
@@ -20427,19 +18870,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 			ntris     = faces3.length * 1,
 			nlines    = faces3.length * 3,
 
-			material = getBufferMaterial( object, geometryGroup ),
-
-			uvType = bufferGuessUVType( material ),
-			normalType = bufferGuessNormalType( material ),
-			vertexColorType = bufferGuessVertexColorType( material );
-
-		// console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
+			material = getBufferMaterial( object, geometryGroup );
 
 		geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
+		geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
+		geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
+		geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
 
-		if ( normalType ) {
+		if ( geometry.faceVertexUvs.length > 1 ) {
 
-			geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
+			geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
 
 		}
 
@@ -20449,28 +18889,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( vertexColorType ) {
-
-			geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
-
-		}
-
-		if ( uvType ) {
-
-			if ( geometry.faceVertexUvs.length > 0 ) {
-
-				geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
-
-			}
-
-			if ( geometry.faceVertexUvs.length > 1 ) {
-
-				geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
-
-			}
-
-		}
-
 		if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
 
 			geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
@@ -20478,16 +18896,19 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
-		geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
+		var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3
+
+		geometryGroup.__typeArray = UintArray;
+		geometryGroup.__faceArray = new UintArray( ntris * 3 );
+		geometryGroup.__lineArray = new UintArray( nlines * 2 );
 
-		var m, ml;
+		var numMorphTargets = geometryGroup.numMorphTargets;
 
-		if ( geometryGroup.numMorphTargets ) {
+		if ( numMorphTargets ) {
 
 			geometryGroup.__morphTargetsArrays = [];
 
-			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+			for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) {
 
 				geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
 
@@ -20495,11 +18916,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( geometryGroup.numMorphNormals ) {
+		var numMorphNormals = geometryGroup.numMorphNormals;
+
+		if ( numMorphNormals ) {
 
 			geometryGroup.__morphNormalsArrays = [];
 
-			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+			for ( var m = 0, ml = numMorphNormals; m < ml; m ++ ) {
 
 				geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) );
 
@@ -20521,12 +18944,12 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			for ( var a in material.attributes ) {
+			for ( var name in material.attributes ) {
 
 				// Do a shallow copy of the attribute object so different geometryGroup chunks use different
 				// attribute buffers which are correctly indexed in the setMeshBuffers function
 
-				var originalAttribute = material.attributes[ a ];
+				var originalAttribute = material.attributes[ name ];
 
 				var attribute = {};
 
@@ -20536,23 +18959,23 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				}
 
-				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+				if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) {
 
 					attribute.__webglInitialized = true;
 
-					var size = 1;		// "f" and "i"
+					var size = 1;   // "f" and "i"
 
-					if( attribute.type === "v2" ) size = 2;
-					else if( attribute.type === "v3" ) size = 3;
-					else if( attribute.type === "v4" ) size = 4;
-					else if( attribute.type === "c"  ) size = 3;
+					if ( attribute.type === 'v2' ) size = 2;
+					else if ( attribute.type === 'v3' ) size = 3;
+					else if ( attribute.type === 'v4' ) size = 4;
+					else if ( attribute.type === 'c'  ) size = 3;
 
 					attribute.size = size;
 
 					attribute.array = new Float32Array( nvertices * size );
 
 					attribute.buffer = _gl.createBuffer();
-					attribute.buffer.belongsToAttribute = a;
+					attribute.buffer.belongsToAttribute = name;
 
 					originalAttribute.needsUpdate = true;
 					attribute.__original = originalAttribute;
@@ -20572,110 +18995,22 @@ THREE.WebGLRenderer = function ( parameters ) {
 	function getBufferMaterial( object, geometryGroup ) {
 
 		return object.material instanceof THREE.MeshFaceMaterial
-			? object.material.materials[ geometryGroup.materialIndex ]
-			: object.material;
-
-	};
-
-	function materialNeedsSmoothNormals ( material ) {
-
-		return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
-
-	};
-
-	function bufferGuessNormalType ( material ) {
-
-		// only MeshBasicMaterial and MeshDepthMaterial don't need normals
-
-		if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
-
-			return false;
-
-		}
-
-		if ( materialNeedsSmoothNormals( material ) ) {
-
-			return THREE.SmoothShading;
-
-		} else {
-
-			return THREE.FlatShading;
-
-		}
+			 ? object.material.materials[ geometryGroup.materialIndex ]
+			 : object.material;
 
-	};
-
-	function bufferGuessVertexColorType( material ) {
-
-		if ( material.vertexColors ) {
-
-			return material.vertexColors;
-
-		}
-
-		return false;
-
-	};
-
-	function bufferGuessUVType( material ) {
-
-		// material must use some texture to require uvs
-
-		if ( material.map ||
-		     material.lightMap ||
-		     material.bumpMap ||
-		     material.normalMap ||
-		     material.specularMap ||
-		     material instanceof THREE.ShaderMaterial ) {
-
-			return true;
-
-		}
-
-		return false;
-
-	};
-
-	//
-
-	function initDirectBuffers( geometry ) {
-
-		var a, attribute, type;
-
-		for ( a in geometry.attributes ) {
-
-			if ( a === "index" ) {
-
-				type = _gl.ELEMENT_ARRAY_BUFFER;
-
-			} else {
-
-				type = _gl.ARRAY_BUFFER;
-
-			}
-
-			attribute = geometry.attributes[ a ];
-
-			if ( attribute.numItems === undefined ) {
-
-				attribute.numItems = attribute.array.length;
-
-			}
-
-			attribute.buffer = _gl.createBuffer();
+	}
 
-			_gl.bindBuffer( type, attribute.buffer );
-			_gl.bufferData( type, attribute.array, _gl.STATIC_DRAW );
+	function materialNeedsFaceNormals ( material ) {
 
-		}
+		return material instanceof THREE.MeshPhongMaterial === false && material.shading === THREE.FlatShading;
 
-	};
+	}
 
 	// Buffer setting
 
 	function setParticleBuffers ( geometry, hint, object ) {
 
-		var v, c, vertex, offset, index, color,
+		var v, c, vertex, offset, color,
 
 		vertices = geometry.vertices,
 		vl = vertices.length,
@@ -20686,39 +19021,20 @@ THREE.WebGLRenderer = function ( parameters ) {
 		vertexArray = geometry.__vertexArray,
 		colorArray = geometry.__colorArray,
 
-		sortArray = geometry.__sortArray,
-
 		dirtyVertices = geometry.verticesNeedUpdate,
-		dirtyElements = geometry.elementsNeedUpdate,
 		dirtyColors = geometry.colorsNeedUpdate,
 
 		customAttributes = geometry.__webglCustomAttributesList,
 		i, il,
-		a, ca, cal, value,
+		ca, cal, value,
 		customAttribute;
 
-		if ( object.sortParticles ) {
-
-			_projScreenMatrixPS.copy( _projScreenMatrix );
-			_projScreenMatrixPS.multiply( object.matrixWorld );
+		if ( dirtyVertices ) {
 
 			for ( v = 0; v < vl; v ++ ) {
 
 				vertex = vertices[ v ];
 
-				_vector3.copy( vertex );
-				_vector3.applyProjection( _projScreenMatrixPS );
-
-				sortArray[ v ] = [ _vector3.z, v ];
-
-			}
-
-			sortArray.sort( numericalSort );
-
-			for ( v = 0; v < vl; v ++ ) {
-
-				vertex = vertices[ sortArray[v][1] ];
-
 				offset = v * 3;
 
 				vertexArray[ offset ]     = vertex.x;
@@ -20727,11 +19043,18 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
+
+		}
+
+		if ( dirtyColors ) {
+
 			for ( c = 0; c < cl; c ++ ) {
 
-				offset = c * 3;
+				color = colors[ c ];
 
-				color = colors[ sortArray[c][1] ];
+				offset = c * 3;
 
 				colorArray[ offset ]     = color.r;
 				colorArray[ offset + 1 ] = color.g;
@@ -20739,25 +19062,28 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			if ( customAttributes ) {
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
 
-				for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+		}
 
-					customAttribute = customAttributes[ i ];
+		if ( customAttributes ) {
 
-					if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
 
-					offset = 0;
+				customAttribute = customAttributes[ i ];
+
+				if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined ||  customAttribute.boundTo === 'vertices' ) ) {
 
 					cal = customAttribute.value.length;
 
+					offset = 0;
+
 					if ( customAttribute.size === 1 ) {
 
 						for ( ca = 0; ca < cal; ca ++ ) {
 
-							index = sortArray[ ca ][ 1 ];
-
-							customAttribute.array[ ca ] = customAttribute.value[ index ];
+							customAttribute.array[ ca ] = customAttribute.value[ ca ];
 
 						}
 
@@ -20765,11 +19091,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						for ( ca = 0; ca < cal; ca ++ ) {
 
-							index = sortArray[ ca ][ 1 ];
-
-							value = customAttribute.value[ index ];
+							value = customAttribute.value[ ca ];
 
-							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset ]   = value.x;
 							customAttribute.array[ offset + 1 ] = value.y;
 
 							offset += 2;
@@ -20778,15 +19102,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					} else if ( customAttribute.size === 3 ) {
 
-						if ( customAttribute.type === "c" ) {
+						if ( customAttribute.type === 'c' ) {
 
 							for ( ca = 0; ca < cal; ca ++ ) {
 
-								index = sortArray[ ca ][ 1 ];
-
-								value = customAttribute.value[ index ];
+								value = customAttribute.value[ ca ];
 
-								customAttribute.array[ offset ]     = value.r;
+								customAttribute.array[ offset ]   = value.r;
 								customAttribute.array[ offset + 1 ] = value.g;
 								customAttribute.array[ offset + 2 ] = value.b;
 
@@ -20798,11 +19120,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 							for ( ca = 0; ca < cal; ca ++ ) {
 
-								index = sortArray[ ca ][ 1 ];
-
-								value = customAttribute.value[ index ];
+								value = customAttribute.value[ ca ];
 
-								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset ]   = value.x;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 2 ] = value.z;
 
@@ -20816,9 +19136,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						for ( ca = 0; ca < cal; ca ++ ) {
 
-							index = sortArray[ ca ][ 1 ];
-
-							value = customAttribute.value[ index ];
+							value = customAttribute.value[ ca ];
 
 							customAttribute.array[ offset ]      = value.x;
 							customAttribute.array[ offset + 1  ] = value.y;
@@ -20833,167 +19151,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				}
 
-			}
-
-		} else {
-
-			if ( dirtyVertices ) {
-
-				for ( v = 0; v < vl; v ++ ) {
-
-					vertex = vertices[ v ];
-
-					offset = v * 3;
-
-					vertexArray[ offset ]     = vertex.x;
-					vertexArray[ offset + 1 ] = vertex.y;
-					vertexArray[ offset + 2 ] = vertex.z;
-
-				}
-
-			}
-
-			if ( dirtyColors ) {
-
-				for ( c = 0; c < cl; c ++ ) {
-
-					color = colors[ c ];
-
-					offset = c * 3;
-
-					colorArray[ offset ]     = color.r;
-					colorArray[ offset + 1 ] = color.g;
-					colorArray[ offset + 2 ] = color.b;
-
-				}
-
-			}
-
-			if ( customAttributes ) {
-
-				for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
-
-					customAttribute = customAttributes[ i ];
-
-					if ( customAttribute.needsUpdate &&
-						 ( customAttribute.boundTo === undefined ||
-						   customAttribute.boundTo === "vertices") ) {
-
-						cal = customAttribute.value.length;
-
-						offset = 0;
-
-						if ( customAttribute.size === 1 ) {
-
-							for ( ca = 0; ca < cal; ca ++ ) {
-
-								customAttribute.array[ ca ] = customAttribute.value[ ca ];
-
-							}
-
-						} else if ( customAttribute.size === 2 ) {
-
-							for ( ca = 0; ca < cal; ca ++ ) {
-
-								value = customAttribute.value[ ca ];
-
-								customAttribute.array[ offset ] 	= value.x;
-								customAttribute.array[ offset + 1 ] = value.y;
-
-								offset += 2;
-
-							}
-
-						} else if ( customAttribute.size === 3 ) {
-
-							if ( customAttribute.type === "c" ) {
-
-								for ( ca = 0; ca < cal; ca ++ ) {
-
-									value = customAttribute.value[ ca ];
-
-									customAttribute.array[ offset ] 	= value.r;
-									customAttribute.array[ offset + 1 ] = value.g;
-									customAttribute.array[ offset + 2 ] = value.b;
-
-									offset += 3;
-
-								}
-
-							} else {
-
-								for ( ca = 0; ca < cal; ca ++ ) {
-
-									value = customAttribute.value[ ca ];
-
-									customAttribute.array[ offset ] 	= value.x;
-									customAttribute.array[ offset + 1 ] = value.y;
-									customAttribute.array[ offset + 2 ] = value.z;
-
-									offset += 3;
-
-								}
-
-							}
-
-						} else if ( customAttribute.size === 4 ) {
-
-							for ( ca = 0; ca < cal; ca ++ ) {
-
-								value = customAttribute.value[ ca ];
-
-								customAttribute.array[ offset ]      = value.x;
-								customAttribute.array[ offset + 1  ] = value.y;
-								customAttribute.array[ offset + 2  ] = value.z;
-								customAttribute.array[ offset + 3  ] = value.w;
-
-								offset += 4;
-
-							}
-
-						}
-
-					}
-
-				}
-
-			}
-
-		}
-
-		if ( dirtyVertices || object.sortParticles ) {
-
-			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
-			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
-
-		}
-
-		if ( dirtyColors || object.sortParticles ) {
-
-			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
-			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
-
-		}
-
-		if ( customAttributes ) {
-
-			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
-
-				customAttribute = customAttributes[ i ];
-
-				if ( customAttribute.needsUpdate || object.sortParticles ) {
-
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
-					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
 
-				}
+				customAttribute.needsUpdate = false;
 
 			}
 
 		}
 
-
-	};
+	}
 
 	function setLineBuffers ( geometry, hint ) {
 
@@ -21018,7 +19185,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		customAttributes = geometry.__webglCustomAttributesList,
 
 		i, il,
-		a, ca, cal, value,
+		ca, cal, value,
 		customAttribute;
 
 		if ( dirtyVertices ) {
@@ -21078,9 +19245,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				customAttribute = customAttributes[ i ];
 
-				if ( customAttribute.needsUpdate &&
-					 ( customAttribute.boundTo === undefined ||
-					   customAttribute.boundTo === "vertices" ) ) {
+				if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) {
 
 					offset = 0;
 
@@ -21100,7 +19265,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 							value = customAttribute.value[ ca ];
 
-							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset ]   = value.x;
 							customAttribute.array[ offset + 1 ] = value.y;
 
 							offset += 2;
@@ -21109,13 +19274,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					} else if ( customAttribute.size === 3 ) {
 
-						if ( customAttribute.type === "c" ) {
+						if ( customAttribute.type === 'c' ) {
 
 							for ( ca = 0; ca < cal; ca ++ ) {
 
 								value = customAttribute.value[ ca ];
 
-								customAttribute.array[ offset ] 	= value.r;
+								customAttribute.array[ offset ]   = value.r;
 								customAttribute.array[ offset + 1 ] = value.g;
 								customAttribute.array[ offset + 2 ] = value.b;
 
@@ -21129,7 +19294,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 								value = customAttribute.value[ ca ];
 
-								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset ]   = value.x;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 2 ] = value.z;
 
@@ -21145,7 +19310,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 							value = customAttribute.value[ ca ];
 
-							customAttribute.array[ offset ] 	 = value.x;
+							customAttribute.array[ offset ]    = value.x;
 							customAttribute.array[ offset + 1  ] = value.y;
 							customAttribute.array[ offset + 2  ] = value.z;
 							customAttribute.array[ offset + 3  ] = value.w;
@@ -21159,13 +19324,15 @@ THREE.WebGLRenderer = function ( parameters ) {
 					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
 					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
 
+					customAttribute.needsUpdate = false;
+
 				}
 
 			}
 
 		}
 
-	};
+	}
 
 	function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
 
@@ -21175,27 +19342,20 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		var normalType = bufferGuessNormalType( material ),
-		vertexColorType = bufferGuessVertexColorType( material ),
-		uvType = bufferGuessUVType( material ),
-
-		needsSmoothNormals = ( normalType === THREE.SmoothShading );
+		var needsFaceNormals = materialNeedsFaceNormals( material );
 
 		var f, fl, fi, face,
-		vertexNormals, faceNormal, normal,
+		vertexNormals, faceNormal,
 		vertexColors, faceColor,
 		vertexTangents,
-		uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4,
-		c1, c2, c3, c4,
-		sw1, sw2, sw3, sw4,
-		si1, si2, si3, si4,
-		sa1, sa2, sa3, sa4,
-		sb1, sb2, sb3, sb4,
-		m, ml, i, il,
+		uv, uv2, v1, v2, v3, t1, t2, t3, n1, n2, n3,
+		c1, c2, c3,
+		sw1, sw2, sw3,
+		si1, si2, si3,
+		i, il,
 		vn, uvi, uv2i,
 		vk, vkl, vka,
 		nka, chf, faceVertexNormals,
-		a,
 
 		vertexIndex = 0,
 
@@ -21210,7 +19370,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 		offset_skin = 0,
 		offset_morphTarget = 0,
 		offset_custom = 0,
-		offset_customSrc = 0,
 
 		value,
 
@@ -21250,8 +19409,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 		obj_uvs  = geometry.faceVertexUvs[ 0 ],
 		obj_uvs2 = geometry.faceVertexUvs[ 1 ],
 
-		obj_colors = geometry.colors,
-
 		obj_skinIndices = geometry.skinIndices,
 		obj_skinWeights = geometry.skinWeights,
 
@@ -21308,7 +19465,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					vka = morphTargetsArrays[ vk ];
 
-					vka[ offset_morphTarget ] 	  = v1.x;
+					vka[ offset_morphTarget ]     = v1.x;
 					vka[ offset_morphTarget + 1 ] = v1.y;
 					vka[ offset_morphTarget + 2 ] = v1.z;
 
@@ -21324,7 +19481,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					if ( material.morphNormals ) {
 
-						if ( needsSmoothNormals ) {
+						if ( needsFaceNormals ) {
+
+							n1 = morphNormals[ vk ].faceNormals[ chf ];
+							n2 = n1;
+							n3 = n1;
+
+						} else {
 
 							faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
 
@@ -21332,17 +19495,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 							n2 = faceVertexNormals.b;
 							n3 = faceVertexNormals.c;
 
-						} else {
-
-							n1 = morphNormals[ vk ].faceNormals[ chf ];
-							n2 = n1;
-							n3 = n1;
-
 						}
 
 						nka = morphNormalsArrays[ vk ];
 
-						nka[ offset_morphTarget ] 	  = n1.x;
+						nka[ offset_morphTarget ]     = n1.x;
 						nka[ offset_morphTarget + 1 ] = n1.y;
 						nka[ offset_morphTarget + 2 ] = n1.z;
 
@@ -21380,7 +19537,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 				// weights
 
@@ -21440,16 +19597,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( dirtyColors && vertexColorType ) {
+		if ( dirtyColors ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 				vertexColors = face.vertexColors;
 				faceColor = face.color;
 
-				if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
+				if ( vertexColors.length === 3 && material.vertexColors === THREE.VertexColors ) {
 
 					c1 = vertexColors[ 0 ];
 					c2 = vertexColors[ 1 ];
@@ -21492,7 +19649,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 				vertexTangents = face.vertexTangents;
 
@@ -21524,16 +19681,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( dirtyNormals && normalType ) {
+		if ( dirtyNormals ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 				vertexNormals = face.vertexNormals;
 				faceNormal = face.normal;
 
-				if ( vertexNormals.length === 3 && needsSmoothNormals ) {
+				if ( vertexNormals.length === 3 && needsFaceNormals === false ) {
 
 					for ( i = 0; i < 3; i ++ ) {
 
@@ -21568,7 +19725,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( dirtyUvs && obj_uvs && uvType ) {
+		if ( dirtyUvs && obj_uvs ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21600,7 +19757,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( dirtyUvs && obj_uvs2 && uvType ) {
+		if ( dirtyUvs && obj_uvs2 ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21636,7 +19793,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-				faceArray[ offset_face ] 	 = vertexIndex;
+				faceArray[ offset_face ]   = vertexIndex;
 				faceArray[ offset_face + 1 ] = vertexIndex + 1;
 				faceArray[ offset_face + 2 ] = vertexIndex + 2;
 
@@ -21674,17 +19831,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 				if ( ! customAttribute.__original.needsUpdate ) continue;
 
 				offset_custom = 0;
-				offset_customSrc = 0;
 
 				if ( customAttribute.size === 1 ) {
 
-					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
-							customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
+							customAttribute.array[ offset_custom ]     = customAttribute.value[ face.a ];
 							customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
 							customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
 
@@ -21692,13 +19848,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faces" ) {
+					} else if ( customAttribute.boundTo === 'faces' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 							value = customAttribute.value[ chunk_faces3[ f ] ];
 
-							customAttribute.array[ offset_custom ] 	   = value;
+							customAttribute.array[ offset_custom ]     = value;
 							customAttribute.array[ offset_custom + 1 ] = value;
 							customAttribute.array[ offset_custom + 2 ] = value;
 
@@ -21710,17 +19866,17 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				} else if ( customAttribute.size === 2 ) {
 
-					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 
-							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom ]     = v1.x;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
@@ -21733,7 +19889,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faces" ) {
+					} else if ( customAttribute.boundTo === 'faces' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21743,7 +19899,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v3 = value;
 
-							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom ]     = v1.x;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
@@ -21762,27 +19918,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					var pp;
 
-					if ( customAttribute.type === "c" ) {
+					if ( customAttribute.type === 'c' ) {
 
-						pp = [ "r", "g", "b" ];
+						pp = [ 'r', 'g', 'b' ];
 
 					} else {
 
-						pp = [ "x", "y", "z" ];
+						pp = [ 'x', 'y', 'z' ];
 
 					}
 
-					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
@@ -21798,7 +19954,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faces" ) {
+					} else if ( customAttribute.boundTo === 'faces' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21808,7 +19964,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v3 = value;
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
@@ -21824,7 +19980,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faceVertices" ) {
+					} else if ( customAttribute.boundTo === 'faceVertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21834,7 +19990,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value[ 1 ];
 							v3 = value[ 2 ];
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
@@ -21854,17 +20010,17 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				} else if ( customAttribute.size === 4 ) {
 
-					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -21883,7 +20039,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faces" ) {
+					} else if ( customAttribute.boundTo === 'faces' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21893,7 +20049,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v3 = value;
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -21912,7 +20068,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						}
 
-					} else if ( customAttribute.boundTo === "faceVertices" ) {
+					} else if ( customAttribute.boundTo === 'faceVertices' ) {
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
@@ -21922,7 +20078,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value[ 1 ];
 							v3 = value[ 2 ];
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -21970,48 +20126,12 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
-	function setDirectBuffers ( geometry, hint, dispose ) {
-
-		var attributes = geometry.attributes;
-
-		var attributeName, attributeItem;
-
-		for ( attributeName in attributes ) {
-
-			attributeItem = attributes[ attributeName ];
-
-			if ( attributeItem.needsUpdate ) {
-
-				if ( attributeName === 'index' ) {
-
-					_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer );
-					_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint );
-
-				} else {
-
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-					_gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint );
-
-				}
-
-				attributeItem.needsUpdate = false;
-
-			}
-
-			if ( dispose && ! attributeItem.dynamic ) {
-
-				attributeItem.array = null;
-
-			}
-
-		}
-
-	};
-
 	// Buffer rendering
 
 	this.renderBufferImmediate = function ( object, program, material ) {
 
+		state.initAttributes();
+
 		if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer();
 		if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer();
 		if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer();
@@ -22021,7 +20141,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
-			_gl.enableVertexAttribArray( program.attributes.position );
+
+			state.enableAttribute( program.attributes.position );
+
 			_gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 );
 
 		}
@@ -22030,14 +20152,15 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer );
 
-			if ( material.shading === THREE.FlatShading ) {
+			if ( material instanceof THREE.MeshPhongMaterial === false &&
+				   material.shading === THREE.FlatShading ) {
 
 				var nx, ny, nz,
 					nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz,
 					normalArray,
 					i, il = object.count * 3;
 
-				for( i = 0; i < il; i += 9 ) {
+				for ( i = 0; i < il; i += 9 ) {
 
 					normalArray = object.normalArray;
 
@@ -22057,7 +20180,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					ny = ( nay + nby + ncy ) / 3;
 					nz = ( naz + nbz + ncz ) / 3;
 
-					normalArray[ i ] 	 = nx;
+					normalArray[ i ]   = nx;
 					normalArray[ i + 1 ] = ny;
 					normalArray[ i + 2 ] = nz;
 
@@ -22074,7 +20197,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 			}
 
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
-			_gl.enableVertexAttribArray( program.attributes.normal );
+
+			state.enableAttribute( program.attributes.normal );
+
 			_gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
 
 		}
@@ -22083,7 +20208,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
-			_gl.enableVertexAttribArray( program.attributes.uv );
+
+			state.enableAttribute( program.attributes.uv );
+
 			_gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
 
 		}
@@ -22092,1688 +20219,1295 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
-			_gl.enableVertexAttribArray( program.attributes.color );
+
+			state.enableAttribute( program.attributes.color );
+
 			_gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 );
 
 		}
 
+		state.disableUnusedAttributes();
+
 		_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
 
 		object.count = 0;
 
 	};
 
-	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
-
-		if ( material.visible === false ) return;
-
-		var linewidth, a, attribute;
-		var attributeItem, attributeName, attributePointer, attributeSize;
-
-		var program = setProgram( camera, lights, fog, material, object );
+	function setupVertexAttributes( material, program, geometry, startIndex ) {
 
-		var programAttributes = program.attributes;
 		var geometryAttributes = geometry.attributes;
 
-		var updateBuffers = false,
-			wireframeBit = material.wireframe ? 1 : 0,
-			geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
-
-		if ( geometryHash !== _currentGeometryGroupHash ) {
-
-			_currentGeometryGroupHash = geometryHash;
-			updateBuffers = true;
-
-		}
-
-		if ( updateBuffers ) {
-
-			disableAttributes();
-
-		}
-
-		// render mesh
-
-		if ( object instanceof THREE.Mesh ) {
-
-			var index = geometryAttributes[ "index" ];
-
-			// indexed triangles
-
-			if ( index ) {
-
-				var offsets = geometry.offsets;
-
-				// if there is more than 1 chunk
-				// must set attribute pointers to use new offsets for each chunk
-				// even if geometry and materials didn't change
-
-				if ( offsets.length > 1 ) updateBuffers = true;
-
-				for ( var i = 0, il = offsets.length; i < il; i ++ ) {
-
-					var startIndex = offsets[ i ].index;
-
-					if ( updateBuffers ) {
-
-						for ( attributeName in programAttributes ) {
-
-							attributePointer = programAttributes[ attributeName ];
-							attributeItem = geometryAttributes[ attributeName ];
-
-							if ( attributePointer >= 0 ) {
-
-								if ( attributeItem ) {
-
-									attributeSize = attributeItem.itemSize;
-									_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-									enableAttribute( attributePointer );
-									_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32
-
-								} else if ( material.defaultAttributeValues ) {
-
-									if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
-
-										_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-									} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
-
-										_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-									}
-
-								}
-
-							}
-
-						}
-
-						// indices
-
-						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
-
-					}
-
-					// render indexed triangles
-
-					_gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16
-
-					_this.info.render.calls ++;
-					_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
-					_this.info.render.faces += offsets[ i ].count / 3;
-
-				}
-
-			// non-indexed triangles
-
-			} else {
-
-				if ( updateBuffers ) {
-
-					for ( attributeName in programAttributes ) {
-
-						if ( attributeName === 'index') continue;
-
-						attributePointer = programAttributes[ attributeName ];
-						attributeItem = geometryAttributes[ attributeName ];
-
-						if ( attributePointer >= 0 ) {
-
-							if ( attributeItem ) {
-
-								attributeSize = attributeItem.itemSize;
-								_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-								enableAttribute( attributePointer );
-								_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
-
-							} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
-
-								if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
-
-									_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-								} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
-
-									_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-								}
-
-							}
-
-						}
-
-					}
-
-				}
-
-				var position = geometry.attributes[ "position" ];
-
-				// render non-indexed triangles
-
-				_gl.drawArrays( _gl.TRIANGLES, 0, position.numItems / 3 );
-
-				_this.info.render.calls ++;
-				_this.info.render.vertices += position.numItems / 3;
-				_this.info.render.faces += position.numItems / 3 / 3;
-
-			}
-
-		// render particles
+		var programAttributes = program.attributes;
+		var programAttributesKeys = program.attributesKeys;
 
-		} else if ( object instanceof THREE.ParticleSystem ) {
+		for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) {
 
-			if ( updateBuffers ) {
+			var key = programAttributesKeys[ i ];
+			var programAttribute = programAttributes[ key ];
 
-				for ( attributeName in programAttributes ) {
+			if ( programAttribute >= 0 ) {
 
-					attributePointer = programAttributes[ attributeName ];
-					attributeItem = geometryAttributes[ attributeName ];
+				var geometryAttribute = geometryAttributes[ key ];
 
-					if ( attributePointer >= 0 ) {
+				if ( geometryAttribute !== undefined ) {
 
-						if ( attributeItem ) {
+					var size = geometryAttribute.itemSize;
 
-							attributeSize = attributeItem.itemSize;
-							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-							enableAttribute( attributePointer );
-							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer );
 
-						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+					state.enableAttribute( programAttribute );
 
-							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+					_gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
 
-								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
-							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+					if ( material.defaultAttributeValues[ key ].length === 2 ) {
 
-								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+						_gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] );
 
-							}
+					} else if ( material.defaultAttributeValues[ key ].length === 3 ) {
 
-						}
+						_gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] );
 
 					}
 
 				}
 
-				var position = geometryAttributes[ "position" ];
-
-				// render particles
-
-				_gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 );
-
-				_this.info.render.calls ++;
-				_this.info.render.points += position.numItems / 3;
-
 			}
 
-		} else if ( object instanceof THREE.Line ) {
-
-			if ( updateBuffers ) {
-
-				for ( attributeName in programAttributes ) {
-
-					attributePointer = programAttributes[ attributeName ];
-					attributeItem = geometryAttributes[ attributeName ];
-
-					if ( attributePointer >= 0 ) {
-
-						if ( attributeItem ) {
-
-							attributeSize = attributeItem.itemSize;
-							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-							enableAttribute( attributePointer );
-							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
-
-						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
-
-							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
-
-								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
-
-								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
-
-							}
-
-						}
-
-					}
-
-				}
-
-				// render lines
-
-				var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
-
-				setLineWidth( material.linewidth );
-
-				var position = geometryAttributes[ "position" ];
-
-				_gl.drawArrays( primitives, 0, position.numItems / 3 );
-
-				_this.info.render.calls ++;
-				_this.info.render.points += position.numItems;
-
-			}
+		}
 
-    	}
+		state.disableUnusedAttributes();
 
-	};
+	}
 
-	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
+	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
 
 		if ( material.visible === false ) return;
 
-		var linewidth, a, attribute, i, il;
+		updateObject( object );
 
 		var program = setProgram( camera, lights, fog, material, object );
 
-		var attributes = program.attributes;
-
 		var updateBuffers = false,
 			wireframeBit = material.wireframe ? 1 : 0,
-			geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+			geometryProgram = 'direct_' + geometry.id + '_' + program.id + '_' + wireframeBit;
 
-		if ( geometryGroupHash !== _currentGeometryGroupHash ) {
+		if ( geometryProgram !== _currentGeometryProgram ) {
 
-			_currentGeometryGroupHash = geometryGroupHash;
+			_currentGeometryProgram = geometryProgram;
 			updateBuffers = true;
 
 		}
 
 		if ( updateBuffers ) {
 
-			disableAttributes();
-
-		}
-
-		// vertices
-
-		if ( !material.morphTargets && attributes.position >= 0 ) {
-
-			if ( updateBuffers ) {
-
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
-				enableAttribute( attributes.position );
-				_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
-
-			}
-
-		} else {
-
-			if ( object.morphTargetBase ) {
-
-				setupMorphTargets( material, geometryGroup, object );
-
-			}
+			state.initAttributes();
 
 		}
 
+		// render mesh
 
-		if ( updateBuffers ) {
-
-			// custom attributes
-
-			// Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers
-
-			if ( geometryGroup.__webglCustomAttributesList ) {
-
-				for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
-
-					attribute = geometryGroup.__webglCustomAttributesList[ i ];
-
-					if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
-
-						_gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer );
-						enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] );
-						_gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 );
-
-					}
-
-				}
-
-			}
-
-
-			// colors
-
-			if ( attributes.color >= 0 ) {
-
-				if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) {
-
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
-					enableAttribute( attributes.color );
-					_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
-
-				} else if ( material.defaultAttributeValues ) {
-
+		if ( object instanceof THREE.Mesh ) {
 
-					_gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color );
+			var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES;
 
-				}
+			var index = geometry.attributes.index;
 
-			}
+			if ( index ) {
 
-			// normals
+				// indexed triangles
 
-			if ( attributes.normal >= 0 ) {
+				var type, size;
 
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
-				enableAttribute( attributes.normal );
-				_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
+				if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {
 
-			}
+					type = _gl.UNSIGNED_INT;
+					size = 4;
 
-			// tangents
+				} else {
 
-			if ( attributes.tangent >= 0 ) {
+					type = _gl.UNSIGNED_SHORT;
+					size = 2;
 
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
-				enableAttribute( attributes.tangent );
-				_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
+				}
 
-			}
+				var offsets = geometry.offsets;
 
-			// uvs
+				if ( offsets.length === 0 ) {
 
-			if ( attributes.uv >= 0 ) {
+					if ( updateBuffers ) {
 
-				if ( object.geometry.faceVertexUvs[0] ) {
+						setupVertexAttributes( material, program, geometry, 0 );
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
-					enableAttribute( attributes.uv );
-					_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
+					}
 
-				} else if ( material.defaultAttributeValues ) {
+					_gl.drawElements( mode, index.array.length, type, 0 );
 
+					_this.info.render.calls ++;
+					_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
+					_this.info.render.faces += index.array.length / 3;
 
-					_gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv );
+				} else {
 
-				}
+					// if there is more than 1 chunk
+					// must set attribute pointers to use new offsets for each chunk
+					// even if geometry and materials didn't change
 
-			}
+					updateBuffers = true;
 
-			if ( attributes.uv2 >= 0 ) {
+					for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
-				if ( object.geometry.faceVertexUvs[1] ) {
+						var startIndex = offsets[ i ].index;
 
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
-					enableAttribute( attributes.uv2 );
-					_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
+						if ( updateBuffers ) {
 
-				} else if ( material.defaultAttributeValues ) {
+							setupVertexAttributes( material, program, geometry, startIndex );
+							_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
+						}
 
-					_gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 );
+						// render indexed triangles
 
-				}
+						_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size );
 
-			}
+						_this.info.render.calls ++;
+						_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
+						_this.info.render.faces += offsets[ i ].count / 3;
 
-			if ( material.skinning &&
-				 attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
+					}
 
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
-				enableAttribute( attributes.skinIndex );
-				_gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 );
+				}
 
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
-				enableAttribute( attributes.skinWeight );
-				_gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 );
+			} else {
 
-			}
+				// non-indexed triangles
 
-			// line distances
+				if ( updateBuffers ) {
 
-			if ( attributes.lineDistance >= 0 ) {
+					setupVertexAttributes( material, program, geometry, 0 );
 
-				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer );
-				enableAttribute( attributes.lineDistance );
-				_gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 );
+				}
 
-			}
+				var position = geometry.attributes[ 'position' ];
 
-		}
+				// render non-indexed triangles
 
-		// render mesh
+				_gl.drawArrays( mode, 0, position.array.length / position.itemSize );
 
-		if ( object instanceof THREE.Mesh ) {
+				_this.info.render.calls ++;
+				_this.info.render.vertices += position.array.length / position.itemSize;
+				_this.info.render.faces += position.array.length / ( 3 * position.itemSize );
 
-			// wireframe
+			}
 
-			if ( material.wireframe ) {
+		} else if ( object instanceof THREE.PointCloud ) {
 
-				setLineWidth( material.wireframeLinewidth );
+			// render particles
 
-				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
-				_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 );
+			var mode = _gl.POINTS;
 
-			// triangles
+			var index = geometry.attributes.index;
 
-			} else {
+			if ( index ) {
 
-				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
-				_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 );
+				// indexed points
 
-			}
+				var type, size;
 
-			_this.info.render.calls ++;
-			_this.info.render.vertices += geometryGroup.__webglFaceCount;
-			_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
+				if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {
 
-		// render lines
+					type = _gl.UNSIGNED_INT;
+					size = 4;
 
-		} else if ( object instanceof THREE.Line ) {
+				} else {
 
-			var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+					type = _gl.UNSIGNED_SHORT;
+					size = 2;
 
-			setLineWidth( material.linewidth );
+				}
 
-			_gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount );
+				var offsets = geometry.offsets;
 
-			_this.info.render.calls ++;
+				if ( offsets.length === 0 ) {
 
-		// render particles
+					if ( updateBuffers ) {
 
-		} else if ( object instanceof THREE.ParticleSystem ) {
+						setupVertexAttributes( material, program, geometry, 0 );
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
-			_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
+					}
 
-			_this.info.render.calls ++;
-			_this.info.render.points += geometryGroup.__webglParticleCount;
+					_gl.drawElements( mode, index.array.length, type, 0);
 
-		}
+					_this.info.render.calls ++;
+					_this.info.render.points += index.array.length;
 
-	};
+				} else {
 
-	function enableAttribute( attribute ) {
+					// if there is more than 1 chunk
+					// must set attribute pointers to use new offsets for each chunk
+					// even if geometry and materials didn't change
 
-		if ( ! _enabledAttributes[ attribute ] ) {
+					if ( offsets.length > 1 ) updateBuffers = true;
 
-			_gl.enableVertexAttribArray( attribute );
-			_enabledAttributes[ attribute ] = true;
+					for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
-		}
+						var startIndex = offsets[ i ].index;
 
-	};
+						if ( updateBuffers ) {
 
-	function disableAttributes() {
+							setupVertexAttributes( material, program, geometry, startIndex );
+							_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
-		for ( var attribute in _enabledAttributes ) {
+						}
 
-			if ( _enabledAttributes[ attribute ] ) {
+						// render indexed points
 
-				_gl.disableVertexAttribArray( attribute );
-				_enabledAttributes[ attribute ] = false;
+						_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size );
 
-			}
+						_this.info.render.calls ++;
+						_this.info.render.points += offsets[ i ].count;
 
-		}
+					}
 
-	};
+				}
 
-	function setupMorphTargets ( material, geometryGroup, object ) {
+			} else {
 
-		// set base
+				// non-indexed points
 
-		var attributes = material.program.attributes;
+				if ( updateBuffers ) {
 
-		if ( object.morphTargetBase !== -1 && attributes.position >= 0 ) {
+					setupVertexAttributes( material, program, geometry, 0 );
 
-			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] );
-			enableAttribute( attributes.position );
-			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+				}
 
-		} else if ( attributes.position >= 0 ) {
+				var position = geometry.attributes.position;
+				var offsets = geometry.offsets;
 
-			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
-			enableAttribute( attributes.position );
-			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+				if ( offsets.length === 0 ) {
 
-		}
+					_gl.drawArrays( mode, 0, position.array.length / 3 );
 
-		if ( object.morphTargetForcedOrder.length ) {
+					_this.info.render.calls ++;
+					_this.info.render.points += position.array.length / 3;
 
-			// set forced order
+				} else {
 
-			var m = 0;
-			var order = object.morphTargetForcedOrder;
-			var influences = object.morphTargetInfluences;
+					for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
-			while ( m < material.numSupportedMorphTargets && m < order.length ) {
+						_gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count );
 
-				if ( attributes[ "morphTarget" + m ] >= 0 ) {
+						_this.info.render.calls ++;
+						_this.info.render.points += offsets[ i ].count;
 
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
-					enableAttribute( attributes[ "morphTarget" + m ] );
-					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+					}
 
 				}
 
-				if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+			}
 
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] );
-					enableAttribute( attributes[ "morphNormal" + m ] );
-					_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+		} else if ( object instanceof THREE.Line ) {
 
-				}
+			var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
 
-				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
+			state.setLineWidth( material.linewidth * pixelRatio );
 
-				m ++;
-			}
+			var index = geometry.attributes.index;
 
-		} else {
+			if ( index ) {
 
-			// find the most influencing
+				// indexed lines
 
-			var influence, activeInfluenceIndices = [];
-			var influences = object.morphTargetInfluences;
-			var i, il = influences.length;
+				var type, size;
 
-			for ( i = 0; i < il; i ++ ) {
+				if ( index.array instanceof Uint32Array ) {
 
-				influence = influences[ i ];
+					type = _gl.UNSIGNED_INT;
+					size = 4;
 
-				if ( influence > 0 ) {
+				} else {
 
-					activeInfluenceIndices.push( [ influence, i ] );
+					type = _gl.UNSIGNED_SHORT;
+					size = 2;
 
 				}
 
-			}
+				var offsets = geometry.offsets;
 
-			if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
+				if ( offsets.length === 0 ) {
 
-				activeInfluenceIndices.sort( numericalSort );
-				activeInfluenceIndices.length = material.numSupportedMorphTargets;
+					if ( updateBuffers ) {
 
-			} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
+						setupVertexAttributes( material, program, geometry, 0 );
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
-				activeInfluenceIndices.sort( numericalSort );
+					}
 
-			} else if ( activeInfluenceIndices.length === 0 ) {
+					_gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array
 
-				activeInfluenceIndices.push( [ 0, 0 ] );
+					_this.info.render.calls ++;
+					_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
 
-			};
+				} else {
 
-			var influenceIndex, m = 0;
+					// if there is more than 1 chunk
+					// must set attribute pointers to use new offsets for each chunk
+					// even if geometry and materials didn't change
 
-			while ( m < material.numSupportedMorphTargets ) {
+					if ( offsets.length > 1 ) updateBuffers = true;
 
-				if ( activeInfluenceIndices[ m ] ) {
+					for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
-					influenceIndex = activeInfluenceIndices[ m ][ 1 ];
+						var startIndex = offsets[ i ].index;
 
-					if ( attributes[ "morphTarget" + m ] >= 0 ) {
+						if ( updateBuffers ) {
 
-						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
-						enableAttribute( attributes[ "morphTarget" + m ] );
-						_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+							setupVertexAttributes( material, program, geometry, startIndex );
+							_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
-					}
+						}
 
-					if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+						// render indexed lines
 
-						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] );
-						enableAttribute( attributes[ "morphNormal" + m ] );
-						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+						_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
 
+						_this.info.render.calls ++;
+						_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
 
 					}
 
-					object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
-
-				} else {
-
-					/*
-					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+				}
 
-					if ( material.morphNormals ) {
+			} else {
 
-						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+				// non-indexed lines
 
-					}
-					*/
+				if ( updateBuffers ) {
 
-					object.__webglMorphTargetInfluences[ m ] = 0;
+					setupVertexAttributes( material, program, geometry, 0 );
 
 				}
 
-				m ++;
-
-			}
-
-		}
-
-		// load updated influences uniform
+				var position = geometry.attributes.position;
+				var offsets = geometry.offsets;
 
-		if ( material.program.uniforms.morphTargetInfluences !== null ) {
+				if ( offsets.length === 0 ) {
 
-			_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
+					_gl.drawArrays( mode, 0, position.array.length / 3 );
 
-		}
+					_this.info.render.calls ++;
+					_this.info.render.vertices += position.array.length / 3;
 
-	};
+				} else {
 
-	// Sorting
+					for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
-	function painterSortStable ( a, b ) {
+						_gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count );
 
-		if ( a.z !== b.z ) {
+						_this.info.render.calls ++;
+						_this.info.render.vertices += offsets[ i ].count;
 
-			return b.z - a.z;
+					}
 
-		} else {
+				}
 
-			return a.id - b.id;
+			}
 
 		}
 
 	};
 
-	function numericalSort ( a, b ) {
+	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
 
-		return b[ 0 ] - a[ 0 ];
+		if ( material.visible === false ) return;
 
-	};
+		updateObject( object );
 
+		var program = setProgram( camera, lights, fog, material, object );
 
-	// Rendering
+		var attributes = program.attributes;
 
-	this.render = function ( scene, camera, renderTarget, forceClear ) {
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryProgram = geometryGroup.id + '_' + program.id + '_' + wireframeBit;
 
-		if ( camera instanceof THREE.Camera === false ) {
+		if ( geometryProgram !== _currentGeometryProgram ) {
 
-			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
-			return;
+			_currentGeometryProgram = geometryProgram;
+			updateBuffers = true;
 
 		}
 
-		var i, il,
+		if ( updateBuffers ) {
 
-		webglObject, object,
-		renderList,
+			state.initAttributes();
 
-		lights = scene.__lights,
-		fog = scene.fog;
+		}
 
-		// reset caching for this frame
+		// vertices
 
-		_currentMaterialId = -1;
-		_lightsNeedUpdate = true;
+		if ( ! material.morphTargets && attributes.position >= 0 ) {
 
-		// update scene graph
+			if ( updateBuffers ) {
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
 
-		// update camera matrices and frustum
+				state.enableAttribute( attributes.position );
 
-		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+				_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
 
-		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+			}
 
-		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-		_frustum.setFromMatrix( _projScreenMatrix );
+		} else {
 
-		// update WebGL objects
+			if ( object.morphTargetBase ) {
 
-		if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
+				setupMorphTargets( material, geometryGroup, object );
 
-		// custom render plugins (pre pass)
+			}
 
-		renderPlugins( this.renderPluginsPre, scene, camera );
+		}
 
-		//
 
-		_this.info.render.calls = 0;
-		_this.info.render.vertices = 0;
-		_this.info.render.faces = 0;
-		_this.info.render.points = 0;
+		if ( updateBuffers ) {
 
-		this.setRenderTarget( renderTarget );
+			// custom attributes
 
-		if ( this.autoClear || forceClear ) {
+			// Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers
 
-			this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
+			if ( geometryGroup.__webglCustomAttributesList ) {
 
-		}
+				for ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
 
-		// set matrices for regular objects (frustum culled)
+					var attribute = geometryGroup.__webglCustomAttributesList[ i ];
 
-		renderList = scene.__webglObjects;
+					if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
 
-		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer );
 
-			webglObject = renderList[ i ];
-			object = webglObject.object;
+						state.enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] );
 
-			webglObject.id = i;
-			webglObject.render = false;
+						_gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 );
 
-			if ( object.visible ) {
+					}
 
-				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+				}
 
-					setupMatrices( object, camera );
+			}
 
-					unrollBufferMaterial( webglObject );
 
-					webglObject.render = true;
+			// colors
 
-					if ( this.sortObjects === true ) {
+			if ( attributes.color >= 0 ) {
 
-						if ( object.renderDepth !== null ) {
+				if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) {
 
-							webglObject.z = object.renderDepth;
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
 
-						} else {
+					state.enableAttribute( attributes.color );
 
-							_vector3.getPositionFromMatrix( object.matrixWorld );
-							_vector3.applyProjection( _projScreenMatrix );
+					_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
 
-							webglObject.z = _vector3.z;
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
-						}
 
-					}
+					_gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color );
 
 				}
 
 			}
 
-		}
+			// normals
 
-		if ( this.sortObjects ) {
+			if ( attributes.normal >= 0 ) {
 
-			renderList.sort( painterSortStable );
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
 
-		}
+				state.enableAttribute( attributes.normal );
 
-		// set matrices for immediate objects
+				_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
 
-		renderList = scene.__webglObjectsImmediate;
+			}
 
-		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+			// tangents
 
-			webglObject = renderList[ i ];
-			object = webglObject.object;
+			if ( attributes.tangent >= 0 ) {
 
-			if ( object.visible ) {
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
 
-				setupMatrices( object, camera );
+				state.enableAttribute( attributes.tangent );
 
-				unrollImmediateBufferMaterial( webglObject );
+				_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
 
 			}
 
-		}
-
-		if ( scene.overrideMaterial ) {
+			// uvs
 
-			var material = scene.overrideMaterial;
+			if ( attributes.uv >= 0 ) {
 
-			this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
-			this.setDepthTest( material.depthTest );
-			this.setDepthWrite( material.depthWrite );
-			setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+				if ( object.geometry.faceVertexUvs[ 0 ] ) {
 
-			renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material );
-			renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material );
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
 
-		} else {
+					state.enableAttribute( attributes.uv );
 
-			var material = null;
+					_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
 
-			// opaque pass (front-to-back order)
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
-			this.setBlending( THREE.NoBlending );
 
-			renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material );
-			renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material );
+					_gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv );
 
-			// transparent pass (back-to-front order)
+				}
 
-			renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material );
-			renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material );
+			}
 
-		}
+			if ( attributes.uv2 >= 0 ) {
 
-		// custom render plugins (post pass)
+				if ( object.geometry.faceVertexUvs[ 1 ] ) {
 
-		renderPlugins( this.renderPluginsPost, scene, camera );
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
 
+					state.enableAttribute( attributes.uv2 );
 
-		// Generate mipmap if we're using any kind of mipmap filtering
+					_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
 
-		if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
-			updateRenderTargetMipmap( renderTarget );
 
-		}
+					_gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 );
 
-		// Ensure depth buffer writing is enabled so it can be cleared on next render
+				}
 
-		this.setDepthTest( true );
-		this.setDepthWrite( true );
+			}
 
-		// _gl.finish();
+			if ( material.skinning &&
+				 attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
 
-	};
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
 
-	function renderPlugins( plugins, scene, camera ) {
+				state.enableAttribute( attributes.skinIndex );
 
-		if ( ! plugins.length ) return;
+				_gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 );
 
-		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
 
-			// reset state for plugin (to start from clean slate)
+				state.enableAttribute( attributes.skinWeight );
 
-			_currentProgram = null;
-			_currentCamera = null;
+				_gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 );
 
-			_oldBlending = -1;
-			_oldDepthTest = -1;
-			_oldDepthWrite = -1;
-			_oldDoubleSided = -1;
-			_oldFlipSided = -1;
-			_currentGeometryGroupHash = -1;
-			_currentMaterialId = -1;
+			}
 
-			_lightsNeedUpdate = true;
+			// line distances
 
-			plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
+			if ( attributes.lineDistance >= 0 ) {
 
-			// reset state after plugin (anything could have changed)
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer );
 
-			_currentProgram = null;
-			_currentCamera = null;
+				state.enableAttribute( attributes.lineDistance );
 
-			_oldBlending = -1;
-			_oldDepthTest = -1;
-			_oldDepthWrite = -1;
-			_oldDoubleSided = -1;
-			_oldFlipSided = -1;
-			_currentGeometryGroupHash = -1;
-			_currentMaterialId = -1;
+				_gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 );
 
-			_lightsNeedUpdate = true;
+			}
 
 		}
 
-	};
-
-	function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
-
-		var webglObject, object, buffer, material, start, end, delta;
+		state.disableUnusedAttributes();
 
-		if ( reverse ) {
-
-			start = renderList.length - 1;
-			end = -1;
-			delta = -1;
+		// render mesh
 
-		} else {
+		if ( object instanceof THREE.Mesh ) {
 
-			start = 0;
-			end = renderList.length;
-			delta = 1;
-		}
+			var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT;
 
-		for ( var i = start; i !== end; i += delta ) {
+			// wireframe
 
-			webglObject = renderList[ i ];
+			if ( material.wireframe ) {
 
-			if ( webglObject.render ) {
+				state.setLineWidth( material.wireframeLinewidth * pixelRatio );
 
-				object = webglObject.object;
-				buffer = webglObject.buffer;
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
+				_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 );
 
-				if ( overrideMaterial ) {
+			// triangles
 
-					material = overrideMaterial;
+			} else {
 
-				} else {
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
+				_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 );
 
-					material = webglObject[ materialType ];
+			}
 
-					if ( ! material ) continue;
+			_this.info.render.calls ++;
+			_this.info.render.vertices += geometryGroup.__webglFaceCount;
+			_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
 
-					if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+		// render lines
 
-					_this.setDepthTest( material.depthTest );
-					_this.setDepthWrite( material.depthWrite );
-					setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+		} else if ( object instanceof THREE.Line ) {
 
-				}
+			var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
 
-				_this.setMaterialFaces( material );
+			state.setLineWidth( material.linewidth * pixelRatio );
 
-				if ( buffer instanceof THREE.BufferGeometry ) {
+			_gl.drawArrays( mode, 0, geometryGroup.__webglLineCount );
 
-					_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
+			_this.info.render.calls ++;
 
-				} else {
+		// render particles
 
-					_this.renderBuffer( camera, lights, fog, material, buffer, object );
+		} else if ( object instanceof THREE.PointCloud ) {
 
-				}
+			_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
 
-			}
+			_this.info.render.calls ++;
+			_this.info.render.points += geometryGroup.__webglParticleCount;
 
 		}
 
 	};
 
-	function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
-
-		var webglObject, object, material, program;
-
-		for ( var i = 0, il = renderList.length; i < il; i ++ ) {
-
-			webglObject = renderList[ i ];
-			object = webglObject.object;
-
-			if ( object.visible ) {
+	function setupMorphTargets ( material, geometryGroup, object ) {
 
-				if ( overrideMaterial ) {
+		// set base
 
-					material = overrideMaterial;
+		var attributes = material.program.attributes;
 
-				} else {
+		if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) {
 
-					material = webglObject[ materialType ];
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] );
 
-					if ( ! material ) continue;
+			state.enableAttribute( attributes.position );
 
-					if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
 
-					_this.setDepthTest( material.depthTest );
-					_this.setDepthWrite( material.depthWrite );
-					setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+		} else if ( attributes.position >= 0 ) {
 
-				}
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
 
-				_this.renderImmediateObject( camera, lights, fog, material, object );
+			state.enableAttribute( attributes.position );
 
-			}
+			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
 
 		}
 
-	};
+		if ( object.morphTargetForcedOrder.length ) {
 
-	this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
+			// set forced order
 
-		var program = setProgram( camera, lights, fog, material, object );
+			var m = 0;
+			var order = object.morphTargetForcedOrder;
+			var influences = object.morphTargetInfluences;
 
-		_currentGeometryGroupHash = -1;
+			var attribute;
 
-		_this.setMaterialFaces( material );
+			while ( m < material.numSupportedMorphTargets && m < order.length ) {
 
-		if ( object.immediateRenderCallback ) {
+				attribute = attributes[ 'morphTarget' + m ];
 
-			object.immediateRenderCallback( program, _gl, _frustum );
+				if ( attribute >= 0 ) {
 
-		} else {
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
 
-			object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } );
+					state.enableAttribute( attribute );
 
-		}
+					_gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 );
 
-	};
+				}
 
-	function unrollImmediateBufferMaterial ( globject ) {
+				attribute = attributes[ 'morphNormal' + m ];
 
-		var object = globject.object,
-			material = object.material;
+				if ( attribute >= 0 && material.morphNormals ) {
 
-		if ( material.transparent ) {
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] );
 
-			globject.transparent = material;
-			globject.opaque = null;
+					state.enableAttribute( attribute );
 
-		} else {
+					_gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 );
 
-			globject.opaque = material;
-			globject.transparent = null;
+				}
 
-		}
+				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
 
-	};
+				m ++;
 
-	function unrollBufferMaterial ( globject ) {
+			}
 
-		var object = globject.object,
-			buffer = globject.buffer,
-			material, materialIndex, meshMaterial;
+		} else {
 
-		meshMaterial = object.material;
+			// find the most influencing
 
-		if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
+			var activeInfluenceIndices = [];
+			var influences = object.morphTargetInfluences;
+			var morphTargets = object.geometry.morphTargets;
 
-			materialIndex = buffer.materialIndex;
+			if ( influences.length > morphTargets.length ) {
 
-			material = meshMaterial.materials[ materialIndex ];
+				console.warn( 'THREE.WebGLRenderer: Influences array is bigger than morphTargets array.' );
+				influences.length = morphTargets.length;
 
-			if ( material.transparent ) {
+			}
 
-				globject.transparent = material;
-				globject.opaque = null;
+			for ( var i = 0, il = influences.length; i < il; i ++ ) {
 
-			} else {
+				var influence = influences[ i ];
 
-				globject.opaque = material;
-				globject.transparent = null;
+				activeInfluenceIndices.push( [ influence, i ] );
 
 			}
 
-		} else {
-
-			material = meshMaterial;
-
-			if ( material ) {
+			if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
 
-				if ( material.transparent ) {
+				activeInfluenceIndices.sort( numericalSort );
+				activeInfluenceIndices.length = material.numSupportedMorphTargets;
 
-					globject.transparent = material;
-					globject.opaque = null;
+			} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
 
-				} else {
+				activeInfluenceIndices.sort( numericalSort );
 
-					globject.opaque = material;
-					globject.transparent = null;
+			} else if ( activeInfluenceIndices.length === 0 ) {
 
-				}
+				activeInfluenceIndices.push( [ 0, 0 ] );
 
 			}
 
-		}
+			var attribute;
 
-	};
+			for ( var m = 0, ml = material.numSupportedMorphTargets; m < ml; m ++ ) {
 
-	// Geometry splitting
+				if ( activeInfluenceIndices[ m ] ) {
 
-	function sortFacesByMaterial ( geometry, material ) {
+					var influenceIndex = activeInfluenceIndices[ m ][ 1 ];
 
-		var f, fl, face, materialIndex, vertices,
-			groupHash, hash_map = {};
+					attribute = attributes[ 'morphTarget' + m ];
 
-		var numMorphTargets = geometry.morphTargets.length;
-		var numMorphNormals = geometry.morphNormals.length;
+					if ( attribute >= 0 ) {
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
 
-		var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+						state.enableAttribute( attribute );
 
-		geometry.geometryGroups = {};
+						_gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 );
 
-		for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+					}
 
-			face = geometry.faces[ f ];
-			materialIndex = usesFaceMaterial ? face.materialIndex : 0;
+					attribute = attributes[ 'morphNormal' + m ];
 
-			if ( hash_map[ materialIndex ] === undefined ) {
+					if ( attribute >= 0 && material.morphNormals ) {
 
-				hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 };
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] );
 
-			}
+						state.enableAttribute( attribute );
 
-			groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+						_gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 );
 
-			if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+					}
 
-				geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+					object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
 
-			}
+				} else {
 
-			vertices = 3;
+					/*
+					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
 
-			if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
+					if ( material.morphNormals ) {
 
-				hash_map[ materialIndex ].counter += 1;
-				groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
 
-				if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+					}
+					*/
 
-					geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+					object.__webglMorphTargetInfluences[ m ] = 0;
 
 				}
 
 			}
 
-			geometry.geometryGroups[ groupHash ].faces3.push( f );
-			geometry.geometryGroups[ groupHash ].vertices += vertices;
-
 		}
 
-		geometry.geometryGroupsList = [];
-
-		for ( var g in geometry.geometryGroups ) {
+		// load updated influences uniform
 
-			geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
+		if ( material.program.uniforms.morphTargetInfluences !== null ) {
 
-			geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
+			_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
 
 		}
 
-	};
+	}
 
-	// Objects refresh
+	// Sorting
 
-	this.initWebGLObjects = function ( scene ) {
+	function painterSortStable ( a, b ) {
 
-		if ( !scene.__webglObjects ) {
+		if ( a.object.renderOrder !== b.object.renderOrder ) {
 
-			scene.__webglObjects = [];
-			scene.__webglObjectsImmediate = [];
-			scene.__webglSprites = [];
-			scene.__webglFlares = [];
+			return a.object.renderOrder - b.object.renderOrder;
 
-		}
+		} else if ( a.material.id !== b.material.id ) {
 
-		while ( scene.__objectsAdded.length ) {
+			return a.material.id - b.material.id;
 
-			addObject( scene.__objectsAdded[ 0 ], scene );
-			scene.__objectsAdded.splice( 0, 1 );
+		} else if ( a.z !== b.z ) {
 
-		}
+			return a.z - b.z;
 
-		while ( scene.__objectsRemoved.length ) {
+		} else {
 
-			removeObject( scene.__objectsRemoved[ 0 ], scene );
-			scene.__objectsRemoved.splice( 0, 1 );
+			return a.id - b.id;
 
 		}
 
-		// update must be called after objects adding / removal
+	}
 
-		for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
+	function reversePainterSortStable ( a, b ) {
 
-			var object = scene.__webglObjects[ o ].object;
+		if ( a.object.renderOrder !== b.object.renderOrder ) {
 
-			// TODO: Remove this hack (WebGLRenderer refactoring)
+			return a.object.renderOrder - b.object.renderOrder;
 
-			if ( object.__webglInit === undefined ) {
+		} if ( a.z !== b.z ) {
 
-				if ( object.__webglActive !== undefined ) {
+			return b.z - a.z;
 
-					removeObject( object, scene );
+		} else {
 
-				}
+			return a.id - b.id;
 
-				addObject( object, scene );
+		}
 
-			}
+	}
 
-			updateObject( object );
+	function numericalSort ( a, b ) {
 
-		}
+		return b[ 0 ] - a[ 0 ];
 
-	};
+	}
 
-	// Objects adding
+	// Rendering
 
-	function addObject( object, scene ) {
+	this.render = function ( scene, camera, renderTarget, forceClear ) {
 
-		var g, geometry, material, geometryGroup;
+		if ( camera instanceof THREE.Camera === false ) {
 
-		if ( object.__webglInit === undefined ) {
+			THREE.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
 
-			object.__webglInit = true;
+		}
 
-			object._modelViewMatrix = new THREE.Matrix4();
-			object._normalMatrix = new THREE.Matrix3();
+		var fog = scene.fog;
 
-			if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) {
+		// reset caching for this frame
 
-				object.geometry.__webglInit = true;
-				object.geometry.addEventListener( 'dispose', onGeometryDispose );
+		_currentGeometryProgram = '';
+		_currentMaterialId = - 1;
+		_currentCamera = null;
+		_lightsNeedUpdate = true;
 
-			}
+		// update scene graph
 
-			geometry = object.geometry;
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
 
-			if ( geometry === undefined ) {
+		// update camera matrices and frustum
 
-				// fail silently for now
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
 
-			} else if ( geometry instanceof THREE.BufferGeometry ) {
+		// update Skeleton objects
 
-				initDirectBuffers( geometry );
+		scene.traverse( function ( object ) {
 
-			} else if ( object instanceof THREE.Mesh ) {
+			if ( object instanceof THREE.SkinnedMesh ) {
 
-				material = object.material;
+				object.skeleton.update();
 
-				if ( geometry.geometryGroups === undefined ) {
+			}
 
-					sortFacesByMaterial( geometry, material );
+		} );
 
-				}
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
 
-				// create separate VBOs per geometry chunk
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
 
-				for ( g in geometry.geometryGroups ) {
+		lights.length = 0;
+		opaqueObjects.length = 0;
+		transparentObjects.length = 0;
 
-					geometryGroup = geometry.geometryGroups[ g ];
+		sprites.length = 0;
+		lensFlares.length = 0;
 
-					// initialise VBO on the first access
+		projectObject( scene );
 
-					if ( ! geometryGroup.__webglVertexBuffer ) {
+		if ( _this.sortObjects === true ) {
 
-						createMeshBuffers( geometryGroup );
-						initMeshBuffers( geometryGroup, object );
+			opaqueObjects.sort( painterSortStable );
+			transparentObjects.sort( reversePainterSortStable );
 
-						geometry.verticesNeedUpdate = true;
-						geometry.morphTargetsNeedUpdate = true;
-						geometry.elementsNeedUpdate = true;
-						geometry.uvsNeedUpdate = true;
-						geometry.normalsNeedUpdate = true;
-						geometry.tangentsNeedUpdate = true;
-						geometry.colorsNeedUpdate = true;
+		}
 
-					}
+		// custom render plugins (pre pass)
 
-				}
+		shadowMapPlugin.render( scene, camera );
 
-			} else if ( object instanceof THREE.Line ) {
+		//
+
+		_this.info.render.calls = 0;
+		_this.info.render.vertices = 0;
+		_this.info.render.faces = 0;
+		_this.info.render.points = 0;
+
+		this.setRenderTarget( renderTarget );
 
-				if ( ! geometry.__webglVertexBuffer ) {
+		if ( this.autoClear || forceClear ) {
 
-					createLineBuffers( geometry );
-					initLineBuffers( geometry, object );
+			this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
 
-					geometry.verticesNeedUpdate = true;
-					geometry.colorsNeedUpdate = true;
-					geometry.lineDistancesNeedUpdate = true;
+		}
 
-				}
+		// set matrices for immediate objects
 
-			} else if ( object instanceof THREE.ParticleSystem ) {
+		for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) {
 
-				if ( ! geometry.__webglVertexBuffer ) {
+			var webglObject = _webglObjectsImmediate[ i ];
+			var object = webglObject.object;
 
-					createParticleBuffers( geometry );
-					initParticleBuffers( geometry, object );
+			if ( object.visible ) {
 
-					geometry.verticesNeedUpdate = true;
-					geometry.colorsNeedUpdate = true;
+				setupMatrices( object, camera );
 
-				}
+				unrollImmediateBufferMaterial( webglObject );
 
 			}
 
 		}
 
-		if ( object.__webglActive === undefined ) {
+		if ( scene.overrideMaterial ) {
 
-			if ( object instanceof THREE.Mesh ) {
+			var overrideMaterial = scene.overrideMaterial;
 
-				geometry = object.geometry;
+			setMaterial( overrideMaterial );
 
-				if ( geometry instanceof THREE.BufferGeometry ) {
+			renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial );
+			renderObjects( transparentObjects, camera, lights, fog, overrideMaterial );
+			renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, overrideMaterial );
 
-					addBuffer( scene.__webglObjects, geometry, object );
+		} else {
 
-				} else if ( geometry instanceof THREE.Geometry ) {
+			// opaque pass (front-to-back order)
 
-					for ( g in geometry.geometryGroups ) {
+			state.setBlending( THREE.NoBlending );
 
-						geometryGroup = geometry.geometryGroups[ g ];
+			renderObjects( opaqueObjects, camera, lights, fog, null );
+			renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, null );
 
-						addBuffer( scene.__webglObjects, geometryGroup, object );
+			// transparent pass (back-to-front order)
 
-					}
+			renderObjects( transparentObjects, camera, lights, fog, null );
+			renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, null );
 
-				}
+		}
 
-			} else if ( object instanceof THREE.Line ||
-						object instanceof THREE.ParticleSystem ) {
+		// custom render plugins (post pass)
 
-				geometry = object.geometry;
-				addBuffer( scene.__webglObjects, geometry, object );
+		spritePlugin.render( scene, camera );
+		lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
 
-			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+		// Generate mipmap if we're using any kind of mipmap filtering
 
-				addBufferImmediate( scene.__webglObjectsImmediate, object );
+		if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
 
-			} else if ( object instanceof THREE.Sprite ) {
+			updateRenderTargetMipmap( renderTarget );
 
-				scene.__webglSprites.push( object );
+		}
 
-			} else if ( object instanceof THREE.LensFlare ) {
+		// Ensure depth buffer writing is enabled so it can be cleared on next render
 
-				scene.__webglFlares.push( object );
+		state.setDepthTest( true );
+		state.setDepthWrite( true );
+		state.setColorWrite( true );
 
-			}
+		// _gl.finish();
 
-			object.__webglActive = true;
+	};
 
-		}
+	function projectObject( object ) {
 
-	};
+		if ( object.visible === false ) return;
 
-	function addBuffer( objlist, buffer, object ) {
+		if ( object instanceof THREE.Scene || object instanceof THREE.Group ) {
 
-		objlist.push(
-			{
-				id: null,
-				buffer: buffer,
-				object: object,
-				opaque: null,
-				transparent: null,
-				z: 0
-			}
-		);
+			// skip
 
-	};
+		} else {
 
-	function addBufferImmediate( objlist, object ) {
+			initObject( object );
 
-		objlist.push(
-			{
-				id: null,
-				object: object,
-				opaque: null,
-				transparent: null,
-				z: 0
-			}
-		);
+			if ( object instanceof THREE.Light ) {
 
-	};
+				lights.push( object );
 
-	// Objects updates
+			} else if ( object instanceof THREE.Sprite ) {
 
-	function updateObject( object ) {
+				sprites.push( object );
 
-		var geometry = object.geometry,
-			geometryGroup, customAttributesDirty, material;
+			} else if ( object instanceof THREE.LensFlare ) {
 
-		if ( geometry instanceof THREE.BufferGeometry ) {
+				lensFlares.push( object );
 
-			setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
+			} else {
 
-		} else if ( object instanceof THREE.Mesh ) {
+				var webglObjects = _webglObjects[ object.id ];
 
-			// check all geometry groups
+				if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
 
-			for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
+					for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
 
-				geometryGroup = geometry.geometryGroupsList[ i ];
+						var webglObject = webglObjects[ i ];
 
-				material = getBufferMaterial( object, geometryGroup );
+						unrollBufferMaterial( webglObject );
 
-				if ( geometry.buffersNeedUpdate ) {
+						webglObject.render = true;
 
-					initMeshBuffers( geometryGroup, object );
+						if ( _this.sortObjects === true ) {
 
-				}
+							_vector3.setFromMatrixPosition( object.matrixWorld );
+							_vector3.applyProjection( _projScreenMatrix );
 
-				customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+							webglObject.z = _vector3.z;
 
-				if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate ||
-					 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
-					 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) {
+						}
 
-					setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material );
+					}
 
 				}
 
 			}
 
-			geometry.verticesNeedUpdate = false;
-			geometry.morphTargetsNeedUpdate = false;
-			geometry.elementsNeedUpdate = false;
-			geometry.uvsNeedUpdate = false;
-			geometry.normalsNeedUpdate = false;
-			geometry.colorsNeedUpdate = false;
-			geometry.tangentsNeedUpdate = false;
+		}
 
-			geometry.buffersNeedUpdate = false;
+		for ( var i = 0, l = object.children.length; i < l; i ++ ) {
 
-			material.attributes && clearCustomAttributes( material );
+			projectObject( object.children[ i ] );
 
-		} else if ( object instanceof THREE.Line ) {
+		}
 
-			material = getBufferMaterial( object, geometry );
+	}
 
-			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+	function renderObjects( renderList, camera, lights, fog, overrideMaterial ) {
 
-			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
+		var material;
 
-				setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
+		for ( var i = 0, l = renderList.length; i < l; i ++ ) {
 
-			}
+			var webglObject = renderList[ i ];
 
-			geometry.verticesNeedUpdate = false;
-			geometry.colorsNeedUpdate = false;
-			geometry.lineDistancesNeedUpdate = false;
+			var object = webglObject.object;
+			var buffer = webglObject.buffer;
 
-			material.attributes && clearCustomAttributes( material );
+			setupMatrices( object, camera );
 
+			if ( overrideMaterial ) {
 
-		} else if ( object instanceof THREE.ParticleSystem ) {
+				material = overrideMaterial;
 
-			material = getBufferMaterial( object, geometry );
+			} else {
 
-			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+				material = webglObject.material;
 
-			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) {
+				if ( ! material ) continue;
 
-				setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
+				setMaterial( material );
 
 			}
 
-			geometry.verticesNeedUpdate = false;
-			geometry.colorsNeedUpdate = false;
-
-			material.attributes && clearCustomAttributes( material );
-
-		}
+			_this.setMaterialFaces( material );
 
-	};
+			if ( buffer instanceof THREE.BufferGeometry ) {
 
-	// Objects updates - custom attributes check
+				_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
 
-	function areCustomAttributesDirty( material ) {
+			} else {
 
-		for ( var a in material.attributes ) {
+				_this.renderBuffer( camera, lights, fog, material, buffer, object );
 
-			if ( material.attributes[ a ].needsUpdate ) return true;
+			}
 
 		}
 
-		return false;
+	}
 
-	};
+	function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, overrideMaterial ) {
 
-	function clearCustomAttributes( material ) {
+		var material;
 
-		for ( var a in material.attributes ) {
+		for ( var i = 0, l = renderList.length; i < l; i ++ ) {
 
-			material.attributes[ a ].needsUpdate = false;
+			var webglObject = renderList[ i ];
+			var object = webglObject.object;
 
-		}
+			if ( object.visible ) {
 
-	};
+				if ( overrideMaterial ) {
 
-	// Objects removal
+					material = overrideMaterial;
 
-	function removeObject( object, scene ) {
+				} else {
 
-		if ( object instanceof THREE.Mesh  ||
-			 object instanceof THREE.ParticleSystem ||
-			 object instanceof THREE.Line ) {
+					material = webglObject[ materialType ];
 
-			removeInstances( scene.__webglObjects, object );
+					if ( ! material ) continue;
 
-		} else if ( object instanceof THREE.Sprite ) {
+					setMaterial( material );
 
-			removeInstancesDirect( scene.__webglSprites, object );
+				}
 
-		} else if ( object instanceof THREE.LensFlare ) {
+				_this.renderImmediateObject( camera, lights, fog, material, object );
 
-			removeInstancesDirect( scene.__webglFlares, object );
+			}
 
-		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+		}
 
-			removeInstances( scene.__webglObjectsImmediate, object );
+	}
 
-		}
+	this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
 
-		delete object.__webglActive;
+		var program = setProgram( camera, lights, fog, material, object );
 
-	};
+		_currentGeometryProgram = '';
 
-	function removeInstances( objlist, object ) {
+		_this.setMaterialFaces( material );
 
-		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+		if ( object.immediateRenderCallback ) {
 
-			if ( objlist[ o ].object === object ) {
+			object.immediateRenderCallback( program, _gl, _frustum );
 
-				objlist.splice( o, 1 );
+		} else {
 
-			}
+			object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } );
 
 		}
 
 	};
 
-	function removeInstancesDirect( objlist, object ) {
-
-		for ( var o = objlist.length - 1; o >= 0; o -- ) {
-
-			if ( objlist[ o ] === object ) {
-
-				objlist.splice( o, 1 );
+	function unrollImmediateBufferMaterial ( globject ) {
 
-			}
+		var object = globject.object,
+			material = object.material;
 
-		}
+		if ( material.transparent ) {
 
-	};
+			globject.transparent = material;
+			globject.opaque = null;
 
-	// Materials
+		} else {
 
-	this.initMaterial = function ( material, lights, fog, object ) {
+			globject.opaque = material;
+			globject.transparent = null;
 
-		material.addEventListener( 'dispose', onMaterialDispose );
+		}
 
-		var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
+	}
 
-		if ( material instanceof THREE.MeshDepthMaterial ) {
+	function unrollBufferMaterial ( globject ) {
 
-			shaderID = 'depth';
+		var object = globject.object;
+		var buffer = globject.buffer;
 
-		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+		var geometry = object.geometry;
+		var material = object.material;
 
-			shaderID = 'normal';
+		if ( material instanceof THREE.MeshFaceMaterial ) {
 
-		} else if ( material instanceof THREE.MeshBasicMaterial ) {
+			var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex;
 
-			shaderID = 'basic';
+			material = material.materials[ materialIndex ];
 
-		} else if ( material instanceof THREE.MeshLambertMaterial ) {
+			globject.material = material;
 
-			shaderID = 'lambert';
+			if ( material.transparent ) {
 
-		} else if ( material instanceof THREE.MeshPhongMaterial ) {
+				transparentObjects.push( globject );
 
-			shaderID = 'phong';
+			} else {
 
-		} else if ( material instanceof THREE.LineBasicMaterial ) {
+				opaqueObjects.push( globject );
 
-			shaderID = 'basic';
+			}
 
-		} else if ( material instanceof THREE.LineDashedMaterial ) {
+		} else if ( material ) {
 
-			shaderID = 'dashed';
+			globject.material = material;
 
-		} else if ( material instanceof THREE.ParticleSystemMaterial ) {
+			if ( material.transparent ) {
 
-			shaderID = 'particle_basic';
+				transparentObjects.push( globject );
 
-		}
+			} else {
 
-		if ( shaderID ) {
+				opaqueObjects.push( globject );
 
-			setMaterialShaders( material, THREE.ShaderLib[ shaderID ] );
+			}
 
 		}
 
-		// heuristics to create shader parameters according to lights in the scene
-		// (not to blow over maxLights budget)
+	}
 
-		maxLightCount = allocateLights( lights );
+	function initObject( object ) {
 
-		maxShadows = allocateShadows( lights );
+		if ( object.__webglInit === undefined ) {
 
-		maxBones = allocateBones( object );
+			object.__webglInit = true;
+			object._modelViewMatrix = new THREE.Matrix4();
+			object._normalMatrix = new THREE.Matrix3();
 
-		parameters = {
+			object.addEventListener( 'removed', onObjectRemoved );
 
-			map: !!material.map,
-			envMap: !!material.envMap,
-			lightMap: !!material.lightMap,
-			bumpMap: !!material.bumpMap,
-			normalMap: !!material.normalMap,
-			specularMap: !!material.specularMap,
+		}
 
-			vertexColors: material.vertexColors,
+		var geometry = object.geometry;
 
-			fog: fog,
-			useFog: material.fog,
-			fogExp: fog instanceof THREE.FogExp2,
+		if ( geometry === undefined ) {
 
-			sizeAttenuation: material.sizeAttenuation,
+			// ImmediateRenderObject
 
-			skinning: material.skinning,
-			maxBones: maxBones,
-			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
+		} else if ( geometry.__webglInit === undefined ) {
 
-			morphTargets: material.morphTargets,
-			morphNormals: material.morphNormals,
-			maxMorphTargets: this.maxMorphTargets,
-			maxMorphNormals: this.maxMorphNormals,
+			geometry.__webglInit = true;
+			geometry.addEventListener( 'dispose', onGeometryDispose );
 
-			maxDirLights: maxLightCount.directional,
-			maxPointLights: maxLightCount.point,
-			maxSpotLights: maxLightCount.spot,
-			maxHemiLights: maxLightCount.hemi,
+			if ( geometry instanceof THREE.BufferGeometry ) {
 
-			maxShadows: maxShadows,
-			shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow,
-			shadowMapType: this.shadowMapType,
-			shadowMapDebug: this.shadowMapDebug,
-			shadowMapCascade: this.shadowMapCascade,
+				_this.info.memory.geometries ++;
 
-			alphaTest: material.alphaTest,
-			metal: material.metal,
-			perPixel: material.perPixel,
-			wrapAround: material.wrapAround,
-			doubleSided: material.side === THREE.DoubleSide,
-			flipSided: material.side === THREE.BackSide
+			} else if ( object instanceof THREE.Mesh ) {
 
-		};
+				initGeometryGroups( object, geometry );
 
-		material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters, material.index0AttributeName );
+			} else if ( object instanceof THREE.Line ) {
 
-		var attributes = material.program.attributes;
+				if ( geometry.__webglVertexBuffer === undefined ) {
 
-		if ( material.morphTargets ) {
+					createLineBuffers( geometry );
+					initLineBuffers( geometry, object );
 
-			material.numSupportedMorphTargets = 0;
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+					geometry.lineDistancesNeedUpdate = true;
 
-			var id, base = "morphTarget";
+				}
 
-			for ( i = 0; i < this.maxMorphTargets; i ++ ) {
+			} else if ( object instanceof THREE.PointCloud ) {
 
-				id = base + i;
+				if ( geometry.__webglVertexBuffer === undefined ) {
 
-				if ( attributes[ id ] >= 0 ) {
+					createParticleBuffers( geometry );
+					initParticleBuffers( geometry, object );
 
-					material.numSupportedMorphTargets ++;
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
 
 				}
 
@@ -23781,1646 +21515,1744 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if ( material.morphNormals ) {
+		if ( object.__webglActive === undefined) {
 
-			material.numSupportedMorphNormals = 0;
+			object.__webglActive = true;
 
-			var id, base = "morphNormal";
+			if ( object instanceof THREE.Mesh ) {
 
-			for ( i = 0; i < this.maxMorphNormals; i ++ ) {
+				if ( geometry instanceof THREE.BufferGeometry ) {
 
-				id = base + i;
+					addBuffer( _webglObjects, geometry, object );
 
-				if ( attributes[ id ] >= 0 ) {
+				} else if ( geometry instanceof THREE.Geometry ) {
 
-					material.numSupportedMorphNormals ++;
+					var geometryGroupsList = geometryGroups[ geometry.id ];
+
+					for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) {
+
+						addBuffer( _webglObjects, geometryGroupsList[ i ], object );
+
+					}
 
 				}
 
+			} else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) {
+
+				addBuffer( _webglObjects, geometry, object );
+
+			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+				addBufferImmediate( _webglObjectsImmediate, object );
+
 			}
 
 		}
 
-		material.uniformsList = [];
+	}
 
-		for ( u in material.uniforms ) {
+	// Geometry splitting
 
-			material.uniformsList.push( [ material.uniforms[ u ], u ] );
+	var geometryGroups = {};
+	var geometryGroupCounter = 0;
 
-		}
+	function makeGroups( geometry, usesFaceMaterial ) {
 
-	};
+		var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535;
 
-	function setMaterialShaders( material, shaders ) {
+		var groupHash, hash_map = {};
 
-		material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
-		material.vertexShader = shaders.vertexShader;
-		material.fragmentShader = shaders.fragmentShader;
+		var numMorphTargets = geometry.morphTargets.length;
+		var numMorphNormals = geometry.morphNormals.length;
 
-	};
+		var group;
+		var groups = {};
+		var groupsList = [];
 
-	function setProgram( camera, lights, fog, material, object ) {
+		for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
 
-		_usedTextureUnits = 0;
+			var face = geometry.faces[ f ];
+			var materialIndex = usesFaceMaterial ? face.materialIndex : 0;
 
-		if ( material.needsUpdate ) {
+			if ( ! ( materialIndex in hash_map ) ) {
 
-			if ( material.program ) deallocateMaterial( material );
+				hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 };
 
-			_this.initMaterial( material, lights, fog, object );
-			material.needsUpdate = false;
+			}
 
-		}
+			groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
 
-		if ( material.morphTargets ) {
+			if ( ! ( groupHash in groups ) ) {
 
-			if ( ! object.__webglMorphTargetInfluences ) {
+				group = {
+					id: geometryGroupCounter ++,
+					faces3: [],
+					materialIndex: materialIndex,
+					vertices: 0,
+					numMorphTargets: numMorphTargets,
+					numMorphNormals: numMorphNormals
+				};
 
-				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
+				groups[ groupHash ] = group;
+				groupsList.push( group );
 
 			}
 
-		}
-
-		var refreshMaterial = false;
+			if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) {
 
-		var program = material.program,
-			p_uniforms = program.uniforms,
-			m_uniforms = material.uniforms;
+				hash_map[ materialIndex ].counter += 1;
+				groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
 
-		if ( program !== _currentProgram ) {
+				if ( ! ( groupHash in groups ) ) {
 
-			_gl.useProgram( program );
-			_currentProgram = program;
+					group = {
+						id: geometryGroupCounter ++,
+						faces3: [],
+						materialIndex: materialIndex,
+						vertices: 0,
+						numMorphTargets: numMorphTargets,
+						numMorphNormals: numMorphNormals
+					};
 
-			refreshMaterial = true;
+					groups[ groupHash ] = group;
+					groupsList.push( group );
 
-		}
+				}
 
-		if ( material.id !== _currentMaterialId ) {
+			}
 
-			_currentMaterialId = material.id;
-			refreshMaterial = true;
+			groups[ groupHash ].faces3.push( f );
+			groups[ groupHash ].vertices += 3;
 
 		}
 
-		if ( refreshMaterial || camera !== _currentCamera ) {
+		return groupsList;
 
-			_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+	}
 
-			if ( camera !== _currentCamera ) _currentCamera = camera;
+	function initGeometryGroups( object, geometry ) {
 
-		}
+		var material = object.material, addBuffers = false;
 
-		// skinning uniforms must be set even if material didn't change
-		// auto-setting of texture unit for bone texture must go before other textures
-		// not sure why, but otherwise weird things happen
+		if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) {
 
-		if ( material.skinning ) {
+			delete _webglObjects[ object.id ];
 
-			if ( _supportsBoneTextures && object.useVertexTexture ) {
+			geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial );
 
-				if ( p_uniforms.boneTexture !== null ) {
+			geometry.groupsNeedUpdate = false;
 
-					var textureUnit = getTextureUnit();
+		}
 
-					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
-					_this.setTexture( object.boneTexture, textureUnit );
+		var geometryGroupsList = geometryGroups[ geometry.id ];
 
-				}
+		// create separate VBOs per geometry chunk
 
-				if ( p_uniforms.boneTextureWidth !== null ) {
+		for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureWidth, object.boneTextureWidth );
+			var geometryGroup = geometryGroupsList[ i ];
 
-				}
+			// initialise VBO on the first access
 
-				if ( p_uniforms.boneTextureHeight !== null ) {
+			if ( geometryGroup.__webglVertexBuffer === undefined ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight );
+				createMeshBuffers( geometryGroup );
+				initMeshBuffers( geometryGroup, object );
 
-				}
+				geometry.verticesNeedUpdate = true;
+				geometry.morphTargetsNeedUpdate = true;
+				geometry.elementsNeedUpdate = true;
+				geometry.uvsNeedUpdate = true;
+				geometry.normalsNeedUpdate = true;
+				geometry.tangentsNeedUpdate = true;
+				geometry.colorsNeedUpdate = true;
+
+				addBuffers = true;
 
 			} else {
 
-				if ( p_uniforms.boneGlobalMatrices !== null ) {
+				addBuffers = false;
 
-					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
+			}
 
-				}
+			if ( addBuffers || object.__webglActive === undefined ) {
+
+				addBuffer( _webglObjects, geometryGroup, object );
 
 			}
 
 		}
 
-		if ( refreshMaterial ) {
-
-			// refresh uniforms common to several materials
+		object.__webglActive = true;
 
-			if ( fog && material.fog ) {
+	}
 
-				refreshUniformsFog( m_uniforms, fog );
+	function addBuffer( objlist, buffer, object ) {
 
+		var id = object.id;
+		objlist[id] = objlist[id] || [];
+		objlist[id].push(
+			{
+				id: id,
+				buffer: buffer,
+				object: object,
+				material: null,
+				z: 0
 			}
+		);
 
-			if ( material instanceof THREE.MeshPhongMaterial ||
-				 material instanceof THREE.MeshLambertMaterial ||
-				 material.lights ) {
-
-				if ( _lightsNeedUpdate ) {
+	};
 
-					setupLights( program, lights );
-					_lightsNeedUpdate = false;
+	function addBufferImmediate( objlist, object ) {
 
-				}
+		objlist.push(
+			{
+				id: null,
+				object: object,
+				opaque: null,
+				transparent: null,
+				z: 0
+			}
+		);
 
-				refreshUniformsLights( m_uniforms, _lights );
+	};
 
-			}
+	// Objects updates
 
-			if ( material instanceof THREE.MeshBasicMaterial ||
-				 material instanceof THREE.MeshLambertMaterial ||
-				 material instanceof THREE.MeshPhongMaterial ) {
+	function updateObject( object ) {
 
-				refreshUniformsCommon( m_uniforms, material );
+		var geometry = object.geometry;
 
-			}
+		if ( geometry instanceof THREE.BufferGeometry ) {
 
-			// refresh single material specific uniforms
+			var attributes = geometry.attributes;
+			var attributesKeys = geometry.attributesKeys;
 
-			if ( material instanceof THREE.LineBasicMaterial ) {
+			for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) {
 
-				refreshUniformsLine( m_uniforms, material );
+				var key = attributesKeys[ i ];
+				var attribute = attributes[ key ];
+				var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER;
 
-			} else if ( material instanceof THREE.LineDashedMaterial ) {
+				if ( attribute.buffer === undefined ) {
 
-				refreshUniformsLine( m_uniforms, material );
-				refreshUniformsDash( m_uniforms, material );
+					attribute.buffer = _gl.createBuffer();
+					_gl.bindBuffer( bufferType, attribute.buffer );
+					_gl.bufferData( bufferType, attribute.array, ( attribute instanceof THREE.DynamicBufferAttribute ) ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW );
 
-			} else if ( material instanceof THREE.ParticleSystemMaterial ) {
+					attribute.needsUpdate = false;
 
-				refreshUniformsParticle( m_uniforms, material );
+				} else if ( attribute.needsUpdate === true ) {
 
-			} else if ( material instanceof THREE.MeshPhongMaterial ) {
+					_gl.bindBuffer( bufferType, attribute.buffer );
 
-				refreshUniformsPhong( m_uniforms, material );
+					if ( attribute.updateRange === undefined || attribute.updateRange.count === -1 ) { // Not using update ranges
 
-			} else if ( material instanceof THREE.MeshLambertMaterial ) {
+						_gl.bufferSubData( bufferType, 0, attribute.array );
 
-				refreshUniformsLambert( m_uniforms, material );
+					} else if ( attribute.updateRange.count === 0 ) {
 
-			} else if ( material instanceof THREE.MeshDepthMaterial ) {
+						console.error( 'THREE.WebGLRenderer.updateObject: using updateRange for THREE.DynamicBufferAttribute and marked as needsUpdate but count is 0, ensure you are using set methods or updating manually.' );
 
-				m_uniforms.mNear.value = camera.near;
-				m_uniforms.mFar.value = camera.far;
-				m_uniforms.opacity.value = material.opacity;
+					} else {
 
-			} else if ( material instanceof THREE.MeshNormalMaterial ) {
+						_gl.bufferSubData( bufferType, attribute.updateRange.offset * attribute.array.BYTES_PER_ELEMENT,
+										   attribute.array.subarray( attribute.updateRange.offset, attribute.updateRange.offset + attribute.updateRange.count ) );
 
-				m_uniforms.opacity.value = material.opacity;
+						attribute.updateRange.count = 0; // reset range
 
-			}
+					}
 
-			if ( object.receiveShadow && ! material._shadowPass ) {
+					attribute.needsUpdate = false;
 
-				refreshUniformsShadow( m_uniforms, lights );
+				}
 
 			}
 
-			// load common uniforms
+		} else if ( object instanceof THREE.Mesh ) {
 
-			loadUniformsGeneric( program, material.uniformsList );
+			// check all geometry groups
 
-			// load material specific uniforms
-			// (shader material also gets them for the sake of genericity)
+			if ( geometry.groupsNeedUpdate === true ) {
 
-			if ( material instanceof THREE.ShaderMaterial ||
-				 material instanceof THREE.MeshPhongMaterial ||
-				 material.envMap ) {
+				initGeometryGroups( object, geometry );
 
-				if ( p_uniforms.cameraPosition !== null ) {
+			}
 
-					_vector3.getPositionFromMatrix( camera.matrixWorld );
-					_gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );
+			var geometryGroupsList = geometryGroups[ geometry.id ];
 
-				}
+			for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) {
 
-			}
+				var geometryGroup = geometryGroupsList[ i ];
+				var material = getBufferMaterial( object, geometryGroup );
 
-			if ( material instanceof THREE.MeshPhongMaterial ||
-				 material instanceof THREE.MeshLambertMaterial ||
-				 material instanceof THREE.ShaderMaterial ||
-				 material.skinning ) {
+				var customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
 
-				if ( p_uniforms.viewMatrix !== null ) {
+				if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate ||
+					 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
+					 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) {
 
-					_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
+					setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material );
 
 				}
 
 			}
 
-		}
-
-		loadUniformsMatrices( p_uniforms, object );
-
-		if ( p_uniforms.modelMatrix !== null ) {
+			geometry.verticesNeedUpdate = false;
+			geometry.morphTargetsNeedUpdate = false;
+			geometry.elementsNeedUpdate = false;
+			geometry.uvsNeedUpdate = false;
+			geometry.normalsNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
+			geometry.tangentsNeedUpdate = false;
 
-			_gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
+			material.attributes && clearCustomAttributes( material );
 
-		}
+		} else if ( object instanceof THREE.Line ) {
 
-		return program;
+			var material = getBufferMaterial( object, geometry );
+			var customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
 
-	};
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
 
-	// Uniforms (refresh uniforms objects)
+				setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
 
-	function refreshUniformsCommon ( uniforms, material ) {
+			}
 
-		uniforms.opacity.value = material.opacity;
+			geometry.verticesNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
+			geometry.lineDistancesNeedUpdate = false;
 
-		if ( _this.gammaInput ) {
+			material.attributes && clearCustomAttributes( material );
 
-			uniforms.diffuse.value.copyGammaToLinear( material.color );
+		} else if ( object instanceof THREE.PointCloud ) {
 
-		} else {
+			var material = getBufferMaterial( object, geometry );
+			var customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
 
-			uniforms.diffuse.value = material.color;
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || customAttributesDirty ) {
 
-		}
+				setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
 
-		uniforms.map.value = material.map;
-		uniforms.lightMap.value = material.lightMap;
-		uniforms.specularMap.value = material.specularMap;
+			}
 
-		if ( material.bumpMap ) {
+			geometry.verticesNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
 
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
+			material.attributes && clearCustomAttributes( material );
 
 		}
 
-		if ( material.normalMap ) {
+	}
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
+	// Objects updates - custom attributes check
 
-		}
+	function areCustomAttributesDirty( material ) {
 
-		// uv repeat and offset setting priorities
-		//	1. color map
-		//	2. specular map
-		//	3. normal map
-		//	4. bump map
+		for ( var name in material.attributes ) {
 
-		var uvScaleMap;
+			if ( material.attributes[ name ].needsUpdate ) return true;
 
-		if ( material.map ) {
+		}
 
-			uvScaleMap = material.map;
+		return false;
 
-		} else if ( material.specularMap ) {
+	}
 
-			uvScaleMap = material.specularMap;
+	function clearCustomAttributes( material ) {
 
-		} else if ( material.normalMap ) {
+		for ( var name in material.attributes ) {
 
-			uvScaleMap = material.normalMap;
+			material.attributes[ name ].needsUpdate = false;
 
-		} else if ( material.bumpMap ) {
+		}
 
-			uvScaleMap = material.bumpMap;
+	}
 
-		}
+	// Objects removal
 
-		if ( uvScaleMap !== undefined ) {
+	function removeObject( object ) {
 
-			var offset = uvScaleMap.offset;
-			var repeat = uvScaleMap.repeat;
+		if ( object instanceof THREE.Mesh  ||
+			 object instanceof THREE.PointCloud ||
+			 object instanceof THREE.Line ) {
 
-			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+			delete _webglObjects[ object.id ];
 
-		}
+		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
 
-		uniforms.envMap.value = material.envMap;
-		uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
+			removeInstances( _webglObjectsImmediate, object );
 
-		if ( _this.gammaInput ) {
+		}
 
-			//uniforms.reflectivity.value = material.reflectivity * material.reflectivity;
-			uniforms.reflectivity.value = material.reflectivity;
+		delete object.__webglInit;
+		delete object._modelViewMatrix;
+		delete object._normalMatrix;
 
-		} else {
+		delete object.__webglActive;
 
-			uniforms.reflectivity.value = material.reflectivity;
+	}
 
-		}
+	function removeInstances( objlist, object ) {
 
-		uniforms.refractionRatio.value = material.refractionRatio;
-		uniforms.combine.value = material.combine;
-		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
 
-	};
+			if ( objlist[ o ].object === object ) {
 
-	function refreshUniformsLine ( uniforms, material ) {
+				objlist.splice( o, 1 );
 
-		uniforms.diffuse.value = material.color;
-		uniforms.opacity.value = material.opacity;
+			}
 
-	};
+		}
 
-	function refreshUniformsDash ( uniforms, material ) {
+	}
 
-		uniforms.dashSize.value = material.dashSize;
-		uniforms.totalSize.value = material.dashSize + material.gapSize;
-		uniforms.scale.value = material.scale;
+	// Materials
 
+	var shaderIDs = {
+		MeshDepthMaterial: 'depth',
+		MeshNormalMaterial: 'normal',
+		MeshBasicMaterial: 'basic',
+		MeshLambertMaterial: 'lambert',
+		MeshPhongMaterial: 'phong',
+		LineBasicMaterial: 'basic',
+		LineDashedMaterial: 'dashed',
+		PointCloudMaterial: 'particle_basic'
 	};
 
-	function refreshUniformsParticle ( uniforms, material ) {
+	function initMaterial( material, lights, fog, object ) {
 
-		uniforms.psColor.value = material.color;
-		uniforms.opacity.value = material.opacity;
-		uniforms.size.value = material.size;
-		uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
+		material.addEventListener( 'dispose', onMaterialDispose );
 
-		uniforms.map.value = material.map;
+		var shaderID = shaderIDs[ material.type ];
 
-	};
+		if ( shaderID ) {
 
-	function refreshUniformsFog ( uniforms, fog ) {
+			var shader = THREE.ShaderLib[ shaderID ];
 
-		uniforms.fogColor.value = fog.color;
+			material.__webglShader = {
+				uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+				vertexShader: shader.vertexShader,
+				fragmentShader: shader.fragmentShader
+			}
 
-		if ( fog instanceof THREE.Fog ) {
+		} else {
 
-			uniforms.fogNear.value = fog.near;
-			uniforms.fogFar.value = fog.far;
+			material.__webglShader = {
+				uniforms: material.uniforms,
+				vertexShader: material.vertexShader,
+				fragmentShader: material.fragmentShader
+			}
 
-		} else if ( fog instanceof THREE.FogExp2 ) {
+		}
 
-			uniforms.fogDensity.value = fog.density;
+		// heuristics to create shader parameters according to lights in the scene
+		// (not to blow over maxLights budget)
 
-		}
+		var maxLightCount = allocateLights( lights );
+		var maxShadows = allocateShadows( lights );
+		var maxBones = allocateBones( object );
 
-	};
+		var parameters = {
 
-	function refreshUniformsPhong ( uniforms, material ) {
+			precision: _precision,
+			supportsVertexTextures: _supportsVertexTextures,
 
-		uniforms.shininess.value = material.shininess;
+			map: !! material.map,
+			envMap: !! material.envMap,
+			envMapMode: material.envMap && material.envMap.mapping,
+			lightMap: !! material.lightMap,
+			bumpMap: !! material.bumpMap,
+			normalMap: !! material.normalMap,
+			specularMap: !! material.specularMap,
+			alphaMap: !! material.alphaMap,
 
-		if ( _this.gammaInput ) {
+			combine: material.combine,
 
-			uniforms.ambient.value.copyGammaToLinear( material.ambient );
-			uniforms.emissive.value.copyGammaToLinear( material.emissive );
-			uniforms.specular.value.copyGammaToLinear( material.specular );
+			vertexColors: material.vertexColors,
 
-		} else {
+			fog: fog,
+			useFog: material.fog,
+			fogExp: fog instanceof THREE.FogExp2,
 
-			uniforms.ambient.value = material.ambient;
-			uniforms.emissive.value = material.emissive;
-			uniforms.specular.value = material.specular;
+			flatShading: material.shading === THREE.FlatShading,
 
-		}
+			sizeAttenuation: material.sizeAttenuation,
+			logarithmicDepthBuffer: _logarithmicDepthBuffer,
 
-		if ( material.wrapAround ) {
+			skinning: material.skinning,
+			maxBones: maxBones,
+			useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture,
 
-			uniforms.wrapRGB.value.copy( material.wrapRGB );
+			morphTargets: material.morphTargets,
+			morphNormals: material.morphNormals,
+			maxMorphTargets: _this.maxMorphTargets,
+			maxMorphNormals: _this.maxMorphNormals,
 
-		}
+			maxDirLights: maxLightCount.directional,
+			maxPointLights: maxLightCount.point,
+			maxSpotLights: maxLightCount.spot,
+			maxHemiLights: maxLightCount.hemi,
 
-	};
+			maxShadows: maxShadows,
+			shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0,
+			shadowMapType: _this.shadowMapType,
+			shadowMapDebug: _this.shadowMapDebug,
+			shadowMapCascade: _this.shadowMapCascade,
 
-	function refreshUniformsLambert ( uniforms, material ) {
+			alphaTest: material.alphaTest,
+			metal: material.metal,
+			wrapAround: material.wrapAround,
+			doubleSided: material.side === THREE.DoubleSide,
+			flipSided: material.side === THREE.BackSide
 
-		if ( _this.gammaInput ) {
+		};
 
-			uniforms.ambient.value.copyGammaToLinear( material.ambient );
-			uniforms.emissive.value.copyGammaToLinear( material.emissive );
+		// Generate code
 
-		} else {
+		var chunks = [];
 
-			uniforms.ambient.value = material.ambient;
-			uniforms.emissive.value = material.emissive;
+		if ( shaderID ) {
 
-		}
+			chunks.push( shaderID );
 
-		if ( material.wrapAround ) {
+		} else {
 
-			uniforms.wrapRGB.value.copy( material.wrapRGB );
+			chunks.push( material.fragmentShader );
+			chunks.push( material.vertexShader );
 
 		}
 
-	};
+		if ( material.defines !== undefined ) {
 
-	function refreshUniformsLights ( uniforms, lights ) {
+			for ( var name in material.defines ) {
 
-		uniforms.ambientLightColor.value = lights.ambient;
+				chunks.push( name );
+				chunks.push( material.defines[ name ] );
 
-		uniforms.directionalLightColor.value = lights.directional.colors;
-		uniforms.directionalLightDirection.value = lights.directional.positions;
+			}
 
-		uniforms.pointLightColor.value = lights.point.colors;
-		uniforms.pointLightPosition.value = lights.point.positions;
-		uniforms.pointLightDistance.value = lights.point.distances;
+		}
 
-		uniforms.spotLightColor.value = lights.spot.colors;
-		uniforms.spotLightPosition.value = lights.spot.positions;
-		uniforms.spotLightDistance.value = lights.spot.distances;
-		uniforms.spotLightDirection.value = lights.spot.directions;
-		uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
-		uniforms.spotLightExponent.value = lights.spot.exponents;
+		for ( var name in parameters ) {
 
-		uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
-		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
-		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
+			chunks.push( name );
+			chunks.push( parameters[ name ] );
 
-	};
+		}
 
-	function refreshUniformsShadow ( uniforms, lights ) {
+		var code = chunks.join();
 
-		if ( uniforms.shadowMatrix ) {
+		var program;
 
-			var j = 0;
+		// Check if code has been already compiled
 
-			for ( var i = 0, il = lights.length; i < il; i ++ ) {
+		for ( var p = 0, pl = _programs.length; p < pl; p ++ ) {
 
-				var light = lights[ i ];
+			var programInfo = _programs[ p ];
 
-				if ( ! light.castShadow ) continue;
+			if ( programInfo.code === code ) {
 
-				if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) {
+				program = programInfo;
+				program.usedTimes ++;
 
-					uniforms.shadowMap.value[ j ] = light.shadowMap;
-					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
+				break;
 
-					uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
+			}
 
-					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
-					uniforms.shadowBias.value[ j ] = light.shadowBias;
+		}
 
-					j ++;
+		if ( program === undefined ) {
 
-				}
+			program = new THREE.WebGLProgram( _this, code, material, parameters );
+			_programs.push( program );
 
-			}
+			_this.info.memory.programs = _programs.length;
 
 		}
 
-	};
-
-	// Uniforms (load to GPU)
+		material.program = program;
 
-	function loadUniformsMatrices ( uniforms, object ) {
+		var attributes = program.attributes;
 
-		_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements );
+		if ( material.morphTargets ) {
 
-		if ( uniforms.normalMatrix ) {
+			material.numSupportedMorphTargets = 0;
 
-			_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements );
+			var id, base = 'morphTarget';
 
-		}
+			for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
 
-	};
+				id = base + i;
 
-	function getTextureUnit() {
+				if ( attributes[ id ] >= 0 ) {
 
-		var textureUnit = _usedTextureUnits;
+					material.numSupportedMorphTargets ++;
 
-		if ( textureUnit >= _maxTextures ) {
+				}
 
-			console.warn( "WebGLRenderer: trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures );
+			}
 
 		}
 
-		_usedTextureUnits += 1;
+		if ( material.morphNormals ) {
 
-		return textureUnit;
+			material.numSupportedMorphNormals = 0;
 
-	};
+			var id, base = 'morphNormal';
+
+			for ( i = 0; i < _this.maxMorphNormals; i ++ ) {
+
+				id = base + i;
 
-	function loadUniformsGeneric ( program, uniforms ) {
+				if ( attributes[ id ] >= 0 ) {
 
-		var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset;
+					material.numSupportedMorphNormals ++;
 
-		for ( j = 0, jl = uniforms.length; j < jl; j ++ ) {
+				}
 
-			location = program.uniforms[ uniforms[ j ][ 1 ] ];
-			if ( !location ) continue;
+			}
 
-			uniform = uniforms[ j ][ 0 ];
+		}
 
-			type = uniform.type;
-			value = uniform.value;
+		material.uniformsList = [];
 
-			if ( type === "i" ) { // single integer
+		for ( var u in material.__webglShader.uniforms ) {
 
-				_gl.uniform1i( location, value );
+			var location = material.program.uniforms[ u ];
 
-			} else if ( type === "f" ) { // single float
+			if ( location ) {
+				material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] );
+			}
 
-				_gl.uniform1f( location, value );
+		}
 
-			} else if ( type === "v2" ) { // single THREE.Vector2
+	}
 
-				_gl.uniform2f( location, value.x, value.y );
+	function setMaterial( material ) {
 
-			} else if ( type === "v3" ) { // single THREE.Vector3
+		if ( material.transparent === true ) {
 
-				_gl.uniform3f( location, value.x, value.y, value.z );
+			state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha );
 
-			} else if ( type === "v4" ) { // single THREE.Vector4
+		} else {
 
-				_gl.uniform4f( location, value.x, value.y, value.z, value.w );
+			state.setBlending( THREE.NoBlending );
 
-			} else if ( type === "c" ) { // single THREE.Color
+		}
 
-				_gl.uniform3f( location, value.r, value.g, value.b );
+		state.setDepthTest( material.depthTest );
+		state.setDepthWrite( material.depthWrite );
+		state.setColorWrite( material.colorWrite );
+		state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
 
-			} else if ( type === "iv1" ) { // flat array of integers (JS or typed array)
+	}
 
-				_gl.uniform1iv( location, value );
+	function setProgram( camera, lights, fog, material, object ) {
 
-			} else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array)
+		_usedTextureUnits = 0;
 
-				_gl.uniform3iv( location, value );
+		if ( material.needsUpdate ) {
 
-			} else if ( type === "fv1" ) { // flat array of floats (JS or typed array)
+			if ( material.program ) deallocateMaterial( material );
 
-				_gl.uniform1fv( location, value );
+			initMaterial( material, lights, fog, object );
+			material.needsUpdate = false;
 
-			} else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array)
+		}
 
-				_gl.uniform3fv( location, value );
+		if ( material.morphTargets ) {
 
-			} else if ( type === "v2v" ) { // array of THREE.Vector2
+			if ( ! object.__webglMorphTargetInfluences ) {
 
-				if ( uniform._array === undefined ) {
+				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
 
-					uniform._array = new Float32Array( 2 * value.length );
+			}
 
-				}
+		}
 
-				for ( i = 0, il = value.length; i < il; i ++ ) {
+		var refreshProgram = false;
+		var refreshMaterial = false;
+		var refreshLights = false;
 
-					offset = i * 2;
+		var program = material.program,
+			p_uniforms = program.uniforms,
+			m_uniforms = material.__webglShader.uniforms;
 
-					uniform._array[ offset ] 	 = value[ i ].x;
-					uniform._array[ offset + 1 ] = value[ i ].y;
+		if ( program.id !== _currentProgram ) {
 
-				}
+			_gl.useProgram( program.program );
+			_currentProgram = program.id;
 
-				_gl.uniform2fv( location, uniform._array );
+			refreshProgram = true;
+			refreshMaterial = true;
+			refreshLights = true;
 
-			} else if ( type === "v3v" ) { // array of THREE.Vector3
+		}
 
-				if ( uniform._array === undefined ) {
+		if ( material.id !== _currentMaterialId ) {
 
-					uniform._array = new Float32Array( 3 * value.length );
+			if ( _currentMaterialId === -1 ) refreshLights = true;
+			_currentMaterialId = material.id;
 
-				}
+			refreshMaterial = true;
 
-				for ( i = 0, il = value.length; i < il; i ++ ) {
+		}
 
-					offset = i * 3;
+		if ( refreshProgram || camera !== _currentCamera ) {
 
-					uniform._array[ offset ] 	 = value[ i ].x;
-					uniform._array[ offset + 1 ] = value[ i ].y;
-					uniform._array[ offset + 2 ] = value[ i ].z;
+			_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
 
-				}
+			if ( _logarithmicDepthBuffer ) {
 
-				_gl.uniform3fv( location, uniform._array );
+				_gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
 
-			} else if ( type === "v4v" ) { // array of THREE.Vector4
+			}
 
-				if ( uniform._array === undefined ) {
 
-					uniform._array = new Float32Array( 4 * value.length );
+			if ( camera !== _currentCamera ) _currentCamera = camera;
 
-				}
+			// load material specific uniforms
+			// (shader material also gets them for the sake of genericity)
 
-				for ( i = 0, il = value.length; i < il; i ++ ) {
+			if ( material instanceof THREE.ShaderMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ||
+				 material.envMap ) {
 
-					offset = i * 4;
+				if ( p_uniforms.cameraPosition !== null ) {
 
-					uniform._array[ offset ] 	 = value[ i ].x;
-					uniform._array[ offset + 1 ] = value[ i ].y;
-					uniform._array[ offset + 2 ] = value[ i ].z;
-					uniform._array[ offset + 3 ] = value[ i ].w;
+					_vector3.setFromMatrixPosition( camera.matrixWorld );
+					_gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );
 
 				}
 
-				_gl.uniform4fv( location, uniform._array );
+			}
 
-			} else if ( type === "m4") { // single THREE.Matrix4
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.MeshBasicMaterial ||
+				 material instanceof THREE.ShaderMaterial ||
+				 material.skinning ) {
 
-				if ( uniform._array === undefined ) {
+				if ( p_uniforms.viewMatrix !== null ) {
 
-					uniform._array = new Float32Array( 16 );
+					_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
 
 				}
 
-				value.flattenToArray( uniform._array );
-				_gl.uniformMatrix4fv( location, false, uniform._array );
+			}
+
+		}
 
-			} else if ( type === "m4v" ) { // array of THREE.Matrix4
+		// skinning uniforms must be set even if material didn't change
+		// auto-setting of texture unit for bone texture must go before other textures
+		// not sure why, but otherwise weird things happen
 
-				if ( uniform._array === undefined ) {
+		if ( material.skinning ) {
 
-					uniform._array = new Float32Array( 16 * value.length );
+			if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) {
 
-				}
+				_gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );
 
-				for ( i = 0, il = value.length; i < il; i ++ ) {
+			}
 
-					value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+			if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) {
 
-				}
+				_gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );
 
-				_gl.uniformMatrix4fv( location, false, uniform._array );
+			}
 
-			} else if ( type === "t" ) { // single THREE.Texture (2d or cube)
+			if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) {
 
-				texture = value;
-				textureUnit = getTextureUnit();
+				if ( p_uniforms.boneTexture !== null ) {
 
-				_gl.uniform1i( location, textureUnit );
+					var textureUnit = getTextureUnit();
 
-				if ( !texture ) continue;
+					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
+					_this.setTexture( object.skeleton.boneTexture, textureUnit );
 
-				if ( texture.image instanceof Array && texture.image.length === 6 ) {
+				}
 
-					setCubeTexture( texture, textureUnit );
+				if ( p_uniforms.boneTextureWidth !== null ) {
 
-				} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+					_gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
 
-					setCubeTextureDynamic( texture, textureUnit );
+				}
 
-				} else {
+				if ( p_uniforms.boneTextureHeight !== null ) {
 
-					_this.setTexture( texture, textureUnit );
+					_gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
 
 				}
 
-			} else if ( type === "tv" ) { // array of THREE.Texture (2d)
+			} else if ( object.skeleton && object.skeleton.boneMatrices ) {
 
-				if ( uniform._array === undefined ) {
+				if ( p_uniforms.boneGlobalMatrices !== null ) {
 
-					uniform._array = [];
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
 
 				}
 
-				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+			}
+
+		}
 
-					uniform._array[ i ] = getTextureUnit();
+		if ( refreshMaterial ) {
 
-				}
+			// refresh uniforms common to several materials
 
-				_gl.uniform1iv( location, uniform._array );
+			if ( fog && material.fog ) {
 
-				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+				refreshUniformsFog( m_uniforms, fog );
 
-					texture = uniform.value[ i ];
-					textureUnit = uniform._array[ i ];
+			}
 
-					if ( !texture ) continue;
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material.lights ) {
 
-					_this.setTexture( texture, textureUnit );
+				if ( _lightsNeedUpdate ) {
 
+					refreshLights = true;
+					setupLights( lights );
+					_lightsNeedUpdate = false;
 				}
 
-			} else {
-
-				console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );
+				if ( refreshLights ) {
+					refreshUniformsLights( m_uniforms, _lights );
+					markUniformsLightsNeedsUpdate( m_uniforms, true );
+				} else {
+					markUniformsLightsNeedsUpdate( m_uniforms, false );
+				}
 
 			}
 
-		}
+			if ( material instanceof THREE.MeshBasicMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ) {
 
-	};
+				refreshUniformsCommon( m_uniforms, material );
 
-	function setupMatrices ( object, camera ) {
+			}
 
-		object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
-		object._normalMatrix.getNormalMatrix( object._modelViewMatrix );
+			// refresh single material specific uniforms
 
-	};
+			if ( material instanceof THREE.LineBasicMaterial ) {
 
-	//
+				refreshUniformsLine( m_uniforms, material );
 
-	function setColorGamma( array, offset, color, intensitySq ) {
+			} else if ( material instanceof THREE.LineDashedMaterial ) {
 
-		array[ offset ]     = color.r * color.r * intensitySq;
-		array[ offset + 1 ] = color.g * color.g * intensitySq;
-		array[ offset + 2 ] = color.b * color.b * intensitySq;
+				refreshUniformsLine( m_uniforms, material );
+				refreshUniformsDash( m_uniforms, material );
 
-	};
+			} else if ( material instanceof THREE.PointCloudMaterial ) {
 
-	function setColorLinear( array, offset, color, intensity ) {
+				refreshUniformsParticle( m_uniforms, material );
 
-		array[ offset ]     = color.r * intensity;
-		array[ offset + 1 ] = color.g * intensity;
-		array[ offset + 2 ] = color.b * intensity;
+			} else if ( material instanceof THREE.MeshPhongMaterial ) {
 
-	};
+				refreshUniformsPhong( m_uniforms, material );
 
-	function setupLights ( program, lights ) {
+			} else if ( material instanceof THREE.MeshLambertMaterial ) {
 
-		var l, ll, light, n,
-		r = 0, g = 0, b = 0,
-		color, skyColor, groundColor,
-		intensity,  intensitySq,
-		position,
-		distance,
+				refreshUniformsLambert( m_uniforms, material );
 
-		zlights = _lights,
+			} else if ( material instanceof THREE.MeshDepthMaterial ) {
 
-		dirColors = zlights.directional.colors,
-		dirPositions = zlights.directional.positions,
+				m_uniforms.mNear.value = camera.near;
+				m_uniforms.mFar.value = camera.far;
+				m_uniforms.opacity.value = material.opacity;
 
-		pointColors = zlights.point.colors,
-		pointPositions = zlights.point.positions,
-		pointDistances = zlights.point.distances,
+			} else if ( material instanceof THREE.MeshNormalMaterial ) {
 
-		spotColors = zlights.spot.colors,
-		spotPositions = zlights.spot.positions,
-		spotDistances = zlights.spot.distances,
-		spotDirections = zlights.spot.directions,
-		spotAnglesCos = zlights.spot.anglesCos,
-		spotExponents = zlights.spot.exponents,
+				m_uniforms.opacity.value = material.opacity;
+
+			}
 
-		hemiSkyColors = zlights.hemi.skyColors,
-		hemiGroundColors = zlights.hemi.groundColors,
-		hemiPositions = zlights.hemi.positions,
+			if ( object.receiveShadow && ! material._shadowPass ) {
 
-		dirLength = 0,
-		pointLength = 0,
-		spotLength = 0,
-		hemiLength = 0,
+				refreshUniformsShadow( m_uniforms, lights );
 
-		dirCount = 0,
-		pointCount = 0,
-		spotCount = 0,
-		hemiCount = 0,
+			}
 
-		dirOffset = 0,
-		pointOffset = 0,
-		spotOffset = 0,
-		hemiOffset = 0;
+			// load common uniforms
 
-		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+			loadUniformsGeneric( material.uniformsList );
 
-			light = lights[ l ];
+		}
 
-			if ( light.onlyShadow ) continue;
+		loadUniformsMatrices( p_uniforms, object );
 
-			color = light.color;
-			intensity = light.intensity;
-			distance = light.distance;
+		if ( p_uniforms.modelMatrix !== null ) {
 
-			if ( light instanceof THREE.AmbientLight ) {
+			_gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
 
-				if ( ! light.visible ) continue;
+		}
 
-				if ( _this.gammaInput ) {
+		return program;
 
-					r += color.r * color.r;
-					g += color.g * color.g;
-					b += color.b * color.b;
+	}
 
-				} else {
+	// Uniforms (refresh uniforms objects)
 
-					r += color.r;
-					g += color.g;
-					b += color.b;
+	function refreshUniformsCommon ( uniforms, material ) {
 
-				}
+		uniforms.opacity.value = material.opacity;
 
-			} else if ( light instanceof THREE.DirectionalLight ) {
+		uniforms.diffuse.value = material.color;
 
-				dirCount += 1;
+		uniforms.map.value = material.map;
+		uniforms.lightMap.value = material.lightMap;
+		uniforms.specularMap.value = material.specularMap;
+		uniforms.alphaMap.value = material.alphaMap;
 
-				if ( ! light.visible ) continue;
+		if ( material.bumpMap ) {
 
-				_direction.getPositionFromMatrix( light.matrixWorld );
-				_vector3.getPositionFromMatrix( light.target.matrixWorld );
-				_direction.sub( _vector3 );
-				_direction.normalize();
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
 
-				// skip lights with undefined direction
-				// these create troubles in OpenGL (making pixel black)
+		}
 
-				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+		if ( material.normalMap ) {
 
-				dirOffset = dirLength * 3;
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
 
-				dirPositions[ dirOffset ]     = _direction.x;
-				dirPositions[ dirOffset + 1 ] = _direction.y;
-				dirPositions[ dirOffset + 2 ] = _direction.z;
+		}
 
-				if ( _this.gammaInput ) {
+		// uv repeat and offset setting priorities
+		//  1. color map
+		//  2. specular map
+		//  3. normal map
+		//  4. bump map
+		//  5. alpha map
 
-					setColorGamma( dirColors, dirOffset, color, intensity * intensity );
+		var uvScaleMap;
 
-				} else {
+		if ( material.map ) {
 
-					setColorLinear( dirColors, dirOffset, color, intensity );
+			uvScaleMap = material.map;
 
-				}
+		} else if ( material.specularMap ) {
 
-				dirLength += 1;
+			uvScaleMap = material.specularMap;
 
-			} else if ( light instanceof THREE.PointLight ) {
+		} else if ( material.normalMap ) {
 
-				pointCount += 1;
+			uvScaleMap = material.normalMap;
 
-				if ( ! light.visible ) continue;
+		} else if ( material.bumpMap ) {
 
-				pointOffset = pointLength * 3;
+			uvScaleMap = material.bumpMap;
 
-				if ( _this.gammaInput ) {
+		} else if ( material.alphaMap ) {
 
-					setColorGamma( pointColors, pointOffset, color, intensity * intensity );
+			uvScaleMap = material.alphaMap;
 
-				} else {
+		}
 
-					setColorLinear( pointColors, pointOffset, color, intensity );
+		if ( uvScaleMap !== undefined ) {
 
-				}
+			var offset = uvScaleMap.offset;
+			var repeat = uvScaleMap.repeat;
 
-				_vector3.getPositionFromMatrix( light.matrixWorld );
+			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
 
-				pointPositions[ pointOffset ]     = _vector3.x;
-				pointPositions[ pointOffset + 1 ] = _vector3.y;
-				pointPositions[ pointOffset + 2 ] = _vector3.z;
+		}
 
-				pointDistances[ pointLength ] = distance;
+		uniforms.envMap.value = material.envMap;
+		uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1;
 
-				pointLength += 1;
+		uniforms.reflectivity.value = material.reflectivity;
+		uniforms.refractionRatio.value = material.refractionRatio;
 
-			} else if ( light instanceof THREE.SpotLight ) {
+	}
 
-				spotCount += 1;
+	function refreshUniformsLine ( uniforms, material ) {
 
-				if ( ! light.visible ) continue;
+		uniforms.diffuse.value = material.color;
+		uniforms.opacity.value = material.opacity;
 
-				spotOffset = spotLength * 3;
+	}
 
-				if ( _this.gammaInput ) {
+	function refreshUniformsDash ( uniforms, material ) {
 
-					setColorGamma( spotColors, spotOffset, color, intensity * intensity );
+		uniforms.dashSize.value = material.dashSize;
+		uniforms.totalSize.value = material.dashSize + material.gapSize;
+		uniforms.scale.value = material.scale;
 
-				} else {
+	}
 
-					setColorLinear( spotColors, spotOffset, color, intensity );
+	function refreshUniformsParticle ( uniforms, material ) {
 
-				}
+		uniforms.psColor.value = material.color;
+		uniforms.opacity.value = material.opacity;
+		uniforms.size.value = material.size;
+		uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
 
-				_vector3.getPositionFromMatrix( light.matrixWorld );
+		uniforms.map.value = material.map;
 
-				spotPositions[ spotOffset ]     = _vector3.x;
-				spotPositions[ spotOffset + 1 ] = _vector3.y;
-				spotPositions[ spotOffset + 2 ] = _vector3.z;
+		if ( material.map !== null ) {
 
-				spotDistances[ spotLength ] = distance;
+			var offset = material.map.offset;
+			var repeat = material.map.repeat;
 
-				_direction.copy( _vector3 );
-				_vector3.getPositionFromMatrix( light.target.matrixWorld );
-				_direction.sub( _vector3 );
-				_direction.normalize();
+			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
 
-				spotDirections[ spotOffset ]     = _direction.x;
-				spotDirections[ spotOffset + 1 ] = _direction.y;
-				spotDirections[ spotOffset + 2 ] = _direction.z;
+		}
 
-				spotAnglesCos[ spotLength ] = Math.cos( light.angle );
-				spotExponents[ spotLength ] = light.exponent;
+	}
 
-				spotLength += 1;
+	function refreshUniformsFog ( uniforms, fog ) {
 
-			} else if ( light instanceof THREE.HemisphereLight ) {
+		uniforms.fogColor.value = fog.color;
 
-				hemiCount += 1;
+		if ( fog instanceof THREE.Fog ) {
 
-				if ( ! light.visible ) continue;
+			uniforms.fogNear.value = fog.near;
+			uniforms.fogFar.value = fog.far;
 
-				_direction.getPositionFromMatrix( light.matrixWorld );
-				_direction.normalize();
+		} else if ( fog instanceof THREE.FogExp2 ) {
 
-				// skip lights with undefined direction
-				// these create troubles in OpenGL (making pixel black)
+			uniforms.fogDensity.value = fog.density;
 
-				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+		}
 
-				hemiOffset = hemiLength * 3;
+	}
 
-				hemiPositions[ hemiOffset ]     = _direction.x;
-				hemiPositions[ hemiOffset + 1 ] = _direction.y;
-				hemiPositions[ hemiOffset + 2 ] = _direction.z;
+	function refreshUniformsPhong ( uniforms, material ) {
 
-				skyColor = light.color;
-				groundColor = light.groundColor;
+		uniforms.shininess.value = material.shininess;
+
+		uniforms.emissive.value = material.emissive;
+		uniforms.specular.value = material.specular;
 
-				if ( _this.gammaInput ) {
+		if ( material.wrapAround ) {
 
-					intensitySq = intensity * intensity;
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
 
-					setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq );
-					setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq );
+		}
 
-				} else {
+	}
 
-					setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
-					setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
+	function refreshUniformsLambert ( uniforms, material ) {
 
-				}
+		uniforms.emissive.value = material.emissive;
 
-				hemiLength += 1;
+		if ( material.wrapAround ) {
 
-			}
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
 
 		}
 
-		// null eventual remains from removed lights
-		// (this is to avoid if in shader)
+	}
 
-		for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
-		for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
-		for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
-		for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
-		for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
+	function refreshUniformsLights ( uniforms, lights ) {
 
-		zlights.directional.length = dirLength;
-		zlights.point.length = pointLength;
-		zlights.spot.length = spotLength;
-		zlights.hemi.length = hemiLength;
+		uniforms.ambientLightColor.value = lights.ambient;
 
-		zlights.ambient[ 0 ] = r;
-		zlights.ambient[ 1 ] = g;
-		zlights.ambient[ 2 ] = b;
+		uniforms.directionalLightColor.value = lights.directional.colors;
+		uniforms.directionalLightDirection.value = lights.directional.positions;
 
-	};
+		uniforms.pointLightColor.value = lights.point.colors;
+		uniforms.pointLightPosition.value = lights.point.positions;
+		uniforms.pointLightDistance.value = lights.point.distances;
+		uniforms.pointLightDecay.value = lights.point.decays;
 
-	// GL state setting
+		uniforms.spotLightColor.value = lights.spot.colors;
+		uniforms.spotLightPosition.value = lights.spot.positions;
+		uniforms.spotLightDistance.value = lights.spot.distances;
+		uniforms.spotLightDirection.value = lights.spot.directions;
+		uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
+		uniforms.spotLightExponent.value = lights.spot.exponents;
+		uniforms.spotLightDecay.value = lights.spot.decays;
 
-	this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
+		uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
+		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
+		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
 
-		if ( cullFace === THREE.CullFaceNone ) {
+	}
 
-			_gl.disable( _gl.CULL_FACE );
+	// If uniforms are marked as clean, they don't need to be loaded to the GPU.
 
-		} else {
+	function markUniformsLightsNeedsUpdate ( uniforms, value ) {
 
-			if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
+		uniforms.ambientLightColor.needsUpdate = value;
 
-				_gl.frontFace( _gl.CW );
+		uniforms.directionalLightColor.needsUpdate = value;
+		uniforms.directionalLightDirection.needsUpdate = value;
 
-			} else {
+		uniforms.pointLightColor.needsUpdate = value;
+		uniforms.pointLightPosition.needsUpdate = value;
+		uniforms.pointLightDistance.needsUpdate = value;
+		uniforms.pointLightDecay.needsUpdate = value;
 
-				_gl.frontFace( _gl.CCW );
+		uniforms.spotLightColor.needsUpdate = value;
+		uniforms.spotLightPosition.needsUpdate = value;
+		uniforms.spotLightDistance.needsUpdate = value;
+		uniforms.spotLightDirection.needsUpdate = value;
+		uniforms.spotLightAngleCos.needsUpdate = value;
+		uniforms.spotLightExponent.needsUpdate = value;
+		uniforms.spotLightDecay.needsUpdate = value;
 
-			}
+		uniforms.hemisphereLightSkyColor.needsUpdate = value;
+		uniforms.hemisphereLightGroundColor.needsUpdate = value;
+		uniforms.hemisphereLightDirection.needsUpdate = value;
 
-			if ( cullFace === THREE.CullFaceBack ) {
+	}
 
-				_gl.cullFace( _gl.BACK );
+	function refreshUniformsShadow ( uniforms, lights ) {
 
-			} else if ( cullFace === THREE.CullFaceFront ) {
+		if ( uniforms.shadowMatrix ) {
 
-				_gl.cullFace( _gl.FRONT );
+			var j = 0;
 
-			} else {
+			for ( var i = 0, il = lights.length; i < il; i ++ ) {
 
-				_gl.cullFace( _gl.FRONT_AND_BACK );
+				var light = lights[ i ];
 
-			}
+				if ( ! light.castShadow ) continue;
 
-			_gl.enable( _gl.CULL_FACE );
+				if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) {
 
-		}
+					uniforms.shadowMap.value[ j ] = light.shadowMap;
+					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
 
-	};
+					uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
 
-	this.setMaterialFaces = function ( material ) {
+					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
+					uniforms.shadowBias.value[ j ] = light.shadowBias;
 
-		var doubleSided = material.side === THREE.DoubleSide;
-		var flipSided = material.side === THREE.BackSide;
+					j ++;
 
-		if ( _oldDoubleSided !== doubleSided ) {
+				}
 
-			if ( doubleSided ) {
+			}
 
-				_gl.disable( _gl.CULL_FACE );
+		}
 
-			} else {
+	}
 
-				_gl.enable( _gl.CULL_FACE );
+	// Uniforms (load to GPU)
 
-			}
+	function loadUniformsMatrices ( uniforms, object ) {
 
-			_oldDoubleSided = doubleSided;
+		_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements );
 
-		}
+		if ( uniforms.normalMatrix ) {
 
-		if ( _oldFlipSided !== flipSided ) {
+			_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements );
 
-			if ( flipSided ) {
+		}
 
-				_gl.frontFace( _gl.CW );
+	}
 
-			} else {
+	function getTextureUnit() {
 
-				_gl.frontFace( _gl.CCW );
+		var textureUnit = _usedTextureUnits;
 
-			}
+		if ( textureUnit >= _maxTextures ) {
 
-			_oldFlipSided = flipSided;
+			THREE.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures );
 
 		}
 
-	};
+		_usedTextureUnits += 1;
+
+		return textureUnit;
+
+	}
 
-	this.setDepthTest = function ( depthTest ) {
+	function loadUniformsGeneric ( uniforms ) {
 
-		if ( _oldDepthTest !== depthTest ) {
+		var texture, textureUnit, offset;
 
-			if ( depthTest ) {
+		for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {
 
-				_gl.enable( _gl.DEPTH_TEST );
+			var uniform = uniforms[ j ][ 0 ];
 
-			} else {
+			// needsUpdate property is not added to all uniforms.
+			if ( uniform.needsUpdate === false ) continue;
 
-				_gl.disable( _gl.DEPTH_TEST );
+			var type = uniform.type;
+			var value = uniform.value;
+			var location = uniforms[ j ][ 1 ];
 
-			}
+			switch ( type ) {
 
-			_oldDepthTest = depthTest;
+				case '1i':
+					_gl.uniform1i( location, value );
+					break;
 
-		}
+				case '1f':
+					_gl.uniform1f( location, value );
+					break;
 
-	};
+				case '2f':
+					_gl.uniform2f( location, value[ 0 ], value[ 1 ] );
+					break;
 
-	this.setDepthWrite = function ( depthWrite ) {
+				case '3f':
+					_gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] );
+					break;
 
-		if ( _oldDepthWrite !== depthWrite ) {
+				case '4f':
+					_gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] );
+					break;
 
-			_gl.depthMask( depthWrite );
-			_oldDepthWrite = depthWrite;
+				case '1iv':
+					_gl.uniform1iv( location, value );
+					break;
 
-		}
+				case '3iv':
+					_gl.uniform3iv( location, value );
+					break;
 
-	};
+				case '1fv':
+					_gl.uniform1fv( location, value );
+					break;
 
-	function setLineWidth ( width ) {
+				case '2fv':
+					_gl.uniform2fv( location, value );
+					break;
 
-		if ( width !== _oldLineWidth ) {
+				case '3fv':
+					_gl.uniform3fv( location, value );
+					break;
 
-			_gl.lineWidth( width );
+				case '4fv':
+					_gl.uniform4fv( location, value );
+					break;
 
-			_oldLineWidth = width;
+				case 'Matrix3fv':
+					_gl.uniformMatrix3fv( location, false, value );
+					break;
 
-		}
+				case 'Matrix4fv':
+					_gl.uniformMatrix4fv( location, false, value );
+					break;
 
-	};
+				//
 
-	function setPolygonOffset ( polygonoffset, factor, units ) {
+				case 'i':
 
-		if ( _oldPolygonOffset !== polygonoffset ) {
+					// single integer
+					_gl.uniform1i( location, value );
 
-			if ( polygonoffset ) {
+					break;
 
-				_gl.enable( _gl.POLYGON_OFFSET_FILL );
+				case 'f':
 
-			} else {
+					// single float
+					_gl.uniform1f( location, value );
 
-				_gl.disable( _gl.POLYGON_OFFSET_FILL );
+					break;
 
-			}
+				case 'v2':
 
-			_oldPolygonOffset = polygonoffset;
+					// single THREE.Vector2
+					_gl.uniform2f( location, value.x, value.y );
 
-		}
+					break;
 
-		if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) {
+				case 'v3':
 
-			_gl.polygonOffset( factor, units );
+					// single THREE.Vector3
+					_gl.uniform3f( location, value.x, value.y, value.z );
 
-			_oldPolygonOffsetFactor = factor;
-			_oldPolygonOffsetUnits = units;
+					break;
 
-		}
+				case 'v4':
 
-	};
+					// single THREE.Vector4
+					_gl.uniform4f( location, value.x, value.y, value.z, value.w );
 
-	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
+					break;
 
-		if ( blending !== _oldBlending ) {
+				case 'c':
 
-			if ( blending === THREE.NoBlending ) {
+					// single THREE.Color
+					_gl.uniform3f( location, value.r, value.g, value.b );
 
-				_gl.disable( _gl.BLEND );
+					break;
 
-			} else if ( blending === THREE.AdditiveBlending ) {
+				case 'iv1':
 
-				_gl.enable( _gl.BLEND );
-				_gl.blendEquation( _gl.FUNC_ADD );
-				_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
+					// flat array of integers (JS or typed array)
+					_gl.uniform1iv( location, value );
 
-			} else if ( blending === THREE.SubtractiveBlending ) {
+					break;
 
-				// TODO: Find blendFuncSeparate() combination
-				_gl.enable( _gl.BLEND );
-				_gl.blendEquation( _gl.FUNC_ADD );
-				_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
+				case 'iv':
 
-			} else if ( blending === THREE.MultiplyBlending ) {
+					// flat array of integers with 3 x N size (JS or typed array)
+					_gl.uniform3iv( location, value );
 
-				// TODO: Find blendFuncSeparate() combination
-				_gl.enable( _gl.BLEND );
-				_gl.blendEquation( _gl.FUNC_ADD );
-				_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
+					break;
 
-			} else if ( blending === THREE.CustomBlending ) {
+				case 'fv1':
 
-				_gl.enable( _gl.BLEND );
+					// flat array of floats (JS or typed array)
+					_gl.uniform1fv( location, value );
 
-			} else {
+					break;
 
-				_gl.enable( _gl.BLEND );
-				_gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD );
-				_gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
+				case 'fv':
 
-			}
+					// flat array of floats with 3 x N size (JS or typed array)
+					_gl.uniform3fv( location, value );
 
-			_oldBlending = blending;
+					break;
 
-		}
+				case 'v2v':
 
-		if ( blending === THREE.CustomBlending ) {
+					// array of THREE.Vector2
 
-			if ( blendEquation !== _oldBlendEquation ) {
+					if ( uniform._array === undefined ) {
 
-				_gl.blendEquation( paramThreeToGL( blendEquation ) );
+						uniform._array = new Float32Array( 2 * value.length );
 
-				_oldBlendEquation = blendEquation;
+					}
 
-			}
+					for ( var i = 0, il = value.length; i < il; i ++ ) {
 
-			if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) {
+						offset = i * 2;
 
-				_gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) );
+						uniform._array[ offset ]   = value[ i ].x;
+						uniform._array[ offset + 1 ] = value[ i ].y;
 
-				_oldBlendSrc = blendSrc;
-				_oldBlendDst = blendDst;
+					}
 
-			}
+					_gl.uniform2fv( location, uniform._array );
 
-		} else {
+					break;
 
-			_oldBlendEquation = null;
-			_oldBlendSrc = null;
-			_oldBlendDst = null;
+				case 'v3v':
 
-		}
+					// array of THREE.Vector3
 
-	};
+					if ( uniform._array === undefined ) {
 
-	// Defines
+						uniform._array = new Float32Array( 3 * value.length );
 
-	function generateDefines ( defines ) {
+					}
 
-		var value, chunk, chunks = [];
+					for ( var i = 0, il = value.length; i < il; i ++ ) {
 
-		for ( var d in defines ) {
+						offset = i * 3;
 
-			value = defines[ d ];
-			if ( value === false ) continue;
+						uniform._array[ offset ]   = value[ i ].x;
+						uniform._array[ offset + 1 ] = value[ i ].y;
+						uniform._array[ offset + 2 ] = value[ i ].z;
 
-			chunk = "#define " + d + " " + value;
-			chunks.push( chunk );
+					}
 
-		}
+					_gl.uniform3fv( location, uniform._array );
 
-		return chunks.join( "\n" );
+					break;
 
-	};
+				case 'v4v':
 
-	// Shaders
+					// array of THREE.Vector4
 
-	function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters, index0AttributeName ) {
+					if ( uniform._array === undefined ) {
 
-		var p, pl, d, program, code;
-		var chunks = [];
+						uniform._array = new Float32Array( 4 * value.length );
 
-		// Generate code
+					}
 
-		if ( shaderID ) {
+					for ( var i = 0, il = value.length; i < il; i ++ ) {
 
-			chunks.push( shaderID );
+						offset = i * 4;
 
-		} else {
+						uniform._array[ offset ]   = value[ i ].x;
+						uniform._array[ offset + 1 ] = value[ i ].y;
+						uniform._array[ offset + 2 ] = value[ i ].z;
+						uniform._array[ offset + 3 ] = value[ i ].w;
 
-			chunks.push( fragmentShader );
-			chunks.push( vertexShader );
+					}
 
-		}
+					_gl.uniform4fv( location, uniform._array );
 
-		for ( d in defines ) {
+					break;
 
-			chunks.push( d );
-			chunks.push( defines[ d ] );
+				case 'm3':
 
-		}
+					// single THREE.Matrix3
+					_gl.uniformMatrix3fv( location, false, value.elements );
 
-		for ( p in parameters ) {
+					break;
 
-			chunks.push( p );
-			chunks.push( parameters[ p ] );
+				case 'm3v':
 
-		}
+					// array of THREE.Matrix3
 
-		code = chunks.join();
+					if ( uniform._array === undefined ) {
 
-		// Check if code has been already compiled
+						uniform._array = new Float32Array( 9 * value.length );
 
-		for ( p = 0, pl = _programs.length; p < pl; p ++ ) {
+					}
 
-			var programInfo = _programs[ p ];
+					for ( var i = 0, il = value.length; i < il; i ++ ) {
 
-			if ( programInfo.code === code ) {
+						value[ i ].flattenToArrayOffset( uniform._array, i * 9 );
 
-				// console.log( "Code already compiled." /*: \n\n" + code*/ );
+					}
 
-				programInfo.usedTimes ++;
+					_gl.uniformMatrix3fv( location, false, uniform._array );
 
-				return programInfo.program;
+					break;
 
-			}
+				case 'm4':
 
-		}
+					// single THREE.Matrix4
+					_gl.uniformMatrix4fv( location, false, value.elements );
 
-		var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC";
+					break;
 
-		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
+				case 'm4v':
 
-			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF";
+					// array of THREE.Matrix4
 
-		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
+					if ( uniform._array === undefined ) {
 
-			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT";
+						uniform._array = new Float32Array( 16 * value.length );
 
-		}
+					}
 
-		// console.log( "building new program " );
+					for ( var i = 0, il = value.length; i < il; i ++ ) {
 
-		//
+						value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
 
-		var customDefines = generateDefines( defines );
+					}
 
-		//
+					_gl.uniformMatrix4fv( location, false, uniform._array );
 
-		program = _gl.createProgram();
+					break;
 
-		var prefix_vertex = [
+				case 't':
 
-			"precision " + _precision + " float;",
-			"precision " + _precision + " int;",
+					// single THREE.Texture (2d or cube)
 
-			customDefines,
+					texture = value;
+					textureUnit = getTextureUnit();
 
-			_supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
+					_gl.uniform1i( location, textureUnit );
 
-			_this.gammaInput ? "#define GAMMA_INPUT" : "",
-			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
-			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+					if ( ! texture ) continue;
 
-			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
-			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
-			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
-			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
+					if ( texture instanceof THREE.CubeTexture ||
+					   ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/
 
-			"#define MAX_SHADOWS " + parameters.maxShadows,
+						setCubeTexture( texture, textureUnit );
 
-			"#define MAX_BONES " + parameters.maxBones,
+					} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
 
-			parameters.map ? "#define USE_MAP" : "",
-			parameters.envMap ? "#define USE_ENVMAP" : "",
-			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
-			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
-			parameters.normalMap ? "#define USE_NORMALMAP" : "",
-			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
-			parameters.vertexColors ? "#define USE_COLOR" : "",
+						setCubeTextureDynamic( texture, textureUnit );
 
-			parameters.skinning ? "#define USE_SKINNING" : "",
-			parameters.useVertexTexture ? "#define BONE_TEXTURE" : "",
+					} else {
 
-			parameters.morphTargets ? "#define USE_MORPHTARGETS" : "",
-			parameters.morphNormals ? "#define USE_MORPHNORMALS" : "",
-			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
-			parameters.wrapAround ? "#define WRAP_AROUND" : "",
-			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
-			parameters.flipSided ? "#define FLIP_SIDED" : "",
+						_this.setTexture( texture, textureUnit );
 
-			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
-			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
-			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
-			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+					}
 
-			parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
+					break;
 
-			"uniform mat4 modelMatrix;",
-			"uniform mat4 modelViewMatrix;",
-			"uniform mat4 projectionMatrix;",
-			"uniform mat4 viewMatrix;",
-			"uniform mat3 normalMatrix;",
-			"uniform vec3 cameraPosition;",
+				case 'tv':
 
-			"attribute vec3 position;",
-			"attribute vec3 normal;",
-			"attribute vec2 uv;",
-			"attribute vec2 uv2;",
+					// array of THREE.Texture (2d)
 
-			"#ifdef USE_COLOR",
+					if ( uniform._array === undefined ) {
 
-				"attribute vec3 color;",
+						uniform._array = [];
 
-			"#endif",
+					}
 
-			"#ifdef USE_MORPHTARGETS",
+					for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
 
-				"attribute vec3 morphTarget0;",
-				"attribute vec3 morphTarget1;",
-				"attribute vec3 morphTarget2;",
-				"attribute vec3 morphTarget3;",
+						uniform._array[ i ] = getTextureUnit();
 
-				"#ifdef USE_MORPHNORMALS",
+					}
 
-					"attribute vec3 morphNormal0;",
-					"attribute vec3 morphNormal1;",
-					"attribute vec3 morphNormal2;",
-					"attribute vec3 morphNormal3;",
+					_gl.uniform1iv( location, uniform._array );
 
-				"#else",
+					for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
 
-					"attribute vec3 morphTarget4;",
-					"attribute vec3 morphTarget5;",
-					"attribute vec3 morphTarget6;",
-					"attribute vec3 morphTarget7;",
+						texture = uniform.value[ i ];
+						textureUnit = uniform._array[ i ];
 
-				"#endif",
+						if ( ! texture ) continue;
 
-			"#endif",
+						_this.setTexture( texture, textureUnit );
 
-			"#ifdef USE_SKINNING",
+					}
 
-				"attribute vec4 skinIndex;",
-				"attribute vec4 skinWeight;",
+					break;
 
-			"#endif",
+				default:
 
-			""
+					THREE.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );
 
-		].join("\n");
+			}
 
-		var prefix_fragment = [
+		}
 
-			"precision " + _precision + " float;",
-			"precision " + _precision + " int;",
+	}
 
-			( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "",
+	function setupMatrices ( object, camera ) {
 
-			customDefines,
+		object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+		object._normalMatrix.getNormalMatrix( object._modelViewMatrix );
 
-			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
-			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
-			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
-			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
+	}
 
-			"#define MAX_SHADOWS " + parameters.maxShadows,
+	function setColorLinear( array, offset, color, intensity ) {
 
-			parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
+		array[ offset ]     = color.r * intensity;
+		array[ offset + 1 ] = color.g * intensity;
+		array[ offset + 2 ] = color.b * intensity;
 
-			_this.gammaInput ? "#define GAMMA_INPUT" : "",
-			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
-			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+	}
 
-			( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "",
-			( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "",
+	function setupLights ( lights ) {
 
-			parameters.map ? "#define USE_MAP" : "",
-			parameters.envMap ? "#define USE_ENVMAP" : "",
-			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
-			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
-			parameters.normalMap ? "#define USE_NORMALMAP" : "",
-			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
-			parameters.vertexColors ? "#define USE_COLOR" : "",
+		var l, ll, light,
+		r = 0, g = 0, b = 0,
+		color, skyColor, groundColor,
+		intensity,
+		distance,
 
-			parameters.metal ? "#define METAL" : "",
-			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
-			parameters.wrapAround ? "#define WRAP_AROUND" : "",
-			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
-			parameters.flipSided ? "#define FLIP_SIDED" : "",
+		zlights = _lights,
 
-			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
-			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
-			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
-			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+		dirColors = zlights.directional.colors,
+		dirPositions = zlights.directional.positions,
 
-			"uniform mat4 viewMatrix;",
-			"uniform vec3 cameraPosition;",
-			""
+		pointColors = zlights.point.colors,
+		pointPositions = zlights.point.positions,
+		pointDistances = zlights.point.distances,
+		pointDecays = zlights.point.decays,
 
-		].join("\n");
+		spotColors = zlights.spot.colors,
+		spotPositions = zlights.spot.positions,
+		spotDistances = zlights.spot.distances,
+		spotDirections = zlights.spot.directions,
+		spotAnglesCos = zlights.spot.anglesCos,
+		spotExponents = zlights.spot.exponents,
+		spotDecays = zlights.spot.decays,
 
-		var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );
-		var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
+		hemiSkyColors = zlights.hemi.skyColors,
+		hemiGroundColors = zlights.hemi.groundColors,
+		hemiPositions = zlights.hemi.positions,
 
-		_gl.attachShader( program, glVertexShader );
-		_gl.attachShader( program, glFragmentShader );
+		dirLength = 0,
+		pointLength = 0,
+		spotLength = 0,
+		hemiLength = 0,
 
-		//Force a particular attribute to index 0.
-		// because potentially expensive emulation is done by browser if attribute 0 is disabled.
-		//And, color, for example is often automatically bound to index 0 so disabling it
-		if ( index0AttributeName ) {
-			_gl.bindAttribLocation( program, 0, index0AttributeName );
-		}
+		dirCount = 0,
+		pointCount = 0,
+		spotCount = 0,
+		hemiCount = 0,
 
-		_gl.linkProgram( program );
+		dirOffset = 0,
+		pointOffset = 0,
+		spotOffset = 0,
+		hemiOffset = 0;
 
-		if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
+		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
 
-			console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
-			console.error( "Program Info Log: " + _gl.getProgramInfoLog( program ) );
-		}
+			light = lights[ l ];
 
-		// clean up
+			if ( light.onlyShadow ) continue;
 
-		_gl.deleteShader( glFragmentShader );
-		_gl.deleteShader( glVertexShader );
+			color = light.color;
+			intensity = light.intensity;
+			distance = light.distance;
 
-		// console.log( prefix_fragment + fragmentShader );
-		// console.log( prefix_vertex + vertexShader );
+			if ( light instanceof THREE.AmbientLight ) {
 
-		program.uniforms = {};
-		program.attributes = {};
+				if ( ! light.visible ) continue;
 
-		var identifiers, u, a, i;
+				r += color.r;
+				g += color.g;
+				b += color.b;
 
-		// cache uniform locations
+			} else if ( light instanceof THREE.DirectionalLight ) {
 
-		identifiers = [
+				dirCount += 1;
 
-			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
-			'morphTargetInfluences'
+				if ( ! light.visible ) continue;
 
-		];
+				_direction.setFromMatrixPosition( light.matrixWorld );
+				_vector3.setFromMatrixPosition( light.target.matrixWorld );
+				_direction.sub( _vector3 );
+				_direction.normalize();
 
-		if ( parameters.useVertexTexture ) {
+				dirOffset = dirLength * 3;
 
-			identifiers.push( 'boneTexture' );
-			identifiers.push( 'boneTextureWidth' );
-			identifiers.push( 'boneTextureHeight' );
+				dirPositions[ dirOffset ]     = _direction.x;
+				dirPositions[ dirOffset + 1 ] = _direction.y;
+				dirPositions[ dirOffset + 2 ] = _direction.z;
 
-		} else {
+				setColorLinear( dirColors, dirOffset, color, intensity );
 
-			identifiers.push( 'boneGlobalMatrices' );
+				dirLength += 1;
 
-		}
+			} else if ( light instanceof THREE.PointLight ) {
 
-		for ( u in uniforms ) {
+				pointCount += 1;
 
-			identifiers.push( u );
+				if ( ! light.visible ) continue;
 
-		}
+				pointOffset = pointLength * 3;
 
-		cacheUniformLocations( program, identifiers );
+				setColorLinear( pointColors, pointOffset, color, intensity );
 
-		// cache attributes locations
+				_vector3.setFromMatrixPosition( light.matrixWorld );
 
-		identifiers = [
+				pointPositions[ pointOffset ]     = _vector3.x;
+				pointPositions[ pointOffset + 1 ] = _vector3.y;
+				pointPositions[ pointOffset + 2 ] = _vector3.z;
 
-			"position", "normal", "uv", "uv2", "tangent", "color",
-			"skinIndex", "skinWeight", "lineDistance"
+				// distance is 0 if decay is 0, because there is no attenuation at all.
+				pointDistances[ pointLength ] = distance;
+				pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
 
-		];
+				pointLength += 1;
 
-		for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
+			} else if ( light instanceof THREE.SpotLight ) {
 
-			identifiers.push( "morphTarget" + i );
+				spotCount += 1;
 
-		}
+				if ( ! light.visible ) continue;
 
-		for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
+				spotOffset = spotLength * 3;
 
-			identifiers.push( "morphNormal" + i );
+				setColorLinear( spotColors, spotOffset, color, intensity );
 
-		}
+				_direction.setFromMatrixPosition( light.matrixWorld );
 
-		for ( a in attributes ) {
+				spotPositions[ spotOffset ]     = _direction.x;
+				spotPositions[ spotOffset + 1 ] = _direction.y;
+				spotPositions[ spotOffset + 2 ] = _direction.z;
 
-			identifiers.push( a );
+				spotDistances[ spotLength ] = distance;
 
-		}
+				_vector3.setFromMatrixPosition( light.target.matrixWorld );
+				_direction.sub( _vector3 );
+				_direction.normalize();
 
-		cacheAttributeLocations( program, identifiers );
+				spotDirections[ spotOffset ]     = _direction.x;
+				spotDirections[ spotOffset + 1 ] = _direction.y;
+				spotDirections[ spotOffset + 2 ] = _direction.z;
 
-		program.id = _programs_counter ++;
+				spotAnglesCos[ spotLength ] = Math.cos( light.angle );
+				spotExponents[ spotLength ] = light.exponent;
+				spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
 
-		_programs.push( { program: program, code: code, usedTimes: 1 } );
+				spotLength += 1;
 
-		_this.info.memory.programs = _programs.length;
+			} else if ( light instanceof THREE.HemisphereLight ) {
 
-		return program;
+				hemiCount += 1;
 
-	};
+				if ( ! light.visible ) continue;
 
-	// Shader parameters cache
+				_direction.setFromMatrixPosition( light.matrixWorld );
+				_direction.normalize();
 
-	function cacheUniformLocations ( program, identifiers ) {
+				hemiOffset = hemiLength * 3;
 
-		var i, l, id;
+				hemiPositions[ hemiOffset ]     = _direction.x;
+				hemiPositions[ hemiOffset + 1 ] = _direction.y;
+				hemiPositions[ hemiOffset + 2 ] = _direction.z;
 
-		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+				skyColor = light.color;
+				groundColor = light.groundColor;
 
-			id = identifiers[ i ];
-			program.uniforms[ id ] = _gl.getUniformLocation( program, id );
+				setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
+				setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
 
-		}
+				hemiLength += 1;
 
-	};
+			}
 
-	function cacheAttributeLocations ( program, identifiers ) {
+		}
 
-		var i, l, id;
+		// null eventual remains from removed lights
+		// (this is to avoid if in shader)
 
-		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+		for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
+		for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
+		for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
+		for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
+		for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
 
-			id = identifiers[ i ];
-			program.attributes[ id ] = _gl.getAttribLocation( program, id );
+		zlights.directional.length = dirLength;
+		zlights.point.length = pointLength;
+		zlights.spot.length = spotLength;
+		zlights.hemi.length = hemiLength;
 
-		}
+		zlights.ambient[ 0 ] = r;
+		zlights.ambient[ 1 ] = g;
+		zlights.ambient[ 2 ] = b;
 
-	};
+	}
 
-	function addLineNumbers ( string ) {
+	// GL state setting
 
-		var chunks = string.split( "\n" );
+	this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
 
-		for ( var i = 0, il = chunks.length; i < il; i ++ ) {
+		if ( cullFace === THREE.CullFaceNone ) {
 
-			// Chrome reports shader errors on lines
-			// starting counting from 1
+			_gl.disable( _gl.CULL_FACE );
 
-			chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ];
+		} else {
 
-		}
+			if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
 
-		return chunks.join( "\n" );
+				_gl.frontFace( _gl.CW );
 
-	};
+			} else {
 
-	function getShader ( type, string ) {
+				_gl.frontFace( _gl.CCW );
 
-		var shader;
+			}
 
-		if ( type === "fragment" ) {
+			if ( cullFace === THREE.CullFaceBack ) {
 
-			shader = _gl.createShader( _gl.FRAGMENT_SHADER );
+				_gl.cullFace( _gl.BACK );
 
-		} else if ( type === "vertex" ) {
+			} else if ( cullFace === THREE.CullFaceFront ) {
 
-			shader = _gl.createShader( _gl.VERTEX_SHADER );
+				_gl.cullFace( _gl.FRONT );
 
-		}
+			} else {
 
-		_gl.shaderSource( shader, string );
-		_gl.compileShader( shader );
+				_gl.cullFace( _gl.FRONT_AND_BACK );
 
-		if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
+			}
 
-			console.error( _gl.getShaderInfoLog( shader ) );
-			console.error( addLineNumbers( string ) );
-			return null;
+			_gl.enable( _gl.CULL_FACE );
 
 		}
 
-		return shader;
-
 	};
 
-	// Textures
-
-
-	function isPowerOfTwo ( value ) {
+	this.setMaterialFaces = function ( material ) {
 
-		return ( value & ( value - 1 ) ) === 0;
+		state.setDoubleSided( material.side === THREE.DoubleSide );
+		state.setFlipSided( material.side === THREE.BackSide );
 
 	};
 
+	// Textures
+
 	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
 
+		var extension;
+
 		if ( isImagePowerOfTwo ) {
 
 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
@@ -25434,126 +23266,161 @@ THREE.WebGLRenderer = function ( parameters ) {
 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
 
+			if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) {
+
+				THREE.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' );
+
+			}
+
 			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
 			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
 
+			if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {
+
+				THREE.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( ' + texture.sourceFile + ' )' );
+
+			}
+
 		}
 
-		if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) {
+		extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+		if ( extension && texture.type !== THREE.FloatType && texture.type !== THREE.HalfFloatType ) {
 
-			if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
+			if ( texture.anisotropy > 1 || texture.__currentAnisotropy ) {
 
-				_gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) );
-				texture.__oldAnisotropy = texture.anisotropy;
+				_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) );
+				texture.__currentAnisotropy = texture.anisotropy;
 
 			}
 
 		}
 
-	};
+	}
 
-	this.setTexture = function ( texture, slot ) {
+	this.uploadTexture = function ( texture ) {
 
-		if ( texture.needsUpdate ) {
+		if ( texture.__webglInit === undefined ) {
 
-			if ( ! texture.__webglInit ) {
+			texture.__webglInit = true;
 
-				texture.__webglInit = true;
+			texture.addEventListener( 'dispose', onTextureDispose );
 
-				texture.addEventListener( 'dispose', onTextureDispose );
+			texture.__webglTexture = _gl.createTexture();
 
-				texture.__webglTexture = _gl.createTexture();
+			_this.info.memory.textures ++;
 
-				_this.info.memory.textures ++;
+		}
 
-			}
+		_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
 
-			_gl.activeTexture( _gl.TEXTURE0 + slot );
-			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
+		_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+		_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
+		_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
 
-			_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
-			_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
-			_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
+		texture.image = clampToMaxSize( texture.image, _maxTextureSize );
 
-			var image = texture.image,
-			isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
-			glFormat = paramThreeToGL( texture.format ),
-			glType = paramThreeToGL( texture.type );
+		var image = texture.image,
+		isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
+		glFormat = paramThreeToGL( texture.format ),
+		glType = paramThreeToGL( texture.type );
 
-			setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
+		setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
 
-			var mipmap, mipmaps = texture.mipmaps;
+		var mipmap, mipmaps = texture.mipmaps;
 
-			if ( texture instanceof THREE.DataTexture ) {
+		if ( texture instanceof THREE.DataTexture ) {
 
-				// use manually created mipmaps if available
-				// if there are no manual mipmaps
-				// set 0 level mipmap and then use GL to generate other mipmap levels
+			// use manually created mipmaps if available
+			// if there are no manual mipmaps
+			// set 0 level mipmap and then use GL to generate other mipmap levels
 
-				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+			if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
 
-					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+				for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
 
-						mipmap = mipmaps[ i ];
-						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+					mipmap = mipmaps[ i ];
+					_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
 
-					}
+				}
 
-					texture.generateMipmaps = false;
+				texture.generateMipmaps = false;
 
-				} else {
+			} else {
 
-					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
+				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
 
-				}
+			}
 
-			} else if ( texture instanceof THREE.CompressedTexture ) {
+		} else if ( texture instanceof THREE.CompressedTexture ) {
 
-				for( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+			for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+				mipmap = mipmaps[ i ];
+
+				if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+					if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) {
 
-					mipmap = mipmaps[ i ];
-					if ( texture.format!==THREE.RGBAFormat ) {
 						_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
 					} else {
-						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+						THREE.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" );
+
 					}
 
+				} else {
+
+					_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
 				}
 
-			} else { // regular Texture (image, video, canvas)
+			}
 
-				// use manually created mipmaps if available
-				// if there are no manual mipmaps
-				// set 0 level mipmap and then use GL to generate other mipmap levels
+		} else { // regular Texture (image, video, canvas)
 
-				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+			// use manually created mipmaps if available
+			// if there are no manual mipmaps
+			// set 0 level mipmap and then use GL to generate other mipmap levels
 
-					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+			if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
 
-						mipmap = mipmaps[ i ];
-						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
+				for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
 
-					}
+					mipmap = mipmaps[ i ];
+					_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
 
-					texture.generateMipmaps = false;
+				}
 
-				} else {
+				texture.generateMipmaps = false;
 
-					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
+			} else {
 
-				}
+				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
 
 			}
 
-			if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+		}
+
+		if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+		texture.needsUpdate = false;
+
+		if ( texture.onUpdate ) texture.onUpdate();
+
+	};
+
+	this.setTexture = function ( texture, slot ) {
+
+		_gl.activeTexture( _gl.TEXTURE0 + slot );
 
-			texture.needsUpdate = false;
+		if ( texture.needsUpdate ) {
 
-			if ( texture.onUpdate ) texture.onUpdate();
+			_this.uploadTexture( texture );
 
 		} else {
 
-			_gl.activeTexture( _gl.TEXTURE0 + slot );
 			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
 
 		}
@@ -25562,27 +23429,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function clampToMaxSize ( image, maxSize ) {
 
-		if ( image.width <= maxSize && image.height <= maxSize ) {
+		if ( image.width > maxSize || image.height > maxSize ) {
 
-			return image;
+			// Warning: Scaling through the canvas will only work with images that use
+			// premultiplied alpha.
 
-		}
+			var scale = maxSize / Math.max( image.width, image.height );
 
-		// Warning: Scaling through the canvas will only work with images that use
-		// premultiplied alpha.
+			var canvas = document.createElement( 'canvas' );
+			canvas.width = Math.floor( image.width * scale );
+			canvas.height = Math.floor( image.height * scale );
 
-		var maxDimension = Math.max( image.width, image.height );
-		var newWidth = Math.floor( image.width * maxSize / maxDimension );
-		var newHeight = Math.floor( image.height * maxSize / maxDimension );
+			var context = canvas.getContext( '2d' );
+			context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
 
-		var canvas = document.createElement( 'canvas' );
-		canvas.width = newWidth;
-		canvas.height = newHeight;
+			THREE.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
 
-		var ctx = canvas.getContext( "2d" );
-		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight );
+			return canvas;
 
-		return canvas;
+		}
+
+		return image;
 
 	}
 
@@ -25608,25 +23475,26 @@ THREE.WebGLRenderer = function ( parameters ) {
 				_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
 
 				var isCompressed = texture instanceof THREE.CompressedTexture;
+				var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture;
 
 				var cubeImage = [];
 
 				for ( var i = 0; i < 6; i ++ ) {
 
-					if ( _this.autoScaleCubemaps && ! isCompressed ) {
+					if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) {
 
 						cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
 
 					} else {
 
-						cubeImage[ i ] = texture.image[ i ];
+						cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
 
 					}
 
 				}
 
 				var image = cubeImage[ 0 ],
-				isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+				isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
 				glFormat = paramThreeToGL( texture.format ),
 				glType = paramThreeToGL( texture.type );
 
@@ -25634,27 +23502,48 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				for ( var i = 0; i < 6; i ++ ) {
 
-					if( !isCompressed ) {
+					if ( ! isCompressed ) {
+
+						if ( isDataTexture ) {
+
+							_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
+
+						} else {
+
+							_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
 
-						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
+						}
 
 					} else {
 
 						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
 
-						for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+						for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
 
 							mipmap = mipmaps[ j ];
-							if ( texture.format!==THREE.RGBAFormat ) {
 
-								_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+							if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+								if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) {
+
+									_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+								} else {
+
+									THREE.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" );
+
+								}
 
 							} else {
+
 								_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
 							}
 
 						}
+
 					}
+
 				}
 
 				if ( texture.generateMipmaps && isImagePowerOfTwo ) {
@@ -25676,14 +23565,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function setCubeTextureDynamic ( texture, slot ) {
 
 		_gl.activeTexture( _gl.TEXTURE0 + slot );
 		_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture );
 
-	};
+	}
 
 	// Render targets
 
@@ -25692,7 +23581,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
 		_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 );
 
-	};
+	}
 
 	function setupRenderBuffer ( renderbuffer, renderTarget  ) {
 
@@ -25704,7 +23593,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
 
 		/* For some reason this is not working. Defaulting to RGBA4.
-		} else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+		} else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
 
 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
@@ -25720,13 +23609,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	this.setRenderTarget = function ( renderTarget ) {
 
 		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
 
-		if ( renderTarget && ! renderTarget.__webglFramebuffer ) {
+		if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) {
 
 			if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
 			if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
@@ -25739,7 +23628,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			// Setup texture, create render and frame buffers
 
-			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ),
+			var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ),
 				glFormat = paramThreeToGL( renderTarget.format ),
 				glType = paramThreeToGL( renderTarget.type );
 
@@ -25871,6 +23760,54 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
+	this.readRenderTargetPixels = function( renderTarget, x, y, width, height, buffer ) {
+
+		if ( ! ( renderTarget instanceof THREE.WebGLRenderTarget ) ) {
+
+			console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
+			return;
+
+		}
+
+		if ( renderTarget.__webglFramebuffer ) {
+
+			if ( renderTarget.format !== THREE.RGBAFormat ) {
+
+				console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.' );
+				return;
+
+			}
+
+			var restore = false;
+
+			if ( renderTarget.__webglFramebuffer !== _currentFramebuffer ) {
+
+				_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTarget.__webglFramebuffer );
+
+				restore = true;
+
+			}
+
+			if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
+
+				_gl.readPixels( x, y, width, height, _gl.RGBA, _gl.UNSIGNED_BYTE, buffer );
+
+			} else {
+
+				console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
+
+			}
+
+			if ( restore ) {
+
+				_gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );
+
+			}
+
+		}
+
+	};
+
 	function updateRenderTargetMipmap ( renderTarget ) {
 
 		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
@@ -25887,7 +23824,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	// Fallback filters for non-power-of-2 textures
 
@@ -25901,12 +23838,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		return _gl.LINEAR;
 
-	};
+	}
 
 	// Map three.js constants to WebGL constants
 
 	function paramThreeToGL ( p ) {
 
+		var extension;
+
 		if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
 		if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
 		if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
@@ -25931,6 +23870,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 		if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
 		if ( p === THREE.FloatType ) return _gl.FLOAT;
 
+		extension = extensions.get( 'OES_texture_half_float' );
+
+		if ( extension !== null ) {
+
+			if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES;
+
+		}
+
 		if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
 		if ( p === THREE.RGBFormat ) return _gl.RGB;
 		if ( p === THREE.RGBAFormat ) return _gl.RGBA;
@@ -25954,24 +23901,46 @@ THREE.WebGLRenderer = function ( parameters ) {
 		if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
 		if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
 
-		if ( _glExtensionCompressedTextureS3TC !== undefined ) {
+		extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
+
+		if ( extension !== null ) {
+
+			if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+
+		}
+
+		extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
+
+		if ( extension !== null ) {
+
+			if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+			if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+			if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+			if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+
+		}
+
+		extension = extensions.get( 'EXT_blend_minmax' );
+
+		if ( extension !== null ) {
 
-			if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT;
-			if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT;
-			if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT;
-			if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+			if ( p === THREE.MinEquation ) return extension.MIN_EXT;
+			if ( p === THREE.MaxEquation ) return extension.MAX_EXT;
 
 		}
 
 		return 0;
 
-	};
+	}
 
 	// Allocations
 
 	function allocateBones ( object ) {
 
-		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
+		if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
 
 			return 1024;
 
@@ -25981,7 +23950,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			// ( for example when prebuilding shader
 			//   to be used with multiple objects )
 			//
-			// 	- leave some extra space for other uniforms
+			//  - leave some extra space for other uniforms
 			//  - limit here is ANGLE's 254 max uniform vectors
 			//    (up to 54 should be safe)
 
@@ -25992,11 +23961,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
 
-				maxBones = Math.min( object.bones.length, maxBones );
+				maxBones = Math.min( object.skeleton.bones.length, maxBones );
 
-				if ( maxBones < object.bones.length ) {
+				if ( maxBones < object.skeleton.bones.length ) {
 
-					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
+					THREE.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );
 
 				}
 
@@ -26006,7 +23975,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function allocateLights( lights ) {
 
@@ -26019,7 +23988,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			var light = lights[ l ];
 
-			if ( light.onlyShadow ) continue;
+			if ( light.onlyShadow || light.visible === false ) continue;
 
 			if ( light instanceof THREE.DirectionalLight ) dirLights ++;
 			if ( light instanceof THREE.PointLight ) pointLights ++;
@@ -26028,15 +23997,15 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights };
+		return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights };
 
-	};
+	}
 
 	function allocateShadows( lights ) {
 
 		var maxShadows = 0;
 
-		for ( var l = 0, ll = lights.length; l < ll; l++ ) {
+		for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
 
 			var light = lights[ l ];
 
@@ -26049,116 +24018,38 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		return maxShadows;
 
-	};
-
-	// Initialization
-
-	function initGL() {
-
-		try {
-
-			var attributes = {
-				alpha: _alpha,
-				premultipliedAlpha: _premultipliedAlpha,
-				antialias: _antialias,
-				stencil: _stencil,
-				preserveDrawingBuffer: _preserveDrawingBuffer
-			};
-
-			_gl = _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
-
-			if ( _gl === null ) {
-
-				throw 'Error creating WebGL context.';
-
-			}
-
-		} catch ( error ) {
-
-			console.error( error );
-
-		}
-
-		_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
-		_glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' );
-		_glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' );
-
-		_glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
-
-		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
-
-		if ( ! _glExtensionTextureFloat ) {
-
-			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
-
-		}
-
-		if ( ! _glExtensionStandardDerivatives ) {
-
-			console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' );
-
-		}
-
-		if ( ! _glExtensionTextureFilterAnisotropic ) {
-
-			console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' );
-
-		}
-
-		if ( ! _glExtensionCompressedTextureS3TC ) {
-
-			console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' );
-
-		}
-
-		if ( _gl.getShaderPrecisionFormat === undefined ) {
+	}
 
-			_gl.getShaderPrecisionFormat = function() {
+	// DEPRECATED
 
-				return {
-					"rangeMin"  : 1,
-					"rangeMax"  : 1,
-					"precision" : 1
-				};
+	this.initMaterial = function () {
 
-			}
-		}
+		THREE.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
 
 	};
 
-	function setDefaultGLState () {
-
-		_gl.clearColor( 0, 0, 0, 1 );
-		_gl.clearDepth( 1 );
-		_gl.clearStencil( 0 );
-
-		_gl.enable( _gl.DEPTH_TEST );
-		_gl.depthFunc( _gl.LEQUAL );
+	this.addPrePlugin = function () {
 
-		_gl.frontFace( _gl.CCW );
-		_gl.cullFace( _gl.BACK );
-		_gl.enable( _gl.CULL_FACE );
+		THREE.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
 
-		_gl.enable( _gl.BLEND );
-		_gl.blendEquation( _gl.FUNC_ADD );
-		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
+	};
 
-		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+	this.addPostPlugin = function () {
 
-		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+		THREE.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
 
 	};
 
-	// default plugins (order is important)
+	this.updateShadowMap = function () {
 
-	this.shadowMapPlugin = new THREE.ShadowMapPlugin();
-	this.addPrePlugin( this.shadowMapPlugin );
+		THREE.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
 
-	this.addPostPlugin( new THREE.SpritePlugin() );
-	this.addPostPlugin( new THREE.LensFlarePlugin() );
+	};
 
 };
 
+// File:src/renderers/WebGLRenderTarget.js
+
 /**
  * @author szimek / https://github.com/szimek/
  * @author alteredq / http://alteredqualia.com/
@@ -26190,7 +24081,7 @@ THREE.WebGLRenderTarget = function ( width, height, options ) {
 
 	this.generateMipmaps = true;
 
-	this.shareDepthFrom = null;
+	this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null;
 
 };
 
@@ -26198,6 +24089,13 @@ THREE.WebGLRenderTarget.prototype = {
 
 	constructor: THREE.WebGLRenderTarget,
 
+	setSize: function ( width, height ) {
+
+		this.width = width;
+		this.height = height;
+
+	},
+
 	clone: function () {
 
 		var tmp = new THREE.WebGLRenderTarget( this.width, this.height );
@@ -26237,6 +24135,8 @@ THREE.WebGLRenderTarget.prototype = {
 
 THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype );
 
+// File:src/renderers/WebGLRenderTargetCube.js
+
 /**
  * @author alteredq / http://alteredqualia.com
  */
@@ -26245,6705 +24145,6772 @@ THREE.WebGLRenderTargetCube = function ( width, height, options ) {
 
 	THREE.WebGLRenderTarget.call( this, width, height, options );
 
-	this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
-
-};
-
-THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype );
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.RenderableVertex = function () {
-
-	this.positionWorld = new THREE.Vector3();
-	this.positionScreen = new THREE.Vector4();
-
-	this.visible = true;
-
-};
-
-THREE.RenderableVertex.prototype.copy = function ( vertex ) {
-
-	this.positionWorld.copy( vertex.positionWorld );
-	this.positionScreen.copy( vertex.positionScreen );
+	this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
 
 };
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype );
+THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube;
 
-THREE.RenderableFace3 = function () {
+// File:src/renderers/webgl/WebGLExtensions.js
 
-	this.id = 0;
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
 
-	this.v1 = new THREE.RenderableVertex();
-	this.v2 = new THREE.RenderableVertex();
-	this.v3 = new THREE.RenderableVertex();
+THREE.WebGLExtensions = function ( gl ) {
 
-	this.centroidModel = new THREE.Vector3();
+	var extensions = {};
 
-	this.normalModel = new THREE.Vector3();
-	this.normalModelView = new THREE.Vector3();
+	this.get = function ( name ) {
 
-	this.vertexNormalsLength = 0;
-	this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
-	this.vertexNormalsModelView = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+		if ( extensions[ name ] !== undefined ) {
 
-	this.color = null;
-	this.material = null;
-	this.uvs = [[]];
+			return extensions[ name ];
 
-	this.z = 0;
+		}
 
-};
+		var extension;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		switch ( name ) {
 
-THREE.RenderableObject = function () {
+			case 'EXT_texture_filter_anisotropic':
+				extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
+				break;
 
-	this.id = 0;
+			case 'WEBGL_compressed_texture_s3tc':
+				extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
+				break;
 
-	this.object = null;
-	this.z = 0;
+			case 'WEBGL_compressed_texture_pvrtc':
+				extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
+				break;
 
-};
+			default:
+				extension = gl.getExtension( name );
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		}
 
-THREE.RenderableSprite = function () {
+		if ( extension === null ) {
 
-	this.id = 0;
+			THREE.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
 
-	this.object = null;
+		}
 
-	this.x = 0;
-	this.y = 0;
-	this.z = 0;
+		extensions[ name ] = extension;
 
-	this.rotation = 0;
-	this.scale = new THREE.Vector2();
+		return extension;
 
-	this.material = null;
+	};
 
 };
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.RenderableLine = function () {
+// File:src/renderers/webgl/WebGLProgram.js
 
-	this.id = 0;
+THREE.WebGLProgram = ( function () {
 
-	this.v1 = new THREE.RenderableVertex();
-	this.v2 = new THREE.RenderableVertex();
+	var programIdCount = 0;
 
-	this.vertexColors = [ new THREE.Color(), new THREE.Color() ];
-	this.material = null;
+	var generateDefines = function ( defines ) {
 
-	this.z = 0;
-
-};
+		var value, chunk, chunks = [];
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+		for ( var d in defines ) {
 
-THREE.GeometryUtils = {
+			value = defines[ d ];
+			if ( value === false ) continue;
 
-	// Merge two geometries or geometry and geometry from object (using object's transform)
+			chunk = '#define ' + d + ' ' + value;
+			chunks.push( chunk );
 
-	merge: function ( geometry1, object2 /* mesh | geometry */, materialIndexOffset ) {
+		}
 
-		var matrix, normalMatrix,
-		vertexOffset = geometry1.vertices.length,
-		uvPosition = geometry1.faceVertexUvs[ 0 ].length,
-		geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2,
-		vertices1 = geometry1.vertices,
-		vertices2 = geometry2.vertices,
-		faces1 = geometry1.faces,
-		faces2 = geometry2.faces,
-		uvs1 = geometry1.faceVertexUvs[ 0 ],
-		uvs2 = geometry2.faceVertexUvs[ 0 ];
+		return chunks.join( '\n' );
 
-		if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
+	};
 
-		if ( object2 instanceof THREE.Mesh ) {
+	var cacheUniformLocations = function ( gl, program, identifiers ) {
 
-			object2.matrixAutoUpdate && object2.updateMatrix();
+		var uniforms = {};
 
-			matrix = object2.matrix;
+		for ( var i = 0, l = identifiers.length; i < l; i ++ ) {
 
-			normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+			var id = identifiers[ i ];
+			uniforms[ id ] = gl.getUniformLocation( program, id );
 
 		}
 
-		// vertices
+		return uniforms;
 
-		for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
+	};
 
-			var vertex = vertices2[ i ];
+	var cacheAttributeLocations = function ( gl, program, identifiers ) {
 
-			var vertexCopy = vertex.clone();
+		var attributes = {};
 
-			if ( matrix ) vertexCopy.applyMatrix4( matrix );
+		for ( var i = 0, l = identifiers.length; i < l; i ++ ) {
 
-			vertices1.push( vertexCopy );
+			var id = identifiers[ i ];
+			attributes[ id ] = gl.getAttribLocation( program, id );
 
 		}
 
-		// faces
+		return attributes;
 
-		for ( i = 0, il = faces2.length; i < il; i ++ ) {
+	};
 
-			var face = faces2[ i ], faceCopy, normal, color,
-			faceVertexNormals = face.vertexNormals,
-			faceVertexColors = face.vertexColors;
+	return function ( renderer, code, material, parameters ) {
 
-			faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
-			faceCopy.normal.copy( face.normal );
+		var _this = renderer;
+		var _gl = _this.context;
 
-			if ( normalMatrix ) {
+		var defines = material.defines;
+		var uniforms = material.__webglShader.uniforms;
+		var attributes = material.attributes;
 
-				faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
+		var vertexShader = material.__webglShader.vertexShader;
+		var fragmentShader = material.__webglShader.fragmentShader;
 
-			}
+		var index0AttributeName = material.index0AttributeName;
 
-			for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
+		if ( index0AttributeName === undefined && parameters.morphTargets === true ) {
 
-				normal = faceVertexNormals[ j ].clone();
+			// programs with morphTargets displace position out of attribute 0
 
-				if ( normalMatrix ) {
+			index0AttributeName = 'position';
 
-					normal.applyMatrix3( normalMatrix ).normalize();
+		}
 
-				}
+		var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
 
-				faceCopy.vertexNormals.push( normal );
+		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
 
-			}
+			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
 
-			faceCopy.color.copy( face.color );
+		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
 
-			for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
+			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
 
-				color = faceVertexColors[ j ];
-				faceCopy.vertexColors.push( color.clone() );
+		}
 
-			}
+		var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+		var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
+		var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
 
-			faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
+		if ( parameters.envMap ) {
 
-			faceCopy.centroid.copy( face.centroid );
+			switch ( material.envMap.mapping ) {
 
-			if ( matrix ) {
+				case THREE.CubeReflectionMapping:
+				case THREE.CubeRefractionMapping:
+					envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+					break;
+
+				case THREE.EquirectangularReflectionMapping:
+				case THREE.EquirectangularRefractionMapping:
+					envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
+					break;
 
-				faceCopy.centroid.applyMatrix4( matrix );
+				case THREE.SphericalReflectionMapping:
+					envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
+					break;
 
 			}
 
-			faces1.push( faceCopy );
+			switch ( material.envMap.mapping ) {
 
-		}
+				case THREE.CubeRefractionMapping:
+				case THREE.EquirectangularRefractionMapping:
+					envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
+					break;
 
-		// uvs
+			}
 
-		for ( i = 0, il = uvs2.length; i < il; i ++ ) {
+			switch ( material.combine ) {
 
-			var uv = uvs2[ i ], uvCopy = [];
+				case THREE.MultiplyOperation:
+					envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+					break;
 
-			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+				case THREE.MixOperation:
+					envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
+					break;
 
-				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+				case THREE.AddOperation:
+					envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
+					break;
 
 			}
 
-			uvs1.push( uvCopy );
-
 		}
 
-	},
-
-	// Get random point in triangle (via barycentric coordinates)
-	// 	(uniform distribution)
-	// 	http://www.cgafaq.info/wiki/Random_Point_In_Triangle
-
-	randomPointInTriangle: function () {
+		var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;
 
-		var vector = new THREE.Vector3();
-
-		return function ( vectorA, vectorB, vectorC ) {
+		// console.log( 'building new program ' );
 
-			var point = new THREE.Vector3();
-
-			var a = THREE.Math.random16();
-			var b = THREE.Math.random16();
+		//
 
-			if ( ( a + b ) > 1 ) {
+		var customDefines = generateDefines( defines );
 
-				a = 1 - a;
-				b = 1 - b;
+		//
 
-			}
+		var program = _gl.createProgram();
 
-			var c = 1 - a - b;
+		var prefix_vertex, prefix_fragment;
 
-			point.copy( vectorA );
-			point.multiplyScalar( a );
+		if ( material instanceof THREE.RawShaderMaterial ) {
 
-			vector.copy( vectorB );
-			vector.multiplyScalar( b );
+			prefix_vertex = '';
+			prefix_fragment = '';
 
-			point.add( vector );
+		} else {
 
-			vector.copy( vectorC );
-			vector.multiplyScalar( c );
+			prefix_vertex = [
 
-			point.add( vector );
+				'precision ' + parameters.precision + ' float;',
+				'precision ' + parameters.precision + ' int;',
 
-			return point;
+				customDefines,
 
-		};
+				parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
 
-	}(),
+				_this.gammaInput ? '#define GAMMA_INPUT' : '',
+				_this.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+				'#define GAMMA_FACTOR ' + gammaFactorDefine,
 
-	// Get random point in face (triangle / quad)
-	// (uniform distribution)
+				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
 
-	randomPointInFace: function ( face, geometry, useCachedAreas ) {
+				'#define MAX_SHADOWS ' + parameters.maxShadows,
 
-		var vA, vB, vC, vD;
+				'#define MAX_BONES ' + parameters.maxBones,
 
-		vA = geometry.vertices[ face.a ];
-		vB = geometry.vertices[ face.b ];
-		vC = geometry.vertices[ face.c ];
+				parameters.map ? '#define USE_MAP' : '',
+				parameters.envMap ? '#define USE_ENVMAP' : '',
+				parameters.envMap ? '#define ' + envMapModeDefine : '',
+				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+				parameters.normalMap ? '#define USE_NORMALMAP' : '',
+				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+				parameters.vertexColors ? '#define USE_COLOR' : '',
 
-		return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
+				parameters.flatShading ? '#define FLAT_SHADED': '',
 
-	},
+				parameters.skinning ? '#define USE_SKINNING' : '',
+				parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',
 
-	// Get uniformly distributed random points in mesh
-	// 	- create array with cumulative sums of face areas
-	//  - pick random number from 0 to total area
-	//  - find corresponding place in area array by binary search
-	//	- get random point in face
+				parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
+				parameters.morphNormals ? '#define USE_MORPHNORMALS' : '',
+				parameters.wrapAround ? '#define WRAP_AROUND' : '',
+				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+				parameters.flipSided ? '#define FLIP_SIDED' : '',
 
-	randomPointsInGeometry: function ( geometry, n ) {
+				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',
 
-		var face, i,
-			faces = geometry.faces,
-			vertices = geometry.vertices,
-			il = faces.length,
-			totalArea = 0,
-			cumulativeAreas = [],
-			vA, vB, vC, vD;
+				parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
 
-		// precompute face areas
+				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+				//_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '',
 
-		for ( i = 0; i < il; i ++ ) {
 
-			face = faces[ i ];
+				'uniform mat4 modelMatrix;',
+				'uniform mat4 modelViewMatrix;',
+				'uniform mat4 projectionMatrix;',
+				'uniform mat4 viewMatrix;',
+				'uniform mat3 normalMatrix;',
+				'uniform vec3 cameraPosition;',
 
-			vA = vertices[ face.a ];
-			vB = vertices[ face.b ];
-			vC = vertices[ face.c ];
+				'attribute vec3 position;',
+				'attribute vec3 normal;',
+				'attribute vec2 uv;',
+				'attribute vec2 uv2;',
 
-			face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
+				'#ifdef USE_COLOR',
 
-			totalArea += face._area;
+				'	attribute vec3 color;',
 
-			cumulativeAreas[ i ] = totalArea;
+				'#endif',
 
-		}
+				'#ifdef USE_MORPHTARGETS',
 
-		// binary search cumulative areas array
+				'	attribute vec3 morphTarget0;',
+				'	attribute vec3 morphTarget1;',
+				'	attribute vec3 morphTarget2;',
+				'	attribute vec3 morphTarget3;',
 
-		function binarySearchIndices( value ) {
+				'	#ifdef USE_MORPHNORMALS',
 
-			function binarySearch( start, end ) {
+				'		attribute vec3 morphNormal0;',
+				'		attribute vec3 morphNormal1;',
+				'		attribute vec3 morphNormal2;',
+				'		attribute vec3 morphNormal3;',
 
-				// return closest larger index
-				// if exact number is not found
+				'	#else',
 
-				if ( end < start )
-					return start;
+				'		attribute vec3 morphTarget4;',
+				'		attribute vec3 morphTarget5;',
+				'		attribute vec3 morphTarget6;',
+				'		attribute vec3 morphTarget7;',
 
-				var mid = start + Math.floor( ( end - start ) / 2 );
+				'	#endif',
 
-				if ( cumulativeAreas[ mid ] > value ) {
+				'#endif',
 
-					return binarySearch( start, mid - 1 );
+				'#ifdef USE_SKINNING',
 
-				} else if ( cumulativeAreas[ mid ] < value ) {
+				'	attribute vec4 skinIndex;',
+				'	attribute vec4 skinWeight;',
 
-					return binarySearch( mid + 1, end );
+				'#endif',
 
-				} else {
+				''
 
-					return mid;
+			].join( '\n' );
 
-				}
+			prefix_fragment = [
 
-			}
+				'precision ' + parameters.precision + ' float;',
+				'precision ' + parameters.precision + ' int;',
 
-			var result = binarySearch( 0, cumulativeAreas.length - 1 )
-			return result;
+				( parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 
-		}
+				customDefines,
 
-		// pick random face weighted by face area
+				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
 
-		var r, index,
-			result = [];
+				'#define MAX_SHADOWS ' + parameters.maxShadows,
 
-		var stats = {};
+				parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',
 
-		for ( i = 0; i < n; i ++ ) {
+				_this.gammaInput ? '#define GAMMA_INPUT' : '',
+				_this.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+				'#define GAMMA_FACTOR ' + gammaFactorDefine,
 
-			r = THREE.Math.random16() * totalArea;
+				( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
+				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
 
-			index = binarySearchIndices( r );
+				parameters.map ? '#define USE_MAP' : '',
+				parameters.envMap ? '#define USE_ENVMAP' : '',
+				parameters.envMap ? '#define ' + envMapTypeDefine : '',
+				parameters.envMap ? '#define ' + envMapModeDefine : '',
+				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
+				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+				parameters.normalMap ? '#define USE_NORMALMAP' : '',
+				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+				parameters.vertexColors ? '#define USE_COLOR' : '',
 
-			result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
+				parameters.flatShading ? '#define FLAT_SHADED': '',
 
-			if ( ! stats[ index ] ) {
+				parameters.metal ? '#define METAL' : '',
+				parameters.wrapAround ? '#define WRAP_AROUND' : '',
+				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+				parameters.flipSided ? '#define FLIP_SIDED' : '',
 
-				stats[ index ] = 1;
+				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',
 
-			} else {
+				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+				//_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '',
 
-				stats[ index ] += 1;
+				'uniform mat4 viewMatrix;',
+				'uniform vec3 cameraPosition;',
+				''
 
-			}
+			].join( '\n' );
 
 		}
 
-		return result;
+		var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader );
+		var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader );
 
-	},
+		_gl.attachShader( program, glVertexShader );
+		_gl.attachShader( program, glFragmentShader );
 
-	// Get triangle area (half of parallelogram)
-	//	http://mathworld.wolfram.com/TriangleArea.html
+		if ( index0AttributeName !== undefined ) {
 
-	triangleArea: function () {
+			// Force a particular attribute to index 0.
+			// because potentially expensive emulation is done by browser if attribute 0 is disabled.
+			// And, color, for example is often automatically bound to index 0 so disabling it
 
-		var vector1 = new THREE.Vector3();
-		var vector2 = new THREE.Vector3();
+			_gl.bindAttribLocation( program, 0, index0AttributeName );
 
-		return function ( vectorA, vectorB, vectorC ) {
+		}
 
-			vector1.subVectors( vectorB, vectorA );
-			vector2.subVectors( vectorC, vectorA );
-			vector1.cross( vector2 );
+		_gl.linkProgram( program );
 
-			return 0.5 * vector1.length();
+		var programLogInfo = _gl.getProgramInfoLog( program );
 
-		};
+		if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) {
 
-	}(),
+			THREE.error( 'THREE.WebGLProgram: shader error: ' + _gl.getError(), 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ), 'gl.getPRogramInfoLog', programLogInfo );
 
-	// Center geometry so that 0,0,0 is in center of bounding box
+		}
 
-	center: function ( geometry ) {
+		if ( programLogInfo !== '' ) {
 
-		geometry.computeBoundingBox();
+			THREE.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()' + programLogInfo );
+			// THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) );
+			// THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) );
 
-		var bb = geometry.boundingBox;
+		}
 
-		var offset = new THREE.Vector3();
+		// clean up
 
-		offset.addVectors( bb.min, bb.max );
-		offset.multiplyScalar( -0.5 );
+		_gl.deleteShader( glVertexShader );
+		_gl.deleteShader( glFragmentShader );
 
-		geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) );
-		geometry.computeBoundingBox();
+		// cache uniform locations
 
-		return offset;
+		var identifiers = [
 
-	},
+			'viewMatrix',
+			'modelViewMatrix',
+			'projectionMatrix',
+			'normalMatrix',
+			'modelMatrix',
+			'cameraPosition',
+			'morphTargetInfluences',
+			'bindMatrix',
+			'bindMatrixInverse'
 
-	triangulateQuads: function ( geometry ) {
+		];
 
-		var i, il, j, jl;
+		if ( parameters.useVertexTexture ) {
 
-		var faces = [];
-		var faceVertexUvs = [];
+			identifiers.push( 'boneTexture' );
+			identifiers.push( 'boneTextureWidth' );
+			identifiers.push( 'boneTextureHeight' );
 
-		for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
+		} else {
 
-			faceVertexUvs[ i ] = [];
+			identifiers.push( 'boneGlobalMatrices' );
 
 		}
 
-		for ( i = 0, il = geometry.faces.length; i < il; i ++ ) {
+		if ( parameters.logarithmicDepthBuffer ) {
 
-			var face = geometry.faces[ i ];
+			identifiers.push('logDepthBufFC');
 
-			faces.push( face );
+		}
 
-			for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
 
-				faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
+		for ( var u in uniforms ) {
 
-			}
+			identifiers.push( u );
 
 		}
 
-		geometry.faces = faces;
-		geometry.faceVertexUvs = faceVertexUvs;
-
-		geometry.computeCentroids();
-		geometry.computeFaceNormals();
-		geometry.computeVertexNormals();
+		this.uniforms = cacheUniformLocations( _gl, program, identifiers );
 
-		if ( geometry.hasTangents ) geometry.computeTangents();
+		// cache attributes locations
 
-	}
+		identifiers = [
 
-};
+			'position',
+			'normal',
+			'uv',
+			'uv2',
+			'tangent',
+			'color',
+			'skinIndex',
+			'skinWeight',
+			'lineDistance'
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
+		];
 
-THREE.ImageUtils = {
+		for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) {
 
-	crossOrigin: 'anonymous',
+			identifiers.push( 'morphTarget' + i );
 
-	loadTexture: function ( url, mapping, onLoad, onError ) {
+		}
 
-		var loader = new THREE.ImageLoader();
-		loader.crossOrigin = this.crossOrigin;
+		for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) {
 
-		var texture = new THREE.Texture( undefined, mapping );
+			identifiers.push( 'morphNormal' + i );
 
-		var image = loader.load( url, function () {
+		}
 
-			texture.needsUpdate = true;
+		for ( var a in attributes ) {
 
-			if ( onLoad ) onLoad( texture );
+			identifiers.push( a );
 
-		} );
+		}
 
-		texture.image = image;
-		texture.sourceFile = url;
+		this.attributes = cacheAttributeLocations( _gl, program, identifiers );
+		this.attributesKeys = Object.keys( this.attributes );
 
-		return texture;
+		//
 
-	},
+		this.id = programIdCount ++;
+		this.code = code;
+		this.usedTimes = 1;
+		this.program = program;
+		this.vertexShader = glVertexShader;
+		this.fragmentShader = glFragmentShader;
 
-	loadCompressedTexture: function ( url, mapping, onLoad, onError ) {
+		return this;
 
-		var texture = new THREE.CompressedTexture();
-		texture.mapping = mapping;
+	};
 
-		var request = new XMLHttpRequest();
+} )();
 
-		request.onload = function () {
+// File:src/renderers/webgl/WebGLShader.js
 
-			var buffer = request.response;
-			var dds = THREE.ImageUtils.parseDDS( buffer, true );
+THREE.WebGLShader = ( function () {
 
-			texture.format = dds.format;
+	var addLineNumbers = function ( string ) {
 
-			texture.mipmaps = dds.mipmaps;
-			texture.image.width = dds.width;
-			texture.image.height = dds.height;
+		var lines = string.split( '\n' );
 
-			// gl.generateMipmap fails for compressed textures
-			// mipmaps must be embedded in the DDS file
-			// or texture filters must not use mipmapping
+		for ( var i = 0; i < lines.length; i ++ ) {
 
-			texture.generateMipmaps = false;
+			lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
 
-			texture.needsUpdate = true;
+		}
 
-			if ( onLoad ) onLoad( texture );
+		return lines.join( '\n' );
 
-		}
+	};
 
-		request.onerror = onError;
+	return function ( gl, type, string ) {
 
-		request.open( 'GET', url, true );
-		request.responseType = "arraybuffer";
-		request.send( null );
+		var shader = gl.createShader( type ); 
 
-		return texture;
+		gl.shaderSource( shader, string );
+		gl.compileShader( shader );
 
-	},
+		if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
 
-	loadTextureCube: function ( array, mapping, onLoad, onError ) {
+			THREE.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
 
-		var images = [];
-		images.loadCount = 0;
+		}
 
-		var texture = new THREE.Texture();
-		texture.image = images;
-		if ( mapping !== undefined ) texture.mapping = mapping;
+		if ( gl.getShaderInfoLog( shader ) !== '' ) {
 
-		// no flipping needed for cube textures
+			THREE.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
 
-		texture.flipY = false;
+		}
 
-		for ( var i = 0, il = array.length; i < il; ++ i ) {
+		// --enable-privileged-webgl-extension
+		// console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
 
-			var cubeImage = new Image();
-			images[ i ] = cubeImage;
+		return shader;
 
-			cubeImage.onload = function () {
+	};
 
-				images.loadCount += 1;
+} )();
 
-				if ( images.loadCount === 6 ) {
+// File:src/renderers/webgl/WebGLState.js
 
-					texture.needsUpdate = true;
-					if ( onLoad ) onLoad( texture );
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
 
-				}
+THREE.WebGLState = function ( gl, paramThreeToGL ) {
 
-			};
+	var newAttributes = new Uint8Array( 16 );
+	var enabledAttributes = new Uint8Array( 16 );
 
-			cubeImage.onerror = onError;
+	var currentBlending = null;
+	var currentBlendEquation = null;
+	var currentBlendSrc = null;
+	var currentBlendDst = null;
+	var currentBlendEquationAlpha = null;
+	var currentBlendSrcAlpha = null;
+	var currentBlendDstAlpha = null;
 
-			cubeImage.crossOrigin = this.crossOrigin;
-			cubeImage.src = array[ i ];
+	var currentDepthTest = null;
+	var currentDepthWrite = null;
 
-		}
+	var currentColorWrite = null;
 
-		return texture;
+	var currentDoubleSided = null;
+	var currentFlipSided = null;
 
-	},
+	var currentLineWidth = null;
 
-	loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) {
+	var currentPolygonOffset = null;
+	var currentPolygonOffsetFactor = null;
+	var currentPolygonOffsetUnits = null;
 
-		var images = [];
-		images.loadCount = 0;
+	this.initAttributes = function () {
 
-		var texture = new THREE.CompressedTexture();
-		texture.image = images;
-		if ( mapping !== undefined ) texture.mapping = mapping;
+		for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
 
-		// no flipping for cube textures
-		// (also flipping doesn't work for compressed textures )
+			newAttributes[ i ] = 0;
 
-		texture.flipY = false;
+		}
 
-		// can't generate mipmaps for compressed textures
-		// mips must be embedded in DDS files
+	};
 
-		texture.generateMipmaps = false;
+	this.enableAttribute = function ( attribute ) {
 
-		var generateCubeFaceCallback = function ( rq, img ) {
+		newAttributes[ attribute ] = 1;
 
-			return function () {
+		if ( enabledAttributes[ attribute ] === 0 ) {
 
-				var buffer = rq.response;
-				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+			gl.enableVertexAttribArray( attribute );
+			enabledAttributes[ attribute ] = 1;
 
-				img.format = dds.format;
+		}
 
-				img.mipmaps = dds.mipmaps;
-				img.width = dds.width;
-				img.height = dds.height;
+	};
 
-				images.loadCount += 1;
+	this.disableUnusedAttributes = function () {
 
-				if ( images.loadCount === 6 ) {
+		for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {
 
-					texture.format = dds.format;
-					texture.needsUpdate = true;
-					if ( onLoad ) onLoad( texture );
+			if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
 
-				}
+				gl.disableVertexAttribArray( i );
+				enabledAttributes[ i ] = 0;
 
 			}
 
 		}
 
-		// compressed cubemap textures as 6 separate DDS files
-
-		if ( array instanceof Array ) {
+	};
 
-			for ( var i = 0, il = array.length; i < il; ++ i ) {
+	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {
 
-				var cubeImage = {};
-				images[ i ] = cubeImage;
+		if ( blending !== currentBlending ) {
 
-				var request = new XMLHttpRequest();
+			if ( blending === THREE.NoBlending ) {
 
-				request.onload = generateCubeFaceCallback( request, cubeImage );
-				request.onerror = onError;
+				gl.disable( gl.BLEND );
 
-				var url = array[ i ];
+			} else if ( blending === THREE.AdditiveBlending ) {
 
-				request.open( 'GET', url, true );
-				request.responseType = "arraybuffer";
-				request.send( null );
+				gl.enable( gl.BLEND );
+				gl.blendEquation( gl.FUNC_ADD );
+				gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
 
-			}
+			} else if ( blending === THREE.SubtractiveBlending ) {
 
-		// compressed cubemap texture stored in a single DDS file
+				// TODO: Find blendFuncSeparate() combination
+				gl.enable( gl.BLEND );
+				gl.blendEquation( gl.FUNC_ADD );
+				gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );
 
-		} else {
+			} else if ( blending === THREE.MultiplyBlending ) {
 
-			var url = array;
-			var request = new XMLHttpRequest();
+				// TODO: Find blendFuncSeparate() combination
+				gl.enable( gl.BLEND );
+				gl.blendEquation( gl.FUNC_ADD );
+				gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
 
-			request.onload = function( ) {
+			} else if ( blending === THREE.CustomBlending ) {
 
-				var buffer = request.response;
-				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+				gl.enable( gl.BLEND );
 
-				if ( dds.isCubemap ) {
+			} else {
 
-					var faces = dds.mipmaps.length / dds.mipmapCount;
+				gl.enable( gl.BLEND );
+				gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
+				gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
 
-					for ( var f = 0; f < faces; f ++ ) {
+			}
 
-						images[ f ] = { mipmaps : [] };
+			currentBlending = blending;
 
-						for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+		}
 
-							images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
-							images[ f ].format = dds.format;
-							images[ f ].width = dds.width;
-							images[ f ].height = dds.height;
+		if ( blending === THREE.CustomBlending ) {
 
-						}
+			blendEquationAlpha = blendEquationAlpha || blendEquation;
+			blendSrcAlpha = blendSrcAlpha || blendSrc;
+			blendDstAlpha = blendDstAlpha || blendDst;
 
-					}
+			if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
 
-					texture.format = dds.format;
-					texture.needsUpdate = true;
-					if ( onLoad ) onLoad( texture );
+				gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );
 
-				}
+				currentBlendEquation = blendEquation;
+				currentBlendEquationAlpha = blendEquationAlpha;
 
 			}
 
-			request.onerror = onError;
-
-			request.open( 'GET', url, true );
-			request.responseType = "arraybuffer";
-			request.send( null );
-
-		}
-
-		return texture;
+			if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
 
-	},
+				gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );
 
-	loadDDSTexture: function ( url, mapping, onLoad, onError ) {
+				currentBlendSrc = blendSrc;
+				currentBlendDst = blendDst;
+				currentBlendSrcAlpha = blendSrcAlpha;
+				currentBlendDstAlpha = blendDstAlpha;
 
-		var images = [];
-		images.loadCount = 0;
+			}
 
-		var texture = new THREE.CompressedTexture();
-		texture.image = images;
-		if ( mapping !== undefined ) texture.mapping = mapping;
+		} else {
 
-		// no flipping for cube textures
-		// (also flipping doesn't work for compressed textures )
+			currentBlendEquation = null;
+			currentBlendSrc = null;
+			currentBlendDst = null;
+			currentBlendEquationAlpha = null;
+			currentBlendSrcAlpha = null;
+			currentBlendDstAlpha = null;
 
-		texture.flipY = false;
+		}
 
-		// can't generate mipmaps for compressed textures
-		// mips must be embedded in DDS files
+	};
 
-		texture.generateMipmaps = false;
+	this.setDepthTest = function ( depthTest ) {
 
-		{
-			var request = new XMLHttpRequest();
+		if ( currentDepthTest !== depthTest ) {
 
-			request.onload = function( ) {
+			if ( depthTest ) {
 
-				var buffer = request.response;
-				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+				gl.enable( gl.DEPTH_TEST );
 
-				if ( dds.isCubemap ) {
+			} else {
 
-					var faces = dds.mipmaps.length / dds.mipmapCount;
+				gl.disable( gl.DEPTH_TEST );
 
-					for ( var f = 0; f < faces; f ++ ) {
+			}
 
-						images[ f ] = { mipmaps : [] };
+			currentDepthTest = depthTest;
 
-						for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+		}
 
-							images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
-							images[ f ].format = dds.format;
-							images[ f ].width = dds.width;
-							images[ f ].height = dds.height;
+	};
 
-						}
+	this.setDepthWrite = function ( depthWrite ) {
 
-					}
+		if ( currentDepthWrite !== depthWrite ) {
 
+			gl.depthMask( depthWrite );
+			currentDepthWrite = depthWrite;
 
-				} else {
-					texture.image.width = dds.width;
-					texture.image.height = dds.height;
-					texture.mipmaps = dds.mipmaps;
-				}
+		}
 
-				texture.format = dds.format;
-				texture.needsUpdate = true;
-				if ( onLoad ) onLoad( texture );
+	};
 
-			}
+	this.setColorWrite = function ( colorWrite ) {
 
-			request.onerror = onError;
+		if ( currentColorWrite !== colorWrite ) {
 
-			request.open( 'GET', url, true );
-			request.responseType = "arraybuffer";
-			request.send( null );
+			gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
+			currentColorWrite = colorWrite;
 
 		}
 
-		return texture;
+	};
 
-	},
+	this.setDoubleSided = function ( doubleSided ) {
 
-	parseDDS: function ( buffer, loadMipmaps ) {
+		if ( currentDoubleSided !== doubleSided ) {
 
-		var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
+			if ( doubleSided ) {
 
-		// Adapted from @toji's DDS utils
-		//	https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
+				gl.disable( gl.CULL_FACE );
 
-		// All values and structures referenced from:
-		// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
+			} else {
 
-		var DDS_MAGIC = 0x20534444;
+				gl.enable( gl.CULL_FACE );
 
-		var DDSD_CAPS = 0x1,
-			DDSD_HEIGHT = 0x2,
-			DDSD_WIDTH = 0x4,
-			DDSD_PITCH = 0x8,
-			DDSD_PIXELFORMAT = 0x1000,
-			DDSD_MIPMAPCOUNT = 0x20000,
-			DDSD_LINEARSIZE = 0x80000,
-			DDSD_DEPTH = 0x800000;
+			}
 
-		var DDSCAPS_COMPLEX = 0x8,
-			DDSCAPS_MIPMAP = 0x400000,
-			DDSCAPS_TEXTURE = 0x1000;
+			currentDoubleSided = doubleSided;
 
-		var DDSCAPS2_CUBEMAP = 0x200,
-			DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
-			DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
-			DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
-			DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
-			DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
-			DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
-			DDSCAPS2_VOLUME = 0x200000;
+		}
 
-		var DDPF_ALPHAPIXELS = 0x1,
-			DDPF_ALPHA = 0x2,
-			DDPF_FOURCC = 0x4,
-			DDPF_RGB = 0x40,
-			DDPF_YUV = 0x200,
-			DDPF_LUMINANCE = 0x20000;
+	};
 
-		function fourCCToInt32( value ) {
+	this.setFlipSided = function ( flipSided ) {
 
-			return value.charCodeAt(0) +
-				(value.charCodeAt(1) << 8) +
-				(value.charCodeAt(2) << 16) +
-				(value.charCodeAt(3) << 24);
+		if ( currentFlipSided !== flipSided ) {
 
-		}
+			if ( flipSided ) {
 
-		function int32ToFourCC( value ) {
+				gl.frontFace( gl.CW );
 
-			return String.fromCharCode(
-				value & 0xff,
-				(value >> 8) & 0xff,
-				(value >> 16) & 0xff,
-				(value >> 24) & 0xff
-			);
-		}
+			} else {
+
+				gl.frontFace( gl.CCW );
 
-		function loadARGBMip( buffer, dataOffset, width, height ) {
-			var dataLength = width*height*4;
-			var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
-			var byteArray = new Uint8Array( dataLength );
-			var dst = 0;
-			var src = 0;
-			for ( var y = 0; y < height; y++ ) {
-				for ( var x = 0; x < width; x++ ) {
-					var b = srcBuffer[src]; src++;
-					var g = srcBuffer[src]; src++;
-					var r = srcBuffer[src]; src++;
-					var a = srcBuffer[src]; src++;
-					byteArray[dst] = r; dst++;	//r
-					byteArray[dst] = g; dst++;	//g
-					byteArray[dst] = b; dst++;	//b
-					byteArray[dst] = a; dst++;	//a
-				}
 			}
-			return byteArray;
+
+			currentFlipSided = flipSided;
+
 		}
 
-		var FOURCC_DXT1 = fourCCToInt32("DXT1");
-		var FOURCC_DXT3 = fourCCToInt32("DXT3");
-		var FOURCC_DXT5 = fourCCToInt32("DXT5");
+	};
 
-		var headerLengthInt = 31; // The header length in 32 bit ints
+	this.setLineWidth = function ( width ) {
 
-		// Offsets into the header array
+		if ( width !== currentLineWidth ) {
 
-		var off_magic = 0;
+			gl.lineWidth( width );
 
-		var off_size = 1;
-		var off_flags = 2;
-		var off_height = 3;
-		var off_width = 4;
+			currentLineWidth = width;
 
-		var off_mipmapCount = 7;
+		}
 
-		var off_pfFlags = 20;
-		var off_pfFourCC = 21;
-		var off_RGBBitCount = 22;
-		var off_RBitMask = 23;
-		var off_GBitMask = 24;
-		var off_BBitMask = 25;
-		var off_ABitMask = 26;
+	};
 
-		var off_caps = 27;
-		var off_caps2 = 28;
-		var off_caps3 = 29;
-		var off_caps4 = 30;
+	this.setPolygonOffset = function ( polygonoffset, factor, units ) {
 
-		// Parse header
+		if ( currentPolygonOffset !== polygonoffset ) {
 
-		var header = new Int32Array( buffer, 0, headerLengthInt );
+			if ( polygonoffset ) {
 
-		if ( header[ off_magic ] !== DDS_MAGIC ) {
+				gl.enable( gl.POLYGON_OFFSET_FILL );
 
-			console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" );
-			return dds;
+			} else {
 
-		}
+				gl.disable( gl.POLYGON_OFFSET_FILL );
 
-		if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
+			}
 
-			console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" );
-			return dds;
+			currentPolygonOffset = polygonoffset;
 
 		}
 
-		var blockBytes;
+		if ( polygonoffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {
 
-		var fourCC = header[ off_pfFourCC ];
+			gl.polygonOffset( factor, units );
 
-		var isRGBAUncompressed = false;
+			currentPolygonOffsetFactor = factor;
+			currentPolygonOffsetUnits = units;
 
-		switch ( fourCC ) {
+		}
 
-			case FOURCC_DXT1:
+	};
 
-				blockBytes = 8;
-				dds.format = THREE.RGB_S3TC_DXT1_Format;
-				break;
+	this.reset = function () {
 
-			case FOURCC_DXT3:
+		for ( var i = 0; i < enabledAttributes.length; i ++ ) {
 
-				blockBytes = 16;
-				dds.format = THREE.RGBA_S3TC_DXT3_Format;
-				break;
+			enabledAttributes[ i ] = 0;
 
-			case FOURCC_DXT5:
+		}
 
-				blockBytes = 16;
-				dds.format = THREE.RGBA_S3TC_DXT5_Format;
-				break;
+		currentBlending = null;
+		currentDepthTest = null;
+		currentDepthWrite = null;
+		currentColorWrite = null;
+		currentDoubleSided = null;
+		currentFlipSided = null;
 
-			default:
+	};
 
-				if( header[off_RGBBitCount] ==32
-					&& header[off_RBitMask]&0xff0000
-					&& header[off_GBitMask]&0xff00
-					&& header[off_BBitMask]&0xff
-					&& header[off_ABitMask]&0xff000000  ) {
-					isRGBAUncompressed = true;
-					blockBytes = 64;
-					dds.format = THREE.RGBAFormat;
-				} else {
-					console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) );
-					return dds;
-				}
-		}
+};
 
-		dds.mipmapCount = 1;
+// File:src/renderers/webgl/plugins/LensFlarePlugin.js
 
-		if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
+THREE.LensFlarePlugin = function ( renderer, flares ) {
 
-		}
+	var gl = renderer.context;
 
-		//TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
+	var vertexBuffer, elementBuffer;
+	var program, attributes, uniforms;
+	var hasVertexTexture;
 
-		dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
+	var tempTexture, occlusionTexture;
 
-		dds.width = header[ off_width ];
-		dds.height = header[ off_height ];
+	var init = function () {
 
-		var dataOffset = header[ off_size ] + 4;
+		var vertices = new Float32Array( [
+			-1, -1,  0, 0,
+			 1, -1,  1, 0,
+			 1,  1,  1, 1,
+			-1,  1,  0, 1
+		] );
 
-		// Extract mipmaps buffers
+		var faces = new Uint16Array( [
+			0, 1, 2,
+			0, 2, 3
+		] );
 
-		var width = dds.width;
-		var height = dds.height;
+		// buffers
 
-		var faces = dds.isCubemap ? 6 : 1;
+		vertexBuffer     = gl.createBuffer();
+		elementBuffer    = gl.createBuffer();
 
-		for ( var face = 0; face < faces; face ++ ) {
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
 
-			for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
 
-				if( isRGBAUncompressed ) {
-					var byteArray = loadARGBMip( buffer, dataOffset, width, height );
-					var dataLength = byteArray.length;
-				} else {
-					var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
-					var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
-				}
+		// textures
 
-				var mipmap = { "data": byteArray, "width": width, "height": height };
-				dds.mipmaps.push( mipmap );
+		tempTexture      = gl.createTexture();
+		occlusionTexture = gl.createTexture();
 
-				dataOffset += dataLength;
+		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 );
 
-				width = Math.max( width * 0.5, 1 );
-				height = Math.max( height * 0.5, 1 );
+		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 );
 
-			}
+		hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
 
-			width = dds.width;
-			height = dds.height;
+		var shader;
 
-		}
+		if ( hasVertexTexture ) {
 
-		return dds;
+			shader = {
 
-	},
+				vertexShader: [
 
-	getNormalMap: function ( image, depth ) {
+					"uniform lowp int renderType;",
 
-		// Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
+					"uniform vec3 screenPosition;",
+					"uniform vec2 scale;",
+					"uniform float rotation;",
 
-		var cross = function ( a, b ) {
+					"uniform sampler2D occlusionMap;",
 
-			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 ] ];
+					"attribute vec2 position;",
+					"attribute vec2 uv;",
 
-		}
+					"varying vec2 vUV;",
+					"varying float vVisibility;",
 
-		var subtract = function ( a, b ) {
+					"void main() {",
 
-			return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
+						"vUV = uv;",
 
-		}
+						"vec2 pos = position;",
 
-		var normalize = function ( a ) {
+						"if( renderType == 2 ) {",
 
-			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 ];
+							"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 ) );",
 
-		}
+							"vVisibility =        visibility.r / 9.0;",
+							"vVisibility *= 1.0 - visibility.g / 9.0;",
+							"vVisibility *=       visibility.b / 9.0;",
+							"vVisibility *= 1.0 - visibility.a / 9.0;",
 
-		depth = depth | 1;
+							"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+							"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
 
-		var width = image.width;
-		var height = image.height;
+						"}",
 
-		var canvas = document.createElement( 'canvas' );
-		canvas.width = width;
-		canvas.height = height;
+						"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
 
-		var context = canvas.getContext( '2d' );
-		context.drawImage( image, 0, 0 );
+					"}"
 
-		var data = context.getImageData( 0, 0, width, height ).data;
-		var imageData = context.createImageData( width, height );
-		var output = imageData.data;
+				].join( "\n" ),
 
-		for ( var x = 0; x < width; x ++ ) {
+				fragmentShader: [
 
-			for ( var y = 0; y < height; y ++ ) {
+					"uniform lowp int renderType;",
 
-				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;
+					"uniform sampler2D map;",
+					"uniform float opacity;",
+					"uniform vec3 color;",
 
-				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 ] );
+					"varying vec2 vUV;",
+					"varying float vVisibility;",
 
-				var normals = [];
-				var num_points = points.length;
+					"void main() {",
 
-				for ( var i = 0; i < num_points; i ++ ) {
+						// pink square
 
-					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 ) ) );
+						"if( renderType == 0 ) {",
 
-				}
+							"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
 
-				var normal = [ 0, 0, 0 ];
+						// restore
 
-				for ( var i = 0; i < normals.length; i ++ ) {
+						"} else if( renderType == 1 ) {",
 
-					normal[ 0 ] += normals[ i ][ 0 ];
-					normal[ 1 ] += normals[ i ][ 1 ];
-					normal[ 2 ] += normals[ i ][ 2 ];
+							"gl_FragColor = texture2D( map, vUV );",
 
-				}
+						// flare
 
-				normal[ 0 ] /= normals.length;
-				normal[ 1 ] /= normals.length;
-				normal[ 2 ] /= normals.length;
+						"} else {",
 
-				var idx = ( y * width + x ) * 4;
+							"vec4 texture = texture2D( map, vUV );",
+							"texture.a *= opacity * vVisibility;",
+							"gl_FragColor = texture;",
+							"gl_FragColor.rgb *= color;",
 
-				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;
+		} else {
 
-	},
+			shader = {
 
-	generateDataTexture: function ( width, height, color ) {
+				vertexShader: [
 
-		var size = width * height;
-		var data = new Uint8Array( 3 * size );
+					"uniform lowp int renderType;",
 
-		var r = Math.floor( color.r * 255 );
-		var g = Math.floor( color.g * 255 );
-		var b = Math.floor( color.b * 255 );
+					"uniform vec3 screenPosition;",
+					"uniform vec2 scale;",
+					"uniform float rotation;",
 
-		for ( var i = 0; i < size; i ++ ) {
+					"attribute vec2 position;",
+					"attribute vec2 uv;",
 
-			data[ i * 3 ] 	  = r;
-			data[ i * 3 + 1 ] = g;
-			data[ i * 3 + 2 ] = b;
+					"varying vec2 vUV;",
 
-		}
+					"void main() {",
 
-		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
-		texture.needsUpdate = true;
+						"vUV = uv;",
 
-		return texture;
+						"vec2 pos = position;",
 
-	}
+						"if( renderType == 2 ) {",
 
-};
+							"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+							"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+						"}",
 
-THREE.SceneUtils = {
+						"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
 
-	createMultiMaterialObject: function ( geometry, materials ) {
+					"}"
 
-		var group = new THREE.Object3D();
+				].join( "\n" ),
 
-		for ( var i = 0, l = materials.length; i < l; i ++ ) {
+				fragmentShader: [
 
-			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+					"precision mediump float;",
 
-		}
+					"uniform lowp int renderType;",
 
-		return group;
+					"uniform sampler2D map;",
+					"uniform sampler2D occlusionMap;",
+					"uniform float opacity;",
+					"uniform vec3 color;",
 
-	},
+					"varying vec2 vUV;",
 
-	detach : function ( child, parent, scene ) {
+					"void main() {",
 
-		child.applyMatrix( parent.matrixWorld );
-		parent.remove( child );
-		scene.add( child );
+						// pink square
 
-	},
+						"if( renderType == 0 ) {",
 
-	attach: function ( child, scene, parent ) {
+							"gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
 
-		var matrixWorldInverse = new THREE.Matrix4();
-		matrixWorldInverse.getInverse( parent.matrixWorld );
-		child.applyMatrix( matrixWorldInverse );
+						// restore
 
-		scene.remove( child );
-		parent.add( child );
+						"} else if( renderType == 1 ) {",
 
-	}
+							"gl_FragColor = texture2D( map, vUV );",
 
-};
+						// flare
 
-/**
- * @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/
- *
- */
+						"} else {",
 
-THREE.FontUtils = {
+							"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 );",
 
-	faces : {},
+							"vec4 texture = texture2D( map, vUV );",
+							"texture.a *= opacity * visibility;",
+							"gl_FragColor = texture;",
+							"gl_FragColor.rgb *= color;",
 
-	// Just for now. face[weight][style]
+						"}",
 
-	face : "helvetiker",
-	weight: "normal",
-	style : "normal",
-	size : 150,
-	divisions : 10,
+					"}"
 
-	getFace : function() {
+				].join( "\n" )
 
-		return this.faces[ this.face ][ this.weight ][ this.style ];
+			};
 
-	},
+		}
 
-	loadFace : function( data ) {
+		program = createProgram( shader );
 
-		var family = data.familyName.toLowerCase();
+		attributes = {
+			vertex: gl.getAttribLocation ( program, "position" ),
+			uv:     gl.getAttribLocation ( program, "uv" )
+		}
 
-		var ThreeFont = this;
+		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" )
+		};
 
-		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
+	};
 
-		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
-		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+	/*
+	 * Render lens flares
+	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
+	 *         reads these back and calculates occlusion.
+	 */
 
-		var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
 
-		return data;
+		if ( flares.length === 0 ) return;
 
-	},
+		var tempPosition = new THREE.Vector3();
 
-	drawText : function( text ) {
+		var invAspect = viewportHeight / viewportWidth,
+			halfViewportWidth = viewportWidth * 0.5,
+			halfViewportHeight = viewportHeight * 0.5;
 
-		var characterPts = [], allPts = [];
+		var size = 16 / viewportHeight,
+			scale = new THREE.Vector2( size * invAspect, size );
 
-		// RenderText
+		var screenPosition = new THREE.Vector3( 1, 1, 0 ),
+			screenPositionPixels = new THREE.Vector2( 1, 1 );
 
-		var i, p,
-			face = this.getFace(),
-			scale = this.size / face.resolution,
-			offset = 0,
-			chars = String( text ).split( '' ),
-			length = chars.length;
+		if ( program === undefined ) {
 
-		var fontPaths = [];
+			init();
 
-		for ( i = 0; i < length; i ++ ) {
+		}
 
-			var path = new THREE.Path();
+		gl.useProgram( program );
 
-			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
-			offset += ret.offset;
+		gl.enableVertexAttribArray( attributes.vertex );
+		gl.enableVertexAttribArray( attributes.uv );
 
-			fontPaths.push( ret.path );
+		// loop through all lens flares to update their occlusion and positions
+		// setup gl and common used attribs/unforms
 
-		}
+		gl.uniform1i( uniforms.occlusionMap, 0 );
+		gl.uniform1i( uniforms.map, 1 );
 
-		// get the width
+		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 );
 
-		var width = offset / 2;
-		//
-		// for ( p = 0; p < allPts.length; p++ ) {
-		//
-		// 	allPts[ p ].x -= width;
-		//
-		// }
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
 
-		//var extract = this.extractPoints( allPts, characterPts );
-		//extract.contour = allPts;
+		gl.disable( gl.CULL_FACE );
+		gl.depthMask( false );
 
-		//extract.paths = fontPaths;
-		//extract.offset = width;
+		for ( var i = 0, l = flares.length; i < l; i ++ ) {
 
-		return { paths : fontPaths, offset : width };
+			size = 16 / viewportHeight;
+			scale.set( size * invAspect, size );
 
-	},
+			// calc object screen position
 
+			var flare = flares[ i ];
 
+			tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
 
+			tempPosition.applyMatrix4( camera.matrixWorldInverse );
+			tempPosition.applyProjection( camera.projectionMatrix );
 
-	extractGlyphPoints : function( c, face, scale, offset, path ) {
+			// setup arrays for gl programs
 
-		var pts = [];
+			screenPosition.copy( tempPosition )
 
-		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[ '?' ];
+			screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
+			screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
 
-		if ( !glyph ) return;
+			// screen cull
 
-		if ( glyph.o ) {
+			if ( hasVertexTexture || (
+				screenPositionPixels.x > 0 &&
+				screenPositionPixels.x < viewportWidth &&
+				screenPositionPixels.y > 0 &&
+				screenPositionPixels.y < viewportHeight ) ) {
 
-			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
-			length = outline.length;
+				// save current RGB to temp texture
 
-			scaleX = scale;
-			scaleY = scale;
+				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 );
 
-			for ( i = 0; i < length; ) {
 
-				action = outline[ i ++ ];
+				// render pink quad
 
-				//console.log( action );
+				gl.uniform1i( uniforms.renderType, 0 );
+				gl.uniform2f( uniforms.scale, scale.x, scale.y );
+				gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
 
-				switch( action ) {
+				gl.disable( gl.BLEND );
+				gl.enable( gl.DEPTH_TEST );
 
-				case 'm':
+				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-					// Move To
 
-					x = outline[ i++ ] * scaleX + offset;
-					y = outline[ i++ ] * scaleY;
+				// copy result to occlusionMap
 
-					path.moveTo( x, y );
-					break;
+				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 );
 
-				case 'l':
 
-					// Line To
+				// restore graphics
 
-					x = outline[ i++ ] * scaleX + offset;
-					y = outline[ i++ ] * scaleY;
-					path.lineTo(x,y);
-					break;
+				gl.uniform1i( uniforms.renderType, 1 );
+				gl.disable( gl.DEPTH_TEST );
 
-				case 'q':
+				gl.activeTexture( gl.TEXTURE1 );
+				gl.bindTexture( gl.TEXTURE_2D, tempTexture );
+				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-					// QuadraticCurveTo
 
-					cpx  = outline[ i++ ] * scaleX + offset;
-					cpy  = outline[ i++ ] * scaleY;
-					cpx1 = outline[ i++ ] * scaleX + offset;
-					cpy1 = outline[ i++ ] * scaleY;
+				// update object positions
 
-					path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
+				flare.positionScreen.copy( screenPosition )
 
-					laste = pts[ pts.length - 1 ];
+				if ( flare.customUpdateCallback ) {
 
-					if ( laste ) {
+					flare.customUpdateCallback( flare );
 
-						cpx0 = laste.x;
-						cpy0 = laste.y;
+				} else {
 
-						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+					flare.updateLensFlares();
 
-							var t = i2 / divisions;
-							var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
-							var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
-					  }
+				}
 
-				  }
+				// render flares
 
-				  break;
+				gl.uniform1i( uniforms.renderType, 2 );
+				gl.enable( gl.BLEND );
 
-				case 'b':
+				for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
 
-					// Cubic Bezier Curve
+					var sprite = flare.lensFlares[ j ];
 
-					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 ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
 
-					path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
+						screenPosition.x = sprite.x;
+						screenPosition.y = sprite.y;
+						screenPosition.z = sprite.z;
 
-					laste = pts[ pts.length - 1 ];
+						size = sprite.size * sprite.scale / viewportHeight;
 
-					if ( laste ) {
+						scale.x = size * invAspect;
+						scale.y = size;
 
-						cpx0 = laste.x;
-						cpy0 = laste.y;
+						gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+						gl.uniform2f( uniforms.scale, scale.x, scale.y );
+						gl.uniform1f( uniforms.rotation, sprite.rotation );
 
-						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+						gl.uniform1f( uniforms.opacity, sprite.opacity );
+						gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
 
-							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 );
+						renderer.state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
+						renderer.setTexture( sprite.texture, 1 );
 
-						}
+						gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
 					}
 
-					break;
-
 				}
 
 			}
-		}
-
-
-
-		return { offset: glyph.ha*scale, path:path};
-	}
 
-};
+		}
 
+		// restore gl
 
-THREE.FontUtils.generateShapes = function( text, parameters ) {
+		gl.enable( gl.CULL_FACE );
+		gl.enable( gl.DEPTH_TEST );
+		gl.depthMask( true );
 
-	// Parameters
+		renderer.resetGLState();
 
-	parameters = parameters || {};
+	};
 
-	var size = parameters.size !== undefined ? parameters.size : 100;
-	var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4;
+	function createProgram ( shader ) {
 
-	var font = parameters.font !== undefined ? parameters.font : "helvetiker";
-	var weight = parameters.weight !== undefined ? parameters.weight : "normal";
-	var style = parameters.style !== undefined ? parameters.style : "normal";
+		var program = gl.createProgram();
 
-	THREE.FontUtils.size = size;
-	THREE.FontUtils.divisions = curveSegments;
+		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
 
-	THREE.FontUtils.face = font;
-	THREE.FontUtils.weight = weight;
-	THREE.FontUtils.style = style;
+		var prefix = "precision " + renderer.getPrecision() + " float;\n";
 
-	// Get a Font data json object
+		gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+		gl.shaderSource( vertexShader, prefix + shader.vertexShader );
 
-	var data = THREE.FontUtils.drawText( text );
+		gl.compileShader( fragmentShader );
+		gl.compileShader( vertexShader );
 
-	var paths = data.paths;
-	var shapes = [];
+		gl.attachShader( program, fragmentShader );
+		gl.attachShader( program, vertexShader );
 
-	for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+		gl.linkProgram( program );
 
-		Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+		return program;
 
 	}
 
-	return shapes;
-
 };
 
+// File:src/renderers/webgl/plugins/ShadowMapPlugin.js
 
 /**
- * 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
- *
+ * @author alteredq / http://alteredqualia.com/
  */
 
+THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) {
 
-( function( namespace ) {
+	var _gl = _renderer.context;
 
-	var EPSILON = 0.0000000001;
+	var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
 
-	// takes in an contour array and returns
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4(),
 
-	var process = function( contour, indices ) {
+	_min = new THREE.Vector3(),
+	_max = new THREE.Vector3(),
 
-		var n = contour.length;
+	_matrixPosition = new THREE.Vector3(),
 
-		if ( n < 3 ) return null;
+	_renderList = [];
 
-		var result = [],
-			verts = [],
-			vertIndices = [];
+	// init
 
-		/* we want a counter-clockwise polygon in verts */
+	var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+	var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
 
-		var u, v, w;
+	_depthMaterial = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader
+	 } );
 
-		if ( area( contour ) > 0.0 ) {
+	_depthMaterialMorph = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		morphTargets: true
+	} );
 
-			for ( v = 0; v < n; v++ ) verts[ v ] = v;
+	_depthMaterialSkin = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		skinning: true
+	} );
 
-		} else {
+	_depthMaterialMorphSkin = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		morphTargets: true,
+		skinning: true
+	} );
 
-			for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
+	_depthMaterial._shadowPass = true;
+	_depthMaterialMorph._shadowPass = true;
+	_depthMaterialSkin._shadowPass = true;
+	_depthMaterialMorphSkin._shadowPass = true;
 
-		}
+	this.render = function ( scene, camera ) {
 
-		var nv = n;
+		if ( _renderer.shadowMapEnabled === false ) return;
 
-		/*  remove nv - 2 vertices, creating 1 triangle every time */
+		var i, il, j, jl, n,
 
-		var count = 2 * nv;   /* error detection */
+		shadowMap, shadowMatrix, shadowCamera,
+		buffer, material,
+		webglObject, object, light,
 
-		for( v = nv - 1; nv > 2; ) {
+		lights = [],
+		k = 0,
 
-			/* if we loop, it is probably a non-simple polygon */
+		fog = null;
 
-			if ( ( count-- ) <= 0 ) {
+		// set GL state for depth map
 
-				//** Triangulate: ERROR - probable bad polygon!
+		_gl.clearColor( 1, 1, 1, 1 );
+		_gl.disable( _gl.BLEND );
 
-				//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!" );
+		_gl.enable( _gl.CULL_FACE );
+		_gl.frontFace( _gl.CCW );
 
-				if ( indices ) return vertIndices;
-				return result;
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
 
-			}
+			_gl.cullFace( _gl.FRONT );
 
-			/* three consecutive vertices in current polygon, <u,v,w> */
+		} else {
 
-			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     */
+			_gl.cullFace( _gl.BACK );
 
-			if ( snip( contour, u, v, w, nv, verts ) ) {
+		}
 
-				var a, b, c, s, t;
+		_renderer.state.setDepthTest( true );
 
-				/* true names of the vertices */
+		// preprocess lights
+		// 	- skip lights that are not casting shadows
+		//	- create virtual lights for cascaded shadow maps
 
-				a = verts[ u ];
-				b = verts[ v ];
-				c = verts[ w ];
+		for ( i = 0, il = _lights.length; i < il; i ++ ) {
 
-				/* output Triangle */
+			light = _lights[ i ];
 
-				result.push( [ contour[ a ],
-					contour[ b ],
-					contour[ c ] ] );
+			if ( ! light.castShadow ) continue;
 
+			if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
 
-				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
+				for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
 
-				/* remove v from the remaining polygon */
+					var virtualLight;
 
-				for( s = v, t = v + 1; t < nv; s++, t++ ) {
+					if ( ! light.shadowCascadeArray[ n ] ) {
 
-					verts[ s ] = verts[ t ];
+						virtualLight = createVirtualLight( light, n );
+						virtualLight.originalCamera = camera;
 
-				}
+						var gyro = new THREE.Gyroscope();
+						gyro.position.copy( light.shadowCascadeOffset );
 
-				nv--;
+						gyro.add( virtualLight );
+						gyro.add( virtualLight.target );
 
-				/* reset error detection counter */
+						camera.add( gyro );
 
-				count = 2 * nv;
+						light.shadowCascadeArray[ n ] = virtualLight;
 
-			}
+						//console.log( "Created virtualLight", virtualLight );
 
-		}
+					} else {
 
-		if ( indices ) return vertIndices;
-		return result;
+						virtualLight = light.shadowCascadeArray[ n ];
 
-	};
+					}
 
-	// calculate area of the contour polygon
+					updateVirtualLight( light, n );
 
-	var area = function ( contour ) {
+					lights[ k ] = virtualLight;
+					k ++;
 
-		var n = contour.length;
-		var a = 0.0;
+				}
 
-		for( var p = n - 1, q = 0; q < n; p = q++ ) {
+			} else {
 
-			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
+				lights[ k ] = light;
+				k ++;
+
+			}
 
 		}
 
-		return a * 0.5;
+		// render depth map
 
-	};
+		for ( i = 0, il = lights.length; i < il; i ++ ) {
 
-	var snip = function ( contour, u, v, w, n, verts ) {
+			light = lights[ i ];
 
-		var p;
-		var ax, ay, bx, by;
-		var cx, cy, px, py;
+			if ( ! light.shadowMap ) {
 
-		ax = contour[ verts[ u ] ].x;
-		ay = contour[ verts[ u ] ].y;
+				var shadowFilter = THREE.LinearFilter;
 
-		bx = contour[ verts[ v ] ].x;
-		by = contour[ verts[ v ] ].y;
+				if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
 
-		cx = contour[ verts[ w ] ].x;
-		cy = contour[ verts[ w ] ].y;
+					shadowFilter = THREE.NearestFilter;
 
-		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;
+				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
 
-		aX = cx - bx;  aY = cy - by;
-		bX = ax - cx;  bY = ay - cy;
-		cX = bx - ax;  cY = by - ay;
+				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
+				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
+
+				light.shadowMatrix = new THREE.Matrix4();
 
-		for ( p = 0; p < n; p++ ) {
+			}
 
-			if( (p === u) || (p === v) || (p === w) ) continue;
+			if ( ! light.shadowCamera ) {
 
-			px = contour[ verts[ p ] ].x
-			py = contour[ verts[ p ] ].y
+				if ( light instanceof THREE.SpotLight ) {
 
-			apx = px - ax;  apy = py - ay;
-			bpx = px - bx;  bpy = py - by;
-			cpx = px - cx;  cpy = py - cy;
+					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
 
-			// see if p is inside triangle abc
+				} else if ( light instanceof THREE.DirectionalLight ) {
 
-			aCROSSbp = aX*bpy - aY*bpx;
-			cCROSSap = cX*apy - cY*apx;
-			bCROSScp = bX*cpy - bY*cpx;
+					light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
 
-			if ( (aCROSSbp >= -EPSILON) && (bCROSScp >= -EPSILON) && (cCROSSap >= -EPSILON) ) return false;
+				} else {
 
-		}
+					THREE.error( "THREE.ShadowMapPlugin: Unsupported light type for shadow", light );
+					continue;
 
-		return true;
+				}
 
-	};
+				scene.add( light.shadowCamera );
 
+				if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
 
-	namespace.Triangulate = process;
-	namespace.Triangulate.area = area;
+			}
 
-	return namespace;
+			if ( light.shadowCameraVisible && ! light.cameraHelper ) {
 
-})(THREE.FontUtils);
+				light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
+				scene.add( light.cameraHelper );
 
-// 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;
+			}
 
-/**
- * @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
- *
- **/
+			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
 
-/**************************************************************
- *	Abstract Curve base class
- **************************************************************/
+				updateShadowCamera( camera, light );
 
-THREE.Curve = function () {
+			}
 
-};
+			shadowMap = light.shadowMap;
+			shadowMatrix = light.shadowMatrix;
+			shadowCamera = light.shadowCamera;
 
-// Virtual base class method to overwrite and implement in subclasses
-//	- t [0 .. 1]
+			//
 
-THREE.Curve.prototype.getPoint = function ( t ) {
+			shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
+			_matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
+			shadowCamera.lookAt( _matrixPosition );
+			shadowCamera.updateMatrixWorld();
 
-	console.log( "Warning, getPoint() not implemented!" );
-	return null;
+			shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
 
-};
+			//
 
-// Get point at relative position in curve according to arc length
-// - u [0 .. 1]
+			if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
+			if ( light.shadowCameraVisible ) light.cameraHelper.update();
 
-THREE.Curve.prototype.getPointAt = function ( u ) {
+			// compute shadow matrix
 
-	var t = this.getUtoTmapping( u );
-	return this.getPoint( t );
+			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 );
 
-// Get sequence of points using getPoint( t )
+			// update camera matrices and frustum
 
-THREE.Curve.prototype.getPoints = function ( divisions ) {
+			_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+			_frustum.setFromMatrix( _projScreenMatrix );
 
-	if ( !divisions ) divisions = 5;
+			// render shadow map
 
-	var d, pts = [];
+			_renderer.setRenderTarget( shadowMap );
+			_renderer.clear();
 
-	for ( d = 0; d <= divisions; d ++ ) {
+			// set object matrices & frustum culling
 
-		pts.push( this.getPoint( d / divisions ) );
+			_renderList.length = 0;
 
-	}
+			projectObject( scene, scene, shadowCamera );
 
-	return pts;
 
-};
+			// render regular objects
 
-// Get sequence of points using getPointAt( u )
+			var objectMaterial, useMorphing, useSkinning;
 
-THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
+			for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
 
-	if ( !divisions ) divisions = 5;
+				webglObject = _renderList[ j ];
 
-	var d, pts = [];
+				object = webglObject.object;
+				buffer = webglObject.buffer;
 
-	for ( d = 0; d <= divisions; d ++ ) {
+				// culling is overriden globally for all objects
+				// while rendering depth map
 
-		pts.push( this.getPointAt( d / divisions ) );
+				// 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)
 
-	}
+				objectMaterial = getObjectMaterial( object );
 
-	return pts;
+				useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
+				useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
 
-};
+				if ( object.customDepthMaterial ) {
 
-// Get total curve arc length
+					material = object.customDepthMaterial;
 
-THREE.Curve.prototype.getLength = function () {
+				} else if ( useSkinning ) {
 
-	var lengths = this.getLengths();
-	return lengths[ lengths.length - 1 ];
+					material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
 
-};
+				} else if ( useMorphing ) {
 
-// Get list of cumulative segment lengths
+					material = _depthMaterialMorph;
 
-THREE.Curve.prototype.getLengths = function ( divisions ) {
+				} else {
 
-	if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
+					material = _depthMaterial;
 
-	if ( this.cacheArcLengths
-		&& ( this.cacheArcLengths.length == divisions + 1 )
-		&& !this.needsUpdate) {
+				}
 
-		//console.log( "cached", this.cacheArcLengths );
-		return this.cacheArcLengths;
+				_renderer.setMaterialFaces( objectMaterial );
 
-	}
+				if ( buffer instanceof THREE.BufferGeometry ) {
 
-	this.needsUpdate = false;
+					_renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object );
 
-	var cache = [];
-	var current, last = this.getPoint( 0 );
-	var p, sum = 0;
+				} else {
 
-	cache.push( 0 );
+					_renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object );
 
-	for ( p = 1; p <= divisions; p ++ ) {
+				}
 
-		current = this.getPoint ( p / divisions );
-		sum += current.distanceTo( last );
-		cache.push( sum );
-		last = current;
+			}
 
-	}
+			// set matrices and render immediate objects
 
-	this.cacheArcLengths = cache;
+			for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
 
-	return cache; // { sums: cache, sum:sum }; Sum is in the last element.
+				webglObject = _webglObjectsImmediate[ j ];
+				object = webglObject.object;
 
-};
+				if ( object.visible && object.castShadow ) {
 
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
 
-THREE.Curve.prototype.updateArcLengths = function() {
-	this.needsUpdate = true;
-	this.getLengths();
-};
+					_renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object );
 
-// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
+				}
 
-THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
+			}
 
-	var arcLengths = this.getLengths();
+		}
 
-	var i = 0, il = arcLengths.length;
+		// restore GL state
 
-	var targetArcLength; // The targeted u distance value to get
+		var clearColor = _renderer.getClearColor(),
+		clearAlpha = _renderer.getClearAlpha();
 
-	if ( distance ) {
+		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
+		_gl.enable( _gl.BLEND );
 
-		targetArcLength = distance;
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
 
-	} else {
+			_gl.cullFace( _gl.BACK );
 
-		targetArcLength = u * arcLengths[ il - 1 ];
+		}
 
-	}
+		_renderer.resetGLState();
 
-	//var time = Date.now();
+	};
 
-	// binary search for the index with largest value smaller than target u distance
+	function projectObject( scene, object, shadowCamera ) {
 
-	var low = 0, high = il - 1, comparison;
+		if ( object.visible ) {
 
-	while ( low <= high ) {
+			var webglObjects = _webglObjects[ object.id ];
 
-		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
+			if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
 
-		comparison = arcLengths[ i ] - targetArcLength;
+				for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
 
-		if ( comparison < 0 ) {
+					var webglObject = webglObjects[ i ];
 
-			low = i + 1;
-			continue;
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+					_renderList.push( webglObject );
 
-		} else if ( comparison > 0 ) {
+				}
 
-			high = i - 1;
-			continue;
+			}
 
-		} else {
+			for ( var i = 0, l = object.children.length; i < l; i ++ ) {
 
-			high = i;
-			break;
+				projectObject( scene, object.children[ i ], shadowCamera );
 
-			// DONE
+			}
 
 		}
 
 	}
 
-	i = high;
+	function createVirtualLight( light, cascade ) {
 
-	//console.log('b' , i, low, high, Date.now()- time);
+		var virtualLight = new THREE.DirectionalLight();
 
-	if ( arcLengths[ i ] == targetArcLength ) {
+		virtualLight.isVirtual = true;
 
-		var t = i / ( il - 1 );
-		return t;
+		virtualLight.onlyShadow = true;
+		virtualLight.castShadow = true;
 
-	}
+		virtualLight.shadowCameraNear = light.shadowCameraNear;
+		virtualLight.shadowCameraFar = light.shadowCameraFar;
 
-	// we could get finer grain at lengths, or use simple interpolatation between two points
+		virtualLight.shadowCameraLeft = light.shadowCameraLeft;
+		virtualLight.shadowCameraRight = light.shadowCameraRight;
+		virtualLight.shadowCameraBottom = light.shadowCameraBottom;
+		virtualLight.shadowCameraTop = light.shadowCameraTop;
 
-	var lengthBefore = arcLengths[ i ];
-    var lengthAfter = arcLengths[ i + 1 ];
+		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
 
-    var segmentLength = lengthAfter - lengthBefore;
+		virtualLight.shadowDarkness = light.shadowDarkness;
 
-    // determine where we are between the 'before' and 'after' points
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+		virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
+		virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
 
-    var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+		virtualLight.pointsWorld = [];
+		virtualLight.pointsFrustum = [];
 
-    // add that fractional amount to t
+		var pointsWorld = virtualLight.pointsWorld,
+			pointsFrustum = virtualLight.pointsFrustum;
 
-    var t = ( i + segmentFraction ) / ( il -1 );
+		for ( var i = 0; i < 8; i ++ ) {
 
-	return t;
+			pointsWorld[ i ] = new THREE.Vector3();
+			pointsFrustum[ i ] = new THREE.Vector3();
 
-};
+		}
 
-// 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 nearZ = light.shadowCascadeNearZ[ cascade ];
+		var farZ = light.shadowCascadeFarZ[ cascade ];
 
-THREE.Curve.prototype.getTangent = function( t ) {
+		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 delta = 0.0001;
-	var t1 = t - delta;
-	var t2 = t + delta;
+		pointsFrustum[ 4 ].set( - 1, - 1, farZ );
+		pointsFrustum[ 5 ].set(  1, - 1, farZ );
+		pointsFrustum[ 6 ].set( - 1,  1, farZ );
+		pointsFrustum[ 7 ].set(  1,  1, farZ );
 
-	// Capping in case of danger
+		return virtualLight;
 
-	if ( t1 < 0 ) t1 = 0;
-	if ( t2 > 1 ) t2 = 1;
+	}
 
-	var pt1 = this.getPoint( t1 );
-	var pt2 = this.getPoint( t2 );
+	// Synchronize virtual light with the original light
 
-	var vec = pt2.clone().sub(pt1);
-	return vec.normalize();
+	function updateVirtualLight( light, cascade ) {
+
+		var virtualLight = light.shadowCascadeArray[ cascade ];
 
-};
+		virtualLight.position.copy( light.position );
+		virtualLight.target.position.copy( light.target.position );
+		virtualLight.lookAt( virtualLight.target );
 
+		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
+		virtualLight.shadowDarkness = light.shadowDarkness;
 
-THREE.Curve.prototype.getTangentAt = function ( u ) {
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
 
-	var t = this.getUtoTmapping( u );
-	return this.getTangent( t );
+		var nearZ = light.shadowCascadeNearZ[ cascade ];
+		var farZ = light.shadowCascadeFarZ[ cascade ];
 
-};
+		var pointsFrustum = virtualLight.pointsFrustum;
 
+		pointsFrustum[ 0 ].z = nearZ;
+		pointsFrustum[ 1 ].z = nearZ;
+		pointsFrustum[ 2 ].z = nearZ;
+		pointsFrustum[ 3 ].z = nearZ;
 
+		pointsFrustum[ 4 ].z = farZ;
+		pointsFrustum[ 5 ].z = farZ;
+		pointsFrustum[ 6 ].z = farZ;
+		pointsFrustum[ 7 ].z = farZ;
 
+	}
 
+	// Fit shadow camera's ortho frustum to camera frustum
 
-/**************************************************************
- *	Utils
- **************************************************************/
+	function updateShadowCamera( camera, light ) {
 
-THREE.Curve.Utils = {
+		var shadowCamera = light.shadowCamera,
+			pointsFrustum = light.pointsFrustum,
+			pointsWorld = light.pointsWorld;
 
-	tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
+		_min.set( Infinity, Infinity, Infinity );
+		_max.set( - Infinity, - Infinity, - Infinity );
 
-		return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+		for ( var i = 0; i < 8; i ++ ) {
 
-	},
+			var p = pointsWorld[ i ];
 
-	// Puay Bing, thanks for helping with this derivative!
+			p.copy( pointsFrustum[ i ] );
+			p.unproject( camera );
 
-	tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
+			p.applyMatrix4( shadowCamera.matrixWorldInverse );
 
-		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;
-	},
+			if ( p.x < _min.x ) _min.x = p.x;
+			if ( p.x > _max.x ) _max.x = p.x;
 
+			if ( p.y < _min.y ) _min.y = p.y;
+			if ( p.y > _max.y ) _max.y = p.y;
 
-	tangentSpline: function ( t, p0, p1, p2, p3 ) {
+			if ( p.z < _min.z ) _min.z = p.z;
+			if ( p.z > _max.z ) _max.z = p.z;
 
-		// 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
+		shadowCamera.left = _min.x;
+		shadowCamera.right = _max.x;
+		shadowCamera.top = _max.y;
+		shadowCamera.bottom = _min.y;
 
-		return h00 + h10 + h01 + h11;
+		// can't really fit near/far
+		//shadowCamera.near = _min.z;
+		//shadowCamera.far = _max.z;
 
-	},
+		shadowCamera.updateProjectionMatrix();
 
-	// Catmull-Rom
+	}
 
-	interpolate: function( p0, p1, p2, p3, t ) {
+	// 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
 
-		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;
+	function getObjectMaterial( object ) {
 
-	}
+		return object.material instanceof THREE.MeshFaceMaterial
+			? object.material.materials[ 0 ]
+			: object.material;
+
+	};
 
 };
 
+// File:src/renderers/webgl/plugins/SpritePlugin.js
 
-// TODO: Transformation for Curves?
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-/**************************************************************
- *	3D Curves
- **************************************************************/
+THREE.SpritePlugin = function ( renderer, sprites ) {
 
-// A Factory method for creating new curve subclasses
+	var gl = renderer.context;
 
-THREE.Curve.create = function ( constructor, getPointFunc ) {
+	var vertexBuffer, elementBuffer;
+	var program, attributes, uniforms;
 
-	constructor.prototype = Object.create( THREE.Curve.prototype );
-	constructor.prototype.getPoint = getPointFunc;
+	var texture;
 
-	return constructor;
+	// decompose matrixWorld
 
-};
+	var spritePosition = new THREE.Vector3();
+	var spriteRotation = new THREE.Quaternion();
+	var spriteScale = new THREE.Vector3();
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- **/
+	var init = function () {
 
-/**************************************************************
- *	Curved Path - a curve path is simply a array of connected
- *  curves, but retains the api of a curve
- **************************************************************/
+		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
+		] );
 
-THREE.CurvePath = function () {
+		var faces = new Uint16Array( [
+			0, 1, 2,
+			0, 2, 3
+		] );
 
-	this.curves = [];
-	this.bends = [];
+		vertexBuffer  = gl.createBuffer();
+		elementBuffer = gl.createBuffer();
 
-	this.autoClose = false; // Automatically closes the path
-};
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
 
-THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
 
-THREE.CurvePath.prototype.add = function ( curve ) {
+		program = createProgram();
 
-	this.curves.push( curve );
+		attributes = {
+			position:			gl.getAttribLocation ( program, 'position' ),
+			uv:					gl.getAttribLocation ( program, 'uv' )
+		};
 
-};
+		uniforms = {
+			uvOffset:			gl.getUniformLocation( program, 'uvOffset' ),
+			uvScale:			gl.getUniformLocation( program, 'uvScale' ),
 
-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
-};
+			rotation:			gl.getUniformLocation( program, 'rotation' ),
+			scale:				gl.getUniformLocation( program, 'scale' ),
 
-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);
+			color:				gl.getUniformLocation( program, 'color' ),
+			map:				gl.getUniformLocation( program, 'map' ),
+			opacity:			gl.getUniformLocation( program, 'opacity' ),
 
-	if (!startPoint.equals(endPoint)) {
-		this.curves.push( new THREE.LineCurve(endPoint, startPoint) );
-	}
+			modelViewMatrix: 	gl.getUniformLocation( program, 'modelViewMatrix' ),
+			projectionMatrix:	gl.getUniformLocation( program, 'projectionMatrix' ),
 
-};
+			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' ),
 
-// To get accurate point with reference to
-// entire path distance at time t,
-// following has to be done:
+			alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
+		};
 
-// 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')
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = 8;
+		canvas.height = 8;
 
-THREE.CurvePath.prototype.getPoint = function( t ) {
+		var context = canvas.getContext( '2d' );
+		context.fillStyle = 'white';
+		context.fillRect( 0, 0, 8, 8 );
 
-	var d = t * this.getLength();
-	var curveLengths = this.getCurveLengths();
-	var i = 0, diff, curve;
+		texture = new THREE.Texture( canvas );
+		texture.needsUpdate = true;
 
-	// To think about boundaries points.
+	};
 
-	while ( i < curveLengths.length ) {
+	this.render = function ( scene, camera ) {
 
-		if ( curveLengths[ i ] >= d ) {
+		if ( sprites.length === 0 ) return;
 
-			diff = curveLengths[ i ] - d;
-			curve = this.curves[ i ];
+		// setup gl
 
-			var u = 1 - diff / curve.getLength();
+		if ( program === undefined ) {
 
-			return curve.getPointAt( u );
+			init();
 
-			break;
 		}
 
-		i ++;
-
-	}
+		gl.useProgram( program );
 
-	return null;
+		gl.enableVertexAttribArray( attributes.position );
+		gl.enableVertexAttribArray( attributes.uv );
 
-	// loop where sum != 0, sum > d , sum+1 <d
+		gl.disable( gl.CULL_FACE );
+		gl.enable( gl.BLEND );
 
-};
+		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 );
 
-/*
-THREE.CurvePath.prototype.getTangent = function( t ) {
-};*/
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
 
+		gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
 
-// 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
+		gl.activeTexture( gl.TEXTURE0 );
+		gl.uniform1i( uniforms.map, 0 );
 
-THREE.CurvePath.prototype.getLength = function() {
+		var oldFogType = 0;
+		var sceneFogType = 0;
+		var fog = scene.fog;
 
-	var lens = this.getCurveLengths();
-	return lens[ lens.length - 1 ];
+		if ( fog ) {
 
-};
+			gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
 
-// Compute lengths and cache them
-// We cannot overwrite getLengths() because UtoT mapping uses it.
+			if ( fog instanceof THREE.Fog ) {
 
-THREE.CurvePath.prototype.getCurveLengths = function() {
+				gl.uniform1f( uniforms.fogNear, fog.near );
+				gl.uniform1f( uniforms.fogFar, fog.far );
 
-	// We use cache values if curves and cache array are same length
+				gl.uniform1i( uniforms.fogType, 1 );
+				oldFogType = 1;
+				sceneFogType = 1;
 
-	if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
+			} else if ( fog instanceof THREE.FogExp2 ) {
 
-		return this.cacheLengths;
+				gl.uniform1f( uniforms.fogDensity, fog.density );
 
-	};
+				gl.uniform1i( uniforms.fogType, 2 );
+				oldFogType = 2;
+				sceneFogType = 2;
 
-	// Get length of subsurve
-	// Push sums into cached array
+			}
 
-	var lengths = [], sums = 0;
-	var i, il = this.curves.length;
+		} else {
 
-	for ( i = 0; i < il; i ++ ) {
+			gl.uniform1i( uniforms.fogType, 0 );
+			oldFogType = 0;
+			sceneFogType = 0;
 
-		sums += this.curves[ i ].getLength();
-		lengths.push( sums );
+		}
 
-	}
 
-	this.cacheLengths = lengths;
+		// update positions and sort
 
-	return lengths;
+		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
 
-};
+			var sprite = sprites[ i ];
 
+			sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+			sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
 
+		}
 
-// Returns min and max coordinates, as well as centroid
+		sprites.sort( painterSortStable );
 
-THREE.CurvePath.prototype.getBoundingBox = function () {
+		// render all sprites
 
-	var points = this.getPoints();
+		var scale = [];
 
-	var maxX, maxY, maxZ;
-	var minX, minY, minZ;
+		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
 
-	maxX = maxY = Number.NEGATIVE_INFINITY;
-	minX = minY = Number.POSITIVE_INFINITY;
+			var sprite = sprites[ i ];
+			var material = sprite.material;
 
-	var p, i, il, sum;
+			gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+			gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
 
-	var v3 = points[0] instanceof THREE.Vector3;
+			sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );
 
-	sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
+			scale[ 0 ] = spriteScale.x;
+			scale[ 1 ] = spriteScale.y;
 
-	for ( i = 0, il = points.length; i < il; i ++ ) {
+			var fogType = 0;
 
-		p = points[ i ];
+			if ( scene.fog && material.fog ) {
 
-		if ( p.x > maxX ) maxX = p.x;
-		else if ( p.x < minX ) minX = p.x;
+				fogType = sceneFogType;
 
-		if ( p.y > maxY ) maxY = p.y;
-		else if ( p.y < minY ) minY = p.y;
+			}
 
-		if ( v3 ) {
+			if ( oldFogType !== fogType ) {
 
-			if ( p.z > maxZ ) maxZ = p.z;
-			else if ( p.z < minZ ) minZ = p.z;
+				gl.uniform1i( uniforms.fogType, fogType );
+				oldFogType = fogType;
 
-		}
+			}
 
-		sum.add( p );
+			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 );
 
-	var ret = {
+			} else {
 
-		minX: minX,
-		minY: minY,
-		maxX: maxX,
-		maxY: maxY,
-		centroid: sum.divideScalar( il )
+				gl.uniform2f( uniforms.uvOffset, 0, 0 );
+				gl.uniform2f( uniforms.uvScale, 1, 1 );
 
-	};
+			}
 
-	if ( v3 ) {
+			gl.uniform1f( uniforms.opacity, material.opacity );
+			gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
 
-		ret.maxZ = maxZ;
-		ret.minZ = minZ;
+			gl.uniform1f( uniforms.rotation, material.rotation );
+			gl.uniform2fv( uniforms.scale, scale );
 
-	}
+			renderer.state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+			renderer.state.setDepthTest( material.depthTest );
+			renderer.state.setDepthWrite( material.depthWrite );
 
-	return ret;
+			if ( material.map && material.map.image && material.map.image.width ) {
 
-};
+				renderer.setTexture( material.map, 0 );
 
-/**************************************************************
- *	Create Geometries Helpers
- **************************************************************/
+			} else {
 
-/// Generate geometry from path points (for Line or ParticleSystem objects)
+				renderer.setTexture( texture, 0 );
 
-THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
+			}
 
-	var pts = this.getPoints( divisions, true );
-	return this.createGeometry( pts );
+			gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-};
+		}
 
-// Generate geometry from equidistance sampling along the path
+		// restore gl
 
-THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
+		gl.enable( gl.CULL_FACE );
 
-	var pts = this.getSpacedPoints( divisions, true );
-	return this.createGeometry( pts );
+		renderer.resetGLState();
 
-};
+	};
 
-THREE.CurvePath.prototype.createGeometry = function( points ) {
+	function createProgram () {
 
-	var geometry = new THREE.Geometry();
+		var program = gl.createProgram();
 
-	for ( var i = 0; i < points.length; i ++ ) {
+		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
 
-		geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
+		gl.shaderSource( vertexShader, [
 
-	}
+			'precision ' + renderer.getPrecision() + ' float;',
 
-	return geometry;
+			'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;',
 
-/**************************************************************
- *	Bend / Wrap Helper Methods
- **************************************************************/
+			'void main() {',
 
-// Wrap path / Bend modifiers?
+				'vUV = uvOffset + uv * uvScale;',
 
-THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
+				'vec2 alignedPosition = position * scale;',
 
-	this.bends.push( bendpath );
+				'vec2 rotatedPosition;',
+				'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
+				'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
 
-};
+				'vec4 finalPosition;',
 
-THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
+				'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+				'finalPosition.xy += rotatedPosition;',
+				'finalPosition = projectionMatrix * finalPosition;',
 
-	var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
-	var i, il;
+				'gl_Position = finalPosition;',
 
-	if ( !bends ) {
+			'}'
 
-		bends = this.bends;
+		].join( '\n' ) );
 
-	}
+		gl.shaderSource( fragmentShader, [
 
-	for ( i = 0, il = bends.length; i < il; i ++ ) {
+			'precision ' + renderer.getPrecision() + ' float;',
 
-		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+			'uniform vec3 color;',
+			'uniform sampler2D map;',
+			'uniform float opacity;',
 
-	}
+			'uniform int fogType;',
+			'uniform vec3 fogColor;',
+			'uniform float fogDensity;',
+			'uniform float fogNear;',
+			'uniform float fogFar;',
+			'uniform float alphaTest;',
 
-	return oldPts;
+			'varying vec2 vUV;',
 
-};
+			'void main() {',
 
-THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
+				'vec4 texture = texture2D( map, vUV );',
 
-	var oldPts = this.getSpacedPoints( segments );
+				'if ( texture.a < alphaTest ) discard;',
 
-	var i, il;
+				'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
 
-	if ( !bends ) {
+				'if ( fogType > 0 ) {',
 
-		bends = this.bends;
+					'float depth = gl_FragCoord.z / gl_FragCoord.w;',
+					'float fogFactor = 0.0;',
 
-	}
+					'if ( fogType == 1 ) {',
 
-	for ( i = 0, il = bends.length; i < il; i ++ ) {
+						'fogFactor = smoothstep( fogNear, fogFar, depth );',
 
-		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+					'} else {',
 
-	}
+						'const float LOG2 = 1.442695;',
+						'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+						'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
 
-	return oldPts;
+					'}',
 
-};
+					'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
 
-// This returns getPoints() bend/wrapped around the contour of a path.
-// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
+				'}',
 
-THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
+			'}'
 
-	var bounds = this.getBoundingBox();
+		].join( '\n' ) );
 
-	var i, il, p, oldX, oldY, xNorm;
+		gl.compileShader( vertexShader );
+		gl.compileShader( fragmentShader );
 
-	for ( i = 0, il = oldPts.length; i < il; i ++ ) {
+		gl.attachShader( program, vertexShader );
+		gl.attachShader( program, fragmentShader );
 
-		p = oldPts[ i ];
+		gl.linkProgram( program );
 
-		oldX = p.x;
-		oldY = p.y;
+		return program;
 
-		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
+	function painterSortStable ( a, b ) {
 
-		xNorm = path.getUtoTmapping( xNorm, oldX );
+		if ( a.z !== b.z ) {
 
-		// check for out of bounds?
+			return b.z - a.z;
 
-		var pathPt = path.getPoint( xNorm );
-		var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY );
+		} else {
 
-		p.x = pathPt.x + normal.x;
-		p.y = pathPt.y + normal.y;
+			return b.id - a.id;
 
-	}
+		}
 
-	return oldPts;
+	};
 
 };
 
+// File:src/extras/GeometryUtils.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.Gyroscope = function () {
+THREE.GeometryUtils = {
 
-	THREE.Object3D.call( this );
+	merge: function ( geometry1, geometry2, materialIndexOffset ) {
 
-};
+		THREE.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
 
-THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
+		var matrix;
 
-THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) {
+		if ( geometry2 instanceof THREE.Mesh ) {
 
-	this.matrixAutoUpdate && this.updateMatrix();
+			geometry2.matrixAutoUpdate && geometry2.updateMatrix();
 
-	// update matrixWorld
+			matrix = geometry2.matrix;
+			geometry2 = geometry2.geometry;
 
-	if ( this.matrixWorldNeedsUpdate || force ) {
+		}
 
-		if ( this.parent ) {
+		geometry1.merge( geometry2, matrix, materialIndexOffset );
 
-			this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+	},
 
-			this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld );
-			this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject );
+	center: function ( geometry ) {
 
-			this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld );
+		THREE.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
+		return geometry.center();
 
+	}
 
-		} else {
+};
 
-			this.matrixWorld.copy( this.matrix );
+// File:src/extras/ImageUtils.js
 
-		}
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ */
 
+THREE.ImageUtils = {
 
-		this.matrixWorldNeedsUpdate = false;
+	crossOrigin: undefined,
 
-		force = true;
+	loadTexture: function ( url, mapping, onLoad, onError ) {
 
-	}
+		var loader = new THREE.ImageLoader();
+		loader.crossOrigin = this.crossOrigin;
 
-	// update children
+		var texture = new THREE.Texture( undefined, mapping );
 
-	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+		loader.load( url, function ( image ) {
 
-		this.children[ i ].updateMatrixWorld( force );
+			texture.image = image;
+			texture.needsUpdate = true;
 
-	}
+			if ( onLoad ) onLoad( texture );
 
-};
+		}, undefined, function ( event ) {
 
-THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3();
-THREE.Gyroscope.prototype.translationObject = new THREE.Vector3();
-THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion();
-THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion();
-THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3();
-THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3();
+			if ( onError ) onError( event );
 
+		} );
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Creates free form 2d path using series of points, lines or curves.
- *
- **/
+		texture.sourceFile = url;
 
-THREE.Path = function ( points ) {
+		return texture;
 
-	THREE.CurvePath.call(this);
+	},
 
-	this.actions = [];
+	loadTextureCube: function ( array, mapping, onLoad, onError ) {
 
-	if ( points ) {
+		var images = [];
 
-		this.fromPoints( points );
+		var loader = new THREE.ImageLoader();
+		loader.crossOrigin = this.crossOrigin;
 
-	}
+		var texture = new THREE.CubeTexture( images, mapping );
 
-};
+		// no flipping needed for cube textures
 
-THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
+		texture.flipY = false;
 
-THREE.PathActions = {
+		var loaded = 0;
 
-	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 loadTexture = function ( i ) {
 
-// TODO Clean up PATH API
+			loader.load( array[ i ], function ( image ) {
 
-// Create path using straight lines to connect all points
-// - vectors: array of Vector2
+				texture.images[ i ] = image;
 
-THREE.Path.prototype.fromPoints = function ( vectors ) {
+				loaded += 1;
 
-	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
+				if ( loaded === 6 ) {
 
-	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
+					texture.needsUpdate = true;
 
-		this.lineTo( vectors[ v ].x, vectors[ v ].y );
+					if ( onLoad ) onLoad( texture );
 
-	};
+				}
 
-};
+			}, undefined, onError );
 
-// startPath() endPath()?
+		}
 
-THREE.Path.prototype.moveTo = function ( x, y ) {
+		for ( var i = 0, il = array.length; i < il; ++ i ) {
 
-	var args = Array.prototype.slice.call( arguments );
-	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
+			loadTexture( i );
 
-};
+		}
 
-THREE.Path.prototype.lineTo = function ( x, y ) {
+		return texture;
 
-	var args = Array.prototype.slice.call( arguments );
+	},
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+	loadCompressedTexture: function () {
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+		THREE.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
 
-	var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
-	this.curves.push( curve );
+	},
 
-	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
+	loadCompressedTextureCube: function () {
 
-};
+		THREE.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
 
-THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
+	},
 
-	var args = Array.prototype.slice.call( arguments );
+	getNormalMap: function ( image, depth ) {
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+		// Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+		var cross = function ( a, b ) {
 
-	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 [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
 
-	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
+		}
 
-};
+		var subtract = function ( a, b ) {
 
-THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
-											   aCP2x, aCP2y,
-											   aX, aY ) {
+			return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
 
-	var args = Array.prototype.slice.call( arguments );
+		}
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+		var normalize = function ( a ) {
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+			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 ];
 
-	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 } );
+		depth = depth | 1;
 
-};
+		var width = image.width;
+		var height = image.height;
 
-THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = width;
+		canvas.height = height;
 
-	var args = Array.prototype.slice.call( arguments );
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+		var context = canvas.getContext( '2d' );
+		context.drawImage( image, 0, 0 );
 
-	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 );
+		var data = context.getImageData( 0, 0, width, height ).data;
+		var imageData = context.createImageData( width, height );
+		var output = imageData.data;
 
-	var curve = new THREE.SplineCurve( npts );
-	this.curves.push( curve );
+		for ( var x = 0; x < width; x ++ ) {
 
-	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
+			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;
 
-// FUTURE: Change the API or follow canvas API?
+				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 ] );
 
-THREE.Path.prototype.arc = function ( aX, aY, aRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
+				var normals = [];
+				var num_points = points.length;
 
-	var lastargs = this.actions[ this.actions.length - 1].args;
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+				for ( var i = 0; i < num_points; i ++ ) {
+
+					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.absarc(aX + x0, aY + y0, aRadius,
-		aStartAngle, aEndAngle, aClockwise );
+				var normal = [ 0, 0, 0 ];
 
- };
+				for ( var i = 0; i < normals.length; i ++ ) {
 
- THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
-	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
- };
+					normal[ 0 ] += normals[ i ][ 0 ];
+					normal[ 1 ] += normals[ i ][ 1 ];
+					normal[ 2 ] += normals[ i ][ 2 ];
 
-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 ];
+				normal[ 0 ] /= normals.length;
+				normal[ 1 ] /= normals.length;
+				normal[ 2 ] /= normals.length;
 
-	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
-		aStartAngle, aEndAngle, aClockwise );
+				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;
 
+			}
 
-THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
+		}
 
-	var args = Array.prototype.slice.call( arguments );
-	var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
-									aStartAngle, aEndAngle, aClockwise );
-	this.curves.push( curve );
+		context.putImageData( imageData, 0, 0 );
 
-	var lastPoint = curve.getPoint(1);
-	args.push(lastPoint.x);
-	args.push(lastPoint.y);
+		return canvas;
 
-	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
+	},
 
- };
+	generateDataTexture: function ( width, height, color ) {
 
-THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
+		var size = width * height;
+		var data = new Uint8Array( 3 * size );
 
-	if ( ! divisions ) divisions = 40;
+		var r = Math.floor( color.r * 255 );
+		var g = Math.floor( color.g * 255 );
+		var b = Math.floor( color.b * 255 );
 
-	var points = [];
+		for ( var i = 0; i < size; i ++ ) {
 
-	for ( var i = 0; i < divisions; i ++ ) {
+			data[ i * 3 ] 	   = r;
+			data[ i * 3 + 1 ] = g;
+			data[ i * 3 + 2 ] = b;
 
-		points.push( this.getPoint( i / divisions ) );
+		}
 
-		//if( !this.getPoint( i / divisions ) ) throw "DIE";
+		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
+		texture.needsUpdate = true;
+
+		return texture;
 
 	}
 
-	// if ( closedPath ) {
-	//
-	// 	points.push( points[ 0 ] );
-	//
-	// }
+};
 
-	return points;
+// File:src/extras/SceneUtils.js
 
-};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-/* Return an array of vectors based on contour of the path */
+THREE.SceneUtils = {
 
-THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
+	createMultiMaterialObject: function ( geometry, materials ) {
 
-	if (this.useSpacedPoints) {
-		console.log('tata');
-		return this.getSpacedPoints( divisions, closedPath );
-	}
+		var group = new THREE.Object3D();
 
-	divisions = divisions || 12;
+		for ( var i = 0, l = materials.length; i < l; i ++ ) {
 
-	var points = [];
+			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
 
-	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 ++ ) {
+		return group;
 
-		item = this.actions[ i ];
+	},
 
-		action = item.action;
-		args = item.args;
+	detach: function ( child, parent, scene ) {
 
-		switch( action ) {
+		child.applyMatrix( parent.matrixWorld );
+		parent.remove( child );
+		scene.add( child );
 
-		case THREE.PathActions.MOVE_TO:
+	},
 
-			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+	attach: function ( child, scene, parent ) {
 
-			break;
+		var matrixWorldInverse = new THREE.Matrix4();
+		matrixWorldInverse.getInverse( parent.matrixWorld );
+		child.applyMatrix( matrixWorldInverse );
 
-		case THREE.PathActions.LINE_TO:
+		scene.remove( child );
+		parent.add( child );
 
-			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+	}
 
-			break;
+};
 
-		case THREE.PathActions.QUADRATIC_CURVE_TO:
+// File:src/extras/FontUtils.js
 
-			cpx  = args[ 2 ];
-			cpy  = args[ 3 ];
+/**
+ * @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/
+ *
+ */
 
-			cpx1 = args[ 0 ];
-			cpy1 = args[ 1 ];
+THREE.FontUtils = {
 
-			if ( points.length > 0 ) {
+	faces: {},
 
-				laste = points[ points.length - 1 ];
+	// Just for now. face[weight][style]
 
-				cpx0 = laste.x;
-				cpy0 = laste.y;
+	face: 'helvetiker',
+	weight: 'normal',
+	style: 'normal',
+	size: 150,
+	divisions: 10,
 
-			} else {
+	getFace: function () {
 
-				laste = this.actions[ i - 1 ].args;
+		try {
 
-				cpx0 = laste[ laste.length - 2 ];
-				cpy0 = laste[ laste.length - 1 ];
+			return this.faces[ this.face ][ this.weight ][ this.style ];
 
-			}
+		} catch (e) {
 
-			for ( j = 1; j <= divisions; j ++ ) {
+			throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
 
-				t = j / divisions;
+		};
 
-				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
-				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+	},
 
-				points.push( new THREE.Vector2( tx, ty ) );
+	loadFace: function ( data ) {
 
-			}
+		var family = data.familyName.toLowerCase();
 
-			break;
+		var ThreeFont = this;
 
-		case THREE.PathActions.BEZIER_CURVE_TO:
+		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
 
-			cpx  = args[ 4 ];
-			cpy  = args[ 5 ];
+		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
+		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
 
-			cpx1 = args[ 0 ];
-			cpy1 = args[ 1 ];
+		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
 
-			cpx2 = args[ 2 ];
-			cpy2 = args[ 3 ];
+		return data;
 
-			if ( points.length > 0 ) {
+	},
 
-				laste = points[ points.length - 1 ];
+	drawText: function ( text ) {
 
-				cpx0 = laste.x;
-				cpy0 = laste.y;
+		// RenderText
 
-			} else {
+		var i,
+			face = this.getFace(),
+			scale = this.size / face.resolution,
+			offset = 0,
+			chars = String( text ).split( '' ),
+			length = chars.length;
 
-				laste = this.actions[ i - 1 ].args;
+		var fontPaths = [];
 
-				cpx0 = laste[ laste.length - 2 ];
-				cpy0 = laste[ laste.length - 1 ];
+		for ( i = 0; i < length; i ++ ) {
 
-			}
+			var path = new THREE.Path();
 
+			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
+			offset += ret.offset;
 
-			for ( j = 1; j <= divisions; j ++ ) {
+			fontPaths.push( ret.path );
 
-				t = j / divisions;
+		}
 
-				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
-				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
+		// get the width
 
-				points.push( new THREE.Vector2( tx, ty ) );
+		var width = offset / 2;
+		//
+		// for ( p = 0; p < allPts.length; p++ ) {
+		//
+		// 	allPts[ p ].x -= width;
+		//
+		// }
 
-			}
+		//var extract = this.extractPoints( allPts, characterPts );
+		//extract.contour = allPts;
 
-			break;
+		//extract.paths = fontPaths;
+		//extract.offset = width;
 
-		case THREE.PathActions.CSPLINE_THRU:
+		return { paths: fontPaths, offset: width };
 
-			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 ] );
 
-			var spline = new THREE.SplineCurve( spts );
+	extractGlyphPoints: function ( c, face, scale, offset, path ) {
 
-			for ( j = 1; j <= n; j ++ ) {
+		var pts = [];
 
-				points.push( spline.getPointAt( j / n ) ) ;
+		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;
 
-			break;
+		if ( glyph.o ) {
 
-		case THREE.PathActions.ARC:
+			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+			length = outline.length;
 
-			var aX = args[ 0 ], aY = args[ 1 ],
-				aRadius = args[ 2 ],
-				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
-				aClockwise = !!args[ 5 ];
+			scaleX = scale;
+			scaleY = scale;
 
-			var deltaAngle = aEndAngle - aStartAngle;
-			var angle;
-			var tdivisions = divisions * 2;
+			for ( i = 0; i < length; ) {
+
+				action = outline[ i ++ ];
+
+				//console.log( action );
 
-			for ( j = 1; j <= tdivisions; j ++ ) {
+				switch ( action ) {
 
-				t = j / tdivisions;
+				case 'm':
 
-				if ( ! aClockwise ) {
+					// Move To
 
-					t = 1 - t;
+					x = outline[ i ++ ] * scaleX + offset;
+					y = outline[ i ++ ] * scaleY;
 
-				}
+					path.moveTo( x, y );
+					break;
 
-				angle = aStartAngle + t * deltaAngle;
+				case 'l':
 
-				tx = aX + aRadius * Math.cos( angle );
-				ty = aY + aRadius * Math.sin( angle );
+					// Line To
 
-				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+					x = outline[ i ++ ] * scaleX + offset;
+					y = outline[ i ++ ] * scaleY;
+					path.lineTo( x, y );
+					break;
 
-				points.push( new THREE.Vector2( tx, ty ) );
+				case 'q':
 
-			}
+					// QuadraticCurveTo
 
-			//console.log(points);
+					cpx  = outline[ i ++ ] * scaleX + offset;
+					cpy  = outline[ i ++ ] * scaleY;
+					cpx1 = outline[ i ++ ] * scaleX + offset;
+					cpy1 = outline[ i ++ ] * scaleY;
 
-		  break;
+					path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
 
-		case THREE.PathActions.ELLIPSE:
+					laste = pts[ pts.length - 1 ];
 
-			var aX = args[ 0 ], aY = args[ 1 ],
-				xRadius = args[ 2 ],
-				yRadius = args[ 3 ],
-				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
-				aClockwise = !!args[ 6 ];
+					if ( laste ) {
 
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-			var deltaAngle = aEndAngle - aStartAngle;
-			var angle;
-			var tdivisions = divisions * 2;
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
 
-			for ( j = 1; j <= tdivisions; j ++ ) {
+							var t = i2 / divisions;
+							THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+							THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+						}
 
-				t = j / tdivisions;
+					}
 
-				if ( ! aClockwise ) {
+					break;
 
-					t = 1 - t;
+				case 'b':
 
-				}
+					// Cubic Bezier Curve
 
-				angle = aStartAngle + t * deltaAngle;
+					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;
 
-				tx = aX + xRadius * Math.cos( angle );
-				ty = aY + yRadius * Math.sin( angle );
+					path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
 
-				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+					laste = pts[ pts.length - 1 ];
 
-				points.push( new THREE.Vector2( tx, ty ) );
+					if ( laste ) {
 
-			}
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-			//console.log(points);
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
 
-		  break;
+							var t = i2 / divisions;
+							THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
+							THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
 
-		} // end switch
+						}
 
-	}
+					}
+
+					break;
 
+				}
 
+			}
+		}
 
-	// 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 { offset: glyph.ha * scale, path:path };
 	}
 
-	return points;
-
 };
 
-// Breaks path into shapes
 
-THREE.Path.prototype.toShapes = function( isCCW ) {
+THREE.FontUtils.generateShapes = function ( text, parameters ) {
 
-	var i, il, item, action, args;
+	// Parameters 
 
-	var subPaths = [], lastPath = new THREE.Path();
+	parameters = parameters || {};
 
-	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+	var size = parameters.size !== undefined ? parameters.size : 100;
+	var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
 
-		item = this.actions[ i ];
+	var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
+	var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
+	var style = parameters.style !== undefined ? parameters.style : 'normal';
 
-		args = item.args;
-		action = item.action;
+	THREE.FontUtils.size = size;
+	THREE.FontUtils.divisions = curveSegments;
 
-		if ( action == THREE.PathActions.MOVE_TO ) {
+	THREE.FontUtils.face = font;
+	THREE.FontUtils.weight = weight;
+	THREE.FontUtils.style = style;
 
-			if ( lastPath.actions.length != 0 ) {
+	// Get a Font data json object
 
-				subPaths.push( lastPath );
-				lastPath = new THREE.Path();
+	var data = THREE.FontUtils.drawText( text );
 
-			}
+	var paths = data.paths;
+	var shapes = [];
 
-		}
+	for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
 
-		lastPath[ action ].apply( lastPath, args );
+		Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
 
 	}
 
-	if ( lastPath.actions.length != 0 ) {
+	return shapes;
+
+};
 
-		subPaths.push( lastPath );
 
-	}
+/**
+ * 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
+ *
+ */
 
-	// console.log(subPaths);
 
-	if ( subPaths.length == 0 ) return [];
+( function ( namespace ) {
 
-	var solid, tmpPath, tmpShape, shapes = [];
+	var EPSILON = 0.0000000001;
 
-	if ( subPaths.length == 1) {
+	// takes in an contour array and returns
 
-		tmpPath = subPaths[0];
-		tmpShape = new THREE.Shape();
-		tmpShape.actions = tmpPath.actions;
-		tmpShape.curves = tmpPath.curves;
-		shapes.push( tmpShape );
-		return shapes;
+	var process = function ( contour, indices ) {
 
-	}
+		var n = contour.length;
 
-	var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
-	holesFirst = isCCW ? !holesFirst : holesFirst;
+		if ( n < 3 ) return null;
 
-	// console.log("Holes first", holesFirst);
+		var result = [],
+			verts = [],
+			vertIndices = [];
 
-	if ( holesFirst ) {
+		/* we want a counter-clockwise polygon in verts */
 
-		tmpShape = new THREE.Shape();
+		var u, v, w;
 
-		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+		if ( area( contour ) > 0.0 ) {
 
-			tmpPath = subPaths[ i ];
-			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
-			solid = isCCW ? !solid : solid;
+			for ( v = 0; v < n; v ++ ) verts[ v ] = v;
 
-			if ( solid ) {
+		} else {
 
-				tmpShape.actions = tmpPath.actions;
-				tmpShape.curves = tmpPath.curves;
+			for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
 
-				shapes.push( tmpShape );
-				tmpShape = new THREE.Shape();
+		}
 
-				//console.log('cw', i);
+		var nv = n;
 
-			} else {
+		/*  remove nv - 2 vertices, creating 1 triangle every time */
 
-				tmpShape.holes.push( tmpPath );
+		var count = 2 * nv;   /* error detection */
 
-				//console.log('ccw', i);
+		for ( v = nv - 1; nv > 2; ) {
 
-			}
+			/* if we loop, it is probably a non-simple polygon */
 
-		}
+			if ( ( count -- ) <= 0 ) {
 
-	} else {
+				//** Triangulate: ERROR - probable bad polygon!
 
-		// Shapes first
-		tmpShape = undefined;
+				//throw ( "Warning, unable to triangulate polygon!" );
+				//return null;
+				// Sometimes warning is fine, especially polygons are triangulated in reverse.
+				THREE.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' );
 
-		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+				if ( indices ) return vertIndices;
+				return result;
 
-			tmpPath = subPaths[ i ];
-			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
-			solid = isCCW ? !solid : solid;
+			}
 
-			if ( solid ) {
+			/* three consecutive vertices in current polygon, <u,v,w> */
 
-				if ( tmpShape ) shapes.push( tmpShape );
+			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     */
 
-				tmpShape = new THREE.Shape();
-				tmpShape.actions = tmpPath.actions;
-				tmpShape.curves = tmpPath.curves;
+			if ( snip( contour, u, v, w, nv, verts ) ) {
 
-			} else {
+				var a, b, c, s, t;
 
-				tmpShape.holes.push( tmpPath );
+				/* true names of the vertices */
 
-			}
+				a = verts[ u ];
+				b = verts[ v ];
+				c = verts[ w ];
 
-		}
+				/* output Triangle */
 
-		shapes.push( tmpShape );
+				result.push( [ contour[ a ],
+					contour[ b ],
+					contour[ c ] ] );
 
-	}
 
-	//console.log("shape", shapes);
+				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
 
-	return shapes;
+				/* remove v from the remaining polygon */
 
-};
+				for ( s = v, t = v + 1; t < nv; s ++, t ++ ) {
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Defines a 2d shape plane using paths.
- **/
+					verts[ s ] = verts[ t ];
 
-// 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 () {
+				nv --;
 
-	THREE.Path.apply( this, arguments );
-	this.holes = [];
+				/* reset error detection counter */
 
-};
+				count = 2 * nv;
 
-THREE.Shape.prototype = Object.create( THREE.Path.prototype );
+			}
 
-// Convenience method to return ExtrudeGeometry
+		}
 
-THREE.Shape.prototype.extrude = function ( options ) {
+		if ( indices ) return vertIndices;
+		return result;
 
-	var extruded = new THREE.ExtrudeGeometry( this, options );
-	return extruded;
+	};
 
-};
+	// calculate area of the contour polygon
 
-// Convenience method to return ShapeGeometry
+	var area = function ( contour ) {
 
-THREE.Shape.prototype.makeGeometry = function ( options ) {
+		var n = contour.length;
+		var a = 0.0;
 
-	var geometry = new THREE.ShapeGeometry( this, options );
-	return geometry;
+		for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
 
-};
+			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
 
-// Get points of holes
+		}
 
-THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
+		return a * 0.5;
 
-	var i, il = this.holes.length, holesPts = [];
+	};
 
-	for ( i = 0; i < il; i ++ ) {
+	var snip = function ( contour, u, v, w, n, verts ) {
 
-		holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
+		var p;
+		var ax, ay, bx, by;
+		var cx, cy, px, py;
 
-	}
+		ax = contour[ verts[ u ] ].x;
+		ay = contour[ verts[ u ] ].y;
 
-	return holesPts;
+		bx = contour[ verts[ v ] ].x;
+		by = contour[ verts[ v ] ].y;
 
-};
+		cx = contour[ verts[ w ] ].x;
+		cy = contour[ verts[ w ] ].y;
 
-// Get points of holes (spaced by regular distance)
+		if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
 
-THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
+		var aX, aY, bX, bY, cX, cY;
+		var apx, apy, bpx, bpy, cpx, cpy;
+		var cCROSSap, bCROSScp, aCROSSbp;
 
-	var i, il = this.holes.length, holesPts = [];
+		aX = cx - bx;  aY = cy - by;
+		bX = ax - cx;  bY = ay - cy;
+		cX = bx - ax;  cY = by - ay;
 
-	for ( i = 0; i < il; i ++ ) {
+		for ( p = 0; p < n; p ++ ) {
+
+			px = contour[ verts[ p ] ].x
+			py = contour[ verts[ p ] ].y
+
+			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;
 
-		holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
+			// see if p is inside triangle abc
 
-	}
+			aCROSSbp = aX * bpy - aY * bpx;
+			cCROSSap = cX * apy - cY * apx;
+			bCROSScp = bX * cpy - bY * cpx;
 
-	return holesPts;
+			if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
 
-};
+		}
 
+		return true;
 
-// Get points of shape and holes (keypoints based on segments parameter)
+	};
 
-THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
 
-	return {
+	namespace.Triangulate = process;
+	namespace.Triangulate.area = area;
 
-		shape: this.getTransformedPoints( divisions ),
-		holes: this.getPointsHoles( divisions )
+	return namespace;
 
-	};
+} )( 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;
 
-THREE.Shape.prototype.extractPoints = function ( divisions ) {
+// File:src/extras/audio/Audio.js
 
-	if (this.useSpacedPoints) {
-		return this.extractAllSpacedPoints(divisions);
-	}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	return this.extractAllPoints(divisions);
+THREE.Audio = function ( listener ) {
 
-};
+	THREE.Object3D.call( this );
 
-//
-// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
-//
-// 	return {
-//
-// 		shape: this.transform( bend, divisions ),
-// 		holes: this.getPointsHoles( divisions, bend )
-//
-// 	};
-//
-// };
+	this.type = 'Audio';
 
-// Get points of shape and holes (spaced by regular distance)
+	this.context = listener.context;
+	this.source = this.context.createBufferSource();
+	this.source.onended = this.onEnded.bind(this);
 
-THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
+	this.gain = this.context.createGain();
+	this.gain.connect( this.context.destination );
 
-	return {
+	this.panner = this.context.createPanner();
+	this.panner.connect( this.gain );
 
-		shape: this.getTransformedSpacedPoints( divisions ),
-		holes: this.getSpacedPointsHoles( divisions )
+	this.autoplay = false;
 
-	};
+	this.startTime = 0;
+	this.isPlaying = false;
 
 };
 
-/**************************************************************
- *	Utils
- **************************************************************/
+THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Audio.prototype.constructor = THREE.Audio;
 
-THREE.Shape.Utils = {
+THREE.Audio.prototype.load = function ( file ) {
 
-	/*
-		contour - array of vector2 for contour
-		holes   - array of array of vector2
-	*/
+	var scope = this;
+
+	var request = new XMLHttpRequest();
+	request.open( 'GET', file, true );
+	request.responseType = 'arraybuffer';
+	request.onload = function ( e ) {
 
-	removeHoles: function ( contour, holes ) {
+		scope.context.decodeAudioData( this.response, function ( buffer ) {
 
-		var shape = contour.concat(); // work on this shape
-		var allpoints = shape.concat();
+			scope.source.buffer = buffer;
 
-		/* For each isolated shape, find the closest points and break to the hole to allow triangulation */
+			if( scope.autoplay ) scope.play();
 
+		} );
 
-		var prevShapeVert, nextShapeVert,
-			prevHoleVert, nextHoleVert,
-			holeIndex, shapeIndex,
-			shapeId, shapeGroup,
-			h, h2,
-			hole, shortest, d,
-			p, pts1, pts2,
-			tmpShape1, tmpShape2,
-			tmpHole1, tmpHole2,
-			verts = [];
+	};
+	request.send();
 
-		for ( h = 0; h < holes.length; h ++ ) {
+	return this;
 
-			hole = holes[ h ];
+};
 
-			/*
-			shapeholes[ h ].concat(); // preserves original
-			holes.push( hole );
-			*/
+THREE.Audio.prototype.play = function () {
 
-			Array.prototype.push.apply( allpoints, hole );
+	if ( this.isPlaying === true ) {
 
-			shortest = Number.POSITIVE_INFINITY;
+		THREE.warn( 'THREE.Audio: Audio is already playing.' );
+		return;
 
+	}
 
-			// Find the shortest pair of pts between shape and hole
+	var source = this.context.createBufferSource();
 
-			// Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n)
-			// Using distanceToSquared() intead of distanceTo() should speed a little
-			// since running square roots operations are reduced.
+	source.buffer = this.source.buffer;
+	source.loop = this.source.loop;
+	source.onended = this.source.onended;
+	source.connect( this.panner );
+	source.start( 0, this.startTime );
 
-			for ( h2 = 0; h2 < hole.length; h2 ++ ) {
+	this.isPlaying = true;
 
-				pts1 = hole[ h2 ];
-				var dist = [];
+	this.source = source;
 
-				for ( p = 0; p < shape.length; p++ ) {
+};
 
-					pts2 = shape[ p ];
-					d = pts1.distanceToSquared( pts2 );
-					dist.push( d );
+THREE.Audio.prototype.pause = function () {
 
-					if ( d < shortest ) {
+	this.source.stop();
+	this.startTime = this.context.currentTime;
 
-						shortest = d;
-						holeIndex = h2;
-						shapeIndex = p;
+};
 
-					}
+THREE.Audio.prototype.stop = function () {
 
-				}
+	this.source.stop();
+	this.startTime = 0;
 
-			}
+};
 
-			//console.log("shortest", shortest, dist);
+THREE.Audio.prototype.onEnded = function() {
 
-			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
-			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+	this.isPlaying = false;
 
-			var areaapts = [
+};
 
-				hole[ holeIndex ],
-				shape[ shapeIndex ],
-				shape[ prevShapeVert ]
+THREE.Audio.prototype.setLoop = function ( value ) {
 
-			];
+	this.source.loop = value;
 
-			var areaa = THREE.FontUtils.Triangulate.area( areaapts );
+};
 
-			var areabpts = [
+THREE.Audio.prototype.setRefDistance = function ( value ) {
 
-				hole[ holeIndex ],
-				hole[ prevHoleVert ],
-				shape[ shapeIndex ]
+	this.panner.refDistance = value;
 
-			];
+};
 
-			var areab = THREE.FontUtils.Triangulate.area( areabpts );
+THREE.Audio.prototype.setRolloffFactor = function ( value ) {
 
-			var shapeOffset = 1;
-			var holeOffset = -1;
+	this.panner.rolloffFactor = value;
 
-			var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
-			shapeIndex += shapeOffset;
-			holeIndex += holeOffset;
+};
 
-			if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
-			shapeIndex %= shape.length;
+THREE.Audio.prototype.setVolume = function ( value ) {
 
-			if ( holeIndex < 0 ) { holeIndex += hole.length;  }
-			holeIndex %= hole.length;
+	this.gain.gain.value = value;
 
-			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
-			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+};
 
-			areaapts = [
+THREE.Audio.prototype.updateMatrixWorld = ( function () {
 
-				hole[ holeIndex ],
-				shape[ shapeIndex ],
-				shape[ prevShapeVert ]
+	var position = new THREE.Vector3();
 
-			];
+	return function ( force ) {
 
-			var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
+		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-			areabpts = [
+		position.setFromMatrixPosition( this.matrixWorld );
 
-				hole[ holeIndex ],
-				hole[ prevHoleVert ],
-				shape[ shapeIndex ]
+		this.panner.setPosition( position.x, position.y, position.z );
 
-			];
+	};
 
-			var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
-			//console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ),  ( areaa2 + areab2 ));
+} )();
 
-			if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
+// File:src/extras/audio/AudioListener.js
 
-				// In case areas are not correct.
-				//console.log("USE THIS");
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				shapeIndex = oldShapeIndex;
-				holeIndex = oldHoleIndex ;
+THREE.AudioListener = function () {
 
-				if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
-				shapeIndex %= shape.length;
+	THREE.Object3D.call( this );
 
-				if ( holeIndex < 0 ) { holeIndex += hole.length;  }
-				holeIndex %= hole.length;
+	this.type = 'AudioListener';
 
-				prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
-				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+	this.context = new ( window.AudioContext || window.webkitAudioContext )();
 
-			} else {
+};
 
-				//console.log("USE THAT ")
+THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
+THREE.AudioListener.prototype.constructor = THREE.AudioListener;
 
-			}
+THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
 
-			tmpShape1 = shape.slice( 0, shapeIndex );
-			tmpShape2 = shape.slice( shapeIndex );
-			tmpHole1 = hole.slice( holeIndex );
-			tmpHole2 = hole.slice( 0, holeIndex );
+	var position = new THREE.Vector3();
+	var quaternion = new THREE.Quaternion();
+	var scale = new THREE.Vector3();
 
-			// Should check orders here again?
+	var orientation = new THREE.Vector3();
+	var velocity = new THREE.Vector3();
 
-			var trianglea = [
+	var positionPrev = new THREE.Vector3();
 
-				hole[ holeIndex ],
-				shape[ shapeIndex ],
-				shape[ prevShapeVert ]
+	return function ( force ) {
 
-			];
+		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-			var triangleb = [
+		var listener = this.context.listener;
+		var up = this.up;
 
-				hole[ holeIndex ] ,
-				hole[ prevHoleVert ],
-				shape[ shapeIndex ]
+		this.matrixWorld.decompose( position, quaternion, scale );
 
-			];
+		orientation.set( 0, 0, -1 ).applyQuaternion( quaternion );
+		velocity.subVectors( position, positionPrev );
 
-			verts.push( trianglea );
-			verts.push( triangleb );
+		listener.setPosition( position.x, position.y, position.z );
+		listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
+		listener.setVelocity( velocity.x, velocity.y, velocity.z );
 
-			shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+		positionPrev.copy( position );
 
-		}
+	};
 
-		return {
+} )();
 
-			shape:shape, 		/* shape with no holes */
-			isolatedPts: verts, /* isolated faces */
-			allpoints: allpoints
+// File:src/extras/core/Curve.js
 
-		}
+/**
+ * @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
+ *
+ **/
 
+/**************************************************************
+ *	Abstract Curve base class
+ **************************************************************/
 
-	},
+THREE.Curve = function () {
 
-	triangulateShape: function ( contour, holes ) {
+};
 
-		var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
+// Virtual base class method to overwrite and implement in subclasses
+//	- t [0 .. 1]
 
-		var shape = shapeWithoutHoles.shape,
-			allpoints = shapeWithoutHoles.allpoints,
-			isolatedPts = shapeWithoutHoles.isolatedPts;
+THREE.Curve.prototype.getPoint = function ( t ) {
 
-		var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
+	THREE.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
+	return null;
 
-		// 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.
+};
 
-		//console.log( "triangles",triangles, triangles.length );
-		//console.log( "allpoints",allpoints, allpoints.length );
+// Get point at relative position in curve according to arc length
+// - u [0 .. 1]
 
-		var i, il, f, face,
-			key, index,
-			allPointsMap = {},
-			isolatedPointsMap = {};
+THREE.Curve.prototype.getPointAt = function ( u ) {
 
-		// prepare all points map
+	var t = this.getUtoTmapping( u );
+	return this.getPoint( t );
 
-		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
+};
 
-			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
+// Get sequence of points using getPoint( t )
 
-			if ( allPointsMap[ key ] !== undefined ) {
+THREE.Curve.prototype.getPoints = function ( divisions ) {
 
-				console.log( "Duplicate point", key );
+	if ( ! divisions ) divisions = 5;
 
-			}
+	var d, pts = [];
 
-			allPointsMap[ key ] = i;
+	for ( d = 0; d <= divisions; d ++ ) {
 
-		}
+		pts.push( this.getPoint( d / divisions ) );
 
-		// check all face vertices against all points map
+	}
 
-		for ( i = 0, il = triangles.length; i < il; i ++ ) {
+	return pts;
 
-			face = triangles[ i ];
+};
 
-			for ( f = 0; f < 3; f ++ ) {
+// Get sequence of points using getPointAt( u )
 
-				key = face[ f ].x + ":" + face[ f ].y;
+THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
 
-				index = allPointsMap[ key ];
+	if ( ! divisions ) divisions = 5;
 
-				if ( index !== undefined ) {
+	var d, pts = [];
 
-					face[ f ] = index;
+	for ( d = 0; d <= divisions; d ++ ) {
 
-				}
+		pts.push( this.getPointAt( d / divisions ) );
 
-			}
+	}
 
-		}
+	return pts;
 
-		// check isolated points vertices against all points map
+};
 
-		for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
+// Get total curve arc length
 
-			face = isolatedPts[ i ];
+THREE.Curve.prototype.getLength = function () {
 
-			for ( f = 0; f < 3; f ++ ) {
+	var lengths = this.getLengths();
+	return lengths[ lengths.length - 1 ];
 
-				key = face[ f ].x + ":" + face[ f ].y;
+};
 
-				index = allPointsMap[ key ];
+// Get list of cumulative segment lengths
 
-				if ( index !== undefined ) {
+THREE.Curve.prototype.getLengths = function ( divisions ) {
 
-					face[ f ] = index;
+	if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions) : 200;
 
-				}
+	if ( this.cacheArcLengths
+		&& ( this.cacheArcLengths.length == divisions + 1 )
+		&& ! this.needsUpdate) {
 
-			}
+		//console.log( "cached", this.cacheArcLengths );
+		return this.cacheArcLengths;
 
-		}
+	}
 
-		return triangles.concat( isolatedPts );
+	this.needsUpdate = false;
 
-	}, // end triangulate shapes
+	var cache = [];
+	var current, last = this.getPoint( 0 );
+	var p, sum = 0;
 
-	/*
-	triangulate2 : function( pts, holes ) {
+	cache.push( 0 );
 
-		// For use with Poly2Tri.js
+	for ( p = 1; p <= divisions; p ++ ) {
 
-		var allpts = pts.concat();
-		var shape = [];
-		for (var p in pts) {
-			shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
-		}
+		current = this.getPoint ( p / divisions );
+		sum += current.distanceTo( last );
+		cache.push( sum );
+		last = current;
 
-		var swctx = new js.poly2tri.SweepContext(shape);
+	}
 
-		for (var h in holes) {
-			var aHole = holes[h];
-			var newHole = []
-			for (i in aHole) {
-				newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y));
-				allpts.push(aHole[i]);
-			}
-			swctx.AddHole(newHole);
-		}
+	this.cacheArcLengths = cache;
 
-		var find;
-		var findIndexForPt = function (pt) {
-			find = new THREE.Vector2(pt.x, pt.y);
-			var p;
-			for (p=0, pl = allpts.length; p<pl; p++) {
-				if (allpts[p].equals(find)) return p;
-			}
-			return -1;
-		};
+	return cache; // { sums: cache, sum:sum }; Sum is in the last element.
 
-		// triangulate
-		js.poly2tri.sweep.Triangulate(swctx);
+};
 
-		var triangles =  swctx.GetTriangles();
-		var tr ;
-		var facesPts = [];
-		for (var t in triangles) {
-			tr =  triangles[t];
-			facesPts.push([
-				findIndexForPt(tr.GetPoint(0)),
-				findIndexForPt(tr.GetPoint(1)),
-				findIndexForPt(tr.GetPoint(2))
-					]);
-		}
 
+THREE.Curve.prototype.updateArcLengths = function() {
+	this.needsUpdate = true;
+	this.getLengths();
+};
 
-	//	console.log(facesPts);
-	//	console.log("triangles", triangles.length, triangles);
+// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
 
-		// Returns array of faces with 3 element each
-	return facesPts;
-	},
-*/
+THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
 
-	isClockWise: function ( pts ) {
+	var arcLengths = this.getLengths();
 
-		return THREE.FontUtils.Triangulate.area( pts ) < 0;
+	var i = 0, il = arcLengths.length;
 
-	},
+	var targetArcLength; // The targeted u distance value to get
 
-	// Bezier Curves formulas obtained from
-	// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+	if ( distance ) {
 
-	// Quad Bezier Functions
+		targetArcLength = distance;
 
-	b2p0: function ( t, p ) {
+	} else {
 
-		var k = 1 - t;
-		return k * k * p;
+		targetArcLength = u * arcLengths[ il - 1 ];
 
-	},
+	}
 
-	b2p1: function ( t, p ) {
+	//var time = Date.now();
+
+	// binary search for the index with largest value smaller than target u distance
 
-		return 2 * ( 1 - t ) * t * p;
+	var low = 0, high = il - 1, comparison;
 
-	},
+	while ( low <= high ) {
 
-	b2p2: function ( t, p ) {
+		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
 
-		return t * t * p;
+		comparison = arcLengths[ i ] - targetArcLength;
 
-	},
+		if ( comparison < 0 ) {
 
-	b2: function ( t, p0, p1, p2 ) {
+			low = i + 1;
 
-		return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
+		} else if ( comparison > 0 ) {
 
-	},
+			high = i - 1;
 
-	// Cubic Bezier Functions
+		} else {
 
-	b3p0: function ( t, p ) {
+			high = i;
+			break;
 
-		var k = 1 - t;
-		return k * k * k * p;
+			// DONE
 
-	},
+		}
 
-	b3p1: function ( t, p ) {
+	}
 
-		var k = 1 - t;
-		return 3 * k * k * t * p;
+	i = high;
 
-	},
+	//console.log('b' , i, low, high, Date.now()- time);
 
-	b3p2: function ( t, p ) {
+	if ( arcLengths[ i ] == targetArcLength ) {
 
-		var k = 1 - t;
-		return 3 * k * t * t * p;
+		var t = i / ( il - 1 );
+		return t;
 
-	},
+	}
 
-	b3p3: function ( t, p ) {
+	// we could get finer grain at lengths, or use simple interpolatation between two points
 
-		return t * t * t * p;
+	var lengthBefore = arcLengths[ i ];
+	var lengthAfter = arcLengths[ i + 1 ];
 
-	},
+	var segmentLength = lengthAfter - lengthBefore;
 
-	b3: function ( t, p0, p1, p2, p3 ) {
+    // determine where we are between the 'before' and 'after' points
 
-		return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) +  this.b3p3( t, p3 );
+	var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
 
-	}
+    // add that fractional amount to t
 
-};
+	var t = ( i + segmentFraction ) / ( il - 1 );
 
+	return t;
 
-/**************************************************************
- *	Line
- **************************************************************/
+};
 
-THREE.LineCurve = function ( v1, v2 ) {
+// 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
 
-	this.v1 = v1;
-	this.v2 = v2;
+THREE.Curve.prototype.getTangent = function( t ) {
 
-};
+	var delta = 0.0001;
+	var t1 = t - delta;
+	var t2 = t + delta;
 
-THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+	// Capping in case of danger
 
-THREE.LineCurve.prototype.getPoint = function ( t ) {
+	if ( t1 < 0 ) t1 = 0;
+	if ( t2 > 1 ) t2 = 1;
 
-	var point = this.v2.clone().sub(this.v1);
-	point.multiplyScalar( t ).add( this.v1 );
+	var pt1 = this.getPoint( t1 );
+	var pt2 = this.getPoint( t2 );
 
-	return point;
+	var vec = pt2.clone().sub(pt1);
+	return vec.normalize();
 
 };
 
-// Line curve is linear, so we can overwrite default getPointAt
 
-THREE.LineCurve.prototype.getPointAt = function ( u ) {
+THREE.Curve.prototype.getTangentAt = function ( u ) {
 
-	return this.getPoint( u );
+	var t = this.getUtoTmapping( u );
+	return this.getTangent( t );
 
 };
 
-THREE.LineCurve.prototype.getTangent = function( t ) {
 
-	var tangent = this.v2.clone().sub(this.v1);
 
-	return tangent.normalize();
 
-};
+
 /**************************************************************
- *	Quadratic Bezier curve
+ *	Utils
  **************************************************************/
 
+THREE.Curve.Utils = {
 
-THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
-
-	this.v0 = v0;
-	this.v1 = v1;
-	this.v2 = v2;
+	tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
 
-};
+		return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
 
-THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+	},
 
+	// Puay Bing, thanks for helping with this derivative!
 
-THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
+	tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
 
-	var tx, ty;
+		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;
 
-	tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
-	ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
+	},
 
-	return new THREE.Vector2( tx, ty );
+	tangentSpline: function ( t, p0, p1, p2, p3 ) {
 
-};
+		// 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
 
-THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+		return h00 + h10 + h01 + h11;
 
-	var tx, ty;
+	},
 
-	tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
-	ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
+	// Catmull-Rom
 
-	// returns unit vector
+	interpolate: function( p0, p1, p2, p3, t ) {
 
-	var tangent = new THREE.Vector2( tx, ty );
-	tangent.normalize();
+		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;
 
-	return tangent;
+	}
 
 };
-/**************************************************************
- *	Cubic Bezier curve
- **************************************************************/
-
-THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
 
-	this.v0 = v0;
-	this.v1 = v1;
-	this.v2 = v2;
-	this.v3 = v3;
 
-};
+// TODO: Transformation for Curves?
 
-THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+/**************************************************************
+ *	3D Curves
+ **************************************************************/
 
-THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
+// A Factory method for creating new curve subclasses
 
-	var tx, ty;
+THREE.Curve.create = function ( constructor, getPointFunc ) {
 
-	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 );
+	constructor.prototype = Object.create( THREE.Curve.prototype );
+	constructor.prototype.constructor = constructor;
+	constructor.prototype.getPoint = getPointFunc;
 
-	return new THREE.Vector2( tx, ty );
+	return constructor;
 
 };
 
-THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
-
-	var tx, ty;
-
-	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 tangent = new THREE.Vector2( tx, ty );
-	tangent.normalize();
+// File:src/extras/core/CurvePath.js
 
-	return tangent;
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ **/
 
-};
 /**************************************************************
- *	Spline curve
+ *	Curved Path - a curve path is simply a array of connected
+ *  curves, but retains the api of a curve
  **************************************************************/
 
-THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
-
-	this.points = (points == undefined) ? [] : points;
+THREE.CurvePath = function () {
 
+	this.curves = [];
+	this.bends = [];
+	
+	this.autoClose = false; // Automatically closes the path
 };
 
-THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
-
-THREE.SplineCurve.prototype.getPoint = function ( t ) {
-
-	var v = new THREE.Vector2();
-	var c = [];
-	var points = this.points, point, intPoint, weight;
-	point = ( points.length - 1 ) * t;
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+THREE.CurvePath.prototype.constructor = THREE.CurvePath;
 
-	intPoint = Math.floor( point );
-	weight = point - intPoint;
+THREE.CurvePath.prototype.add = function ( curve ) {
 
-	c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
-	c[ 1 ] = intPoint;
-	c[ 2 ] = intPoint  > points.length - 2 ? points.length -1 : intPoint + 1;
-	c[ 3 ] = intPoint  > points.length - 3 ? points.length -1 : intPoint + 2;
+	this.curves.push( curve );
 
-	v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
-	v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
+};
 
-	return v;
+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
+};
 
+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) );
+	}
+	
 };
-/**************************************************************
- *	Ellipse curve
- **************************************************************/
 
-THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
+// To get accurate point with reference to
+// entire path distance at time t,
+// following has to be done:
 
-	this.aX = aX;
-	this.aY = aY;
+// 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')
 
-	this.xRadius = xRadius;
-	this.yRadius = yRadius;
+THREE.CurvePath.prototype.getPoint = function( t ) {
 
-	this.aStartAngle = aStartAngle;
-	this.aEndAngle = aEndAngle;
+	var d = t * this.getLength();
+	var curveLengths = this.getCurveLengths();
+	var i = 0, diff, curve;
 
-	this.aClockwise = aClockwise;
+	// To think about boundaries points.
 
-};
+	while ( i < curveLengths.length ) {
 
-THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+		if ( curveLengths[ i ] >= d ) {
 
-THREE.EllipseCurve.prototype.getPoint = function ( t ) {
+			diff = curveLengths[ i ] - d;
+			curve = this.curves[ i ];
 
-	var angle;
-	var deltaAngle = this.aEndAngle - this.aStartAngle;
+			var u = 1 - diff / curve.getLength();
 
-	if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
-	if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
+			return curve.getPointAt( u );
 
-	if ( this.aClockwise === true ) {
+		}
 
-		angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
+		i ++;
 
-	} else {
+	}
 
-		angle = this.aStartAngle + t * deltaAngle;
+	return null;
 
-	}
+	// loop where sum != 0, sum > d , sum+1 <d
 
-	var tx = this.aX + this.xRadius * Math.cos( angle );
-	var ty = this.aY + this.yRadius * Math.sin( angle );
+};
+
+/*
+THREE.CurvePath.prototype.getTangent = function( t ) {
+};*/
 
-	return new THREE.Vector2( tx, ty );
 
-};
+// 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
 
-/**************************************************************
- *	Arc curve
- **************************************************************/
+THREE.CurvePath.prototype.getLength = function() {
 
-THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+	var lens = this.getCurveLengths();
+	return lens[ lens.length - 1 ];
 
-	THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
 };
 
-THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
-/**************************************************************
- *	Line3D
- **************************************************************/
+// Compute lengths and cache them
+// We cannot overwrite getLengths() because UtoT mapping uses it.
 
-THREE.LineCurve3 = THREE.Curve.create(
+THREE.CurvePath.prototype.getCurveLengths = function() {
 
-	function ( v1, v2 ) {
+	// We use cache values if curves and cache array are same length
 
-		this.v1 = v1;
-		this.v2 = v2;
+	if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
 
-	},
+		return this.cacheLengths;
 
-	function ( t ) {
+	};
 
-		var r = new THREE.Vector3();
+	// Get length of subsurve
+	// Push sums into cached array
 
+	var lengths = [], sums = 0;
+	var i, il = this.curves.length;
 
-		r.subVectors( this.v2, this.v1 ); // diff
-		r.multiplyScalar( t );
-		r.add( this.v1 );
+	for ( i = 0; i < il; i ++ ) {
 
-		return r;
+		sums += this.curves[ i ].getLength();
+		lengths.push( sums );
 
 	}
 
-);
-
-/**************************************************************
- *	Quadratic Bezier 3D curve
- **************************************************************/
-
-THREE.QuadraticBezierCurve3 = THREE.Curve.create(
-
-	function ( v0, v1, v2 ) {
+	this.cacheLengths = lengths;
 
-		this.v0 = v0;
-		this.v1 = v1;
-		this.v2 = v2;
+	return lengths;
 
-	},
+};
 
-	function ( t ) {
 
-		var tx, ty, tz;
 
-		tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
-		ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
-		tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
+// Returns min and max coordinates
 
-		return new THREE.Vector3( tx, ty, tz );
+THREE.CurvePath.prototype.getBoundingBox = function () {
 
-	}
+	var points = this.getPoints();
 
-);
-/**************************************************************
- *	Cubic Bezier 3D curve
- **************************************************************/
+	var maxX, maxY, maxZ;
+	var minX, minY, minZ;
 
-THREE.CubicBezierCurve3 = THREE.Curve.create(
+	maxX = maxY = Number.NEGATIVE_INFINITY;
+	minX = minY = Number.POSITIVE_INFINITY;
 
-	function ( v0, v1, v2, v3 ) {
+	var p, i, il, sum;
 
-		this.v0 = v0;
-		this.v1 = v1;
-		this.v2 = v2;
-		this.v3 = v3;
+	var v3 = points[0] instanceof THREE.Vector3;
 
-	},
+	sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
 
-	function ( t ) {
+	for ( i = 0, il = points.length; i < il; i ++ ) {
 
-		var tx, ty, tz;
+		p = points[ i ];
 
-		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 );
-		tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
+		if ( p.x > maxX ) maxX = p.x;
+		else if ( p.x < minX ) minX = p.x;
 
-		return new THREE.Vector3( tx, ty, tz );
+		if ( p.y > maxY ) maxY = p.y;
+		else if ( p.y < minY ) minY = p.y;
 
-	}
+		if ( v3 ) {
 
-);
-/**************************************************************
- *	Spline 3D curve
- **************************************************************/
+			if ( p.z > maxZ ) maxZ = p.z;
+			else if ( p.z < minZ ) minZ = p.z;
 
+		}
 
-THREE.SplineCurve3 = THREE.Curve.create(
+		sum.add( p );
 
-	function ( points /* array of Vector3 */) {
+	}
 
-		this.points = (points == undefined) ? [] : points;
+	var ret = {
 
-	},
+		minX: minX,
+		minY: minY,
+		maxX: maxX,
+		maxY: maxY
 
-	function ( t ) {
+	};
 
-		var v = new THREE.Vector3();
-		var c = [];
-		var points = this.points, point, intPoint, weight;
-		point = ( points.length - 1 ) * t;
+	if ( v3 ) {
 
-		intPoint = Math.floor( point );
-		weight = point - intPoint;
+		ret.maxZ = maxZ;
+		ret.minZ = minZ;
 
-		c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
-		c[ 1 ] = intPoint;
-		c[ 2 ] = intPoint  > points.length - 2 ? points.length - 1 : intPoint + 1;
-		c[ 3 ] = intPoint  > points.length - 3 ? points.length - 1 : intPoint + 2;
+	}
 
-		var pt0 = points[ c[0] ],
-			pt1 = points[ c[1] ],
-			pt2 = points[ c[2] ],
-			pt3 = points[ c[3] ];
+	return ret;
 
-		v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
-		v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
-		v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
+};
 
-		return v;
+/**************************************************************
+ *	Create Geometries Helpers
+ **************************************************************/
 
-	}
+/// Generate geometry from path points (for Line or Points objects)
 
-);
+THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
 
+	var pts = this.getPoints( divisions, true );
+	return this.createGeometry( pts );
 
-// THREE.SplineCurve3.prototype.getTangent = function(t) {
-// 		var v = new THREE.Vector3();
-// 		var c = [];
-// 		var points = this.points, point, intPoint, weight;
-// 		point = ( points.length - 1 ) * t;
+};
 
-// 		intPoint = Math.floor( point );
-// 		weight = point - intPoint;
+// Generate geometry from equidistance sampling along the path
 
-// 		c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
-// 		c[ 1 ] = intPoint;
-// 		c[ 2 ] = intPoint  > points.length - 2 ? points.length - 1 : intPoint + 1;
-// 		c[ 3 ] = intPoint  > points.length - 3 ? points.length - 1 : intPoint + 2;
+THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
 
-// 		var pt0 = points[ c[0] ],
-// 			pt1 = points[ c[1] ],
-// 			pt2 = points[ c[2] ],
-// 			pt3 = points[ c[3] ];
+	var pts = this.getSpacedPoints( divisions, true );
+	return this.createGeometry( pts );
 
-// 	// t = weight;
-// 	v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x );
-// 	v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y );
-// 	v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z );
+};
 
-// 	return v;
+THREE.CurvePath.prototype.createGeometry = function( points ) {
 
-// }
-/**************************************************************
- *	Closed Spline 3D curve
- **************************************************************/
+	var geometry = new THREE.Geometry();
 
+	for ( var i = 0; i < points.length; i ++ ) {
 
-THREE.ClosedSplineCurve3 = THREE.Curve.create(
+		geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
 
-	function ( points /* array of Vector3 */) {
+	}
 
-		this.points = (points == undefined) ? [] : points;
+	return geometry;
 
-	},
+};
 
-    function ( t ) {
 
-        var v = new THREE.Vector3();
-        var c = [];
-        var points = this.points, point, intPoint, weight;
-        point = ( points.length - 0 ) * t;
-            // This needs to be from 0-length +1
+/**************************************************************
+ *	Bend / Wrap Helper Methods
+ **************************************************************/
 
-        intPoint = Math.floor( point );
-        weight = point - intPoint;
+// Wrap path / Bend modifiers?
 
-        intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
-        c[ 0 ] = ( intPoint - 1 ) % points.length;
-        c[ 1 ] = ( intPoint ) % points.length;
-        c[ 2 ] = ( intPoint + 1 ) % points.length;
-        c[ 3 ] = ( intPoint + 2 ) % points.length;
+THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
 
-        v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
-        v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
-        v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight );
+	this.bends.push( bendpath );
 
-        return v;
+};
 
-    }
+THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
 
-);
-/**
- * @author mikael emtinger / http://gomo.se/
- */
+	var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
+	var i, il;
 
-THREE.AnimationHandler = (function() {
+	if ( ! bends ) {
 
-	var playing = [];
-	var library = {};
-	var that    = {};
+		bends = this.bends;
 
+	}
 
-	//--- update ---
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
 
-	that.update = function( deltaTimeMS ) {
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
 
-		for( var i = 0; i < playing.length; i ++ )
-			playing[ i ].update( deltaTimeMS );
+	}
 
-	};
+	return oldPts;
 
+};
 
-	//--- add ---
+THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
 
-	that.addToUpdate = function( animation ) {
+	var oldPts = this.getSpacedPoints( segments );
 
-		if ( playing.indexOf( animation ) === -1 )
-			playing.push( animation );
+	var i, il;
 
-	};
+	if ( ! bends ) {
 
+		bends = this.bends;
 
-	//--- remove ---
+	}
 
-	that.removeFromUpdate = function( animation ) {
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
 
-		var index = playing.indexOf( animation );
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
 
-		if( index !== -1 )
-			playing.splice( index, 1 );
+	}
 
-	};
+	return oldPts;
 
+};
 
-	//--- add ---
+// This returns getPoints() bend/wrapped around the contour of a path.
+// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
 
-	that.add = function( data ) {
+THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
 
-		if ( library[ data.name ] !== undefined )
-			console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." );
+	var bounds = this.getBoundingBox();
 
-		library[ data.name ] = data;
-		initData( data );
+	var i, il, p, oldX, oldY, xNorm;
 
-	};
+	for ( i = 0, il = oldPts.length; i < il; i ++ ) {
 
+		p = oldPts[ i ];
 
-	//--- get ---
+		oldX = p.x;
+		oldY = p.y;
 
-	that.get = function( name ) {
+		xNorm = oldX / bounds.maxX;
 
-		if ( typeof name === "string" ) {
+		// 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
 
-			if ( library[ name ] ) {
+		xNorm = path.getUtoTmapping( xNorm, oldX );
 
-				return library[ name ];
+		// check for out of bounds?
 
-			} else {
+		var pathPt = path.getPoint( xNorm );
+		var normal = path.getTangent( xNorm );
+		normal.set( - normal.y, normal.x ).multiplyScalar( oldY );
 
-				console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name );
-				return null;
+		p.x = pathPt.x + normal.x;
+		p.y = pathPt.y + normal.y;
 
-			}
+	}
 
-		} else {
+	return oldPts;
 
-			// todo: add simple tween library
+};
 
-		}
 
-	};
+// File:src/extras/core/Gyroscope.js
 
-	//--- parse ---
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	that.parse = function( root ) {
+THREE.Gyroscope = function () {
 
-		// setup hierarchy
+	THREE.Object3D.call( this );
 
-		var hierarchy = [];
+};
 
-		if ( root instanceof THREE.SkinnedMesh ) {
+THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Gyroscope.prototype.constructor = THREE.Gyroscope;
 
-			for( var b = 0; b < root.bones.length; b++ ) {
+THREE.Gyroscope.prototype.updateMatrixWorld = ( function () {
 
-				hierarchy.push( root.bones[ b ] );
+	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();
 
-		} else {
+	return function ( force ) {
 
-			parseRecurseHierarchy( root, hierarchy );
+		this.matrixAutoUpdate && this.updateMatrix();
 
-		}
+		// update matrixWorld
 
-		return hierarchy;
+		if ( this.matrixWorldNeedsUpdate || force ) {
 
-	};
+			if ( this.parent ) {
 
-	var parseRecurseHierarchy = function( root, hierarchy ) {
+				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
 
-		hierarchy.push( root );
+				this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld );
+				this.matrix.decompose( translationObject, quaternionObject, scaleObject );
 
-		for( var c = 0; c < root.children.length; c++ )
-			parseRecurseHierarchy( root.children[ c ], hierarchy );
+				this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld );
 
-	}
 
+			} else {
 
-	//--- init data ---
+				this.matrixWorld.copy( this.matrix );
 
-	var initData = function( data ) {
+			}
 
-		if( data.initialized === true )
-			return;
 
+			this.matrixWorldNeedsUpdate = false;
 
-		// loop through all keys
+			force = true;
 
-		for( var h = 0; h < data.hierarchy.length; h ++ ) {
+		}
 
-			for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+		// update children
 
-				// remove minus times
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-				if( data.hierarchy[ h ].keys[ k ].time < 0 )
-					data.hierarchy[ h ].keys[ k ].time = 0;
+			this.children[ i ].updateMatrixWorld( force );
 
+		}
 
-				// create quaternions
+	};
+	
+}() );
 
-				if( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
-				 !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
+// File:src/extras/core/Path.js
 
-					var quat = data.hierarchy[ h ].keys[ k ].rot;
-					data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] );
+/**
+ * @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);
 
+	this.actions = [];
 
-			// prepare morph target keys
+	if ( points ) {
 
-			if( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
+		this.fromPoints( points );
 
-				// get all used
+	}
 
-				var usedMorphTargets = {};
+};
 
-				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
+THREE.Path.prototype.constructor = THREE.Path;
 
-					for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+THREE.PathActions = {
 
-						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
-						usedMorphTargets[ morphTargetName ] = -1;
+	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
 
-				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
+THREE.Path.prototype.fromPoints = function ( vectors ) {
 
+	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
 
-				// set all used on all frames
+	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
 
-				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+		this.lineTo( vectors[ v ].x, vectors[ v ].y );
 
-					var influences = {};
+	};
 
-					for ( var morphTargetName in usedMorphTargets ) {
+};
 
-						for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+// startPath() endPath()?
 
-							if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
+THREE.Path.prototype.moveTo = function ( x, y ) {
 
-								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
-								break;
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
 
-							}
+};
 
-						}
+THREE.Path.prototype.lineTo = function ( x, y ) {
 
-						if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
+	var args = Array.prototype.slice.call( arguments );
 
-							influences[ morphTargetName ] = 0;
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-						}
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-					}
+	var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
+	this.curves.push( curve );
 
-					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
+	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
 
-				}
+};
 
-			}
+THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
 
+	var args = Array.prototype.slice.call( arguments );
 
-			// remove all keys that are on the same time
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-			for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-				if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
+	var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
+												new THREE.Vector2( aCPx, aCPy ),
+												new THREE.Vector2( aX, aY ) );
+	this.curves.push( curve );
 
-					data.hierarchy[ h ].keys.splice( k, 1 );
-					k --;
+	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 );
 
-			// set index
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-				data.hierarchy[ h ].keys[ k ].index = k;
+	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 } );
 
-		}
+};
 
+THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
 
-		// JIT
+	var args = Array.prototype.slice.call( arguments );
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-		var lengthInFrames = parseInt( data.length * data.fps, 10 );
+	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 );
 
-		data.JIT = {};
-		data.JIT.hierarchy = [];
+	var curve = new THREE.SplineCurve( npts );
+	this.curves.push( curve );
 
-		for( var h = 0; h < data.hierarchy.length; h ++ )
-			data.JIT.hierarchy.push( new Array( lengthInFrames ) );
+	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
 
+};
 
-		// done
+// FUTURE: Change the API or follow canvas API?
 
-		data.initialized = true;
+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 ];
 
+	this.absarc(aX + x0, aY + y0, aRadius,
+		aStartAngle, aEndAngle, aClockwise );
 
-	// interpolation types
+ };
 
-	that.LINEAR = 0;
-	that.CATMULLROM = 1;
-	that.CATMULLROM_FORWARD = 2;
+ THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
+	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
+ };
 
-	return that;
+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 ];
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
+		aStartAngle, aEndAngle, aClockwise );
 
-THREE.Animation = function ( root, name, interpolationType ) {
+ };
 
-	this.root = root;
-	this.data = THREE.AnimationHandler.get( name );
-	this.hierarchy = THREE.AnimationHandler.parse( root );
 
-	this.currentTime = 0;
-	this.timeScale = 1;
+THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
 
-	this.isPlaying = false;
-	this.isPaused = true;
-	this.loop = true;
+	var args = Array.prototype.slice.call( arguments );
+	var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
+									aStartAngle, aEndAngle, aClockwise );
+	this.curves.push( curve );
 
-	this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
+	var lastPoint = curve.getPoint(1);
+	args.push(lastPoint.x);
+	args.push(lastPoint.y);
 
-	this.points = [];
-	this.target = new THREE.Vector3();
+	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
 
-};
+ };
 
-THREE.Animation.prototype.play = function ( loop, startTimeMS ) {
+THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
 
-	if ( this.isPlaying === false ) {
+	if ( ! divisions ) divisions = 40;
 
-		this.isPlaying = true;
-		this.loop = loop !== undefined ? loop : true;
-		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+	var points = [];
 
-		// reset key cache
+	for ( var i = 0; i < divisions; i ++ ) {
 
-		var h, hl = this.hierarchy.length,
-			object;
+		points.push( this.getPoint( i / divisions ) );
 
-		for ( h = 0; h < hl; h ++ ) {
+		//if( !this.getPoint( i / divisions ) ) throw "DIE";
 
-			object = this.hierarchy[ h ];
+	}
 
-			object.matrixAutoUpdate = true;
+	// if ( closedPath ) {
+	//
+	// 	points.push( points[ 0 ] );
+	//
+	// }
 
-			if ( object.animationCache === undefined ) {
+	return points;
 
-				object.animationCache = {};
-				object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
-				object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
-				object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+};
 
-			}
+/* Return an array of vectors based on contour of the path */
 
-			var prevKey = object.animationCache.prevKey;
-			var nextKey = object.animationCache.nextKey;
+THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
 
-			prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
-			prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
-			prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
+	if (this.useSpacedPoints) {
+		console.log('tata');
+		return this.getSpacedPoints( divisions, closedPath );
+	}
 
-			nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
-			nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
-			nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+	divisions = divisions || 12;
 
-		}
+	var points = [];
 
-		this.update( 0 );
+	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 ++ ) {
 
-	this.isPaused = false;
+		item = this.actions[ i ];
 
-	THREE.AnimationHandler.addToUpdate( this );
+		action = item.action;
+		args = item.args;
 
-};
+		switch ( action ) {
 
+		case THREE.PathActions.MOVE_TO:
 
-THREE.Animation.prototype.pause = function() {
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
 
-	if ( this.isPaused === true ) {
+			break;
 
-		THREE.AnimationHandler.addToUpdate( this );
+		case THREE.PathActions.LINE_TO:
 
-	} else {
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
 
-		THREE.AnimationHandler.removeFromUpdate( this );
+			break;
 
-	}
+		case THREE.PathActions.QUADRATIC_CURVE_TO:
 
-	this.isPaused = !this.isPaused;
+			cpx  = args[ 2 ];
+			cpy  = args[ 3 ];
 
-};
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
 
+			if ( points.length > 0 ) {
 
-THREE.Animation.prototype.stop = function() {
+				laste = points[ points.length - 1 ];
 
-	this.isPlaying = false;
-	this.isPaused  = false;
-	THREE.AnimationHandler.removeFromUpdate( this );
+				cpx0 = laste.x;
+				cpy0 = laste.y;
 
-};
+			} else {
 
+				laste = this.actions[ i - 1 ].args;
 
-THREE.Animation.prototype.update = function ( deltaTimeMS ) {
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
 
-	// early out
+			}
 
-	if ( this.isPlaying === false ) return;
+			for ( j = 1; j <= divisions; j ++ ) {
 
+				t = j / divisions;
 
-	// vars
+				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
 
-	var types = [ "pos", "rot", "scl" ];
-	var type;
-	var scale;
-	var vector;
-	var prevXYZ, nextXYZ;
-	var prevKey, nextKey;
-	var object;
-	var animationCache;
-	var frame;
-	var JIThierarchy = this.data.JIT.hierarchy;
-	var currentTime, unloopedCurrentTime;
-	var currentPoint, forwardPoint, angle;
+				points.push( new THREE.Vector2( tx, ty ) );
 
+			}
 
-	this.currentTime += deltaTimeMS * this.timeScale;
+			break;
 
-	unloopedCurrentTime = this.currentTime;
-	currentTime = this.currentTime = this.currentTime % this.data.length;
-	frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+		case THREE.PathActions.BEZIER_CURVE_TO:
 
+			cpx  = args[ 4 ];
+			cpy  = args[ 5 ];
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
 
-		object = this.hierarchy[ h ];
-		animationCache = object.animationCache;
+			cpx2 = args[ 2 ];
+			cpy2 = args[ 3 ];
 
-		// loop through pos/rot/scl
+			if ( points.length > 0 ) {
 
-		for ( var t = 0; t < 3; t ++ ) {
+				laste = points[ points.length - 1 ];
 
-			// get keys
+				cpx0 = laste.x;
+				cpy0 = laste.y;
 
-			type    = types[ t ];
-			prevKey = animationCache.prevKey[ type ];
-			nextKey = animationCache.nextKey[ type ];
+			} else {
 
-			// switch keys?
+				laste = this.actions[ i - 1 ].args;
 
-			if ( nextKey.time <= unloopedCurrentTime ) {
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
 
-				// did we loop?
+			}
 
-				if ( currentTime < unloopedCurrentTime ) {
 
-					if ( this.loop ) {
+			for ( j = 1; j <= divisions; j ++ ) {
 
-						prevKey = this.data.hierarchy[ h ].keys[ 0 ];
-						nextKey = this.getNextKeyWith( type, h, 1 );
+				t = j / divisions;
 
-						while( nextKey.time < currentTime ) {
+				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
+				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
 
-							prevKey = nextKey;
-							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+				points.push( new THREE.Vector2( tx, ty ) );
 
-						}
+			}
 
-					} else {
+			break;
 
-						this.stop();
-						return;
+		case THREE.PathActions.CSPLINE_THRU:
 
-					}
+			laste = this.actions[ i - 1 ].args;
 
-				} else {
+			var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
+			var spts = [ last ];
 
-					do {
+			var n = divisions * args[ 0 ].length;
 
-						prevKey = nextKey;
-						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+			spts = spts.concat( args[ 0 ] );
 
-					} while( nextKey.time < currentTime )
+			var spline = new THREE.SplineCurve( spts );
 
-				}
+			for ( j = 1; j <= n; j ++ ) {
 
-				animationCache.prevKey[ type ] = prevKey;
-				animationCache.nextKey[ type ] = nextKey;
+				points.push( spline.getPointAt( j / n ) ) ;
 
 			}
 
+			break;
 
-			object.matrixAutoUpdate = true;
-			object.matrixWorldNeedsUpdate = true;
+		case THREE.PathActions.ARC:
 
-			scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
-			prevXYZ = prevKey[ type ];
-			nextXYZ = nextKey[ type ];
+			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;
 
-			// check scale error
+			for ( j = 1; j <= tdivisions; j ++ ) {
 
-			if ( scale < 0 || scale > 1 ) {
+				t = j / tdivisions;
 
-				console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
-				scale = scale < 0 ? 0 : 1;
+				if ( ! aClockwise ) {
 
-			}
+					t = 1 - t;
 
-			// interpolate
+				}
 
-			if ( type === "pos" ) {
+				angle = aStartAngle + t * deltaAngle;
 
-				vector = object.position;
+				tx = aX + aRadius * Math.cos( angle );
+				ty = aY + aRadius * Math.sin( angle );
 
-				if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
 
-					vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-					vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-					vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+				points.push( new THREE.Vector2( tx, ty ) );
 
-				} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-						    this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+			}
 
-					this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
-					this.points[ 1 ] = prevXYZ;
-					this.points[ 2 ] = nextXYZ;
-					this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
+			//console.log(points);
 
-					scale = scale * 0.33 + 0.33;
+			break;
+		  
+		case THREE.PathActions.ELLIPSE:
 
-					currentPoint = this.interpolateCatmullRom( this.points, scale );
+			var aX = args[ 0 ], aY = args[ 1 ],
+				xRadius = args[ 2 ],
+				yRadius = args[ 3 ],
+				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
+				aClockwise = !! args[ 6 ];
 
-					vector.x = currentPoint[ 0 ];
-					vector.y = currentPoint[ 1 ];
-					vector.z = currentPoint[ 2 ];
 
-					if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+			var deltaAngle = aEndAngle - aStartAngle;
+			var angle;
+			var tdivisions = divisions * 2;
 
-						forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
+			for ( j = 1; j <= tdivisions; j ++ ) {
 
-						this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
-						this.target.sub( vector );
-						this.target.y = 0;
-						this.target.normalize();
+				t = j / tdivisions;
 
-						angle = Math.atan2( this.target.x, this.target.z );
-						object.rotation.set( 0, angle, 0 );
+				if ( ! aClockwise ) {
 
-					}
+					t = 1 - t;
 
 				}
 
-			} else if ( type === "rot" ) {
-
-				THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
+				angle = aStartAngle + t * deltaAngle;
 
-			} else if ( type === "scl" ) {
+				tx = aX + xRadius * Math.cos( angle );
+				ty = aY + yRadius * Math.sin( angle );
 
-				vector = object.scale;
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
 
-				vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-				vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-				vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+				points.push( new THREE.Vector2( tx, ty ) );
 
 			}
 
-		}
-
-	}
-
-};
+			//console.log(points);
 
-// Catmull-Rom spline
+			break;
 
-THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) {
+		} // end switch
 
-	var c = [], v3 = [],
-	point, intPoint, weight, w2, w3,
-	pa, pb, pc, pd;
+	}
 
-	point = ( points.length - 1 ) * scale;
-	intPoint = Math.floor( point );
-	weight = point - intPoint;
 
-	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;
 
-	pa = points[ c[ 0 ] ];
-	pb = points[ c[ 1 ] ];
-	pc = points[ c[ 2 ] ];
-	pd = points[ c[ 3 ] ];
+	// 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 ) {
 
-	w2 = weight * weight;
-	w3 = weight * w2;
+		points.push( points[ 0 ] );
 
-	v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
-	v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
-	v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
+	}
 
-	return v3;
+	return points;
 
 };
 
-THREE.Animation.prototype.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
+//
+// 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
+//
 
-	var v0 = ( p2 - p0 ) * 0.5,
-		v1 = ( p3 - p1 ) * 0.5;
+THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
 
-	return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+	function extractSubpaths( inActions ) {
 
-};
+		var i, il, item, action, args;
 
+		var subPaths = [], lastPath = new THREE.Path();
 
+		for ( i = 0, il = inActions.length; i < il; i ++ ) {
 
-// Get next key with
+			item = inActions[ i ];
 
-THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
+			args = item.args;
+			action = item.action;
 
-	var keys = this.data.hierarchy[ h ].keys;
+			if ( action == THREE.PathActions.MOVE_TO ) {
 
-	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+				if ( lastPath.actions.length != 0 ) {
 
-		key = key < keys.length - 1 ? key : keys.length - 1;
+					subPaths.push( lastPath );
+					lastPath = new THREE.Path();
 
-	} else {
+				}
 
-		key = key % keys.length;
+			}
 
-	}
+			lastPath[ action ].apply( lastPath, args );
 
-	for ( ; key < keys.length; key++ ) {
+		}
 
-		if ( keys[ key ][ type ] !== undefined ) {
+		if ( lastPath.actions.length != 0 ) {
 
-			return keys[ key ];
+			subPaths.push( lastPath );
 
 		}
 
-	}
+		// console.log(subPaths);
 
-	return this.data.hierarchy[ h ].keys[ 0 ];
+		return	subPaths;
+	}
 
-};
+	function toShapesNoHoles( inSubpaths ) {
 
-// Get previous key with
+		var shapes = [];
 
-THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
+		for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) {
 
-	var keys = this.data.hierarchy[ h ].keys;
+			var tmpPath = inSubpaths[ i ];
 
-	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+			var tmpShape = new THREE.Shape();
+			tmpShape.actions = tmpPath.actions;
+			tmpShape.curves = tmpPath.curves;
 
-		key = key > 0 ? key : 0;
+			shapes.push( tmpShape );
+		}
 
-	} else {
+		//console.log("shape", shapes);
 
-		key = key >= 0 ? key : key + keys.length;
+		return shapes;
+	};
 
-	}
+	function isPointInsidePolygon( inPt, inPolygon ) {
+		var EPSILON = 0.0000000001;
 
+		var polyLen = inPolygon.length;
 
-	for ( ; key >= 0; key -- ) {
+		// 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 ];
 
-		if ( keys[ key ][ type ] !== undefined ) {
+			var edgeDx = edgeHighPt.x - edgeLowPt.x;
+			var edgeDy = edgeHighPt.y - edgeLowPt.y;
 
-			return keys[ key ];
+			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;
+			}
 		}
 
+		return	inside;
 	}
 
-	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
 
-};
+	var subPaths = extractSubpaths( this.actions );
+	if ( subPaths.length == 0 ) return [];
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author khang duong
- * @author erik kitson
- */
+	if ( noHoles === true )	return	toShapesNoHoles( subPaths );
 
-THREE.KeyFrameAnimation = function( root, data, JITCompile ) {
 
-	this.root = root;
-	this.data = THREE.AnimationHandler.get( data );
-	this.hierarchy = THREE.AnimationHandler.parse( root );
-	this.currentTime = 0;
-	this.timeScale = 0.001;
-	this.isPlaying = false;
-	this.isPaused = true;
-	this.loop = true;
-	this.JITCompile = JITCompile !== undefined ? JITCompile : true;
+	var solid, tmpPath, tmpShape, shapes = [];
 
-	// initialize to first keyframes
+	if ( subPaths.length == 1) {
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+		tmpPath = subPaths[0];
+		tmpShape = new THREE.Shape();
+		tmpShape.actions = tmpPath.actions;
+		tmpShape.curves = tmpPath.curves;
+		shapes.push( tmpShape );
+		return shapes;
 
-		var keys = this.data.hierarchy[h].keys,
-			sids = this.data.hierarchy[h].sids,
-			obj = this.hierarchy[h];
+	}
 
-		if ( keys.length && sids ) {
+	var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
+	holesFirst = isCCW ? ! holesFirst : holesFirst;
 
-			for ( var s = 0; s < sids.length; s++ ) {
+	// console.log("Holes first", holesFirst);
+	
+	var betterShapeHoles = [];
+	var newShapes = [];
+	var newShapeHoles = [];
+	var mainIdx = 0;
+	var tmpPoints;
 
-				var sid = sids[ s ],
-					next = this.getNextKeyWith( sid, h, 0 );
+	newShapes[mainIdx] = undefined;
+	newShapeHoles[mainIdx] = [];
 
-				if ( next ) {
+	var i, il;
 
-					next.apply( sid );
+	for ( i = 0, il = subPaths.length; i < il; i ++ ) {
 
-				}
+		tmpPath = subPaths[ i ];
+		tmpPoints = tmpPath.getPoints();
+		solid = THREE.Shape.Utils.isClockWise( tmpPoints );
+		solid = isCCW ? ! solid : solid;
 
-			}
+		if ( solid ) {
 
-			obj.matrixAutoUpdate = false;
-			this.data.hierarchy[h].node.updateMatrix();
-			obj.matrixWorldNeedsUpdate = true;
+			if ( (! holesFirst ) && ( newShapes[mainIdx] ) )	mainIdx ++;
 
-		}
+			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] = [];
 
-	}
+			//console.log('cw', i);
 
-};
+		} else {
 
-// Play
+			newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } );
 
-THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
+			//console.log('ccw', i);
 
-	if( !this.isPlaying ) {
+		}
 
-		this.isPlaying = true;
-		this.loop = loop !== undefined ? loop : true;
-		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
-		this.startTimeMs = startTimeMS;
-		this.startTime = 10000000;
-		this.endTime = -this.startTime;
+	}
 
+	// only Holes? -> probably all Shapes with wrong orientation
+	if ( ! newShapes[0] )	return	toShapesNoHoles( subPaths );
 
-		// reset key cache
 
-		var h, hl = this.hierarchy.length,
-			object,
-			node;
+	if ( newShapes.length > 1 ) {
+		var ambigious = false;
+		var toChange = [];
 
-		for ( h = 0; h < hl; h++ ) {
+		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+			betterShapeHoles[sIdx] = [];
+		}
+		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; 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;
+		}
+	}
 
-			object = this.hierarchy[ h ];
-			node = this.data.hierarchy[ h ];
+	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 );
+		}
+	}
 
-			if ( node.animationCache === undefined ) {
+	//console.log("shape", shapes);
 
-				node.animationCache = {};
-				node.animationCache.prevKey = null;
-				node.animationCache.nextKey = null;
-				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+	return shapes;
 
-			}
+};
 
-			var keys = this.data.hierarchy[h].keys;
+// File:src/extras/core/Shape.js
 
-			if (keys.length) {
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
 
-				node.animationCache.prevKey = keys[ 0 ];
-				node.animationCache.nextKey = keys[ 1 ];
+// 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.
 
-				this.startTime = Math.min( keys[0].time, this.startTime );
-				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+THREE.Shape = function () {
 
-			}
+	THREE.Path.apply( this, arguments );
+	this.holes = [];
 
-		}
+};
 
-		this.update( 0 );
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
+THREE.Shape.prototype.constructor = THREE.Shape;
 
-	}
+// Convenience method to return ExtrudeGeometry
 
-	this.isPaused = false;
+THREE.Shape.prototype.extrude = function ( options ) {
 
-	THREE.AnimationHandler.addToUpdate( this );
+	var extruded = new THREE.ExtrudeGeometry( this, options );
+	return extruded;
 
 };
 
+// Convenience method to return ShapeGeometry
 
+THREE.Shape.prototype.makeGeometry = function ( options ) {
 
-// Pause
+	var geometry = new THREE.ShapeGeometry( this, options );
+	return geometry;
 
-THREE.KeyFrameAnimation.prototype.pause = function() {
+};
 
-	if( this.isPaused ) {
+// Get points of holes
 
-		THREE.AnimationHandler.addToUpdate( this );
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
 
-	} else {
+	var i, il = this.holes.length, holesPts = [];
+
+	for ( i = 0; i < il; i ++ ) {
 
-		THREE.AnimationHandler.removeFromUpdate( this );
+		holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
 
 	}
 
-	this.isPaused = !this.isPaused;
+	return holesPts;
 
 };
 
+// Get points of holes (spaced by regular distance)
 
-// Stop
-
-THREE.KeyFrameAnimation.prototype.stop = function() {
-
-	this.isPlaying = false;
-	this.isPaused  = false;
-	THREE.AnimationHandler.removeFromUpdate( this );
-
-
-	// reset JIT matrix and remove cache
+THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
 
-	for ( var h = 0; h < this.data.hierarchy.length; h++ ) {
+	var i, il = this.holes.length, holesPts = [];
 
-        var obj = this.hierarchy[ h ];
-		var node = this.data.hierarchy[ h ];
+	for ( i = 0; i < il; i ++ ) {
 
-		if ( node.animationCache !== undefined ) {
+		holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
 
-			var original = node.animationCache.originalMatrix;
+	}
 
-			if( obj instanceof THREE.Bone ) {
+	return holesPts;
 
-				original.copy( obj.skinMatrix );
-				obj.skinMatrix = original;
+};
 
-			} else {
 
-				original.copy( obj.matrix );
-				obj.matrix = original;
+// Get points of shape and holes (keypoints based on segments parameter)
 
-			}
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
 
-			delete node.animationCache;
+	return {
 
-		}
+		shape: this.getTransformedPoints( divisions ),
+		holes: this.getPointsHoles( divisions )
 
-	}
+	};
 
 };
 
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
 
-// Update
-
-THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) {
+	if (this.useSpacedPoints) {
+		return this.extractAllSpacedPoints(divisions);
+	}
 
-	// early out
+	return this.extractAllPoints(divisions);
 
-	if( !this.isPlaying ) return;
+};
 
+//
+// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
+//
+// 	return {
+//
+// 		shape: this.transform( bend, divisions ),
+// 		holes: this.getPointsHoles( divisions, bend )
+//
+// 	};
+//
+// };
 
-	// vars
+// Get points of shape and holes (spaced by regular distance)
 
-	var prevKey, nextKey;
-	var object;
-	var node;
-	var frame;
-	var JIThierarchy = this.data.JIT.hierarchy;
-	var currentTime, unloopedCurrentTime;
-	var looped;
+THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
 
+	return {
 
-	// update
+		shape: this.getTransformedSpacedPoints( divisions ),
+		holes: this.getSpacedPointsHoles( divisions )
 
-	this.currentTime += deltaTimeMS * this.timeScale;
+	};
 
-	unloopedCurrentTime = this.currentTime;
-	currentTime         = this.currentTime = this.currentTime % this.data.length;
+};
 
-	// if looped around, the current time should be based on the startTime
-	if ( currentTime < this.startTimeMs ) {
+/**************************************************************
+ *	Utils
+ **************************************************************/
 
-		currentTime = this.currentTime = this.startTimeMs + currentTime;
+THREE.Shape.Utils = {
 
-	}
+	triangulateShape: function ( contour, holes ) {
 
-	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
-	looped 				= currentTime < unloopedCurrentTime;
+		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 ) );
+				}
+			}
+		}
 
-	if ( looped && !this.loop ) {
+		function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
+			var EPSILON = 0.0000000001;
 
-		// Set the animation to the last keyframes and stop
-		for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+			var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x,   seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
+			var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x,   seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
 
-			var keys = this.data.hierarchy[h].keys,
-				sids = this.data.hierarchy[h].sids,
-				end = keys.length-1,
-				obj = this.hierarchy[h];
+			var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
+			var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
 
-			if ( keys.length ) {
+			var limit		= seg1dy * seg2dx - seg1dx * seg2dy;
+			var perpSeg1	= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
 
-				for ( var s = 0; s < sids.length; s++ ) {
+			if ( Math.abs(limit) > EPSILON ) {			// not parallel
 
-					var sid = sids[ s ],
-						prev = this.getPrevKeyWith( sid, h, end );
+				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 [];
+				}
 
-					if ( prev ) {
-						prev.apply( sid );
+				// 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 ];
+
+				// 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 [];
+
+				// 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;
 					}
-
 				}
-
-				this.data.hierarchy[h].node.updateMatrix();
-				obj.matrixWorldNeedsUpdate = true;
-
+				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.stop();
-		return;
-
-	}
-
-	// check pre-infinity
-	if ( currentTime < this.startTime ) {
+		function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
+			// The order of legs is important
 
-		return;
+			var EPSILON = 0.0000000001;
 
-	}
+			// 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;
 
-	// update
+			// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
+			var from2toAngle	= legFromPtX * legToPtY - legFromPtY * legToPtX;
+			var from2otherAngle	= legFromPtX * otherPtY - legFromPtY * otherPtX;
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+			if ( Math.abs(from2toAngle) > EPSILON ) {			// angle != 180 deg.
 
-		object = this.hierarchy[ h ];
-		node = this.data.hierarchy[ h ];
+				var other2toAngle		= otherPtX * legToPtY - otherPtY * legToPtX;
+				// console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
 
-		var keys = node.keys,
-			animationCache = node.animationCache;
+				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 );
+			}
+		}
 
-		// use JIT?
 
-		if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
+		function removeHoles( contour, holes ) {
 
-			if( object instanceof THREE.Bone ) {
+			var shape = contour.concat(); // work on this shape
+			var hole;
 
-				object.skinMatrix = JIThierarchy[ h ][ frame ];
-				object.matrixWorldNeedsUpdate = false;
+			function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
+				// Check if hole point lies within angle around shape point
+				var lastShapeIdx = shape.length - 1;
 
-			} else {
+				var prevShapeIdx = inShapeIdx - 1;
+				if ( prevShapeIdx < 0 )			prevShapeIdx = lastShapeIdx;
 
-				object.matrix = JIThierarchy[ h ][ frame ];
-				object.matrixWorldNeedsUpdate = true;
+				var nextShapeIdx = inShapeIdx + 1;
+				if ( nextShapeIdx > lastShapeIdx )	nextShapeIdx = 0;
 
-			}
+				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;
+				}
 
-		// use interpolation
+				// Check if shape point lies within angle around hole point
+				var lastHoleIdx = hole.length - 1;
 
-		} else if ( keys.length ) {
+				var prevHoleIdx = inHoleIdx - 1;
+				if ( prevHoleIdx < 0 )			prevHoleIdx = lastHoleIdx;
 
-			// make sure so original matrix and not JIT matrix is set
+				var nextHoleIdx = inHoleIdx + 1;
+				if ( nextHoleIdx > lastHoleIdx )	nextHoleIdx = 0;
 
-			if ( this.JITCompile && animationCache ) {
+				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;
+				}
 
-				if( object instanceof THREE.Bone ) {
+				return	true;
+			}
 
-					object.skinMatrix = animationCache.originalMatrix;
+			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;
+				}
 
-				} else {
+				return	false;
+			}
 
-					object.matrix = animationCache.originalMatrix;
+			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;
 			}
 
-			prevKey = animationCache.prevKey;
-			nextKey = animationCache.nextKey;
+			var holeIndex, shapeIndex,
+				shapePt, holePt,
+				holeIdx, cutKey, failedCuts = [],
+				tmpShape1, tmpShape2,
+				tmpHole1, tmpHole2;
+
+			for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
 
-			if ( prevKey && nextKey ) {
+				indepHoles.push( h );
 
-				// switch keys?
+			}
 
-				if ( nextKey.time <= unloopedCurrentTime ) {
+			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;
+				}
 
-					// did we loop?
+				// search for shape-vertex and hole-vertex,
+				// which can be connected without intersections
+				for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
 
-					if ( looped && this.loop ) {
+					shapePt = shape[ shapeIndex ];
+					holeIndex	= - 1;
 
-						prevKey = keys[ 0 ];
-						nextKey = keys[ 1 ];
+					// search for hole which can be reached without intersections
+					for ( var h = 0; h < indepHoles.length; h ++ ) {
+						holeIdx = indepHoles[h];
 
-						while ( nextKey.time < currentTime ) {
+						// prevent multiple checks
+						cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
+						if ( failedCuts[cutKey] !== undefined )			continue;
 
-							prevKey = nextKey;
-							nextKey = keys[ prevKey.index + 1 ];
+						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);
 
-					} else if ( !looped ) {
+							tmpShape1 = shape.slice( 0, shapeIndex + 1 );
+							tmpShape2 = shape.slice( shapeIndex );
+							tmpHole1 = hole.slice( holeIndex );
+							tmpHole2 = hole.slice( 0, holeIndex + 1 );
 
-						var lastIndex = keys.length - 1;
+							shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
 
-						while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
+							minShapeIndex = shapeIndex;
 
-							prevKey = nextKey;
-							nextKey = keys[ prevKey.index + 1 ];
+							// 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
 					}
-
-					animationCache.prevKey = prevKey;
-					animationCache.nextKey = nextKey;
-
+					if ( holeIndex >= 0 )	break;		// hole-vertex found
 				}
-                if(nextKey.time >= currentTime)
-                    prevKey.interpolate( nextKey, currentTime );
-                else
-                    prevKey.interpolate( nextKey, nextKey.time);
-
 			}
 
-			this.data.hierarchy[h].node.updateMatrix();
-			object.matrixWorldNeedsUpdate = true;
-
+			return shape; 			/* shape with no holes */
 		}
 
-	}
-
-	// update JIT?
-
-	if ( this.JITCompile ) {
 
-		if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
-
-			this.hierarchy[ 0 ].updateMatrixWorld( true );
-
-			for ( var h = 0; h < this.hierarchy.length; h++ ) {
-
-				if( this.hierarchy[ h ] instanceof THREE.Bone ) {
-
-					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
+		var i, il, f, face,
+			key, index,
+			allPointsMap = {};
 
-				} else {
+		// 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.
 
-					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
+		var allpoints = contour.concat();
 
-				}
+		for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
 
-			}
+			Array.prototype.push.apply( allpoints, holes[h] );
 
 		}
 
-	}
+		//console.log( "allpoints",allpoints, allpoints.length );
 
-};
+		// prepare all points map
 
-// Get next key with
+		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
 
-THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
+			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
 
-	var keys = this.data.hierarchy[ h ].keys;
-	key = key % keys.length;
+			if ( allPointsMap[ key ] !== undefined ) {
 
-	for ( ; key < keys.length; key++ ) {
+				THREE.warn( "THREE.Shape: Duplicate point", key );
 
-		if ( keys[ key ].hasTarget( sid ) ) {
+			}
 
-			return keys[ key ];
+			allPointsMap[ key ] = i;
 
 		}
 
-	}
+		// remove holes by cutting paths to holes and adding them to the shape
+		var shapeWithoutHoles = removeHoles( contour, holes );
 
-	return keys[ 0 ];
+		var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
+		//console.log( "triangles",triangles, triangles.length );
 
-};
+		// check all face vertices against all points map
 
-// Get previous key with
+		for ( i = 0, il = triangles.length; i < il; i ++ ) {
 
-THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
+			face = triangles[ i ];
 
-	var keys = this.data.hierarchy[ h ].keys;
-	key = key >= 0 ? key : key + keys.length;
+			for ( f = 0; f < 3; f ++ ) {
 
-	for ( ; key >= 0; key-- ) {
+				key = face[ f ].x + ":" + face[ f ].y;
 
-		if ( keys[ key ].hasTarget( sid ) ) {
+				index = allPointsMap[ key ];
 
-			return keys[ key ];
+				if ( index !== undefined ) {
 
-		}
+					face[ f ] = index;
 
-	}
+				}
 
-	return keys[ keys.length - 1 ];
+			}
 
-};
+		}
 
-/**
- * Camera for rendering cube maps
- *	- renders scene into axis-aligned cube
- *
- * @author alteredq / http://alteredqualia.com/
- */
+		return triangles.concat();
 
-THREE.CubeCamera = function ( near, far, cubeResolution ) {
+	},
 
-	THREE.Object3D.call( this );
+	isClockWise: function ( pts ) {
 
-	var fov = 90, aspect = 1;
+		return THREE.FontUtils.Triangulate.area( pts ) < 0;
 
-	var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraPX.up.set( 0, -1, 0 );
-	cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
-	this.add( cameraPX );
+	},
 
-	var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraNX.up.set( 0, -1, 0 );
-	cameraNX.lookAt( new THREE.Vector3( -1, 0, 0 ) );
-	this.add( cameraNX );
+	// Bezier Curves formulas obtained from
+	// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
 
-	var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraPY.up.set( 0, 0, 1 );
-	cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
-	this.add( cameraPY );
+	// Quad Bezier Functions
 
-	var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraNY.up.set( 0, 0, -1 );
-	cameraNY.lookAt( new THREE.Vector3( 0, -1, 0 ) );
-	this.add( cameraNY );
+	b2p0: function ( t, p ) {
 
-	var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraPZ.up.set( 0, -1, 0 );
-	cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
-	this.add( cameraPZ );
+		var k = 1 - t;
+		return k * k * p;
 
-	var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
-	cameraNZ.up.set( 0, -1, 0 );
-	cameraNZ.lookAt( new THREE.Vector3( 0, 0, -1 ) );
-	this.add( cameraNZ );
+	},
 
-	this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
+	b2p1: function ( t, p ) {
 
-	this.updateCubeMap = function ( renderer, scene ) {
+		return 2 * ( 1 - t ) * t * p;
 
-		var renderTarget = this.renderTarget;
-		var generateMipmaps = renderTarget.generateMipmaps;
+	},
 
-		renderTarget.generateMipmaps = false;
+	b2p2: function ( t, p ) {
 
-		renderTarget.activeCubeFace = 0;
-		renderer.render( scene, cameraPX, renderTarget );
+		return t * t * p;
 
-		renderTarget.activeCubeFace = 1;
-		renderer.render( scene, cameraNX, renderTarget );
+	},
 
-		renderTarget.activeCubeFace = 2;
-		renderer.render( scene, cameraPY, renderTarget );
+	b2: function ( t, p0, p1, p2 ) {
 
-		renderTarget.activeCubeFace = 3;
-		renderer.render( scene, cameraNY, renderTarget );
+		return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
 
-		renderTarget.activeCubeFace = 4;
-		renderer.render( scene, cameraPZ, renderTarget );
+	},
 
-		renderTarget.generateMipmaps = generateMipmaps;
+	// Cubic Bezier Functions
 
-		renderTarget.activeCubeFace = 5;
-		renderer.render( scene, cameraNZ, renderTarget );
+	b3p0: function ( t, p ) {
 
-	};
+		var k = 1 - t;
+		return k * k * k * p;
 
-};
+	},
 
-THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
+	b3p1: function ( t, p ) {
 
-/*
- *	@author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
- *
- *	A general perpose camera, for setting FOV, Lens Focal Length,
- *		and switching between perspective and orthographic views easily.
- *		Use this only if you do not wish to manage
- *		both a Orthographic and Perspective Camera
- *
- */
+		var k = 1 - t;
+		return 3 * k * k * t * p;
 
+	},
 
-THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) {
+	b3p2: function ( t, p ) {
 
-	THREE.Camera.call( this );
+		var k = 1 - t;
+		return 3 * k * t * t * p;
 
-	this.fov = fov;
+	},
 
-	this.left = -width / 2;
-	this.right = width / 2
-	this.top = height / 2;
-	this.bottom = -height / 2;
+	b3p3: function ( t, p ) {
 
-	// We could also handle the projectionMatrix internally, but just wanted to test nested camera objects
+		return t * t * t * p;
 
-	this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 	orthoNear, orthoFar );
-	this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far );
+	},
 
-	this.zoom = 1;
+	b3: function ( t, p0, p1, p2, p3 ) {
 
-	this.toPerspective();
+		return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) +  this.b3p3( t, p3 );
 
-	var aspect = width/height;
+	}
 
 };
 
-THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype );
-
-THREE.CombinedCamera.prototype.toPerspective = function () {
 
-	// Switches to the Perspective Camera
+// File:src/extras/curves/LineCurve.js
 
-	this.near = this.cameraP.near;
-	this.far = this.cameraP.far;
-
-	this.cameraP.fov =  this.fov / this.zoom ;
-
-	this.cameraP.updateProjectionMatrix();
+/**************************************************************
+ *	Line
+ **************************************************************/
 
-	this.projectionMatrix = this.cameraP.projectionMatrix;
+THREE.LineCurve = function ( v1, v2 ) {
 
-	this.inPerspectiveMode = true;
-	this.inOrthographicMode = false;
+	this.v1 = v1;
+	this.v2 = v2;
 
 };
 
-THREE.CombinedCamera.prototype.toOrthographic = function () {
-
-	// Switches to the Orthographic camera estimating viewport from Perspective
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.LineCurve.prototype.constructor = THREE.LineCurve;
 
-	var fov = this.fov;
-	var aspect = this.cameraP.aspect;
-	var near = this.cameraP.near;
-	var far = this.cameraP.far;
+THREE.LineCurve.prototype.getPoint = function ( t ) {
 
-	// The size that we set is the mid plane of the viewing frustum
+	var point = this.v2.clone().sub(this.v1);
+	point.multiplyScalar( t ).add( this.v1 );
 
-	var hyperfocus = ( near + far ) / 2;
+	return point;
 
-	var halfHeight = Math.tan( fov / 2 ) * hyperfocus;
-	var planeHeight = 2 * halfHeight;
-	var planeWidth = planeHeight * aspect;
-	var halfWidth = planeWidth / 2;
+};
 
-	halfHeight /= this.zoom;
-	halfWidth /= this.zoom;
+// Line curve is linear, so we can overwrite default getPointAt
 
-	this.cameraO.left = -halfWidth;
-	this.cameraO.right = halfWidth;
-	this.cameraO.top = halfHeight;
-	this.cameraO.bottom = -halfHeight;
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
 
-	// this.cameraO.left = -farHalfWidth;
-	// this.cameraO.right = farHalfWidth;
-	// this.cameraO.top = farHalfHeight;
-	// this.cameraO.bottom = -farHalfHeight;
+	return this.getPoint( u );
 
-	// this.cameraO.left = this.left / this.zoom;
-	// this.cameraO.right = this.right / this.zoom;
-	// this.cameraO.top = this.top / this.zoom;
-	// this.cameraO.bottom = this.bottom / this.zoom;
+};
 
-	this.cameraO.updateProjectionMatrix();
+THREE.LineCurve.prototype.getTangent = function( t ) {
 
-	this.near = this.cameraO.near;
-	this.far = this.cameraO.far;
-	this.projectionMatrix = this.cameraO.projectionMatrix;
+	var tangent = this.v2.clone().sub(this.v1);
 
-	this.inPerspectiveMode = false;
-	this.inOrthographicMode = true;
+	return tangent.normalize();
 
 };
 
+// File:src/extras/curves/QuadraticBezierCurve.js
 
-THREE.CombinedCamera.prototype.setSize = function( width, height ) {
+/**************************************************************
+ *	Quadratic Bezier curve
+ **************************************************************/
 
-	this.cameraP.aspect = width / height;
-	this.left = -width / 2;
-	this.right = width / 2
-	this.top = height / 2;
-	this.bottom = -height / 2;
 
-};
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
 
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
 
-THREE.CombinedCamera.prototype.setFov = function( fov ) {
+};
 
-	this.fov = fov;
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve;
 
-	if ( this.inPerspectiveMode ) {
 
-		this.toPerspective();
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
 
-	} else {
+	var vector = new THREE.Vector2();
 
-		this.toOrthographic();
+	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;
 
 };
 
-// For mantaining similar API with PerspectiveCamera
-
-THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
 
-	if ( this.inPerspectiveMode ) {
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
 
-		this.toPerspective();
+	var vector = new THREE.Vector2();
 
-	} else {
+	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.toPerspective();
-		this.toOrthographic();
+	// returns unit vector
 
-	}
+	return vector.normalize();
 
 };
 
-/*
-* Uses Focal Length (in mm) to estimate and set FOV
-* 35mm (fullframe) camera is used if frame size is not specified;
-* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
-*/
-THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+// File:src/extras/curves/CubicBezierCurve.js
 
-	if ( frameHeight === undefined ) frameHeight = 24;
+/**************************************************************
+ *	Cubic Bezier curve
+ **************************************************************/
 
-	var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
 
-	this.setFov( fov );
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
+	this.v3 = v3;
 
-	return fov;
 };
 
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve;
 
-THREE.CombinedCamera.prototype.setZoom = function( zoom ) {
-
-	this.zoom = zoom;
-
-	if ( this.inPerspectiveMode ) {
-
-		this.toPerspective();
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
 
-	} else {
+	var tx, ty;
 
-		this.toOrthographic();
+	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 );
 
 };
 
-THREE.CombinedCamera.prototype.toFrontView = function() {
+THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
+
+	var tx, ty;
 
-	this.rotation.x = 0;
-	this.rotation.y = 0;
-	this.rotation.z = 0;
+	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 );
 
-	// should we be modifing the matrix instead?
+	var tangent = new THREE.Vector2( tx, ty );
+	tangent.normalize();
 
-	this.rotationAutoUpdate = false;
+	return tangent;
 
 };
 
-THREE.CombinedCamera.prototype.toBackView = function() {
+// File:src/extras/curves/SplineCurve.js
 
-	this.rotation.x = 0;
-	this.rotation.y = Math.PI;
-	this.rotation.z = 0;
-	this.rotationAutoUpdate = false;
-
-};
+/**************************************************************
+ *	Spline curve
+ **************************************************************/
 
-THREE.CombinedCamera.prototype.toLeftView = function() {
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
 
-	this.rotation.x = 0;
-	this.rotation.y = - Math.PI / 2;
-	this.rotation.z = 0;
-	this.rotationAutoUpdate = false;
+	this.points = ( points == undefined ) ? [] : points;
 
 };
 
-THREE.CombinedCamera.prototype.toRightView = function() {
+THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.SplineCurve.prototype.constructor = THREE.SplineCurve;
 
-	this.rotation.x = 0;
-	this.rotation.y = Math.PI / 2;
-	this.rotation.z = 0;
-	this.rotationAutoUpdate = false;
+THREE.SplineCurve.prototype.getPoint = function ( t ) {
 
-};
+	var points = this.points;
+	var point = ( points.length - 1 ) * t;
 
-THREE.CombinedCamera.prototype.toTopView = function() {
+	var intPoint = Math.floor( point );
+	var weight = point - intPoint;
 
-	this.rotation.x = - Math.PI / 2;
-	this.rotation.y = 0;
-	this.rotation.z = 0;
-	this.rotationAutoUpdate = false;
+	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();
 
-THREE.CombinedCamera.prototype.toBottomView = function() {
+	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 );
 
-	this.rotation.x = Math.PI / 2;
-	this.rotation.y = 0;
-	this.rotation.z = 0;
-	this.rotationAutoUpdate = false;
+	return vector;
 
 };
 
+// File:src/extras/curves/EllipseCurve.js
 
-/**
- * @author hughes
- */
+/**************************************************************
+ *	Ellipse curve
+ **************************************************************/
 
-THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
 
-	THREE.Geometry.call( this );
+	this.aX = aX;
+	this.aY = aY;
 
-	this.radius = radius = radius || 50;
-	this.segments = segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+	this.xRadius = xRadius;
+	this.yRadius = yRadius;
 
-	this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	this.thetaLength = thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+	this.aStartAngle = aStartAngle;
+	this.aEndAngle = aEndAngle;
 
-	var i, uvs = [],
-	center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
+	this.aClockwise = aClockwise;
 
-	this.vertices.push(center);
-	uvs.push( centerUV );
+};
 
-	for ( i = 0; i <= segments; i ++ ) {
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve;
 
-		var vertex = new THREE.Vector3();
-		var segment = thetaStart + i / segments * thetaLength;
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
 
-		vertex.x = radius * Math.cos( segment );
-		vertex.y = radius * Math.sin( segment );
+	var deltaAngle = this.aEndAngle - this.aStartAngle;
 
-		this.vertices.push( vertex );
-		uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
+	if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
+	if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
 
-	}
+	var angle;
 
-	var n = new THREE.Vector3( 0, 0, 1 );
+	if ( this.aClockwise === true ) {
 
-	for ( i = 1; i <= segments; i ++ ) {
+		angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
 
-		var v1 = i;
-		var v2 = i + 1 ;
-		var v3 = 0;
+	} else {
 
-		this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
-		this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] );
+		angle = this.aStartAngle + t * deltaAngle;
 
 	}
+	
+	var vector = new THREE.Vector2();
 
-	this.computeCentroids();
-	this.computeFaceNormals();
+	vector.x = this.aX + this.xRadius * Math.cos( angle );
+	vector.y = this.aY + this.yRadius * Math.sin( angle );
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	return vector;
 
 };
 
-THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
- */
-
-THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
-
-	THREE.Geometry.call( this );
+// File:src/extras/curves/ArcCurve.js
 
-	var scope = this;
+/**************************************************************
+ *	Arc curve
+ **************************************************************/
 
-	this.width = width;
-	this.height = height;
-	this.depth = depth;
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
 
-	this.widthSegments = widthSegments || 1;
-	this.heightSegments = heightSegments || 1;
-	this.depthSegments = depthSegments || 1;
+	THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+};
 
-	var width_half = this.width / 2;
-	var height_half = this.height / 2;
-	var depth_half = this.depth / 2;
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
+THREE.ArcCurve.prototype.constructor = THREE.ArcCurve;
 
-	buildPlane( 'z', 'y', - 1, - 1, this.depth, this.height, width_half, 0 ); // px
-	buildPlane( 'z', 'y',   1, - 1, this.depth, this.height, - width_half, 1 ); // nx
-	buildPlane( 'x', 'z',   1,   1, this.width, this.depth, height_half, 2 ); // py
-	buildPlane( 'x', 'z',   1, - 1, this.width, this.depth, - height_half, 3 ); // ny
-	buildPlane( 'x', 'y',   1, - 1, this.width, this.height, depth_half, 4 ); // pz
-	buildPlane( 'x', 'y', - 1, - 1, this.width, this.height, - depth_half, 5 ); // nz
+// File:src/extras/curves/LineCurve3.js
 
-	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+/**************************************************************
+ *	Line3D
+ **************************************************************/
 
-		var w, ix, iy,
-		gridX = scope.widthSegments,
-		gridY = scope.heightSegments,
-		width_half = width / 2,
-		height_half = height / 2,
-		offset = scope.vertices.length;
+THREE.LineCurve3 = THREE.Curve.create(
 
-		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+	function ( v1, v2 ) {
 
-			w = 'z';
+		this.v1 = v1;
+		this.v2 = v2;
 
-		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+	},
 
-			w = 'y';
-			gridY = scope.depthSegments;
+	function ( t ) {
 
-		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+		var vector = new THREE.Vector3();
 
-			w = 'x';
-			gridX = scope.depthSegments;
+		vector.subVectors( this.v2, this.v1 ); // diff
+		vector.multiplyScalar( t );
+		vector.add( this.v1 );
 
-		}
+		return vector;
 
-		var gridX1 = gridX + 1,
-		gridY1 = gridY + 1,
-		segment_width = width / gridX,
-		segment_height = height / gridY,
-		normal = new THREE.Vector3();
+	}
 
-		normal[ w ] = depth > 0 ? 1 : - 1;
+);
 
-		for ( iy = 0; iy < gridY1; iy ++ ) {
+// File:src/extras/curves/QuadraticBezierCurve3.js
 
-			for ( ix = 0; ix < gridX1; ix ++ ) {
+/**************************************************************
+ *	Quadratic Bezier 3D curve
+ **************************************************************/
 
-				var vector = new THREE.Vector3();
-				vector[ u ] = ( ix * segment_width - width_half ) * udir;
-				vector[ v ] = ( iy * segment_height - height_half ) * vdir;
-				vector[ w ] = depth;
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
 
-				scope.vertices.push( vector );
+	function ( v0, v1, v2 ) {
 
-			}
+		this.v0 = v0;
+		this.v1 = v1;
+		this.v2 = v2;
 
-		}
+	},
 
-		for ( iy = 0; iy < gridY; iy++ ) {
+	function ( t ) {
 
-			for ( ix = 0; ix < gridX; ix++ ) {
+		var vector = new THREE.Vector3();
 
-				var a = ix + gridX1 * iy;
-				var b = ix + gridX1 * ( iy + 1 );
-				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
-				var d = ( ix + 1 ) + gridX1 * iy;
+		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 );
 
-				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 vector;
 
-				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 ] );
+);
 
-				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;
+// File:src/extras/curves/CubicBezierCurve3.js
 
-				scope.faces.push( face );
-				scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+/**************************************************************
+ *	Cubic Bezier 3D curve
+ **************************************************************/
 
-			}
+THREE.CubicBezierCurve3 = THREE.Curve.create(
 
-		}
+	function ( v0, v1, v2, v3 ) {
 
-	}
+		this.v0 = v0;
+		this.v1 = v1;
+		this.v2 = v2;
+		this.v3 = v3;
 
-	this.computeCentroids();
-	this.mergeVertices();
+	},
 
-};
+	function ( t ) {
 
-THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		var vector = new THREE.Vector3();
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		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 );
 
-THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
+		return vector;
 
-	THREE.Geometry.call( this );
+	}
 
-	this.radiusTop = radiusTop = radiusTop !== undefined ? radiusTop : 20;
-	this.radiusBottom = radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
-	this.height = height = height !== undefined ? height : 100;
+);
 
-	this.radialSegments = radialSegments = radialSegments || 8;
-	this.heightSegments = heightSegments = heightSegments || 1;
+// File:src/extras/curves/SplineCurve3.js
 
-	this.openEnded = openEnded = openEnded !== undefined ? openEnded : false;
+/**************************************************************
+ *	Spline 3D curve
+ **************************************************************/
 
-	var heightHalf = height / 2;
 
-	var x, y, vertices = [], uvs = [];
+THREE.SplineCurve3 = THREE.Curve.create(
 
-	for ( y = 0; y <= heightSegments; y ++ ) {
+	function ( points /* array of Vector3 */) {
 
-		var verticesRow = [];
-		var uvsRow = [];
+		this.points = ( points == undefined ) ? [] : points;
 
-		var v = y / heightSegments;
-		var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
+	},
 
-		for ( x = 0; x <= radialSegments; x ++ ) {
+	function ( t ) {
 
-			var u = x / radialSegments;
+		var points = this.points;
+		var point = ( points.length - 1 ) * t;
 
-			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 );
+		var intPoint = Math.floor( point );
+		var weight = point - intPoint;
 
-			this.vertices.push( vertex );
+		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 ];
 
-			verticesRow.push( this.vertices.length - 1 );
-			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+		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 );
 
-		vertices.push( verticesRow );
-		uvs.push( uvsRow );
+		return vector;
 
 	}
 
-	var tanTheta = ( radiusBottom - radiusTop ) / height;
-	var na, nb;
+);
 
-	for ( x = 0; x < radialSegments; x ++ ) {
+// File:src/extras/curves/ClosedSplineCurve3.js
 
-		if ( radiusTop !== 0 ) {
+/**************************************************************
+ *	Closed Spline 3D curve
+ **************************************************************/
 
-			na = this.vertices[ vertices[ 0 ][ x ] ].clone();
-			nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
 
-		} else {
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
 
-			na = this.vertices[ vertices[ 1 ][ x ] ].clone();
-			nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
+	function ( points /* array of Vector3 */) {
 
-		}
+		this.points = ( points == undefined ) ? [] : points;
 
-		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();
+	},
 
-		for ( y = 0; y < heightSegments; y ++ ) {
+	function ( t ) {
 
-			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 points = this.points;
+		var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
 
-			var n1 = na.clone();
-			var n2 = na.clone();
-			var n3 = nb.clone();
-			var n4 = nb.clone();
+		var intPoint = Math.floor( point );
+		var weight = point - intPoint;
 
-			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();
+		intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
 
-			this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+		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 ];
 
-			this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv2, uv3, uv4 ] );
+		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 vector;
 
-	// top cap
+	}
 
-	if ( openEnded === false && radiusTop > 0 ) {
+);
 
-		this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
+// File:src/extras/animation/AnimationHandler.js
 
-		for ( x = 0; x < radialSegments; x ++ ) {
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
 
-			var v1 = vertices[ 0 ][ x ];
-			var v2 = vertices[ 0 ][ x + 1 ];
-			var v3 = this.vertices.length - 1;
+THREE.AnimationHandler = {
 
-			var n1 = new THREE.Vector3( 0, 1, 0 );
-			var n2 = new THREE.Vector3( 0, 1, 0 );
-			var n3 = new THREE.Vector3( 0, 1, 0 );
+	LINEAR: 0,
+	CATMULLROM: 1,
+	CATMULLROM_FORWARD: 2,
 
-			var uv1 = uvs[ 0 ][ x ].clone();
-			var uv2 = uvs[ 0 ][ x + 1 ].clone();
-			var uv3 = new THREE.Vector2( uv2.u, 0 );
+	//
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+	add: function () { THREE.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
+	get: function () { THREE.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
+	remove: function () { THREE.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
 
-		}
+	//
 
-	}
+	animations: [],
 
-	// bottom cap
+	init: function ( data ) {
 
-	if ( openEnded === false && radiusBottom > 0 ) {
+		if ( data.initialized === true ) return data;
 
-		this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+		// loop through all keys
 
-		for ( x = 0; x < radialSegments; x ++ ) {
+		for ( var h = 0; h < data.hierarchy.length; h ++ ) {
 
-			var v1 = vertices[ y ][ x + 1 ];
-			var v2 = vertices[ y ][ x ];
-			var v3 = this.vertices.length - 1;
+			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-			var n1 = new THREE.Vector3( 0, - 1, 0 );
-			var n2 = new THREE.Vector3( 0, - 1, 0 );
-			var n3 = new THREE.Vector3( 0, - 1, 0 );
+				// remove minus times
 
-			var uv1 = uvs[ y ][ x + 1 ].clone();
-			var uv2 = uvs[ y ][ x ].clone();
-			var uv3 = new THREE.Vector2( uv2.u, 1 );
+				if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+					 data.hierarchy[ h ].keys[ k ].time = 0;
 
-		}
+				}
 
-	}
+				// create quaternions
 
-	this.computeCentroids();
-	this.computeFaceNormals();
+				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.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+				}
 
-/**
- * @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.ExtrudeGeometry = function ( shapes, options ) {
+			// prepare morph target keys
 
-	if ( typeof( shapes ) === "undefined" ) {
-		shapes = [];
-		return;
-	}
+			if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
 
-	THREE.Geometry.call( this );
+				// get all used
 
-	shapes = shapes instanceof Array ? shapes : [ shapes ];
+				var usedMorphTargets = {};
 
-	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-	this.addShapeList( shapes, options );
+					for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
 
-	this.computeCentroids();
-	this.computeFaceNormals();
+						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
+						usedMorphTargets[ morphTargetName ] = - 1;
 
-	// can't really use automatic vertex normals
-	// as then front and back sides get smoothed too
-	// should do separate smoothing just for sides
+					}
 
-	//this.computeVertexNormals();
+				}
 
-	//console.log( "took", ( Date.now() - startTime ) );
+				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
 
-};
 
-THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+				// set all used on all frames
 
-THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
-	var sl = shapes.length;
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-	for ( var s = 0; s < sl; s ++ ) {
-		var shape = shapes[ s ];
-		this.addShape( shape, options );
-	}
-};
+					var influences = {};
 
-THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
+					for ( var morphTargetName in usedMorphTargets ) {
 
-	var amount = options.amount !== undefined ? options.amount : 100;
+						for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
 
-	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;
+							if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
 
-	var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
+								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
+								break;
 
-	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+							}
 
-	var steps = options.steps !== undefined ? options.steps : 1;
+						}
 
-	var extrudePath = options.extrudePath;
-	var extrudePts, extrudeByPath = false;
+						if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
 
-	var material = options.material;
-	var extrudeMaterial = options.extrudeMaterial;
+							influences[ morphTargetName ] = 0;
 
-	// Use default WorldUVGenerator if no UV generators are specified.
-	var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
+						}
 
-	var shapebb = this.shapebb;
-	//shapebb = shape.getBoundingBox();
+					}
 
+					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
 
+				}
 
-	var splineTube, binormal, normal, position2;
-	if ( extrudePath ) {
+			}
 
-		extrudePts = extrudePath.getSpacedPoints( steps );
 
-		extrudeByPath = true;
-		bevelEnabled = false; // bevels not supported for path extrusion
+			// remove all keys that are on the same time
 
-		// SETUP TNB variables
+			for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-		// Reuse TNB from TubeGeomtry for now.
-		// TODO1 - have a .isClosed in spline?
+				if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
 
-		splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
+					data.hierarchy[ h ].keys.splice( k, 1 );
+					k --;
 
-		// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
+				}
 
-		binormal = new THREE.Vector3();
-		normal = new THREE.Vector3();
-		position2 = new THREE.Vector3();
+			}
 
-	}
 
-	// Safeguards if bevels are not enabled
+			// set index
 
-	if ( ! bevelEnabled ) {
+			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-		bevelSegments = 0;
-		bevelThickness = 0;
-		bevelSize = 0;
+				data.hierarchy[ h ].keys[ k ].index = k;
 
-	}
+			}
 
-	// Variables initalization
+		}
 
-	var ahole, h, hl; // looping of holes
-	var scope = this;
-	var bevelPoints = [];
+		data.initialized = true;
 
-	var shapesOffset = this.vertices.length;
+		return data;
 
-	var shapePoints = shape.extractPoints( curveSegments );
+	},
 
-	var vertices = shapePoints.shape;
-	var holes = shapePoints.holes;
+	parse: function ( root ) {
 
-	var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
+		var parseRecurseHierarchy = function ( root, hierarchy ) {
 
-	if ( reverse ) {
+			hierarchy.push( root );
 
-		vertices = vertices.reverse();
+			for ( var c = 0; c < root.children.length; c ++ )
+				parseRecurseHierarchy( root.children[ c ], hierarchy );
 
-		// 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 ++ ) {
+		// setup hierarchy
 
-			ahole = holes[ h ];
+		var hierarchy = [];
 
-			if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
+		if ( root instanceof THREE.SkinnedMesh ) {
 
-				holes[ h ] = ahole.reverse();
+			for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
+
+				hierarchy.push( root.skeleton.bones[ b ] );
 
 			}
 
+		} else {
+
+			parseRecurseHierarchy( root, hierarchy );
+
 		}
 
-		reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
+		return hierarchy;
 
-	}
+	},
 
+	play: function ( animation ) {
 
-	var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
+		if ( this.animations.indexOf( animation ) === - 1 ) {
 
-	/* Vertices */
+			this.animations.push( animation );
 
-	var contour = vertices; // vertices has all points but contour has only points of circumference
+		}
 
-	for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
+	},
 
-		ahole = holes[ h ];
+	stop: function ( animation ) {
 
-		vertices = vertices.concat( ahole );
+		var index = this.animations.indexOf( animation );
 
-	}
+		if ( index !== - 1 ) {
 
+			this.animations.splice( index, 1 );
 
-	function scalePt2 ( pt, vec, size ) {
+		}
 
-		if ( !vec ) console.log( "die" );
+	},
 
-		return vec.clone().multiplyScalar( size ).add( pt );
+	update: function ( deltaTimeMS ) {
 
-	}
+		for ( var i = 0; i < this.animations.length; i ++ ) {
 
-	var b, bs, t, z,
-		vert, vlen = vertices.length,
-		face, flen = faces.length,
-		cont, clen = contour.length;
+			this.animations[ i ].resetBlendWeights( );
 
+		}
 
-	// Find directions for point movement
+		for ( var i = 0; i < this.animations.length; i ++ ) {
 
-	var RAD_TO_DEGREES = 180 / Math.PI;
+			this.animations[ i ].update( deltaTimeMS );
 
+		}
 
-	function getBevelVec( pt_i, pt_j, pt_k ) {
+	}
 
-		// Algorithm 2
+};
 
-		return getBevelVec2( pt_i, pt_j, pt_k );
+// File:src/extras/animation/Animation.js
 
-	}
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	function getBevelVec1( pt_i, pt_j, pt_k ) {
+THREE.Animation = function ( root, data ) {
 
-		var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
-		var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
+	this.root = root;
+	this.data = THREE.AnimationHandler.init( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
 
-		if ( anglea > angleb ) {
+	this.currentTime = 0;
+	this.timeScale = 1;
 
-			angleb += Math.PI * 2;
+	this.isPlaying = false;
+	this.loop = true;
+	this.weight = 0;
 
-		}
+	this.interpolationType = THREE.AnimationHandler.LINEAR;
 
-		var anglec = ( anglea + angleb ) / 2;
+};
 
+THREE.Animation.prototype = {
 
-		//console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
+	constructor: THREE.Animation,
 
-		var x = - Math.cos( anglec );
-		var y = - Math.sin( anglec );
+	keyTypes:  [ "pos", "rot", "scl" ],
 
-		var vec = new THREE.Vector2( x, y ); //.normalize();
+	play: function ( startTime, weight ) {
 
-		return vec;
+		this.currentTime = startTime !== undefined ? startTime : 0;
+		this.weight = weight !== undefined ? weight : 1;
 
-	}
+		this.isPlaying = true;
 
-	function getBevelVec2( pt_i, pt_j, pt_k ) {
+		this.reset();
 
-		var a = THREE.ExtrudeGeometry.__v1,
-			b = THREE.ExtrudeGeometry.__v2,
-			v_hat = THREE.ExtrudeGeometry.__v3,
-			w_hat = THREE.ExtrudeGeometry.__v4,
-			p = THREE.ExtrudeGeometry.__v5,
-			q = THREE.ExtrudeGeometry.__v6,
-			v, w,
-			v_dot_w_hat, q_sub_p_dot_w_hat,
-			s, intersection;
+		THREE.AnimationHandler.play( this );
 
-		// good reading for line-line intersection
-		// http://sputsoft.com/blog/2010/03/line-line-intersection.html
+	},
 
-		// define a as vector j->i
-		// define b as vectot k->i
+	stop: function() {
 
-		a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
-		b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
+		this.isPlaying = false;
 
-		// get unit vectors
+		THREE.AnimationHandler.stop( this );
 
-		v = a.normalize();
-		w = b.normalize();
+	},
 
-		// normals from pt i
+	reset: function () {
 
-		v_hat.set( -v.y, v.x );
-		w_hat.set( w.y, -w.x );
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-		// pts from i
+			var object = this.hierarchy[ h ];
 
-		p.copy( pt_i ).add( v_hat );
-		q.copy( pt_i ).add( w_hat );
+			if ( object.animationCache === undefined ) {
 
-		if ( p.equals( q ) ) {
+				object.animationCache = {
+					animations: {},
+					blending: {
+						positionWeight: 0.0,
+						quaternionWeight: 0.0,
+						scaleWeight: 0.0
+					}
+				};
+			}
 
-			//console.log("Warning: lines are straight");
-			return w_hat.clone();
+			var name = this.data.name;
+			var animations = object.animationCache.animations;
+			var animationCache = animations[ name ];
 
-		}
+			if ( animationCache === undefined ) {
 
-		// Points from j, k. helps prevents points cross overover most of the time
+				animationCache = {
+					prevKey: { pos: 0, rot: 0, scl: 0 },
+					nextKey: { pos: 0, rot: 0, scl: 0 },
+					originalMatrix: object.matrix
+				};
+
+				animations[ name ] = animationCache;
 
-		p.copy( pt_j ).add( v_hat );
-		q.copy( pt_k ).add( w_hat );
+			}
 
-		v_dot_w_hat = v.dot( w_hat );
-		q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat );
+			// Get keys to match our current time
 
-		// We should not reach these conditions
+			for ( var t = 0; t < 3; t ++ ) {
 
-		if ( v_dot_w_hat === 0 ) {
+				var type = this.keyTypes[ t ];
 
-			console.log( "Either infinite or no solutions!" );
+				var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+				var nextKey = this.getNextKeyWith( type, h, 1 );
 
-			if ( q_sub_p_dot_w_hat === 0 ) {
+				while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-				console.log( "Its finite solutions." );
+					prevKey = nextKey;
+					nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-			} else {
+				}
 
-				console.log( "Too bad, no solutions." );
+				animationCache.prevKey[ type ] = prevKey;
+				animationCache.nextKey[ type ] = nextKey;
 
 			}
 
 		}
 
-		s = q_sub_p_dot_w_hat / v_dot_w_hat;
-
-		if ( s < 0 ) {
-
-			// in case of emergecy, revert to algorithm 1.
+	},
 
-			return getBevelVec1( pt_i, pt_j, pt_k );
+	resetBlendWeights: function () {
 
-		}
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-		intersection = v.multiplyScalar( s ).add( p );
+			var object = this.hierarchy[ h ];
+			var animationCache = object.animationCache;
 
-		return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
+			if ( animationCache !== undefined ) {
 
-	}
+				var blending = animationCache.blending;
 
-	var contourMovements = [];
+				blending.positionWeight = 0.0;
+				blending.quaternionWeight = 0.0;
+				blending.scaleWeight = 0.0;
 
-	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;
+		}
 
-		//  (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 ];
+	update: ( function() {
 
-		contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
+		var points = [];
+		var target = new THREE.Vector3();
+		var newVector = new THREE.Vector3();
+		var newQuat = new THREE.Quaternion();
 
-	}
+		// Catmull-Rom spline
 
-	var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
+		var interpolateCatmullRom = function ( points, scale ) {
 
-	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+			var c = [], v3 = [],
+			point, intPoint, weight, w2, w3,
+			pa, pb, pc, pd;
 
-		ahole = holes[ h ];
+			point = ( points.length - 1 ) * scale;
+			intPoint = Math.floor( point );
+			weight = point - intPoint;
 
-		oneHoleMovements = [];
+			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;
 
-		for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+			pa = points[ c[ 0 ] ];
+			pb = points[ c[ 1 ] ];
+			pc = points[ c[ 2 ] ];
+			pd = points[ c[ 3 ] ];
 
-			if ( j === il ) j = 0;
-			if ( k === il ) k = 0;
+			w2 = weight * weight;
+			w3 = weight * w2;
 
-			//  (j)---(i)---(k)
-			oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
+			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 );
 
-		}
+			return v3;
 
-		holesMovements.push( oneHoleMovements );
-		verticesMovements = verticesMovements.concat( oneHoleMovements );
+		};
 
-	}
+		var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
 
+			var v0 = ( p2 - p0 ) * 0.5,
+				v1 = ( p3 - p1 ) * 0.5;
 
-	// Loop bevelSegments, 1 for the front, 1 for the back
+			return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
 
-	for ( b = 0; b < bevelSegments; b ++ ) {
-	//for ( b = bevelSegments; b > 0; b -- ) {
+		};
 
-		t = b / bevelSegments;
-		z = bevelThickness * ( 1 - t );
+		return function ( delta ) {
 
-		//z = bevelThickness * t;
-		bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
-		//bs = bevelSize * t ; // linear
+			if ( this.isPlaying === false ) return;
 
-		// contract shape
+			this.currentTime += delta * this.timeScale;
 
-		for ( i = 0, il = contour.length; i < il; i ++ ) {
+			if ( this.weight === 0 )
+				return;
 
-			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
-			//vert = scalePt( contour[ i ], contourCentroid, bs, false );
-			v( vert.x, vert.y,  - z );
+			//
 
-		}
+			var duration = this.data.length;
 
-		// expand holes
+			if ( this.currentTime > duration || this.currentTime < 0 ) {
 
-		for ( h = 0, hl = holes.length; h < hl; h++ ) {
+				if ( this.loop ) {
 
-			ahole = holes[ h ];
-			oneHoleMovements = holesMovements[ h ];
+					this.currentTime %= duration;
 
-			for ( i = 0, il = ahole.length; i < il; i++ ) {
+					if ( this.currentTime < 0 )
+						this.currentTime += duration;
 
-				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
-				//vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
+					this.reset();
 
-				v( vert.x, vert.y,  -z );
+				} else {
 
-			}
+					this.stop();
 
-		}
+				}
 
-	}
+			}
 
-	bs = bevelSize;
+			for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-	// Back facing vertices
+				var object = this.hierarchy[ h ];
+				var animationCache = object.animationCache.animations[this.data.name];
+				var blending = object.animationCache.blending;
 
-	for ( i = 0; i < vlen; i ++ ) {
+				// loop through pos/rot/scl
 
-		vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+				for ( var t = 0; t < 3; t ++ ) {
 
-		if ( !extrudeByPath ) {
+					// get keys
 
-			v( vert.x, vert.y, 0 );
+					var type    = this.keyTypes[ t ];
+					var prevKey = animationCache.prevKey[ type ];
+					var nextKey = animationCache.nextKey[ type ];
 
-		} else {
+					if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
+						( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
 
-			// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
+						prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+						nextKey = this.getNextKeyWith( type, h, 1 );
 
-			normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
-			binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
+						while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-			position2.copy( extrudePts[0] ).add(normal).add(binormal);
+							prevKey = nextKey;
+							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-			v( position2.x, position2.y, position2.z );
+						}
 
-		}
+						animationCache.prevKey[ type ] = prevKey;
+						animationCache.nextKey[ type ] = nextKey;
 
-	}
+					}
 
-	// Add stepped vertices...
-	// Including front facing vertices
+					var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
 
-	var s;
+					var prevXYZ = prevKey[ type ];
+					var nextXYZ = nextKey[ type ];
 
-	for ( s = 1; s <= steps; s ++ ) {
+					if ( scale < 0 ) scale = 0;
+					if ( scale > 1 ) scale = 1;
 
-		for ( i = 0; i < vlen; i ++ ) {
+					// interpolate
 
-			vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+					if ( type === "pos" ) {
 
-			if ( !extrudeByPath ) {
+						if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
 
-				v( vert.x, vert.y, amount / steps * s );
+							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;
 
-			} else {
+							// blend
+							var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
+							object.position.lerp( newVector, proportionalWeight );
+							blending.positionWeight += this.weight;
 
-				// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
+						} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+									this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-				normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
-				binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
+							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" ];
 
-				position2.copy( extrudePts[s] ).add( normal ).add( binormal );
+							scale = scale * 0.33 + 0.33;
 
-				v( position2.x, position2.y, position2.z );
+							var currentPoint = interpolateCatmullRom( points, scale );
+							var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
+							blending.positionWeight += this.weight;
 
-			}
+							// blend
 
-		}
+							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 ) {
 
-	// Add bevel segments planes
+								var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
 
-	//for ( b = 1; b <= bevelSegments; b ++ ) {
-	for ( b = bevelSegments - 1; b >= 0; b -- ) {
+								target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
+								target.sub( vector );
+								target.y = 0;
+								target.normalize();
 
-		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 angle = Math.atan2( target.x, target.z );
+								object.rotation.set( 0, angle, 0 );
 
-		// contract shape
+							}
 
-		for ( i = 0, il = contour.length; i < il; i ++ ) {
+						}
 
-			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
-			v( vert.x, vert.y,  amount + z );
+					} else if ( type === "rot" ) {
 
-		}
+						THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
 
-		// expand holes
+						// Avoid paying the cost of an additional slerp if we don't have to
+						if ( blending.quaternionWeight === 0 ) {
 
-		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+							object.quaternion.copy(newQuat);
+							blending.quaternionWeight = this.weight;
 
-			ahole = holes[ h ];
-			oneHoleMovements = holesMovements[ h ];
+						} else {
 
-			for ( i = 0, il = ahole.length; i < il; i ++ ) {
+							var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
+							THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
+							blending.quaternionWeight += this.weight;
 
-				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+						}
 
-				if ( !extrudeByPath ) {
+					} else if ( type === "scl" ) {
 
-					v( vert.x, vert.y,  amount + z );
+						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;
 
-				} else {
+						var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
+						object.scale.lerp( newVector, proportionalWeight );
+						blending.scaleWeight += this.weight;
 
-					v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
+					}
 
 				}
 
 			}
 
-		}
+			return true;
 
-	}
+		};
 
-	/* Faces */
+	} )(),
 
-	// Top and bottom faces
+	getNextKeyWith: function ( type, h, key ) {
 
-	buildLidFaces();
+		var keys = this.data.hierarchy[ h ].keys;
 
-	// Sides faces
+		if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+			 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-	buildSideFaces();
+			key = key < keys.length - 1 ? key : keys.length - 1;
 
+		} else {
 
-	/////  Internal functions
+			key = key % keys.length;
 
-	function buildLidFaces() {
+		}
 
-		if ( bevelEnabled ) {
+		for ( ; key < keys.length; key ++ ) {
 
-			var layer = 0 ; // steps + 1
-			var offset = vlen * layer;
+			if ( keys[ key ][ type ] !== undefined ) {
 
-			// Bottom faces
+				return keys[ key ];
 
-			for ( i = 0; i < flen; i ++ ) {
+			}
 
-				face = faces[ i ];
-				f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
+		}
 
-			}
+		return this.data.hierarchy[ h ].keys[ 0 ];
 
-			layer = steps + bevelSegments * 2;
-			offset = vlen * layer;
+	},
 
-			// Top faces
+	getPrevKeyWith: function ( type, h, key ) {
 
-			for ( i = 0; i < flen; i ++ ) {
+		var keys = this.data.hierarchy[ h ].keys;
 
-				face = faces[ i ];
-				f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
+		if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+			this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-			}
+			key = key > 0 ? key : 0;
 
 		} else {
 
-			// Bottom faces
-
-			for ( i = 0; i < flen; i++ ) {
+			key = key >= 0 ? key : key + keys.length;
 
-				face = faces[ i ];
-				f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
+		}
 
-			}
 
-			// Top faces
+		for ( ; key >= 0; key -- ) {
 
-			for ( i = 0; i < flen; i ++ ) {
+			if ( keys[ key ][ type ] !== undefined ) {
 
-				face = faces[ i ];
-				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
+				return keys[ key ];
 
 			}
-		}
 
-	}
+		}
 
-	// Create faces for the z-sides of the shape
+		return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
 
-	function buildSideFaces() {
+	}
 
-		var layeroffset = 0;
-		sidewalls( contour, layeroffset );
-		layeroffset += contour.length;
+};
 
-		for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
+// File:src/extras/animation/KeyFrameAnimation.js
 
-			ahole = holes[ h ];
-			sidewalls( ahole, layeroffset );
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author khang duong
+ * @author erik kitson
+ */
 
-			//, true
-			layeroffset += ahole.length;
+THREE.KeyFrameAnimation = function ( data ) {
 
-		}
+	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;
 
-	}
+	// initialize to first keyframes
 
-	function sidewalls( contour, layeroffset ) {
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-		var j, k;
-		i = contour.length;
+		var keys = this.data.hierarchy[h].keys,
+			sids = this.data.hierarchy[h].sids,
+			obj = this.hierarchy[h];
 
-		while ( --i >= 0 ) {
+		if ( keys.length && sids ) {
 
-			j = i;
-			k = i - 1;
-			if ( k < 0 ) k = contour.length - 1;
+			for ( var s = 0; s < sids.length; s ++ ) {
 
-			//console.log('b', i,j, i-1, k,vertices.length);
+				var sid = sids[ s ],
+					next = this.getNextKeyWith( sid, h, 0 );
 
-			var s = 0, sl = steps  + bevelSegments * 2;
+				if ( next ) {
 
-			for ( s = 0; s < sl; s ++ ) {
+					next.apply( sid );
 
-				var slen1 = vlen * s;
-				var slen2 = vlen * ( s + 1 );
+				}
 
-				var a = layeroffset + j + slen1,
-					b = layeroffset + k + slen1,
-					c = layeroffset + k + slen2,
-					d = layeroffset + j + slen2;
+			}
 
-				f4( a, b, c, d, contour, s, sl, j, k );
+			obj.matrixAutoUpdate = false;
+			this.data.hierarchy[h].node.updateMatrix();
+			obj.matrixWorldNeedsUpdate = true;
 
-			}
 		}
 
 	}
 
+};
 
-	function v( x, y, z ) {
-
-		scope.vertices.push( new THREE.Vector3( x, y, z ) );
+THREE.KeyFrameAnimation.prototype = {
 
-	}
+	constructor: THREE.KeyFrameAnimation,
 
-	function f3( a, b, c, isBottom ) {
+	play: function ( startTime ) {
 
-		a += shapesOffset;
-		b += shapesOffset;
-		c += shapesOffset;
+		this.currentTime = startTime !== undefined ? startTime : 0;
 
-		// normal, color, material
-		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+		if ( this.isPlaying === false ) {
 
-		var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c );
+			this.isPlaying = true;
 
- 		scope.faceVertexUvs[ 0 ].push( uvs );
+			// reset key cache
 
-	}
+			var h, hl = this.hierarchy.length,
+				object,
+				node;
 
-	function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
+			for ( h = 0; h < hl; h ++ ) {
 
-		a += shapesOffset;
-		b += shapesOffset;
-		c += shapesOffset;
-		d += shapesOffset;
+				object = this.hierarchy[ h ];
+				node = this.data.hierarchy[ h ];
 
- 		scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
- 		scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
+				if ( node.animationCache === undefined ) {
 
- 		var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d,
- 		                                    stepIndex, stepsLength, contourIndex1, contourIndex2 );
+					node.animationCache = {};
+					node.animationCache.prevKey = null;
+					node.animationCache.nextKey = null;
+					node.animationCache.originalMatrix = object.matrix;
 
- 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
- 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
+				}
 
-	}
+				var keys = this.data.hierarchy[h].keys;
 
-};
+				if (keys.length) {
 
-THREE.ExtrudeGeometry.WorldUVGenerator = {
+					node.animationCache.prevKey = keys[ 0 ];
+					node.animationCache.nextKey = keys[ 1 ];
 
-	generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
-		var ax = geometry.vertices[ indexA ].x,
-			ay = geometry.vertices[ indexA ].y,
+					this.startTime = Math.min( keys[0].time, this.startTime );
+					this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
 
-			bx = geometry.vertices[ indexB ].x,
-			by = geometry.vertices[ indexB ].y,
+				}
 
-			cx = geometry.vertices[ indexC ].x,
-			cy = geometry.vertices[ indexC ].y;
+			}
 
-		return [
-			new THREE.Vector2( ax, ay ),
-			new THREE.Vector2( bx, by ),
-			new THREE.Vector2( cx, cy )
-		];
+			this.update( 0 );
 
-	},
+		}
 
-	generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
+		this.isPaused = false;
 
-		return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
+		THREE.AnimationHandler.play( this );
 
 	},
 
-	generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
-	                              indexA, indexB, indexC, indexD, stepIndex, stepsLength,
-	                              contourIndex1, contourIndex2 ) {
+	stop: function () {
 
-		var ax = geometry.vertices[ indexA ].x,
-			ay = geometry.vertices[ indexA ].y,
-			az = geometry.vertices[ indexA ].z,
+		this.isPlaying = false;
+		this.isPaused  = false;
 
-			bx = geometry.vertices[ indexB ].x,
-			by = geometry.vertices[ indexB ].y,
-			bz = geometry.vertices[ indexB ].z,
+		THREE.AnimationHandler.stop( this );
 
-			cx = geometry.vertices[ indexC ].x,
-			cy = geometry.vertices[ indexC ].y,
-			cz = geometry.vertices[ indexC ].z,
+		// reset JIT matrix and remove cache
 
-			dx = geometry.vertices[ indexD ].x,
-			dy = geometry.vertices[ indexD ].y,
-			dz = geometry.vertices[ indexD ].z;
+		for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
 
-		if ( Math.abs( ay - by ) < 0.01 ) {
-			return [
-				new THREE.Vector2( ax, 1 - az ),
-				new THREE.Vector2( bx, 1 - bz ),
-				new THREE.Vector2( cx, 1 - cz ),
-				new THREE.Vector2( dx, 1 - dz )
-			];
-		} else {
-			return [
-				new THREE.Vector2( ay, 1 - az ),
-				new THREE.Vector2( by, 1 - bz ),
-				new THREE.Vector2( cy, 1 - cz ),
-				new THREE.Vector2( dy, 1 - dz )
-			];
-		}
-	}
-};
+			var obj = this.hierarchy[ h ];
+			var node = this.data.hierarchy[ h ];
 
-THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
-THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
-THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
-THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
-THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
-THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();
+			if ( node.animationCache !== undefined ) {
 
-/**
- * @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
- *
- * }
- **/
+				var original = node.animationCache.originalMatrix;
 
-THREE.ShapeGeometry = function ( shapes, options ) {
+				original.copy( obj.matrix );
+				obj.matrix = original;
 
-	THREE.Geometry.call( this );
+				delete node.animationCache;
 
-	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+			}
 
-	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+		}
 
-	this.addShapeList( shapes, options );
+	},
 
-	this.computeCentroids();
-	this.computeFaceNormals();
+	update: function ( delta ) {
+
+		if ( this.isPlaying === false ) return;
+
+		this.currentTime += delta * this.timeScale;
 
-};
+		//
 
-THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		var duration = this.data.length;
 
-/**
- * Add an array of shapes to THREE.ShapeGeometry.
- */
-THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
+		if ( this.loop === true && this.currentTime > duration ) {
 
-	for ( var i = 0, l = shapes.length; i < l; i++ ) {
+			this.currentTime %= duration;
 
-		this.addShape( shapes[ i ], options );
+		}
 
-	}
+		this.currentTime = Math.min( this.currentTime, duration );
 
-	return this;
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-};
+			var object = this.hierarchy[ h ];
+			var node = this.data.hierarchy[ h ];
 
-/**
- * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
- */
-THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
+			var keys = node.keys,
+				animationCache = node.animationCache;
 
-	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;
+			if ( keys.length ) {
 
-	var shapebb = this.shapebb;
+				var prevKey = animationCache.prevKey;
+				var nextKey = animationCache.nextKey;
 
-	//
+				if ( nextKey.time <= this.currentTime ) {
 
-	var i, l, hole, s;
+					while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-	var shapesOffset = this.vertices.length;
-	var shapePoints = shape.extractPoints( curveSegments );
+						prevKey = nextKey;
+						nextKey = keys[ prevKey.index + 1 ];
 
-	var vertices = shapePoints.shape;
-	var holes = shapePoints.holes;
+					}
 
-	var reverse = !THREE.Shape.Utils.isClockWise( vertices );
+					animationCache.prevKey = prevKey;
+					animationCache.nextKey = nextKey;
 
-	if ( reverse ) {
+				}
 
-		vertices = vertices.reverse();
+				if ( nextKey.time >= this.currentTime ) {
 
-		// Maybe we should also check if holes are in the opposite direction, just to be safe...
+					prevKey.interpolate( nextKey, this.currentTime );
 
-		for ( i = 0, l = holes.length; i < l; i++ ) {
+				} else {
 
-			hole = holes[ i ];
+					prevKey.interpolate( nextKey, nextKey.time );
 
-			if ( THREE.Shape.Utils.isClockWise( hole ) ) {
+				}
 
-				holes[ i ] = hole.reverse();
+				this.data.hierarchy[ h ].node.updateMatrix();
+				object.matrixWorldNeedsUpdate = true;
 
 			}
 
 		}
 
-		reverse = false;
+	},
 
-	}
+	getNextKeyWith: function ( sid, h, key ) {
 
-	var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
+		var keys = this.data.hierarchy[ h ].keys;
+		key = key % keys.length;
 
-	// Vertices
+		for ( ; key < keys.length; key ++ ) {
 
-	var contour = vertices;
+			if ( keys[ key ].hasTarget( sid ) ) {
 
-	for ( i = 0, l = holes.length; i < l; i++ ) {
+				return keys[ key ];
 
-		hole = holes[ i ];
-		vertices = vertices.concat( hole );
+			}
 
-	}
+		}
 
-	//
+		return keys[ 0 ];
 
-	var vert, vlen = vertices.length;
-	var face, flen = faces.length;
-	var cont, clen = contour.length;
+	},
 
-	for ( i = 0; i < vlen; i++ ) {
+	getPrevKeyWith: function ( sid, h, key ) {
 
-		vert = vertices[ i ];
+		var keys = this.data.hierarchy[ h ].keys;
+		key = key >= 0 ? key : key + keys.length;
 
-		this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
+		for ( ; key >= 0; key -- ) {
 
-	}
+			if ( keys[ key ].hasTarget( sid ) ) {
 
-	for ( i = 0; i < flen; i++ ) {
+				return keys[ key ];
 
-		face = faces[ i ];
+			}
 
-		var a = face[ 0 ] + shapesOffset;
-		var b = face[ 1 ] + shapesOffset;
-		var c = face[ 2 ] + shapesOffset;
+		}
 
-		this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
-		this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) );
+		return keys[ keys.length - 1 ];
 
 	}
 
 };
 
+// File:src/extras/animation/MorphAnimation.js
+
 /**
- * @author astrodud / http://astrodud.isgreat.org/
- * @author zz85 / https://github.com/zz85
- * @author bhouston / http://exocortex.com
+ * @author mrdoob / http://mrdoob.com
+ * @author willy-vvu / http://willy-vvu.github.io
  */
 
-// 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.
-THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
-
-	THREE.Geometry.call( this );
-
-	segments = segments || 12;
-	phiStart = phiStart || 0;
-	phiLength = phiLength || 2 * Math.PI;
-
-	var inversePointLength = 1.0 / ( points.length - 1 );
-	var inverseSegments = 1.0 / segments;
-
-	for ( var i = 0, il = segments; i <= il; i ++ ) {
+THREE.MorphAnimation = function ( mesh ) {
 
-		var phi = phiStart + i * inverseSegments * phiLength;
+	this.mesh = mesh;
+	this.frames = mesh.morphTargetInfluences.length;
+	this.currentTime = 0;
+	this.duration = 1000;
+	this.loop = true;
+	this.lastFrame = 0;
+	this.currentFrame = 0;
 
-		var c = Math.cos( phi ),
-			s = Math.sin( phi );
+	this.isPlaying = false;
 
-		for ( var j = 0, jl = points.length; j < jl; j ++ ) {
+};
 
-			var pt = points[ j ];
+THREE.MorphAnimation.prototype = {
 
-			var vertex = new THREE.Vector3();
+	constructor: THREE.MorphAnimation,
 
-			vertex.x = c * pt.x - s * pt.y;
-			vertex.y = s * pt.x + c * pt.y;
-			vertex.z = pt.z;
+	play: function () {
 
-			this.vertices.push( vertex );
+		this.isPlaying = true;
 
-		}
+	},
 
-	}
+	pause: function () {
 
-	var np = points.length;
+		this.isPlaying = false;
 
-	for ( var i = 0, il = segments; i < il; i ++ ) {
+	},
 
-		for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
+	update: function ( delta ) {
 
-			var base = j + np * i;
-			var a = base;
-			var b = base + np;
-			var c = base + 1 + np;
-			var d = base + 1;
+		if ( this.isPlaying === false ) return;
 
-			var u0 = i * inverseSegments;
-			var v0 = j * inversePointLength;
-			var u1 = u0 + inverseSegments;
-			var v1 = v0 + inversePointLength;
+		this.currentTime += delta;
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
+		if ( this.loop === true && this.currentTime > this.duration ) {
 
-			this.faceVertexUvs[ 0 ].push( [
+			this.currentTime %= this.duration;
 
-				new THREE.Vector2( u0, v0 ),
-				new THREE.Vector2( u1, v0 ),
-				new THREE.Vector2( u0, v1 )
+		}
 
-			] );
+		this.currentTime = Math.min( this.currentTime, this.duration );
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
+		var interpolation = this.duration / this.frames;
+		var frame = Math.floor( this.currentTime / interpolation );
 
-			this.faceVertexUvs[ 0 ].push( [
+		var influences = this.mesh.morphTargetInfluences;
 
-				new THREE.Vector2( u1, v0 ),
-				new THREE.Vector2( u1, v1 ),
-				new THREE.Vector2( u0, v1 )
+		if ( frame != this.currentFrame ) {
 
-			] );
+			influences[ this.lastFrame ] = 0;
+			influences[ this.currentFrame ] = 1;
+			influences[ frame ] = 0;
 
+			this.lastFrame = this.currentFrame;
+			this.currentFrame = frame;
 
 		}
 
-	}
+		influences[ frame ] = ( this.currentTime % interpolation ) / interpolation;
+		influences[ this.lastFrame ] = 1 - influences[ frame ];
 
-	this.mergeVertices();
-	this.computeCentroids();
-	this.computeFaceNormals();
-	this.computeVertexNormals();
+	}
 
 };
 
-THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
+// File:src/extras/geometries/BoxGeometry.js
 
 /**
  * @author mrdoob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
  */
 
-THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
+THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
 
 	THREE.Geometry.call( this );
 
-	this.width = width;
-	this.height = height;
+	this.type = 'BoxGeometry';
+
+	this.parameters = {
+		width: width,
+		height: height,
+		depth: depth,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments,
+		depthSegments: depthSegments
+	};
 
 	this.widthSegments = widthSegments || 1;
 	this.heightSegments = heightSegments || 1;
+	this.depthSegments = depthSegments || 1;
+
+	var scope = this;
 
-	var ix, iz;
 	var width_half = width / 2;
 	var height_half = height / 2;
+	var depth_half = depth / 2;
 
-	var gridX = this.widthSegments;
-	var gridZ = this.heightSegments;
+	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
 
-	var gridX1 = gridX + 1;
-	var gridZ1 = gridZ + 1;
+	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
 
-	var segment_width = this.width / gridX;
-	var segment_height = this.height / gridZ;
+		var w, ix, iy,
+		gridX = scope.widthSegments,
+		gridY = scope.heightSegments,
+		width_half = width / 2,
+		height_half = height / 2,
+		offset = scope.vertices.length;
 
-	var normal = new THREE.Vector3( 0, 0, 1 );
+		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
 
-	for ( iz = 0; iz < gridZ1; iz ++ ) {
+			w = 'z';
 
-		for ( ix = 0; ix < gridX1; ix ++ ) {
+		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
 
-			var x = ix * segment_width - width_half;
-			var y = iz * segment_height - height_half;
+			w = 'y';
+			gridY = scope.depthSegments;
+
+		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
 
-			this.vertices.push( new THREE.Vector3( x, - y, 0 ) );
+			w = 'x';
+			gridX = scope.depthSegments;
 
 		}
 
-	}
+		var gridX1 = gridX + 1,
+		gridY1 = gridY + 1,
+		segment_width = width / gridX,
+		segment_height = height / gridY,
+		normal = new THREE.Vector3();
+
+		normal[ w ] = depth > 0 ? 1 : - 1;
+
+		for ( iy = 0; iy < gridY1; iy ++ ) {
 
-	for ( iz = 0; iz < gridZ; iz ++ ) {
+			for ( ix = 0; ix < gridX1; ix ++ ) {
 
-		for ( ix = 0; ix < gridX; ix ++ ) {
+				var vector = new THREE.Vector3();
+				vector[ u ] = ( ix * segment_width - width_half ) * udir;
+				vector[ v ] = ( iy * segment_height - height_half ) * vdir;
+				vector[ w ] = depth;
 
-			var a = ix + gridX1 * iz;
-			var b = ix + gridX1 * ( iz + 1 );
-			var c = ( ix + 1 ) + gridX1 * ( iz + 1 );
-			var d = ( ix + 1 ) + gridX1 * iz;
+				scope.vertices.push( vector );
 
-			var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ );
-			var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ );
-			var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ );
-			var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ );
+			}
 
-			var face = new THREE.Face3( a, b, d );
-			face.normal.copy( normal );
-			face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+		}
 
-			this.faces.push( face );
-			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+		for ( iy = 0; iy < gridY; iy ++ ) {
 
-			face = new THREE.Face3( b, c, d );
-			face.normal.copy( normal );
-			face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+			for ( ix = 0; ix < gridX; ix ++ ) {
 
-			this.faces.push( face );
-			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+				var a = ix + gridX1 * iy;
+				var b = ix + gridX1 * ( iy + 1 );
+				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+				var d = ( ix + 1 ) + gridX1 * iy;
+
+				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 );
+
+				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 ] );
+
+				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;
+
+				scope.faces.push( face );
+				scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+			}
 
 		}
 
 	}
 
-	this.computeCentroids();
+	this.mergeVertices();
 
 };
 
-THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry;
+
+// File:src/extras/geometries/CircleGeometry.js
 
 /**
- * @author Kaleb Murphy
+ * @author hughes
  */
 
-THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
 
 	THREE.Geometry.call( this );
 
-	innerRadius = innerRadius || 0;
-	outerRadius = outerRadius || 50;
+	this.type = 'CircleGeometry';
 
-	thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+	this.parameters = {
+		radius: radius,
+		segments: segments,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
 
-	thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
-	phiSegments = phiSegments !== undefined ? Math.max( 3, phiSegments ) : 8;
+	radius = radius || 50;
+	segments = segments !== undefined ? Math.max( 3, segments ) : 8;
 
-	var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
 
-	for ( i = 0; i <= phiSegments; i ++ ) { // concentric circles inside ring
+	var i, uvs = [],
+	center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
 
-		for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle
+	this.vertices.push(center);
+	uvs.push( centerUV );
 
-			var vertex = new THREE.Vector3();
-			var segment = thetaStart + o / thetaSegments * thetaLength;
+	for ( i = 0; i <= segments; i ++ ) {
 
-			vertex.x = radius * Math.cos( segment );
-			vertex.y = radius * Math.sin( segment );
+		var vertex = new THREE.Vector3();
+		var segment = thetaStart + i / segments * thetaLength;
 
-			this.vertices.push( vertex );
-			uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) );
-		}
+		vertex.x = radius * Math.cos( segment );
+		vertex.y = radius * Math.sin( segment );
 
-		radius += radiusStep;
+		this.vertices.push( vertex );
+		uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
 
 	}
 
 	var n = new THREE.Vector3( 0, 0, 1 );
 
-	for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
+	for ( i = 1; i <= segments; i ++ ) {
+
+		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() ] );
 
-		var thetaSegment = i * thetaSegments;
+	}
 
-		for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle
+	this.computeFaceNormals();
 
-			var segment = o + thetaSegment;
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-			var v1 = segment + i;
-			var v2 = segment + thetaSegments + i;
-			var v3 = segment + thetaSegments + 1 + i;
+};
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]);
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry;
 
-			v1 = segment + i;
-			v2 = segment + thetaSegments + 1 + i;
-			v3 = segment + 1 + i;
+// File:src/extras/geometries/CubeGeometry.js
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]);
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		}
-	}
 
-	this.computeCentroids();
-	this.computeFaceNormals();
+THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	THREE.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' );
+	return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
 
-};
+ };
 
-THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
+// File:src/extras/geometries/CylinderGeometry.js
 
 /**
  * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
 
 	THREE.Geometry.call( this );
 
-	this.radius = radius = radius || 50;
+	this.type = 'CylinderGeometry';
+
+	this.parameters = {
+		radiusTop: radiusTop,
+		radiusBottom: radiusBottom,
+		height: height,
+		radialSegments: radialSegments,
+		heightSegments: heightSegments,
+		openEnded: openEnded,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
+
+	radiusTop = radiusTop !== undefined ? radiusTop : 20;
+	radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+	height = height !== undefined ? height : 100;
 
-	this.widthSegments = widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
-	this.heightSegments = heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
+	radialSegments = radialSegments || 8;
+	heightSegments = heightSegments || 1;
 
-	this.phiStart = phiStart = phiStart !== undefined ? phiStart : 0;
-	this.phiLength = phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
+	openEnded = openEnded !== undefined ? openEnded : false;
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI;
 
-	this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	this.thetaLength = thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
+	var heightHalf = height / 2;
 
 	var x, y, vertices = [], uvs = [];
 
@@ -32952,15 +30919,17 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar
 		var verticesRow = [];
 		var uvsRow = [];
 
-		for ( x = 0; x <= widthSegments; x ++ ) {
+		var v = y / heightSegments;
+		var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
 
-			var u = x / widthSegments;
-			var v = y / heightSegments;
+		for ( x = 0; x <= radialSegments; x ++ ) {
+
+			var u = x / radialSegments;
 
 			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 );
+			vertex.x = radius * Math.sin( u * thetaLength + thetaStart );
+			vertex.y = - v * height + heightHalf;
+			vertex.z = radius * Math.cos( u * thetaLength + thetaStart );
 
 			this.vertices.push( vertex );
 
@@ -32974,4007 +30943,4191 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar
 
 	}
 
-	for ( y = 0; y < this.heightSegments; y ++ ) {
-
-		for ( x = 0; x < this.widthSegments; x ++ ) {
-
-			var v1 = vertices[ y ][ x + 1 ];
-			var v2 = vertices[ y ][ x ];
-			var v3 = vertices[ y + 1 ][ x ];
-			var v4 = vertices[ y + 1 ][ x + 1 ];
-
-			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();
-
-			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 ) === this.radius ) {
-
-				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 ) === this.radius ) {
+	var tanTheta = ( radiusBottom - radiusTop ) / height;
+	var na, nb;
 
-				this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+	for ( x = 0; x < radialSegments; x ++ ) {
 
-			} else {
+		if ( radiusTop !== 0 ) {
 
-				this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+			na = this.vertices[ vertices[ 0 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
 
-				this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+		} else {
 
-			}
+			na = this.vertices[ vertices[ 1 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
 
 		}
 
-	}
-
-	this.computeCentroids();
-	this.computeFaceNormals();
-
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
-
-};
-
-THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-/**
- * @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
- *  }
- *
- */
-
-/*	Usage Examples
-
-	// TextGeometry wrapper
-
-	var text3d = new TextGeometry( text, options );
-
-	// Complete manner
-
-	var textShapes = THREE.FontUtils.generateShapes( text, options );
-	var text3d = new ExtrudeGeometry( textShapes, options );
-
-*/
-
-
-THREE.TextGeometry = function ( text, parameters ) {
-
-	parameters = parameters || {};
-
-	var textShapes = THREE.FontUtils.generateShapes( text, parameters );
-
-	// translate parameters to ExtrudeGeometry API
-
-	parameters.amount = parameters.height !== undefined ? parameters.height : 50;
-
-	// defaults
-
-	if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
-	if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
-	if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
+		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();
 
-	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
+		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 ];
 
-THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
+			var n1 = na.clone();
+			var n2 = na.clone();
+			var n3 = nb.clone();
+			var n4 = nb.clone();
 
-/**
- * @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
- */
+			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();
 
-THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
+			this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
 
-	THREE.Geometry.call( this );
+			this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
 
-	var scope = this;
+		}
 
-	this.radius = radius || 100;
-	this.tube = tube || 40;
-	this.radialSegments = radialSegments || 8;
-	this.tubularSegments = tubularSegments || 6;
-	this.arc = arc || Math.PI * 2;
+	}
 
-	var center = new THREE.Vector3(), uvs = [], normals = [];
+	// top cap
 
-	for ( var j = 0; j <= this.radialSegments; j ++ ) {
+	if ( openEnded === false && radiusTop > 0 ) {
 
-		for ( var i = 0; i <= this.tubularSegments; i ++ ) {
+		this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
 
-			var u = i / this.tubularSegments * this.arc;
-			var v = j / this.radialSegments * Math.PI * 2;
+		for ( x = 0; x < radialSegments; x ++ ) {
 
-			center.x = this.radius * Math.cos( u );
-			center.y = this.radius * Math.sin( u );
+			var v1 = vertices[ 0 ][ x ];
+			var v2 = vertices[ 0 ][ x + 1 ];
+			var v3 = this.vertices.length - 1;
 
-			var vertex = new THREE.Vector3();
-			vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u );
-			vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u );
-			vertex.z = this.tube * Math.sin( v );
+			var n1 = new THREE.Vector3( 0, 1, 0 );
+			var n2 = new THREE.Vector3( 0, 1, 0 );
+			var n3 = new THREE.Vector3( 0, 1, 0 );
 
-			this.vertices.push( vertex );
+			var uv1 = uvs[ 0 ][ x ].clone();
+			var uv2 = uvs[ 0 ][ x + 1 ].clone();
+			var uv3 = new THREE.Vector2( uv2.x, 0 );
 
-			uvs.push( new THREE.Vector2( i / this.tubularSegments, j / this.radialSegments ) );
-			normals.push( vertex.clone().sub( center ).normalize() );
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
 
 		}
-	}
 
+	}
 
-	for ( var j = 1; j <= this.radialSegments; j ++ ) {
+	// bottom cap
 
-		for ( var i = 1; i <= this.tubularSegments; i ++ ) {
+	if ( openEnded === false && radiusBottom > 0 ) {
 
-			var a = ( this.tubularSegments + 1 ) * j + i - 1;
-			var b = ( this.tubularSegments + 1 ) * ( j - 1 ) + i - 1;
-			var c = ( this.tubularSegments + 1 ) * ( j - 1 ) + i;
-			var d = ( this.tubularSegments + 1 ) * j + i;
+		this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
 
-			var face = new THREE.Face3( a, b, d, [ normals[ a ], normals[ b ], normals[ d ] ] );
-			face.normal.add( normals[ a ] );
-			face.normal.add( normals[ b ] );
-			face.normal.add( normals[ d ] );
-			face.normal.normalize();
+		for ( x = 0; x < radialSegments; x ++ ) {
 
-			this.faces.push( face );
+			var v1 = vertices[ heightSegments ][ x + 1 ];
+			var v2 = vertices[ heightSegments ][ x ];
+			var v3 = this.vertices.length - 1;
 
-			this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
+			var n1 = new THREE.Vector3( 0, - 1, 0 );
+			var n2 = new THREE.Vector3( 0, - 1, 0 );
+			var n3 = new THREE.Vector3( 0, - 1, 0 );
 
-			face = new THREE.Face3( b, c, d, [ normals[ b ], normals[ c ], normals[ d ] ] );
-			face.normal.add( normals[ b ] );
-			face.normal.add( normals[ c ] );
-			face.normal.add( normals[ d ] );
-			face.normal.normalize();
+			var uv1 = uvs[ heightSegments ][ x + 1 ].clone();
+			var uv2 = uvs[ heightSegments ][ x ].clone();
+			var uv3 = new THREE.Vector2( uv2.x, 1 );
 
-			this.faces.push( face );
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
 
-			this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
 		}
 
 	}
 
-	this.computeCentroids();
+	this.computeFaceNormals();
 
 };
 
-THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-/**
- * @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 );
-
-	var scope = this;
-
-	this.radius = radius || 100;
-	this.tube = tube || 40;
-	this.radialSegments = radialSegments || 64;
-	this.tubularSegments = tubularSegments || 8;
-	this.p = p || 2;
-	this.q = q || 3;
-	this.heightScale = heightScale || 1;
-	this.grid = new Array( this.radialSegments );
-
-	var tang = new THREE.Vector3();
-	var n = new THREE.Vector3();
-	var bitan = new THREE.Vector3();
-
-	for ( var i = 0; i < this.radialSegments; ++ i ) {
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry;
 
-		this.grid[ i ] = new Array( this.tubularSegments );
-		var u = i / this.radialSegments * 2 * this.p * Math.PI;
-		var p1 = getPos( u, this.q, this.p, this.radius, this.heightScale );
-		var p2 = getPos( u + 0.01, this.q, this.p, this.radius, this.heightScale );
-		tang.subVectors( p2, p1 );
-		n.addVectors( p2, p1 );
+// File:src/extras/geometries/ExtrudeGeometry.js
 
-		bitan.crossVectors( tang, n );
-		n.crossVectors( bitan, tang );
-		bitan.normalize();
-		n.normalize();
+/**
+ * @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
+ *
+ * }
+ **/
 
-		for ( var j = 0; j < this.tubularSegments; ++ j ) {
+THREE.ExtrudeGeometry = function ( shapes, options ) {
 
-			var v = j / this.tubularSegments * 2 * Math.PI;
-			var cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
-			var cy = this.tube * Math.sin( v );
+	if ( typeof( shapes ) === "undefined" ) {
+		shapes = [];
+		return;
+	}
 
-			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;
+	THREE.Geometry.call( this );
 
-			this.grid[ i ][ j ] = scope.vertices.push( pos ) - 1;
+	this.type = 'ExtrudeGeometry';
 
-		}
+	shapes = shapes instanceof Array ? shapes : [ shapes ];
 
-	}
+	this.addShapeList( shapes, options );
 
-	for ( var i = 0; i < this.radialSegments; ++ i ) {
+	this.computeFaceNormals();
 
-		for ( var j = 0; j < this.tubularSegments; ++ j ) {
+	// can't really use automatic vertex normals
+	// as then front and back sides get smoothed too
+	// should do separate smoothing just for sides
 
-			var ip = ( i + 1 ) % this.radialSegments;
-			var jp = ( j + 1 ) % this.tubularSegments;
+	//this.computeVertexNormals();
 
-			var a = this.grid[ i ][ j ];
-			var b = this.grid[ ip ][ j ];
-			var c = this.grid[ ip ][ jp ];
-			var d = this.grid[ i ][ jp ];
+	//console.log( "took", ( Date.now() - startTime ) );
 
-			var uva = new THREE.Vector2( i / this.radialSegments, j / this.tubularSegments );
-			var uvb = new THREE.Vector2( ( i + 1 ) / this.radialSegments, j / this.tubularSegments );
-			var uvc = new THREE.Vector2( ( i + 1 ) / this.radialSegments, ( j + 1 ) / this.tubularSegments );
-			var uvd = new THREE.Vector2( i / this.radialSegments, ( j + 1 ) / this.tubularSegments );
+};
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.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 );
 	}
+};
 
-	this.computeCentroids();
-	this.computeFaceNormals();
-	this.computeVertexNormals();
-
-	function getPos( u, in_q, in_p, radius, heightScale ) {
+THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
 
-		var cu = Math.cos( u );
-		var su = Math.sin( u );
-		var quOverP = in_q / in_p * u;
-		var cs = Math.cos( quOverP );
+	var amount = options.amount !== undefined ? options.amount : 100;
 
-		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;
+	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;
 
-		return new THREE.Vector3( tx, ty, tz );
+	var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
 
-	}
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
 
-};
+	var steps = options.steps !== undefined ? options.steps : 1;
 
-THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+	var extrudePath = options.extrudePath;
+	var extrudePts, extrudeByPath = false;
 
-/**
- * @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
- */
+	var material = options.material;
+	var extrudeMaterial = options.extrudeMaterial;
 
-THREE.TubeGeometry = function( path, segments, radius, radialSegments, closed ) {
+	// Use default WorldUVGenerator if no UV generators are specified.
+	var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
 
-	THREE.Geometry.call( this );
+	var splineTube, binormal, normal, position2;
+	if ( extrudePath ) {
 
-	this.path = path;
-	this.segments = segments || 64;
-	this.radius = radius || 1;
-	this.radialSegments = radialSegments || 8;
-	this.closed = closed || false;
+		extrudePts = extrudePath.getSpacedPoints( steps );
 
-	this.grid = [];
+		extrudeByPath = true;
+		bevelEnabled = false; // bevels not supported for path extrusion
 
-	var scope = this,
+		// SETUP TNB variables
 
-		tangent,
-		normal,
-		binormal,
+		// Reuse TNB from TubeGeomtry for now.
+		// TODO1 - have a .isClosed in spline?
 
-		numpoints = this.segments + 1,
+		splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
 
-		x, y, z,
-		tx, ty, tz,
-		u, v,
+		// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
 
-		cx, cy,
-		pos, pos2 = new THREE.Vector3(),
-		i, j,
-		ip, jp,
-		a, b, c, d,
-		uva, uvb, uvc, uvd;
+		binormal = new THREE.Vector3();
+		normal = new THREE.Vector3();
+		position2 = new THREE.Vector3();
 
-	var frames = new THREE.TubeGeometry.FrenetFrames( this.path, this.segments, this.closed ),
-		tangents = frames.tangents,
-		normals = frames.normals,
-		binormals = frames.binormals;
+	}
 
-	// proxy internals
-	this.tangents = tangents;
-	this.normals = normals;
-	this.binormals = binormals;
+	// Safeguards if bevels are not enabled
 
-	function vert( x, y, z ) {
+	if ( ! bevelEnabled ) {
 
-		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
+		bevelSegments = 0;
+		bevelThickness = 0;
+		bevelSize = 0;
 
 	}
 
+	// Variables initalization
 
-	// consruct the grid
+	var ahole, h, hl; // looping of holes
+	var scope = this;
 
-	for ( i = 0; i < numpoints; i++ ) {
+	var shapesOffset = this.vertices.length;
 
-		this.grid[ i ] = [];
+	var shapePoints = shape.extractPoints( curveSegments );
 
-		u = i / ( numpoints - 1 );
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
 
-		pos = path.getPointAt( u );
+	var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
 
-		tangent = tangents[ i ];
-		normal = normals[ i ];
-		binormal = binormals[ i ];
+	if ( reverse ) {
+
+		vertices = vertices.reverse();
+
+		// Maybe we should also check if holes are in the opposite direction, just to be safe ...
 
-		for ( j = 0; j < this.radialSegments; j++ ) {
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-			v = j / this.radialSegments * 2 * Math.PI;
+			ahole = holes[ h ];
 
-			cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
-			cy = this.radius * Math.sin( v );
+			if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
 
-			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;
+				holes[ h ] = ahole.reverse();
 
-			this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
+			}
 
 		}
-	}
 
+		reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
 
-	// construct the mesh
+	}
 
-	for ( i = 0; i < this.segments; i++ ) {
 
-		for ( j = 0; j < this.radialSegments; j++ ) {
+	var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
 
-			ip = ( this.closed ) ? (i + 1) % this.segments : i + 1;
-			jp = (j + 1) % this.radialSegments;
+	/* Vertices */
 
-			a = this.grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
-			b = this.grid[ ip ][ j ];
-			c = this.grid[ ip ][ jp ];
-			d = this.grid[ i ][ jp ];
+	var contour = vertices; // vertices has all points but contour has only points of circumference
 
-			uva = new THREE.Vector2( i / this.segments, j / this.radialSegments );
-			uvb = new THREE.Vector2( ( i + 1 ) / this.segments, j / this.radialSegments );
-			uvc = new THREE.Vector2( ( i + 1 ) / this.segments, ( j + 1 ) / this.radialSegments );
-			uvd = new THREE.Vector2( i / this.segments, ( j + 1 ) / this.radialSegments );
+	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+		ahole = holes[ h ];
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+		vertices = vertices.concat( ahole );
 
-		}
 	}
 
-	this.computeCentroids();
-	this.computeFaceNormals();
-	this.computeVertexNormals();
-
-};
 
-THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+	function scalePt2 ( pt, vec, size ) {
 
+		if ( ! vec ) THREE.error( "THREE.ExtrudeGeometry: vec does not exist" );
 
-// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
-THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) {
+		return vec.clone().multiplyScalar( size ).add( pt );
 
-	var	tangent = new THREE.Vector3(),
-		normal = new THREE.Vector3(),
-		binormal = new THREE.Vector3(),
+	}
 
-		tangents = [],
-		normals = [],
-		binormals = [],
+	var b, bs, t, z,
+		vert, vlen = vertices.length,
+		face, flen = faces.length;
 
-		vec = new THREE.Vector3(),
-		mat = new THREE.Matrix4(),
 
-		numpoints = segments + 1,
-		theta,
-		epsilon = 0.0001,
-		smallest,
+	// Find directions for point movement
 
-		tx, ty, tz,
-		i, u, v;
 
+	function getBevelVec( inPt, inPrev, inNext ) {
 
-	// expose internals
-	this.tangents = tangents;
-	this.normals = normals;
-	this.binormals = binormals;
+		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
+
+		// 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
 
-	// compute the tangent vectors for each segment on the path
+			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; }
+				}
+			}
 
-	for ( i = 0; i < numpoints; i++ ) {
+			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 );
+			}
 
-		u = i / ( numpoints - 1 );
+		}
 
-		tangents[ i ] = path.getTangentAt( u );
-		tangents[ i ].normalize();
+		return	new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
 
 	}
 
-	initialNormal3();
 
-	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 contourMovements = [];
 
-	function initialNormal2() {
+	for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
 
-		// This uses the Frenet-Serret formula for deriving binormal
-		var t2 = path.getTangentAt( epsilon );
+		if ( j === il ) j = 0;
+		if ( k === il ) k = 0;
 
-		normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
-		binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
+		//  (j)---(i)---(k)
+		// console.log('i,j,k', i, j , k)
 
-		normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
-		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
+		contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
 
 	}
 
-	function initialNormal3() {
-		// select an initial normal vector perpenicular to the first tangent vector,
-		// and in the direction of the smallest tangent xyz component
+	var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
 
-		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 );
+	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-		if ( tx <= smallest ) {
-			smallest = tx;
-			normal.set( 1, 0, 0 );
-		}
+		ahole = holes[ h ];
 
-		if ( ty <= smallest ) {
-			smallest = ty;
-			normal.set( 0, 1, 0 );
-		}
+		oneHoleMovements = [];
+
+		for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+
+			if ( j === il ) j = 0;
+			if ( k === il ) k = 0;
+
+			//  (j)---(i)---(k)
+			oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
 
-		if ( tz <= smallest ) {
-			normal.set( 0, 0, 1 );
 		}
 
-		vec.crossVectors( tangents[ 0 ], normal ).normalize();
+		holesMovements.push( oneHoleMovements );
+		verticesMovements = verticesMovements.concat( oneHoleMovements );
 
-		normals[ 0 ].crossVectors( tangents[ 0 ], vec );
-		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
 	}
 
 
-	// compute the slowly-varying normal and binormal vectors for each segment on the path
+	// 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
+
+		for ( i = 0, il = contour.length; i < il; i ++ ) {
+
+			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
 
-	for ( i = 1; i < numpoints; i++ ) {
+			v( vert.x, vert.y,  - z );
 
-		normals[ i ] = normals[ i-1 ].clone();
+		}
 
-		binormals[ i ] = binormals[ i-1 ].clone();
+		// expand holes
 
-		vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-		if ( vec.length() > epsilon ) {
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
 
-			vec.normalize();
+			for ( i = 0, il = ahole.length; i < il; i ++ ) {
 
-			theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), -1, 1 ) ); // clamp for floating pt errors
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
 
-			normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
+				v( vert.x, vert.y,  - z );
 
-		}
+			}
 
-		binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+		}
 
 	}
 
+	bs = bevelSize;
 
-	// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
+	// Back facing vertices
 
-	if ( closed ) {
+	for ( i = 0; i < vlen; i ++ ) {
 
-		theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), -1, 1 ) );
-		theta /= ( numpoints - 1 );
+		vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
 
-		if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
+		if ( ! extrudeByPath ) {
 
-			theta = -theta;
+			v( vert.x, vert.y, 0 );
 
-		}
+		} else {
 
-		for ( i = 1; i < numpoints; i++ ) {
+			// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
 
-			// twist a little...
-			normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
-			binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+			normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
+			binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
+
+			position2.copy( extrudePts[0] ).add(normal).add(binormal);
+
+			v( position2.x, position2.y, position2.z );
 
 		}
 
 	}
-};
 
-/**
- * @author clockworkgeek / https://github.com/clockworkgeek
- * @author timothypratley / https://github.com/timothypratley
- * @author WestLangley / http://github.com/WestLangley
-*/
+	// Add stepped vertices...
+	// Including front facing vertices
 
-THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) {
+	var s;
 
-	THREE.Geometry.call( this );
+	for ( s = 1; s <= steps; s ++ ) {
 
-	radius = radius || 1;
-	detail = detail || 0;
+		for ( i = 0; i < vlen; i ++ ) {
 
-	var that = this;
+			vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
 
-	for ( var i = 0, l = vertices.length; i < l; i ++ ) {
+			if ( ! extrudeByPath ) {
 
-		prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) );
+				v( vert.x, vert.y, amount / steps * s );
 
-	}
+			} else {
 
-	var midpoints = [], p = this.vertices;
+				// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
 
-	var f = [];
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+				normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
+				binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
+
+				position2.copy( extrudePts[s] ).add( normal ).add( binormal );
+
+				v( position2.x, position2.y, position2.z );
 
-		var v1 = p[ faces[ i ][ 0 ] ];
-		var v2 = p[ faces[ i ][ 1 ] ];
-		var v3 = p[ faces[ i ][ 2 ] ];
+			}
 
-		f[ i ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
+		}
 
 	}
 
-	for ( var i = 0, l = f.length; i < l; i ++ ) {
 
-		subdivide(f[ i ], detail);
+	// Add bevel segments planes
 
-	}
+	//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 ) ;
 
-	// Handle case when face straddles the seam
+		// contract shape
 
-	for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
+		for ( i = 0, il = contour.length; i < il; i ++ ) {
 
-		var uvs = this.faceVertexUvs[ 0 ][ i ];
+			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
+			v( vert.x, vert.y,  amount + z );
 
-		var x0 = uvs[ 0 ].x;
-		var x1 = uvs[ 1 ].x;
-		var x2 = uvs[ 2 ].x;
+		}
 
-		var max = Math.max( x0, Math.max( x1, x2 ) );
-		var min = Math.min( x0, Math.min( x1, x2 ) );
+		// expand holes
 
-		if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-			if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
-			if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
-			if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
 
-		}
+			for ( i = 0, il = ahole.length; i < il; i ++ ) {
 
-	}
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
 
+				if ( ! extrudeByPath ) {
 
-	// Apply radius
+					v( vert.x, vert.y,  amount + z );
 
-	for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
+				} else {
 
-		this.vertices[ i ].multiplyScalar( radius );
+					v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
 
-	}
+				}
 
+			}
 
-	// Merge vertices
+		}
 
-	this.mergeVertices();
+	}
 
-	this.computeCentroids();
+	/* Faces */
 
-	this.computeFaceNormals();
+	// Top and bottom faces
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	buildLidFaces();
 
+	// Sides faces
 
-	// Project vector onto sphere's surface
+	buildSideFaces();
 
-	function prepare( vector ) {
 
-		var vertex = vector.normalize().clone();
-		vertex.index = that.vertices.push( vertex ) - 1;
+	/////  Internal functions
 
-		// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
+	function buildLidFaces() {
 
-		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 ( bevelEnabled ) {
 
-		return vertex;
+			var layer = 0 ; // steps + 1
+			var offset = vlen * layer;
 
-	}
+			// Bottom faces
 
+			for ( i = 0; i < flen; i ++ ) {
 
-	// Approximate a curved face with recursively sub-divided triangles.
+				face = faces[ i ];
+				f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
 
-	function make( v1, v2, v3 ) {
+			}
 
-		var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
-		face.centroid.add( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
-		that.faces.push( face );
+			layer = steps + bevelSegments * 2;
+			offset = vlen * layer;
 
-		var azi = azimuth( face.centroid );
+			// Top faces
 
-		that.faceVertexUvs[ 0 ].push( [
-			correctUV( v1.uv, v1, azi ),
-			correctUV( v2.uv, v2, azi ),
-			correctUV( v3.uv, v3, azi )
-		] );
+			for ( i = 0; i < flen; i ++ ) {
 
-	}
+				face = faces[ i ];
+				f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
 
+			}
 
-	// Analytically subdivide a face to the required detail level.
+		} else {
 
-	function subdivide(face, detail ) {
+			// Bottom faces
 
-		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 = [];
+			for ( i = 0; i < flen; i ++ ) {
 
-		// Construct all of the vertices for this subdivision.
+				face = faces[ i ];
+				f3( face[ 2 ], face[ 1 ], face[ 0 ] );
 
-		for ( var i = 0 ; i <= cols; i ++ ) {
+			}
 
-			v[ i ] = [];
+			// Top faces
 
-			var aj = prepare( a.clone().lerp( c, i / cols ) );
-			var bj = prepare( b.clone().lerp( c, i / cols ) );
-			var rows = cols - i;
+			for ( i = 0; i < flen; i ++ ) {
 
-			for ( var j = 0; j <= rows; j ++) {
+				face = faces[ i ];
+				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
 
-				if ( j == 0 && i == cols ) {
+			}
+		}
 
-					v[ i ][ j ] = aj;
+	}
 
-				} else {
+	// Create faces for the z-sides of the shape
 
-					v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
+	function buildSideFaces() {
 
-				}
+		var layeroffset = 0;
+		sidewalls( contour, layeroffset );
+		layeroffset += contour.length;
 
-			}
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+			ahole = holes[ h ];
+			sidewalls( ahole, layeroffset );
+
+			//, true
+			layeroffset += ahole.length;
 
 		}
 
-		// Construct all of the faces.
+	}
 
-		for ( var i = 0; i < cols ; i ++ ) {
+	function sidewalls( contour, layeroffset ) {
 
-			for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
+		var j, k;
+		i = contour.length;
 
-				var k = Math.floor( j / 2 );
+		while ( -- i >= 0 ) {
 
-				if ( j % 2 == 0 ) {
+			j = i;
+			k = i - 1;
+			if ( k < 0 ) k = contour.length - 1;
 
-					make(
-						v[ i ][ k + 1],
-						v[ i + 1 ][ k ],
-						v[ i ][ k ]
-					);
+			//console.log('b', i,j, i-1, k,vertices.length);
 
-				} else {
+			var s = 0, sl = steps  + bevelSegments * 2;
 
-					make(
-						v[ i ][ k + 1 ],
-						v[ i + 1][ k + 1],
-						v[ i + 1 ][ k ]
-					);
+			for ( s = 0; s < sl; s ++ ) {
 
-				}
+				var slen1 = vlen * s;
+				var slen2 = vlen * ( s + 1 );
 
-			}
+				var a = layeroffset + j + slen1,
+					b = layeroffset + k + slen1,
+					c = layeroffset + k + slen2,
+					d = layeroffset + j + slen2;
+
+				f4( a, b, c, d, contour, s, sl, j, k );
 
+			}
 		}
 
 	}
 
 
-	// Angle around the Y axis, counter-clockwise when looking from above.
-
-	function azimuth( vector ) {
+	function v( x, y, z ) {
 
-		return Math.atan2( vector.z, -vector.x );
+		scope.vertices.push( new THREE.Vector3( x, y, z ) );
 
 	}
 
+	function f3( a, b, c ) {
 
-	// Angle above the XZ plane.
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
 
-	function inclination( vector ) {
+		// normal, color, material
+		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+
+		var uvs = uvgen.generateTopUV( scope, a, b, c );
 
-		return Math.atan2( -vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
+		scope.faceVertexUvs[ 0 ].push( uvs );
 
 	}
 
+	function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
 
-	// Texture fixing helper. Spheres have some odd behaviours.
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
+		d += shapesOffset;
 
-	function correctUV( uv, vector, azimuth ) {
+		scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
+		scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
 
-		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();
+		var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
 
-	}
+		scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
+		scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
 
+	}
 
 };
 
-THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ExtrudeGeometry.WorldUVGenerator = {
 
-/**
- * @author timothypratley / https://github.com/timothypratley
- */
+	generateTopUV: function ( geometry, indexA, indexB, indexC ) {
 
-THREE.IcosahedronGeometry = function ( radius, detail ) {
+		var vertices = geometry.vertices;
 
-	this.radius = radius;
-	this.detail = detail;
+		var a = vertices[ indexA ];
+		var b = vertices[ indexB ];
+		var c = vertices[ indexC ];
 
-	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+		return [
+			new THREE.Vector2( a.x, a.y ),
+			new THREE.Vector2( b.x, b.y ),
+			new THREE.Vector2( c.x, c.y )
+		];
 
-	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 faces = [
-		[ 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 ]
-	];
+	generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
+
+		var vertices = geometry.vertices;
 
-	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+		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.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+// File:src/extras/geometries/ShapeGeometry.js
 
 /**
- * @author timothypratley / https://github.com/timothypratley
- */
+ * @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
+ *
+ * }
+ **/
 
-THREE.OctahedronGeometry = function ( radius, detail ) {
+THREE.ShapeGeometry = function ( shapes, options ) {
 
-	var vertices = [
-		[ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ]
-	];
+	THREE.Geometry.call( this );
 
-	var faces = [
-		[ 0, 2, 4 ], [ 0, 4, 3 ], [ 0, 3, 5 ], [ 0, 5, 2 ], [ 1, 2, 5 ], [ 1, 5, 3 ], [ 1, 3, 4 ], [ 1, 4, 2 ]
-	];
+	this.type = 'ShapeGeometry';
+
+	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+
+	this.addShapeList( shapes, options );
+
+	this.computeFaceNormals();
 
-	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
 };
 
-THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry;
 
 /**
- * @author timothypratley / https://github.com/timothypratley
+ * Add an array of shapes to THREE.ShapeGeometry.
  */
+THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
 
-THREE.TetrahedronGeometry = function ( radius, detail ) {
+	for ( var i = 0, l = shapes.length; i < l; i ++ ) {
 
-	var vertices = [
-		[ 1,  1,  1 ], [ -1, -1, 1 ], [ -1, 1, -1 ], [ 1, -1, -1 ]
-	];
+		this.addShape( shapes[ i ], options );
 
-	var faces = [
-		[ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ]
-	];
+	}
 
-	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+	return this;
 
 };
 
-THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
 /**
- * @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 );
- *
+ * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
  */
+THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
 
-THREE.ParametricGeometry = function ( func, slices, stacks ) {
+	if ( options === undefined ) options = {};
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
 
-	THREE.Geometry.call( this );
+	var material = options.material;
+	var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
 
-	var verts = this.vertices;
-	var faces = this.faces;
-	var uvs = this.faceVertexUvs[ 0 ];
+	//
 
-	var i, il, j, p;
-	var u, v;
+	var i, l, hole;
 
-	var stackCount = stacks + 1;
-	var sliceCount = slices + 1;
+	var shapesOffset = this.vertices.length;
+	var shapePoints = shape.extractPoints( curveSegments );
 
-	for ( i = 0; i <= stacks; i ++ ) {
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
 
-		v = i / stacks;
+	var reverse = ! THREE.Shape.Utils.isClockWise( vertices );
 
-		for ( j = 0; j <= slices; j ++ ) {
+	if ( reverse ) {
 
-			u = j / slices;
+		vertices = vertices.reverse();
 
-			p = func( u, v );
-			verts.push( p );
+		// Maybe we should also check if holes are in the opposite direction, just to be safe...
 
-		}
-	}
+		for ( i = 0, l = holes.length; i < l; i ++ ) {
 
-	var a, b, c, d;
-	var uva, uvb, uvc, uvd;
+			hole = holes[ i ];
 
-	for ( i = 0; i < stacks; i ++ ) {
+			if ( THREE.Shape.Utils.isClockWise( hole ) ) {
 
-		for ( j = 0; j < slices; j ++ ) {
+				holes[ i ] = hole.reverse();
 
-			a = i * sliceCount + j;
-			b = i * sliceCount + j + 1;
-			c = (i + 1) * sliceCount + j + 1;
-			d = (i + 1) * sliceCount + j;
+			}
 
-			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 );
+		}
 
-			faces.push( new THREE.Face3( a, b, d ) );
-			uvs.push( [ uva, uvb, uvd ] );
+		reverse = false;
 
-			faces.push( new THREE.Face3( b, c, d ) );
-			uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
+	}
 
-		}
+	var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
 
-	}
+	// Vertices
 
-	// console.log(this);
+	var contour = vertices;
 
-	// magic bullet
-	// var diff = this.mergeVertices();
-	// console.log('removed ', diff, ' vertices by merging');
+	for ( i = 0, l = holes.length; i < l; i ++ ) {
 
-	this.computeCentroids();
-	this.computeFaceNormals();
-	this.computeVertexNormals();
+		hole = holes[ i ];
+		vertices = vertices.concat( hole );
 
-};
+	}
 
-THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+	//
 
-/**
- * @author sroucheray / http://sroucheray.org/
- * @author mrdoob / http://mrdoob.com/
- */
+	var vert, vlen = vertices.length;
+	var face, flen = faces.length;
 
-THREE.AxisHelper = function ( size ) {
+	for ( i = 0; i < vlen; i ++ ) {
 
-	size = size || 1;
+		vert = vertices[ i ];
 
-	var geometry = new THREE.Geometry();
+		this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
 
-	geometry.vertices.push(
-		new THREE.Vector3(), new THREE.Vector3( size, 0, 0 ),
-		new THREE.Vector3(), new THREE.Vector3( 0, size, 0 ),
-		new THREE.Vector3(), new THREE.Vector3( 0, 0, size )
-	);
+	}
 
-	geometry.colors.push(
-		new THREE.Color( 0xff0000 ), new THREE.Color( 0xffaa00 ),
-		new THREE.Color( 0x00ff00 ), new THREE.Color( 0xaaff00 ),
-		new THREE.Color( 0x0000ff ), new THREE.Color( 0x00aaff )
-	);
+	for ( i = 0; i < flen; i ++ ) {
 
-	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+		face = faces[ i ];
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+		var a = face[ 0 ] + shapesOffset;
+		var b = face[ 1 ] + shapesOffset;
+		var c = face[ 2 ] + shapesOffset;
+
+		this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+		this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
+
+	}
 
 };
 
-THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
+// File:src/extras/geometries/LatheGeometry.js
 
 /**
- * @author WestLangley / http://github.com/WestLangley
- * @author zz85 / http://github.com/zz85
+ * @author astrodud / http://astrodud.isgreat.org/
+ * @author zz85 / https://github.com/zz85
  * @author bhouston / http://exocortex.com
- *
- * Creates an arrow for visualizing directions
- *
- * Parameters:
- *  dir - Vector3
- *  origin - Vector3
- *  length - Number
- *  hex - color in hex value
  */
 
-THREE.ArrowHelper = function ( dir, origin, length, hex ) {
-
-	// dir is assumed to be normalized
-
-	THREE.Object3D.call( this );
-
-	if ( hex === undefined ) hex = 0xffff00;
-	if ( length === undefined ) length = 1;
-
-	this.position = origin;
-
-	var lineGeometry = new THREE.Geometry();
-	lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
-	lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) );
-
-	this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) );
-	this.line.matrixAutoUpdate = false;
-	this.add( this.line );
-
-	var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 );
-	coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.875, 0 ) );
-
-	this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) );
-	this.cone.matrixAutoUpdate = false;
-	this.add( this.cone );
-
-	this.setDirection( dir );
-	this.setLength( length );
+// 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.
 
-};
+THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
 
-THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+	THREE.Geometry.call( this );
 
-THREE.ArrowHelper.prototype.setDirection = function () {
+	this.type = 'LatheGeometry';
 
-	var axis = new THREE.Vector3();
-	var radians;
+	this.parameters = {
+		points: points,
+		segments: segments,
+		phiStart: phiStart,
+		phiLength: phiLength
+	};
 
-	return function ( dir ) {
+	segments = segments || 12;
+	phiStart = phiStart || 0;
+	phiLength = phiLength || 2 * Math.PI;
 
-		// dir is assumed to be normalized
+	var inversePointLength = 1.0 / ( points.length - 1 );
+	var inverseSegments = 1.0 / segments;
 
-		if ( dir.y > 0.99999 ) {
+	for ( var i = 0, il = segments; i <= il; i ++ ) {
 
-			this.quaternion.set( 0, 0, 0, 1 );
+		var phi = phiStart + i * inverseSegments * phiLength;
 
-		} else if ( dir.y < - 0.99999 ) {
+		var c = Math.cos( phi ),
+			s = Math.sin( phi );
 
-			this.quaternion.set( 1, 0, 0, 0 );
+		for ( var j = 0, jl = points.length; j < jl; j ++ ) {
 
-		} else {
+			var pt = points[ j ];
 
-			axis.set( dir.z, 0, - dir.x ).normalize();
+			var vertex = new THREE.Vector3();
 
-			radians = Math.acos( dir.y );
+			vertex.x = c * pt.x - s * pt.y;
+			vertex.y = s * pt.x + c * pt.y;
+			vertex.z = pt.z;
 
-			this.quaternion.setFromAxisAngle( axis, radians );
+			this.vertices.push( vertex );
 
 		}
 
-	};
-
-}();
-
-THREE.ArrowHelper.prototype.setLength = function ( length ) {
+	}
 
-	this.scale.set( length, length, length );
+	var np = points.length;
 
-};
+	for ( var i = 0, il = segments; i < il; i ++ ) {
 
-THREE.ArrowHelper.prototype.setColor = function ( hex ) {
+		for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
 
-	this.line.material.color.setHex( hex );
-	this.cone.material.color.setHex( hex );
+			var base = j + np * i;
+			var a = base;
+			var b = base + np;
+			var c = base + 1 + np;
+			var d = base + 1;
 
-};
+			var u0 = i * inverseSegments;
+			var v0 = j * inversePointLength;
+			var u1 = u0 + inverseSegments;
+			var v1 = v0 + inversePointLength;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+			this.faces.push( new THREE.Face3( a, b, d ) );
 
-THREE.BoxHelper = function ( object ) {
+			this.faceVertexUvs[ 0 ].push( [
 
-	//   5____4
-	// 1/___0/|
-	// | 6__|_7
-	// 2/___3/
+				new THREE.Vector2( u0, v0 ),
+				new THREE.Vector2( u1, v0 ),
+				new THREE.Vector2( u0, v1 )
 
-	var vertices = [
-		new THREE.Vector3(   1,   1,   1 ),
-		new THREE.Vector3( - 1,   1,   1 ),
-		new THREE.Vector3( - 1, - 1,   1 ),
-		new THREE.Vector3(   1, - 1,   1 ),
-
-		new THREE.Vector3(   1,   1, - 1 ),
-		new THREE.Vector3( - 1,   1, - 1 ),
-		new THREE.Vector3( - 1, - 1, - 1 ),
-		new THREE.Vector3(   1, - 1, - 1 )
-	];
+			] );
 
-	this.vertices = vertices;
+			this.faces.push( new THREE.Face3( b, c, d ) );
 
-	// TODO: Wouldn't be nice if Line had .segments?
+			this.faceVertexUvs[ 0 ].push( [
 
-	var geometry = new THREE.Geometry();
-	geometry.vertices.push(
-		vertices[ 0 ], vertices[ 1 ],
-		vertices[ 1 ], vertices[ 2 ],
-		vertices[ 2 ], vertices[ 3 ],
-		vertices[ 3 ], vertices[ 0 ],
-
-		vertices[ 4 ], vertices[ 5 ],
-		vertices[ 5 ], vertices[ 6 ],
-		vertices[ 6 ], vertices[ 7 ],
-		vertices[ 7 ], vertices[ 4 ],
-
-		vertices[ 0 ], vertices[ 4 ],
-		vertices[ 1 ], vertices[ 5 ],
-		vertices[ 2 ], vertices[ 6 ],
-		vertices[ 3 ], vertices[ 7 ]
-	);
+				new THREE.Vector2( u1, v0 ),
+				new THREE.Vector2( u1, v1 ),
+				new THREE.Vector2( u0, v1 )
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces );
+			] );
 
-	if ( object !== undefined ) {
 
-		this.update( object );
+		}
 
 	}
 
-};
+	this.mergeVertices();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
-THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
+};
 
-THREE.BoxHelper.prototype.update = function ( object ) {
+THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry;
 
-	var geometry = object.geometry;
+// File:src/extras/geometries/PlaneGeometry.js
 
-	if ( geometry.boundingBox === null ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ */
 
-		geometry.computeBoundingBox();
+THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
 
-	}
+	console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' );
 
-	var min = geometry.boundingBox.min;
-	var max = geometry.boundingBox.max;
-	var vertices = this.vertices;
+	THREE.Geometry.call( this );
 
-	vertices[ 0 ].set( max.x, max.y, max.z );
-	vertices[ 1 ].set( min.x, max.y, max.z );
-	vertices[ 2 ].set( min.x, min.y, max.z );
-	vertices[ 3 ].set( max.x, min.y, max.z );
-	vertices[ 4 ].set( max.x, max.y, min.z );
-	vertices[ 5 ].set( min.x, max.y, min.z );
-	vertices[ 6 ].set( min.x, min.y, min.z );
-	vertices[ 7 ].set( max.x, min.y, min.z );
+	this.type = 'PlaneGeometry';
 
-	this.geometry.computeBoundingSphere();
-	this.geometry.verticesNeedUpdate = true;
+	this.parameters = {
+		width: width,
+		height: height,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments
+	};
 
-	this.matrixAutoUpdate = false;
-	this.matrixWorld = object.matrixWorld;
+	this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
 
 };
 
-/**
- * @author WestLangley / http://github.com/WestLangley
- */
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry;
 
-// a helper to show the world-axis-aligned bounding box for an object
+// File:src/extras/geometries/PlaneBufferGeometry.js
 
-THREE.BoundingBoxHelper = function ( object, hex ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ */
 
-	var color = hex || 0x888888;
+THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
 
-	this.object = object;
+	THREE.BufferGeometry.call( this );
 
-	this.box = new THREE.Box3();
+	this.type = 'PlaneBufferGeometry';
 
-	THREE.Mesh.call( this, new THREE.CubeGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
+	this.parameters = {
+		width: width,
+		height: height,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments
+	};
 
-};
+	var width_half = width / 2;
+	var height_half = height / 2;
 
-THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
+	var gridX = widthSegments || 1;
+	var gridY = heightSegments || 1;
 
-THREE.BoundingBoxHelper.prototype.update = function () {
+	var gridX1 = gridX + 1;
+	var gridY1 = gridY + 1;
 
-	this.box.setFromObject( this.object );
+	var segment_width = width / gridX;
+	var segment_height = height / gridY;
 
-	this.box.size( this.scale );
+	var vertices = new Float32Array( gridX1 * gridY1 * 3 );
+	var normals = new Float32Array( gridX1 * gridY1 * 3 );
+	var uvs = new Float32Array( gridX1 * gridY1 * 2 );
 
-	this.box.center( this.position );
+	var offset = 0;
+	var offset2 = 0;
 
-};
+	for ( var iy = 0; iy < gridY1; iy ++ ) {
 
-/**
- * @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
- */
+		var y = iy * segment_height - height_half;
 
-THREE.CameraHelper = function ( camera ) {
+		for ( var ix = 0; ix < gridX1; ix ++ ) {
 
-	var geometry = new THREE.Geometry();
-	var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
+			var x = ix * segment_width - width_half;
 
-	var pointMap = {};
+			vertices[ offset     ] = x;
+			vertices[ offset + 1 ] = - y;
 
-	// colors
+			normals[ offset + 2 ] = 1;
 
-	var hexFrustum = 0xffaa00;
-	var hexCone = 0xff0000;
-	var hexUp = 0x00aaff;
-	var hexTarget = 0xffffff;
-	var hexCross = 0x333333;
+			uvs[ offset2     ] = ix / gridX;
+			uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
 
-	// near
+			offset += 3;
+			offset2 += 2;
 
-	addLine( "n1", "n2", hexFrustum );
-	addLine( "n2", "n4", hexFrustum );
-	addLine( "n4", "n3", hexFrustum );
-	addLine( "n3", "n1", hexFrustum );
+		}
 
-	// far
+	}
 
-	addLine( "f1", "f2", hexFrustum );
-	addLine( "f2", "f4", hexFrustum );
-	addLine( "f4", "f3", hexFrustum );
-	addLine( "f3", "f1", hexFrustum );
+	offset = 0;
 
-	// sides
+	var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
 
-	addLine( "n1", "f1", hexFrustum );
-	addLine( "n2", "f2", hexFrustum );
-	addLine( "n3", "f3", hexFrustum );
-	addLine( "n4", "f4", hexFrustum );
+	for ( var iy = 0; iy < gridY; iy ++ ) {
 
-	// cone
+		for ( var ix = 0; ix < gridX; ix ++ ) {
 
-	addLine( "p", "n1", hexCone );
-	addLine( "p", "n2", hexCone );
-	addLine( "p", "n3", hexCone );
-	addLine( "p", "n4", hexCone );
+			var a = ix + gridX1 * iy;
+			var b = ix + gridX1 * ( iy + 1 );
+			var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+			var d = ( ix + 1 ) + gridX1 * iy;
 
-	// up
+			indices[ offset     ] = a;
+			indices[ offset + 1 ] = b;
+			indices[ offset + 2 ] = d;
 
-	addLine( "u1", "u2", hexUp );
-	addLine( "u2", "u3", hexUp );
-	addLine( "u3", "u1", hexUp );
+			indices[ offset + 3 ] = b;
+			indices[ offset + 4 ] = c;
+			indices[ offset + 5 ] = d;
 
-	// target
+			offset += 6;
 
-	addLine( "c", "t", hexTarget );
-	addLine( "p", "c", hexCross );
+		}
 
-	// cross
+	}
 
-	addLine( "cn1", "cn2", hexCross );
-	addLine( "cn3", "cn4", hexCross );
+	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 ) );
 
-	addLine( "cf1", "cf2", hexCross );
-	addLine( "cf3", "cf4", hexCross );
+};
 
-	function addLine( a, b, hex ) {
+THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry;
 
-		addPoint( a, hex );
-		addPoint( b, hex );
+// File:src/extras/geometries/RingGeometry.js
 
-	}
+/**
+ * @author Kaleb Murphy
+ */
 
-	function addPoint( id, hex ) {
+THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
 
-		geometry.vertices.push( new THREE.Vector3() );
-		geometry.colors.push( new THREE.Color( hex ) );
+	THREE.Geometry.call( this );
 
-		if ( pointMap[ id ] === undefined ) {
+	this.type = 'RingGeometry';
 
-			pointMap[ id ] = [];
+	this.parameters = {
+		innerRadius: innerRadius,
+		outerRadius: outerRadius,
+		thetaSegments: thetaSegments,
+		phiSegments: phiSegments,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
 
-		}
+	innerRadius = innerRadius || 0;
+	outerRadius = outerRadius || 50;
 
-		pointMap[ id ].push( geometry.vertices.length - 1 );
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
 
-	}
+	thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
+	phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+	var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
 
-	this.camera = camera;
-	this.matrixWorld = camera.matrixWorld;
-	this.matrixAutoUpdate = false;
+	for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring
 
-	this.pointMap = pointMap;
+		for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle
 
-	this.update();
+			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 ) );
+		}
 
-THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
+		radius += radiusStep;
 
-THREE.CameraHelper.prototype.update = function () {
+	}
 
-	var vector = new THREE.Vector3();
-	var camera = new THREE.Camera();
-	var projector = new THREE.Projector();
+	var n = new THREE.Vector3( 0, 0, 1 );
 
-	return function () {
+	for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
 
-		var scope = this;
+		var thetaSegment = i * (thetaSegments + 1);
 
-		var w = 1, h = 1;
+		for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle
 
-		// we need just camera projection matrix
-		// world matrix must be identity
+			var segment = o + thetaSegment;
 
-		camera.projectionMatrix.copy( this.camera.projectionMatrix );
+			var v1 = segment;
+			var v2 = segment + thetaSegments + 1;
+			var v3 = segment + thetaSegments + 2;
 
-		// center / target
+			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() ]);
 
-		setPoint( "c", 0, 0, -1 );
-		setPoint( "t", 0, 0,  1 );
+			v1 = segment;
+			v2 = segment + thetaSegments + 2;
+			v3 = segment + 1;
 
-		// near
+			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() ]);
 
-		setPoint( "n1", -w, -h, -1 );
-		setPoint( "n2",  w, -h, -1 );
-		setPoint( "n3", -w,  h, -1 );
-		setPoint( "n4",  w,  h, -1 );
+		}
+	}
 
-		// far
+	this.computeFaceNormals();
 
-		setPoint( "f1", -w, -h, 1 );
-		setPoint( "f2",  w, -h, 1 );
-		setPoint( "f3", -w,  h, 1 );
-		setPoint( "f4",  w,  h, 1 );
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-		// up
+};
 
-		setPoint( "u1",  w * 0.7, h * 1.1, -1 );
-		setPoint( "u2", -w * 0.7, h * 1.1, -1 );
-		setPoint( "u3",        0, h * 2,   -1 );
+THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.RingGeometry.prototype.constructor = THREE.RingGeometry;
 
-		// cross
 
-		setPoint( "cf1", -w,  0, 1 );
-		setPoint( "cf2",  w,  0, 1 );
-		setPoint( "cf3",  0, -h, 1 );
-		setPoint( "cf4",  0,  h, 1 );
+// File:src/extras/geometries/SphereGeometry.js
 
-		setPoint( "cn1", -w,  0, -1 );
-		setPoint( "cn2",  w,  0, -1 );
-		setPoint( "cn3",  0, -h, -1 );
-		setPoint( "cn4",  0,  h, -1 );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		function setPoint( point, x, y, z ) {
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
 
-			vector.set( x, y, z );
-			projector.unprojectVector( vector, camera );
+	THREE.Geometry.call( this );
 
-			var points = scope.pointMap[ point ];
+	this.type = 'SphereGeometry';
 
-			if ( points !== undefined ) {
+	this.parameters = {
+		radius: radius,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments,
+		phiStart: phiStart,
+		phiLength: phiLength,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength 
+	};
 
-				for ( var i = 0, il = points.length; i < il; i ++ ) {
+	radius = radius || 50;
 
-					scope.geometry.vertices[ points[ i ] ].copy( vector );
+	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;
 
-			}
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
 
-		}
+	var x, y, vertices = [], uvs = [];
 
-		this.geometry.verticesNeedUpdate = true;
+	for ( y = 0; y <= heightSegments; y ++ ) {
 
-	};
+		var verticesRow = [];
+		var uvsRow = [];
 
-}();
+		for ( x = 0; x <= widthSegments; x ++ ) {
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
+			var u = x / widthSegments;
+			var v = y / heightSegments;
 
-THREE.DirectionalLightHelper = function ( light, size ) {
+			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 );
 
-	THREE.Object3D.call( this );
+			this.vertices.push( vertex );
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+			verticesRow.push( this.vertices.length - 1 );
+			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
 
-	this.matrixWorld = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+		}
 
-	var geometry = new THREE.PlaneGeometry( size, size );
-	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
-	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		vertices.push( verticesRow );
+		uvs.push( uvsRow );
 
-	this.lightPlane = new THREE.Mesh( geometry, material );
-	this.add( this.lightPlane );
+	}
 
-	geometry = new THREE.Geometry();
-	geometry.vertices.push( new THREE.Vector3() );
-	geometry.vertices.push( new THREE.Vector3() );
-	geometry.computeLineDistances();
+	for ( y = 0; y < heightSegments; y ++ ) {
 
-	material = new THREE.LineBasicMaterial( { fog: false } );
-	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		for ( x = 0; x < widthSegments; x ++ ) {
 
-	this.targetLine = new THREE.Line( geometry, material );
-	this.add( this.targetLine );
+			var v1 = vertices[ y ][ x + 1 ];
+			var v2 = vertices[ y ][ x ];
+			var v3 = vertices[ y + 1 ][ x ];
+			var v4 = vertices[ y + 1 ][ x + 1 ];
 
-	this.update();
+			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();
 
-};
+			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();
 
-THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+			if ( Math.abs( this.vertices[ v1 ].y ) === radius ) {
 
-THREE.DirectionalLightHelper.prototype.dispose = function () {
+				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.lightPlane.geometry.dispose();
-	this.lightPlane.material.dispose();
-	this.targetLine.geometry.dispose();
-	this.targetLine.material.dispose();
-};
+			} else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) {
 
-THREE.DirectionalLightHelper.prototype.update = function () {
+				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 ] );
 
-	var vector = new THREE.Vector3();
+			} else {
 
-	return function () {
+				this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
 
-		vector.getPositionFromMatrix( this.light.matrixWorld ).negate();
+				this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
 
-		this.lightPlane.lookAt( vector );
-		this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+			}
 
-		this.targetLine.geometry.vertices[ 1 ].copy( vector );
-		this.targetLine.geometry.verticesNeedUpdate = true;
-		this.targetLine.material.color.copy( this.lightPlane.material.color );
+		}
 
 	}
 
-}();
+	this.computeFaceNormals();
 
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+};
 
-THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
+THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry;
 
-	this.object = object;
+// File:src/extras/geometries/TextGeometry.js
 
-	this.size = size || 1;
+/**
+ * @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 color = hex || 0xffff00;
+/*	Usage Examples
 
-	var width = linewidth || 1;
+	// TextGeometry wrapper
 
-	var geometry = new THREE.Geometry();
+	var text3d = new TextGeometry( text, options );
 
-	var faces = this.object.geometry.faces;
+	// Complete manner
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	var textShapes = THREE.FontUtils.generateShapes( text, options );
+	var text3d = new ExtrudeGeometry( textShapes, options );
 
-		geometry.vertices.push( new THREE.Vector3() );
-		geometry.vertices.push( new THREE.Vector3() );
+*/
 
-	}
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
+THREE.TextGeometry = function ( text, parameters ) {
 
-	this.matrixAutoUpdate = false;
+	parameters = parameters || {};
 
-	this.normalMatrix = new THREE.Matrix3();
+	var textShapes = THREE.FontUtils.generateShapes( text, parameters );
 
-	this.update();
+	// translate parameters to ExtrudeGeometry API
 
-};
+	parameters.amount = parameters.height !== undefined ? parameters.height : 50;
 
-THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+	// defaults
 
-THREE.FaceNormalsHelper.prototype.update = ( function ( object ) {
+	if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
+	if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
+	if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
 
-	var v1 = new THREE.Vector3();
+	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
 
-	return function ( object ) {
+	this.type = 'TextGeometry';
 
-		this.object.updateMatrixWorld( true );
+};
 
-		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
+THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
+THREE.TextGeometry.prototype.constructor = THREE.TextGeometry;
 
-		var vertices = this.geometry.vertices;
+// File:src/extras/geometries/TorusGeometry.js
 
-		var faces = this.object.geometry.faces;
+/**
+ * @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
+ */
 
-		var worldMatrix = this.object.matrixWorld;
+THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	THREE.Geometry.call( this );
 
-			var face = faces[ i ];
+	this.type = 'TorusGeometry';
 
-			v1.copy( face.normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
+	this.parameters = {
+		radius: radius,
+		tube: tube,
+		radialSegments: radialSegments,
+		tubularSegments: tubularSegments,
+		arc: arc
+	};
 
-			var idx = 2 * i;
+	radius = radius || 100;
+	tube = tube || 40;
+	radialSegments = radialSegments || 8;
+	tubularSegments = tubularSegments || 6;
+	arc = arc || Math.PI * 2;
 
-			vertices[ idx ].copy( face.centroid ).applyMatrix4( worldMatrix );
+	var center = new THREE.Vector3(), uvs = [], normals = [];
 
-			vertices[ idx + 1 ].addVectors( vertices[ idx ], v1 );
+	for ( var j = 0; j <= radialSegments; j ++ ) {
 
-		}
+		for ( var i = 0; i <= tubularSegments; i ++ ) {
 
-		this.geometry.verticesNeedUpdate = true;
+			var u = i / tubularSegments * arc;
+			var v = j / radialSegments * Math.PI * 2;
 
-		return this;
+			center.x = radius * Math.cos( u );
+			center.y = radius * Math.sin( u );
 
-	}
+			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.vertices.push( vertex );
 
+			uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
+			normals.push( vertex.clone().sub( center ).normalize() );
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		}
 
-THREE.GridHelper = function ( size, step ) {
+	}
 
-	var geometry = new THREE.Geometry();
-	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+	for ( var j = 1; j <= radialSegments; j ++ ) {
 
-	this.color1 = new THREE.Color( 0x444444 );
-	this.color2 = new THREE.Color( 0x888888 );
+		for ( var i = 1; i <= tubularSegments; i ++ ) {
 
-	for ( var i = - size; i <= size; i += step ) {
+			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;
 
-		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 )
-		);
+			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() ] );
 
-		var color = i === 0 ? this.color1 : this.color2;
+			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() ] );
 
-		geometry.colors.push( color, color, color, color );
+		}
 
 	}
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+	this.computeFaceNormals();
 
 };
 
-THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry;
 
-THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
+// File:src/extras/geometries/TorusKnotGeometry.js
 
-	this.color1.set( colorCenterLine );
-	this.color2.set( colorGrid );
+/**
+ * @author oosmoxiecode
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
+ */
 
-	this.geometry.colorsNeedUpdate = true;
+THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
 
-}
+	THREE.Geometry.call( this );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
+	this.type = 'TorusKnotGeometry';
 
-THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
+	this.parameters = {
+		radius: radius,
+		tube: tube,
+		radialSegments: radialSegments,
+		tubularSegments: tubularSegments,
+		p: p,
+		q: q,
+		heightScale: heightScale
+	};
 
-	THREE.Object3D.call( this );
+	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();
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+	for ( var i = 0; i < radialSegments; ++ i ) {
 
-	this.matrixWorld = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+		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.colors = [ new THREE.Color(), new THREE.Color() ];
+		bitan.crossVectors( tang, n );
+		n.crossVectors( bitan, tang );
+		bitan.normalize();
+		n.normalize();
 
-	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
-	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
+		for ( var j = 0; j < tubularSegments; ++ j ) {
 
-	for ( var i = 0, il = 8; i < il; 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 );
 
-		geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
+			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;
+
+			grid[ i ][ j ] = this.vertices.push( pos ) - 1;
+
+		}
 
 	}
 
-	var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
+	for ( var i = 0; i < radialSegments; ++ i ) {
 
-	this.lightSphere = new THREE.Mesh( geometry, material );
-	this.add( this.lightSphere );
+		for ( var j = 0; j < tubularSegments; ++ j ) {
 
-	this.update();
+			var ip = ( i + 1 ) % radialSegments;
+			var jp = ( j + 1 ) % tubularSegments;
 
-};
+			var a = grid[ i ][ j ];
+			var b = grid[ ip ][ j ];
+			var c = grid[ ip ][ jp ];
+			var d = grid[ i ][ jp ];
 
-THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+			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 );
 
-THREE.HemisphereLightHelper.prototype.dispose = function () {
-	this.lightSphere.geometry.dispose();
-	this.lightSphere.material.dispose();
-};
+			this.faces.push( new THREE.Face3( a, b, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
 
-THREE.HemisphereLightHelper.prototype.update = function () {
+			this.faces.push( new THREE.Face3( b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-	var vector = new THREE.Vector3();
+		}
+	}
 
-	return function () {
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
-		this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
-		this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
+	function getPos( u, in_q, in_p, radius, heightScale ) {
 
-		this.lightSphere.lookAt( vector.getPositionFromMatrix( this.light.matrixWorld ).negate() );
-		this.lightSphere.geometry.colorsNeedUpdate = true;
+		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;
+
+		return new THREE.Vector3( tx, ty, tz );
 
 	}
 
-}();
+};
+
+THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry;
 
+// File:src/extras/geometries/TubeGeometry.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / https://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author miningold / https://github.com/miningold
+ * @author jonobr1 / https://github.com/jonobr1
+ *
+ * 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.PointLightHelper = function ( light, sphereSize ) {
+THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) {
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+	THREE.Geometry.call( this );
 
-	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 );
+	this.type = 'TubeGeometry';
 
-	THREE.Mesh.call( this, geometry, material );
+	this.parameters = {
+		path: path,
+		segments: segments,
+		radius: radius,
+		radialSegments: radialSegments,
+		closed: closed
+	};
 
-	this.matrixWorld = this.light.matrixWorld;
-	this.matrixAutoUpdate = false;
+	segments = segments || 64;
+	radius = radius || 1;
+	radialSegments = radialSegments || 8;
+	closed = closed || false;
+	taper = taper || THREE.TubeGeometry.NoTaper;
 
-	/*
-	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
-	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
+	var grid = [];
 
-	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
-	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
+	var scope = this,
 
-	var d = light.distance;
+		tangent,
+		normal,
+		binormal,
 
-	if ( d === 0.0 ) {
+		numpoints = segments + 1,
 
-		this.lightDistance.visible = false;
+		u, v, r,
+
+		cx, cy,
+		pos, pos2 = new THREE.Vector3(),
+		i, j,
+		ip, jp,
+		a, b, c, d,
+		uva, uvb, uvc, uvd;
+
+	var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
+		tangents = frames.tangents,
+		normals = frames.normals,
+		binormals = frames.binormals;
+
+	// proxy internals
+	this.tangents = tangents;
+	this.normals = normals;
+	this.binormals = binormals;
 
-	} else {
+	function vert( x, y, z ) {
 
-		this.lightDistance.scale.set( d, d, d );
+		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
 
 	}
 
-	this.add( this.lightDistance );
-	*/
+	// consruct the grid
 
-};
+	for ( i = 0; i < numpoints; i ++ ) {
 
-THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
+		grid[ i ] = [];
 
-THREE.PointLightHelper.prototype.dispose = function () {
+		u = i / ( numpoints - 1 );
 
-	this.geometry.dispose();
-	this.material.dispose();
-};
+		pos = path.getPointAt( u );
 
-THREE.PointLightHelper.prototype.update = function () {
+		tangent = tangents[ i ];
+		normal = normals[ i ];
+		binormal = binormals[ i ];
 
-	this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		r = radius * taper( u );
 
-	/*
-	var d = this.light.distance;
+		for ( j = 0; j < radialSegments; j ++ ) {
 
-	if ( d === 0.0 ) {
+			v = j / radialSegments * 2 * Math.PI;
 
-		this.lightDistance.visible = false;
+			cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			cy = r * Math.sin( v );
 
-	} else {
+			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.lightDistance.visible = true;
-		this.lightDistance.scale.set( d, d, d );
+			grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
 
+		}
 	}
-	*/
-
-};
 
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+	// construct the mesh
 
-THREE.SpotLightHelper = function ( light ) {
+	for ( i = 0; i < segments; i ++ ) {
 
-	THREE.Object3D.call( this );
+		for ( j = 0; j < radialSegments; j ++ ) {
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+			ip = ( closed ) ? (i + 1) % segments : i + 1;
+			jp = (j + 1) % radialSegments;
 
-	this.matrixWorld = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+			a = grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
+			b = grid[ ip ][ j ];
+			c = grid[ ip ][ jp ];
+			d = grid[ i ][ jp ];
 
-	var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
+			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.applyMatrix( new THREE.Matrix4().makeTranslation( 0, -0.5, 0 ) );
-	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
+			this.faces.push( new THREE.Face3( a, b, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
 
-	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+			this.faces.push( new THREE.Face3( b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-	this.cone = new THREE.Mesh( geometry, material );
-	this.add( this.cone );
+		}
+	}
 
-	this.update();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
 };
 
-THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
 
-THREE.SpotLightHelper.prototype.dispose = function () {
-	this.cone.geometry.dispose();
-	this.cone.material.dispose();
-};
+THREE.TubeGeometry.NoTaper = function ( u ) {
 
-THREE.SpotLightHelper.prototype.update = function () {
+	return 1;
 
-	var vector = new THREE.Vector3();
-	var vector2 = new THREE.Vector3();
+};
 
-	return function () {
+THREE.TubeGeometry.SinusoidalTaper = function ( u ) {
 
-		var coneLength = this.light.distance ? this.light.distance : 10000;
-		var coneWidth = coneLength * Math.tan( this.light.angle );
+	return Math.sin( Math.PI * u );
 
-		this.cone.scale.set( coneWidth, coneWidth, coneLength );
+};
 
-		vector.getPositionFromMatrix( this.light.matrixWorld );
-		vector2.getPositionFromMatrix( this.light.target.matrixWorld );
+// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
+THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
 
-		this.cone.lookAt( vector2.sub( vector ) );
+	var	normal = new THREE.Vector3(),
 
-		this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		tangents = [],
+		normals = [],
+		binormals = [],
 
-	};
+		vec = new THREE.Vector3(),
+		mat = new THREE.Matrix4(),
 
-}();
+		numpoints = segments + 1,
+		theta,
+		epsilon = 0.0001,
+		smallest,
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+		tx, ty, tz,
+		i, u;
 
-THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
 
-	this.object = object;
+	// expose internals
+	this.tangents = tangents;
+	this.normals = normals;
+	this.binormals = binormals;
 
-	this.size = size || 1;
+	// compute the tangent vectors for each segment on the path
 
-	var color = hex || 0xff0000;
+	for ( i = 0; i < numpoints; i ++ ) {
 
-	var width = linewidth || 1;
+		u = i / ( numpoints - 1 );
 
-	var geometry = new THREE.Geometry();
+		tangents[ i ] = path.getTangentAt( u );
+		tangents[ i ].normalize();
 
-	var vertices = object.geometry.vertices;
+	}
 
-	var faces = object.geometry.faces;
+	initialNormal3();
 
-	for ( var i = 0, l = faces.length; i < l; 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();
+	}
 
-		var face = faces[ i ];
+	function initialNormal2() {
 
-		for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+		// This uses the Frenet-Serret formula for deriving binormal
+		var t2 = path.getTangentAt( epsilon );
 
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.vertices.push( new THREE.Vector3() );
+		normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
+		binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
 
-		}
+		normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
+		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
 
 	}
+	*/
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
-
-	this.matrixAutoUpdate = false;
+	function initialNormal3() {
+		// select an initial normal vector perpenicular to the first tangent vector,
+		// and in the direction of the smallest tangent xyz component
 
-	this.normalMatrix = new THREE.Matrix3();
+		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 );
 
-	this.update();
+		if ( tx <= smallest ) {
+			smallest = tx;
+			normal.set( 1, 0, 0 );
+		}
 
-};
+		if ( ty <= smallest ) {
+			smallest = ty;
+			normal.set( 0, 1, 0 );
+		}
 
-THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+		if ( tz <= smallest ) {
+			normal.set( 0, 0, 1 );
+		}
 
-THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
+		vec.crossVectors( tangents[ 0 ], normal ).normalize();
 
-	var v1 = new THREE.Vector3();
+		normals[ 0 ].crossVectors( tangents[ 0 ], vec );
+		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
+	}
 
-	return function( object ) {
 
-		var keys = [ 'a', 'b', 'c', 'd' ];
+	// compute the slowly-varying normal and binormal vectors for each segment on the path
 
-		this.object.updateMatrixWorld( true );
+	for ( i = 1; i < numpoints; i ++ ) {
 
-		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
+		normals[ i ] = normals[ i - 1 ].clone();
 
-		var vertices = this.geometry.vertices;
+		binormals[ i ] = binormals[ i - 1 ].clone();
 
-		var verts = this.object.geometry.vertices;
+		vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
 
-		var faces = this.object.geometry.faces;
+		if ( vec.length() > epsilon ) {
 
-		var worldMatrix = this.object.matrixWorld;
+			vec.normalize();
 
-		var idx = 0;
+			theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
 
-			var face = faces[ i ];
+		}
 
-			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+		binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
 
-				var vertexId = face[ keys[ j ] ];
-				var vertex = verts[ vertexId ];
+	}
 
-				var normal = face.vertexNormals[ j ];
 
-				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
+	// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
 
-				v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
+	if ( closed ) {
 
-				v1.add( vertices[ idx ] );
-				idx = idx + 1;
+		theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) );
+		theta /= ( numpoints - 1 );
 
-				vertices[ idx ].copy( v1 );
-				idx = idx + 1;
+		if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) {
 
-			}
+			theta = - theta;
 
 		}
 
-		this.geometry.verticesNeedUpdate = true;
+		for ( i = 1; i < numpoints; i ++ ) {
 
-		return this;
+			// twist a little...
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
+			binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+
+		}
 
 	}
+};
 
-}());
+// File:src/extras/geometries/PolyhedronGeometry.js
 
 /**
- * @author mrdoob / http://mrdoob.com/
+ * @author clockworkgeek / https://github.com/clockworkgeek
+ * @author timothypratley / https://github.com/timothypratley
  * @author WestLangley / http://github.com/WestLangley
 */
 
-THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
-
-	this.object = object;
-
-	this.size = size || 1;
+THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
 
-	var color = hex || 0x0000ff;
-
-	var width = linewidth || 1;
-
-	var geometry = new THREE.Geometry();
-
-	var vertices = object.geometry.vertices;
+	THREE.Geometry.call( this );
 
-	var faces = object.geometry.faces;
+	this.type = 'PolyhedronGeometry';
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	this.parameters = {
+		vertices: vertices,
+		indices: indices,
+		radius: radius,
+		detail: detail
+	};
 
-		var face = faces[ i ];
+	radius = radius || 1;
+	detail = detail || 0;
 
-		for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
+	var that = this;
 
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.vertices.push( new THREE.Vector3() );
+	for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
 
-		}
+		prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
 
 	}
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
-
-	this.matrixAutoUpdate = false;
-
-	this.update();
-
-};
-
-THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
-
-THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
-
-	var v1 = new THREE.Vector3();
-
-	return function( object ) {
+	var p = this.vertices;
 
-		var keys = [ 'a', 'b', 'c', 'd' ];
+	var faces = [];
 
-		this.object.updateMatrixWorld( true );
+	for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
 
-		var vertices = this.geometry.vertices;
+		var v1 = p[ indices[ i     ] ];
+		var v2 = p[ indices[ i + 1 ] ];
+		var v3 = p[ indices[ i + 2 ] ];
 
-		var verts = this.object.geometry.vertices;
+		faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
 
-		var faces = this.object.geometry.faces;
+	}
 
-		var worldMatrix = this.object.matrixWorld;
+	var centroid = new THREE.Vector3();
 
-		var idx = 0;
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+		subdivide( faces[ i ], detail );
 
-			var face = faces[ i ];
+	}
 
-			for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
 
-				var vertexId = face[ keys[ j ] ];
-				var vertex = verts[ vertexId ];
+	// Handle case when face straddles the seam
 
-				var tangent = face.vertexTangents[ j ];
+	for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
 
-				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
+		var uvs = this.faceVertexUvs[ 0 ][ i ];
 
-				v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
+		var x0 = uvs[ 0 ].x;
+		var x1 = uvs[ 1 ].x;
+		var x2 = uvs[ 2 ].x;
 
-				v1.add( vertices[ idx ] );
-				idx = idx + 1;
+		var max = Math.max( x0, Math.max( x1, x2 ) );
+		var min = Math.min( x0, Math.min( x1, x2 ) );
 
-				vertices[ idx ].copy( v1 );
-				idx = idx + 1;
+		if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
 
-			}
+			if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
+			if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
+			if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
 
 		}
 
-		this.geometry.verticesNeedUpdate = true;
+	}
 
-		return this;
+
+	// Apply radius
+
+	for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
+
+		this.vertices[ i ].multiplyScalar( radius );
 
 	}
 
-}());
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+	// Merge vertices
 
-THREE.WireframeHelper = function ( object ) {
+	this.mergeVertices();
 
-	var edge = [ 0, 0 ], hash = {};
-	var sortFunction = function ( a, b ) { return a - b };
+	this.computeFaceNormals();
 
-	var keys = [ 'a', 'b', 'c', 'd' ];
-	var geometry = new THREE.Geometry();
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-	var vertices = object.geometry.vertices;
-	var faces = object.geometry.faces;
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	// Project vector onto sphere's surface
 
-		var face = faces[ i ];
+	function prepare( vector ) {
 
-		for ( var j = 0; j < 3; j ++ ) {
+		var vertex = vector.normalize().clone();
+		vertex.index = that.vertices.push( vertex ) - 1;
 
-			edge[ 0 ] = face[ keys[ j ] ];
-			edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
-			edge.sort( sortFunction );
+		// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
 
-			var key = edge.toString();
+		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 ( hash[ key ] === undefined ) {
+		return vertex;
 
-				geometry.vertices.push( vertices[ edge[ 0 ] ] );
-				geometry.vertices.push( vertices[ edge[ 1 ] ] );
+	}
 
-				hash[ key ] = true;
 
-			}
+	// Approximate a curved face with recursively sub-divided triangles.
 
-		}
+	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 );
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffffff } ), THREE.LinePieces );
+		centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
 
-	this.matrixAutoUpdate = false;
-	this.matrixWorld = object.matrixWorld;
+		var azi = azimuth( centroid );
 
-};
+		that.faceVertexUvs[ 0 ].push( [
+			correctUV( v1.uv, v1, azi ),
+			correctUV( v2.uv, v2, azi ),
+			correctUV( v3.uv, v3, azi )
+		] );
 
-THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
+	}
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
 
-THREE.ImmediateRenderObject = function () {
+	// Analytically subdivide a face to the required detail level.
 
-	THREE.Object3D.call( this );
+	function subdivide( face, detail ) {
 
-	this.render = function ( renderCallback ) { };
+		var cols = Math.pow(2, detail);
+		var a = prepare( that.vertices[ face.a ] );
+		var b = prepare( that.vertices[ face.b ] );
+		var c = prepare( that.vertices[ face.c ] );
+		var v = [];
 
-};
+		// Construct all of the vertices for this subdivision.
 
-THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
+		for ( var i = 0 ; i <= cols; i ++ ) {
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+			v[ i ] = [];
 
-THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+			var aj = prepare( a.clone().lerp( c, i / cols ) );
+			var bj = prepare( b.clone().lerp( c, i / cols ) );
+			var rows = cols - i;
 
-	THREE.Object3D.call( this );
+			for ( var j = 0; j <= rows; j ++) {
 
-	this.lensFlares = [];
+				if ( j == 0 && i == cols ) {
 
-	this.positionScreen = new THREE.Vector3();
-	this.customUpdateCallback = undefined;
+					v[ i ][ j ] = aj;
 
-	if( texture !== undefined ) {
+				} else {
 
-		this.add( texture, size, distance, blending, color );
+					v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
 
-	}
+				}
 
-};
+			}
 
-THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+		}
 
+		// Construct all of the faces.
 
-/*
- * Add: adds another flare
- */
+		for ( var i = 0; i < cols ; i ++ ) {
 
-THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+			for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
 
-	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;
+				var k = Math.floor( j / 2 );
 
-	distance = Math.min( distance, Math.max( 0, distance ) );
+				if ( j % 2 == 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
+					make(
+						v[ i ][ k + 1],
+						v[ i + 1 ][ k ],
+						v[ i ][ k ]
+					);
 
-};
+				} else {
 
+					make(
+						v[ i ][ k + 1 ],
+						v[ i + 1][ k + 1],
+						v[ i + 1 ][ k ]
+					);
 
-/*
- * 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;
+	// Angle around the Y axis, counter-clockwise when looking from above.
 
-		flare.wantedRotation = flare.x * Math.PI * 0.25;
-		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+	function azimuth( vector ) {
+
+		return Math.atan2( vector.z, - vector.x );
 
 	}
 
-};
 
+	// Angle above the XZ plane.
+
+	function inclination( vector ) {
 
+		return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
 
+	}
 
 
+	// Texture fixing helper. Spheres have some odd behaviours.
 
+	function correctUV( uv, vector, azimuth ) {
 
+		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.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry;
 
+// File:src/extras/geometries/DodecahedronGeometry.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
+ * @author Abe Pazos / https://hamoid.com
  */
 
-THREE.MorphBlendMesh = function( geometry, material ) {
+THREE.DodecahedronGeometry = function ( radius, detail ) {
 
-	THREE.Mesh.call( this, geometry, material );
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
-	this.animationsMap = {};
-	this.animationsList = [];
+	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+	var r = 1 / t;
 
-	// prepare default animation
-	// (all frames played together in 1 second)
+	var vertices = [
 
-	var numFrames = this.geometry.morphTargets.length;
+		// (±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,
 
-	var name = "__default";
+		// (0, ±1/φ, ±φ)
+		 0, -r, -t,     0, -r,  t,
+		 0,  r, -t,     0,  r,  t,
 
-	var startFrame = 0;
-	var endFrame = numFrames - 1;
+		// (±1/φ, ±φ, 0)
+		-r, -t,  0,    -r,  t,  0,
+		 r, -t,  0,     r,  t,  0,
 
-	var fps = numFrames / 1;
+		// (±φ, 0, ±1/φ)
+		-t,  0, -r,     t,  0, -r,
+		-t,  0,  r,     t,  0,  r
+	];
 
-	this.createAnimation( name, startFrame, endFrame, fps );
-	this.setAnimationWeight( name, 1 );
+	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.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
+};
 
-THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
+THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry;
 
-	var animation = {
+// File:src/extras/geometries/IcosahedronGeometry.js
 
-		startFrame: start,
-		endFrame: end,
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
 
-		length: end - start + 1,
+THREE.IcosahedronGeometry = function ( radius, detail ) {
 
-		fps: fps,
-		duration: ( end - start ) / fps,
+	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
 
-		lastFrame: 0,
-		currentFrame: 0,
+	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
+	];
 
-		active: false,
+	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
+	];
 
-		time: 0,
-		direction: 1,
-		weight: 1,
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
-		directionBackwards: false,
-		mirroredLoop: false
+	this.type = 'IcosahedronGeometry';
 
+	this.parameters = {
+		radius: radius,
+		detail: detail
 	};
-
-	this.animationsMap[ name ] = animation;
-	this.animationsList.push( animation );
-
 };
 
-THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry;
 
-	var pattern = /([a-z]+)(\d+)/;
+// File:src/extras/geometries/OctahedronGeometry.js
 
-	var firstAnimation, frameRanges = {};
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
 
-	var geometry = this.geometry;
+THREE.OctahedronGeometry = function ( radius, detail ) {
 
-	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
-		var morph = geometry.morphTargets[ i ];
-		var chunks = morph.name.match( pattern );
+	var vertices = [
+		1, 0, 0,   - 1, 0, 0,    0, 1, 0,    0,- 1, 0,    0, 0, 1,    0, 0,- 1
+	];
 
-		if ( chunks && chunks.length > 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 name = chunks[ 1 ];
-			var num = chunks[ 2 ];
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
-			if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity };
+	this.type = 'OctahedronGeometry';
 
-			var range = frameRanges[ name ];
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
+};
 
-			if ( i < range.start ) range.start = i;
-			if ( i > range.end ) range.end = i;
+THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry;
 
-			if ( ! firstAnimation ) firstAnimation = name;
+// File:src/extras/geometries/TetrahedronGeometry.js
 
-		}
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
 
-	}
+THREE.TetrahedronGeometry = function ( radius, detail ) {
+
+	var vertices = [
+		 1,  1,  1,   - 1, - 1,  1,   - 1,  1, - 1,    1, - 1, - 1
+	];
 
-	for ( var name in frameRanges ) {
+	var indices = [
+		 2,  1,  0,    0,  3,  2,    1,  3,  0,    2,  3,  1
+	];
 
-		var range = frameRanges[ name ];
-		this.createAnimation( name, range.start, range.end, fps );
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
-	}
+	this.type = 'TetrahedronGeometry';
 
-	this.firstAnimation = firstAnimation;
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
 };
 
-THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
-
-	var animation = this.animationsMap[ name ];
+THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry;
 
-	if ( animation ) {
+// File:src/extras/geometries/ParametricGeometry.js
 
-		animation.direction = 1;
-		animation.directionBackwards = false;
+/**
+ * @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 );
 
-THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
+	this.type = 'ParametricGeometry';
 
-	var animation = this.animationsMap[ name ];
+	this.parameters = {
+		func: func,
+		slices: slices,
+		stacks: stacks
+	};
 
-	if ( animation ) {
+	var verts = this.vertices;
+	var faces = this.faces;
+	var uvs = this.faceVertexUvs[ 0 ];
 
-		animation.direction = -1;
-		animation.directionBackwards = true;
+	var i, j, p;
+	var u, v;
 
-	}
+	var sliceCount = slices + 1;
 
-};
+	for ( i = 0; i <= stacks; i ++ ) {
 
-THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
+		v = i / stacks;
 
-	var animation = this.animationsMap[ name ];
+		for ( j = 0; j <= slices; j ++ ) {
 
-	if ( animation ) {
+			u = j / slices;
 
-		animation.fps = fps;
-		animation.duration = ( animation.end - animation.start ) / animation.fps;
+			p = func( u, v );
+			verts.push( p );
 
+		}
 	}
 
-};
+	var a, b, c, d;
+	var uva, uvb, uvc, uvd;
 
-THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
+	for ( i = 0; i < stacks; i ++ ) {
 
-	var animation = this.animationsMap[ name ];
+		for ( j = 0; j < slices; j ++ ) {
 
-	if ( animation ) {
+			a = i * sliceCount + j;
+			b = i * sliceCount + j + 1;
+			c = (i + 1) * sliceCount + j + 1;
+			d = (i + 1) * sliceCount + j;
 
-		animation.duration = duration;
-		animation.fps = ( animation.end - animation.start ) / animation.duration;
+			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 );
 
-	}
+			faces.push( new THREE.Face3( a, b, d ) );
+			uvs.push( [ uva, uvb, uvd ] );
 
-};
+			faces.push( new THREE.Face3( b, c, d ) );
+			uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
+		}
 
-	var animation = this.animationsMap[ name ];
+	}
 
-	if ( animation ) {
+	// console.log(this);
 
-		animation.weight = weight;
+	// magic bullet
+	// var diff = this.mergeVertices();
+	// console.log('removed ', diff, ' vertices by merging');
 
-	}
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
 };
 
-THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry;
 
-	var animation = this.animationsMap[ name ];
+// File:src/extras/helpers/AxisHelper.js
 
-	if ( animation ) {
+/**
+ * @author sroucheray / http://sroucheray.org/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		animation.time = time;
+THREE.AxisHelper = function ( size ) {
 
-	}
+	size = size || 1;
 
-};
+	var vertices = new Float32Array( [
+		0, 0, 0,  size, 0, 0,
+		0, 0, 0,  0, size, 0,
+		0, 0, 0,  0, 0, size
+	] );
 
-THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
+	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 time = 0;
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
 
-	var animation = this.animationsMap[ name ];
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
-	if ( animation ) {
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-		time = animation.time;
+};
 
-	}
+THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.AxisHelper.prototype.constructor = THREE.AxisHelper;
 
-	return time;
+// 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.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
+THREE.ArrowHelper = ( function () {
 
-	var duration = -1;
+	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 ) {
 
-		duration = animation.duration;
+		// dir is assumed to be normalized
 
-	}
+		THREE.Object3D.call( this );
 
-	return duration;
+		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 );
 
-THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
+		this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
+		this.line.matrixAutoUpdate = false;
+		this.add( this.line );
 
-	var animation = this.animationsMap[ name ];
+		this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
+		this.cone.matrixAutoUpdate = false;
+		this.add( this.cone );
 
-	if ( animation ) {
+		this.setDirection( dir );
+		this.setLength( length, headLength, headWidth );
 
-		animation.time = 0;
-		animation.active = true;
+	}
 
-	} else {
+}() );
 
-		console.warn( "animation[" + name + "] undefined" );
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper;
 
-	}
+THREE.ArrowHelper.prototype.setDirection = ( function () {
 
-};
+	var axis = new THREE.Vector3();
+	var radians;
 
-THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
+	return function ( dir ) {
 
-	var animation = this.animationsMap[ name ];
+		// dir is assumed to be normalized
 
-	if ( animation ) {
+		if ( dir.y > 0.99999 ) {
 
-		animation.active = false;
+			this.quaternion.set( 0, 0, 0, 1 );
 
-	}
+		} else if ( dir.y < - 0.99999 ) {
 
-};
+			this.quaternion.set( 1, 0, 0, 0 );
 
-THREE.MorphBlendMesh.prototype.update = function ( delta ) {
+		} else {
 
-	for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
+			axis.set( dir.z, 0, - dir.x ).normalize();
 
-		var animation = this.animationsList[ i ];
+			radians = Math.acos( dir.y );
 
-		if ( ! animation.active ) continue;
+			this.quaternion.setFromAxisAngle( axis, radians );
 
-		var frameTime = animation.duration / animation.length;
+		}
 
-		animation.time += animation.direction * delta;
+	};
 
-		if ( animation.mirroredLoop ) {
+}() );
 
-			if ( animation.time > animation.duration || animation.time < 0 ) {
+THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
 
-				animation.direction *= -1;
+	if ( headLength === undefined ) headLength = 0.2 * length;
+	if ( headWidth === undefined ) headWidth = 0.2 * headLength;
 
-				if ( animation.time > animation.duration ) {
+	this.line.scale.set( 1, length - headLength, 1 );
+	this.line.updateMatrix();
 
-					animation.time = animation.duration;
-					animation.directionBackwards = true;
+	this.cone.scale.set( headWidth, headLength, headWidth );
+	this.cone.position.y = length;
+	this.cone.updateMatrix();
 
-				}
+};
 
-				if ( animation.time < 0 ) {
+THREE.ArrowHelper.prototype.setColor = function ( color ) {
 
-					animation.time = 0;
-					animation.directionBackwards = false;
+	this.line.material.color.set( color );
+	this.cone.material.color.set( color );
 
-				}
+};
 
-			}
+// File:src/extras/helpers/BoxHelper.js
 
-		} else {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			animation.time = animation.time % animation.duration;
+THREE.BoxHelper = function ( object ) {
 
-			if ( animation.time < 0 ) animation.time += animation.duration;
+	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 );
 
-		var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
-		var weight = animation.weight;
+	if ( object !== undefined ) {
 
-		if ( keyframe !== animation.currentFrame ) {
+		this.update( object );
 
-			this.morphTargetInfluences[ animation.lastFrame ] = 0;
-			this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
+	}
 
-			this.morphTargetInfluences[ keyframe ] = 0;
+};
 
-			animation.lastFrame = animation.currentFrame;
-			animation.currentFrame = keyframe;
+THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.BoxHelper.prototype.constructor = THREE.BoxHelper;
 
-		}
+THREE.BoxHelper.prototype.update = function ( object ) {
 
-		var mix = ( animation.time % frameTime ) / frameTime;
+	var geometry = object.geometry;
 
-		if ( animation.directionBackwards ) mix = 1 - mix;
+	if ( geometry.boundingBox === null ) {
 
-		this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
-		this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
+		geometry.computeBoundingBox();
 
 	}
 
-};
+	var min = geometry.boundingBox.min;
+	var max = geometry.boundingBox.max;
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+	/*
+	  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
+	*/
 
-THREE.LensFlarePlugin = function () {
+	var vertices = this.geometry.attributes.position.array;
 
-	var _gl, _renderer, _precision, _lensFlare = {};
+	vertices[  0 ] = max.x; vertices[  1 ] = max.y; vertices[  2 ] = max.z;
+	vertices[  3 ] = min.x; vertices[  4 ] = max.y; vertices[  5 ] = max.z;
 
-	this.init = function ( renderer ) {
+	vertices[  6 ] = min.x; vertices[  7 ] = max.y; vertices[  8 ] = max.z;
+	vertices[  9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z;
 
-		_gl = renderer.context;
-		_renderer = renderer;
+	vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z;
+	vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z;
 
-		_precision = renderer.getPrecision();
+	vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z;
+	vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z;
 
-		_lensFlare.vertices = new Float32Array( 8 + 8 );
-		_lensFlare.faces = new Uint16Array( 6 );
+	//
 
-		var i = 0;
-		_lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1;	// vertex
-		_lensFlare.vertices[ i++ ] = 0;  _lensFlare.vertices[ i++ ] = 0;	// uv... etc.
+	vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z;
+	vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z;
 
-		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = -1;
-		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 0;
+	vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z;
+	vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z;
 
-		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
-		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
+	vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z;
+	vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z;
 
-		_lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1;
-		_lensFlare.vertices[ i++ ] = 0;  _lensFlare.vertices[ i++ ] = 1;
+	vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z;
+	vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z;
 
-		i = 0;
-		_lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2;
-		_lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3;
+	//
 
-		// buffers
+	vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z;
+	vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z;
 
-		_lensFlare.vertexBuffer     = _gl.createBuffer();
-		_lensFlare.elementBuffer    = _gl.createBuffer();
+	vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z;
+	vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z;
 
-		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
-		_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
+	vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z;
+	vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z;
 
-		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
-		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW );
+	vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z;
+	vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z;
 
-		// textures
+	this.geometry.attributes.position.needsUpdate = true;
+
+	this.geometry.computeBoundingSphere();
 
-		_lensFlare.tempTexture      = _gl.createTexture();
-		_lensFlare.occlusionTexture = _gl.createTexture();
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.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 );
+};
 
-		_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.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 );
+// File:src/extras/helpers/BoundingBoxHelper.js
 
-		if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
 
-			_lensFlare.hasVertexTexture = false;
-			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision );
+// a helper to show the world-axis-aligned bounding box for an object
 
-		} else {
+THREE.BoundingBoxHelper = function ( object, hex ) {
 
-			_lensFlare.hasVertexTexture = true;
-			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision );
+	var color = ( hex !== undefined ) ? hex : 0x888888;
 
-		}
+	this.object = object;
 
-		_lensFlare.attributes = {};
-		_lensFlare.uniforms = {};
+	this.box = new THREE.Box3();
 
-		_lensFlare.attributes.vertex       = _gl.getAttribLocation ( _lensFlare.program, "position" );
-		_lensFlare.attributes.uv           = _gl.getAttribLocation ( _lensFlare.program, "uv" );
+	THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
 
-		_lensFlare.uniforms.renderType     = _gl.getUniformLocation( _lensFlare.program, "renderType" );
-		_lensFlare.uniforms.map            = _gl.getUniformLocation( _lensFlare.program, "map" );
-		_lensFlare.uniforms.occlusionMap   = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" );
-		_lensFlare.uniforms.opacity        = _gl.getUniformLocation( _lensFlare.program, "opacity" );
-		_lensFlare.uniforms.color          = _gl.getUniformLocation( _lensFlare.program, "color" );
-		_lensFlare.uniforms.scale          = _gl.getUniformLocation( _lensFlare.program, "scale" );
-		_lensFlare.uniforms.rotation       = _gl.getUniformLocation( _lensFlare.program, "rotation" );
-		_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
+};
 
-	};
+THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
+THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper;
 
+THREE.BoundingBoxHelper.prototype.update = function () {
 
-	/*
-	 * Render lens flares
-	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
-	 *         reads these back and calculates occlusion.
-	 *         Then _lensFlare.update_lensFlares() is called to re-position and
-	 *         update transparency of flares. Then they are rendered.
-	 *
-	 */
+	this.box.setFromObject( this.object );
 
-	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+	this.box.size( this.scale );
+
+	this.box.center( this.position );
 
-		var flares = scene.__webglFlares,
-			nFlares = flares.length;
+};
 
-		if ( ! nFlares ) return;
+// File:src/extras/helpers/CameraHelper.js
 
-		var tempPosition = new THREE.Vector3();
+/**
+ * @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
+ */
 
-		var invAspect = viewportHeight / viewportWidth,
-			halfViewportWidth = viewportWidth * 0.5,
-			halfViewportHeight = viewportHeight * 0.5;
+THREE.CameraHelper = function ( camera ) {
 
-		var size = 16 / viewportHeight,
-			scale = new THREE.Vector2( size * invAspect, size );
+	var geometry = new THREE.Geometry();
+	var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
 
-		var screenPosition = new THREE.Vector3( 1, 1, 0 ),
-			screenPositionPixels = new THREE.Vector2( 1, 1 );
+	var pointMap = {};
 
-		var uniforms = _lensFlare.uniforms,
-			attributes = _lensFlare.attributes;
+	// colors
 
-		// set _lensFlare program and reset blending
+	var hexFrustum = 0xffaa00;
+	var hexCone = 0xff0000;
+	var hexUp = 0x00aaff;
+	var hexTarget = 0xffffff;
+	var hexCross = 0x333333;
 
-		_gl.useProgram( _lensFlare.program );
+	// near
 
-		_gl.enableVertexAttribArray( _lensFlare.attributes.vertex );
-		_gl.enableVertexAttribArray( _lensFlare.attributes.uv );
+	addLine( "n1", "n2", hexFrustum );
+	addLine( "n2", "n4", hexFrustum );
+	addLine( "n4", "n3", hexFrustum );
+	addLine( "n3", "n1", hexFrustum );
 
-		// loop through all lens flares to update their occlusion and positions
-		// setup gl and common used attribs/unforms
+	// far
 
-		_gl.uniform1i( uniforms.occlusionMap, 0 );
-		_gl.uniform1i( uniforms.map, 1 );
+	addLine( "f1", "f2", hexFrustum );
+	addLine( "f2", "f4", hexFrustum );
+	addLine( "f4", "f3", hexFrustum );
+	addLine( "f3", "f1", hexFrustum );
 
-		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
-		_gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
-		_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
+	// sides
 
-		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
+	addLine( "n1", "f1", hexFrustum );
+	addLine( "n2", "f2", hexFrustum );
+	addLine( "n3", "f3", hexFrustum );
+	addLine( "n4", "f4", hexFrustum );
 
-		_gl.disable( _gl.CULL_FACE );
-		_gl.depthMask( false );
+	// cone
 
-		var i, j, jl, flare, sprite;
+	addLine( "p", "n1", hexCone );
+	addLine( "p", "n2", hexCone );
+	addLine( "p", "n3", hexCone );
+	addLine( "p", "n4", hexCone );
 
-		for ( i = 0; i < nFlares; i ++ ) {
+	// up
 
-			size = 16 / viewportHeight;
-			scale.set( size * invAspect, size );
+	addLine( "u1", "u2", hexUp );
+	addLine( "u2", "u3", hexUp );
+	addLine( "u3", "u1", hexUp );
 
-			// calc object screen position
+	// target
 
-			flare = flares[ i ];
+	addLine( "c", "t", hexTarget );
+	addLine( "p", "c", hexCross );
 
-			tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
+	// cross
 
-			tempPosition.applyMatrix4( camera.matrixWorldInverse );
-			tempPosition.applyProjection( camera.projectionMatrix );
+	addLine( "cn1", "cn2", hexCross );
+	addLine( "cn3", "cn4", hexCross );
 
-			// setup arrays for gl programs
+	addLine( "cf1", "cf2", hexCross );
+	addLine( "cf3", "cf4", hexCross );
 
-			screenPosition.copy( tempPosition )
+	function addLine( a, b, hex ) {
 
-			screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
-			screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
+		addPoint( a, hex );
+		addPoint( b, hex );
 
-			// screen cull
+	}
 
-			if ( _lensFlare.hasVertexTexture || (
-				screenPositionPixels.x > 0 &&
-				screenPositionPixels.x < viewportWidth &&
-				screenPositionPixels.y > 0 &&
-				screenPositionPixels.y < viewportHeight ) ) {
+	function addPoint( id, hex ) {
 
-				// save current RGB to temp texture
+		geometry.vertices.push( new THREE.Vector3() );
+		geometry.colors.push( new THREE.Color( hex ) );
 
-				_gl.activeTexture( _gl.TEXTURE1 );
-				_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
-				_gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+		if ( pointMap[ id ] === undefined ) {
 
+			pointMap[ id ] = [];
 
-				// render pink quad
+		}
 
-				_gl.uniform1i( uniforms.renderType, 0 );
-				_gl.uniform2f( uniforms.scale, scale.x, scale.y );
-				_gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+		pointMap[ id ].push( geometry.vertices.length - 1 );
 
-				_gl.disable( _gl.BLEND );
-				_gl.enable( _gl.DEPTH_TEST );
+	}
 
-				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
+	this.camera = camera;
+	this.matrix = camera.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-				// copy result to occlusionMap
+	this.pointMap = pointMap;
 
-				_gl.activeTexture( _gl.TEXTURE0 );
-				_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
-				_gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+	this.update();
 
+};
 
-				// restore graphics
+THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.CameraHelper.prototype.constructor = THREE.CameraHelper;
 
-				_gl.uniform1i( uniforms.renderType, 1 );
-				_gl.disable( _gl.DEPTH_TEST );
+THREE.CameraHelper.prototype.update = function () {
 
-				_gl.activeTexture( _gl.TEXTURE1 );
-				_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
-				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+	var geometry, pointMap;
+	
+	var vector = new THREE.Vector3();
+	var camera = new THREE.Camera();
 
+	var setPoint = function ( point, x, y, z ) {
 
-				// update object positions
+		vector.set( x, y, z ).unproject( camera );
 
-				flare.positionScreen.copy( screenPosition )
+		var points = pointMap[ point ];
 
-				if ( flare.customUpdateCallback ) {
+		if ( points !== undefined ) {
 
-					flare.customUpdateCallback( flare );
+			for ( var i = 0, il = points.length; i < il; i ++ ) {
 
-				} else {
+				geometry.vertices[ points[ i ] ].copy( vector );
 
-					flare.updateLensFlares();
+			}
 
-				}
+		}
 
-				// render flares
+	};
 
-				_gl.uniform1i( uniforms.renderType, 2 );
-				_gl.enable( _gl.BLEND );
+	return function () {
 
-				for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+		geometry = this.geometry;
+		pointMap = this.pointMap;
 
-					sprite = flare.lensFlares[ j ];
+		var w = 1, h = 1;
 
-					if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
+		// we need just camera projection matrix
+		// world matrix must be identity
 
-						screenPosition.x = sprite.x;
-						screenPosition.y = sprite.y;
-						screenPosition.z = sprite.z;
+		camera.projectionMatrix.copy( this.camera.projectionMatrix );
 
-						size = sprite.size * sprite.scale / viewportHeight;
+		// center / target
 
-						scale.x = size * invAspect;
-						scale.y = size;
+		setPoint( "c", 0, 0, - 1 );
+		setPoint( "t", 0, 0,  1 );
 
-						_gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
-						_gl.uniform2f( uniforms.scale, scale.x, scale.y );
-						_gl.uniform1f( uniforms.rotation, sprite.rotation );
+		// near
 
-						_gl.uniform1f( uniforms.opacity, sprite.opacity );
-						_gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
+		setPoint( "n1", - w, - h, - 1 );
+		setPoint( "n2",   w, - h, - 1 );
+		setPoint( "n3", - w,   h, - 1 );
+		setPoint( "n4",   w,   h, - 1 );
 
-						_renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
-						_renderer.setTexture( sprite.texture, 1 );
+		// far
 
-						_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+		setPoint( "f1", - w, - h, 1 );
+		setPoint( "f2",   w, - h, 1 );
+		setPoint( "f3", - w,   h, 1 );
+		setPoint( "f4",   w,   h, 1 );
 
-					}
+		// up
 
-				}
+		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
 
-		}
+		setPoint( "cf1", - w,   0, 1 );
+		setPoint( "cf2",   w,   0, 1 );
+		setPoint( "cf3",   0, - h, 1 );
+		setPoint( "cf4",   0,   h, 1 );
 
-		// restore gl
+		setPoint( "cn1", - w,   0, - 1 );
+		setPoint( "cn2",   w,   0, - 1 );
+		setPoint( "cn3",   0, - h, - 1 );
+		setPoint( "cn4",   0,   h, - 1 );
 
-		_gl.enable( _gl.CULL_FACE );
-		_gl.enable( _gl.DEPTH_TEST );
-		_gl.depthMask( true );
+		geometry.verticesNeedUpdate = true;
 
 	};
 
-	function createProgram ( shader, precision ) {
-
-		var program = _gl.createProgram();
+}();
 
-		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
-		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+// File:src/extras/helpers/DirectionalLightHelper.js
 
-		var prefix = "precision " + precision + " float;\n";
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
 
-		_gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
-		_gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+THREE.DirectionalLightHelper = function ( light, size ) {
 
-		_gl.compileShader( fragmentShader );
-		_gl.compileShader( vertexShader );
+	THREE.Object3D.call( this );
 
-		_gl.attachShader( program, fragmentShader );
-		_gl.attachShader( program, vertexShader );
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-		_gl.linkProgram( program );
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		return program;
+	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 );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+	this.lightPlane = new THREE.Line( geometry, material );
+	this.add( this.lightPlane );
 
-THREE.ShadowMapPlugin = function () {
+	geometry = new THREE.Geometry();
+	geometry.vertices.push(
+		new THREE.Vector3(),
+		new THREE.Vector3()
+	);
 
-	var _gl,
-	_renderer,
-	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+	material = new THREE.LineBasicMaterial( { fog: false } );
+	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-	_frustum = new THREE.Frustum(),
-	_projScreenMatrix = new THREE.Matrix4(),
+	this.targetLine = new THREE.Line( geometry, material );
+	this.add( this.targetLine );
 
-	_min = new THREE.Vector3(),
-	_max = new THREE.Vector3(),
+	this.update();
 
-	_matrixPosition = new THREE.Vector3();
+};
 
-	this.init = function ( renderer ) {
+THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper;
 
-		_gl = renderer.context;
-		_renderer = renderer;
+THREE.DirectionalLightHelper.prototype.dispose = function () {
 
-		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
-		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+	this.lightPlane.geometry.dispose();
+	this.lightPlane.material.dispose();
+	this.targetLine.geometry.dispose();
+	this.targetLine.material.dispose();
+};
 
-		_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 } );
+THREE.DirectionalLightHelper.prototype.update = function () {
 
-		_depthMaterial._shadowPass = true;
-		_depthMaterialMorph._shadowPass = true;
-		_depthMaterialSkin._shadowPass = true;
-		_depthMaterialMorphSkin._shadowPass = true;
+	var v1 = new THREE.Vector3();
+	var v2 = new THREE.Vector3();
+	var v3 = new THREE.Vector3();
 
-	};
+	return function () {
 
-	this.render = function ( scene, camera ) {
+		v1.setFromMatrixPosition( this.light.matrixWorld );
+		v2.setFromMatrixPosition( this.light.target.matrixWorld );
+		v3.subVectors( v2, v1 );
 
-		if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;
+		this.lightPlane.lookAt( v3 );
+		this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		this.update( scene, camera );
+		this.targetLine.geometry.vertices[ 1 ].copy( v3 );
+		this.targetLine.geometry.verticesNeedUpdate = true;
+		this.targetLine.material.color.copy( this.lightPlane.material.color );
 
 	};
 
-	this.update = function ( scene, camera ) {
+}();
 
-		var i, il, j, jl, n,
+// File:src/extras/helpers/EdgesHelper.js
 
-		shadowMap, shadowMatrix, shadowCamera,
-		program, buffer, material,
-		webglObject, object, light,
-		renderList,
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ * @param object THREE.Mesh whose geometry will be used
+ * @param hex line color
+ * @param thresholdAngle the minimim angle (in degrees),
+ * between the face normals of adjacent faces,
+ * that is required to render an edge. A value of 10 means
+ * an edge is only rendered if the angle is at least 10 degrees.
+ */
 
-		lights = [],
-		k = 0,
+THREE.EdgesHelper = function ( object, hex, thresholdAngle ) {
 
-		fog = null;
+	var color = ( hex !== undefined ) ? hex : 0xffffff;
+	thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
 
-		// set GL state for depth map
+	var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) );
 
-		_gl.clearColor( 1, 1, 1, 1 );
-		_gl.disable( _gl.BLEND );
+	var edge = [ 0, 0 ], hash = {};
+	var sortFunction = function ( a, b ) { return a - b };
 
-		_gl.enable( _gl.CULL_FACE );
-		_gl.frontFace( _gl.CCW );
+	var keys = [ 'a', 'b', 'c' ];
+	var geometry = new THREE.BufferGeometry();
 
-		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+	var geometry2;
 
-			_gl.cullFace( _gl.FRONT );
+	if ( object.geometry instanceof THREE.BufferGeometry ) {
 
-		} else {
+		geometry2 = new THREE.Geometry();
+		geometry2.fromBufferGeometry( object.geometry );
 
-			_gl.cullFace( _gl.BACK );
+	} else {
 
-		}
+		geometry2 = object.geometry.clone();
 
-		_renderer.setDepthTest( true );
+	}
 
-		// preprocess lights
-		// 	- skip lights that are not casting shadows
-		//	- create virtual lights for cascaded shadow maps
+	geometry2.mergeVertices();
+	geometry2.computeFaceNormals();
 
-		for ( i = 0, il = scene.__lights.length; i < il; i ++ ) {
+	var vertices = geometry2.vertices;
+	var faces = geometry2.faces;
+	var numEdges = 0;
 
-			light = scene.__lights[ i ];
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-			if ( ! light.castShadow ) continue;
+		var face = faces[ i ];
 
-			if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
+		for ( var j = 0; j < 3; j ++ ) {
 
-				for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
+			edge[ 0 ] = face[ keys[ j ] ];
+			edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+			edge.sort( sortFunction );
 
-					var virtualLight;
+			var key = edge.toString();
 
-					if ( ! light.shadowCascadeArray[ n ] ) {
+			if ( hash[ key ] === undefined ) {
 
-						virtualLight = createVirtualLight( light, n );
-						virtualLight.originalCamera = camera;
+				hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
+				numEdges ++;
 
-						var gyro = new THREE.Gyroscope();
-						gyro.position = light.shadowCascadeOffset;
+			} else {
 
-						gyro.add( virtualLight );
-						gyro.add( virtualLight.target );
+				hash[ key ].face2 = i;
 
-						camera.add( gyro );
+			}
 
-						light.shadowCascadeArray[ n ] = virtualLight;
+		}
 
-						console.log( "Created virtualLight", virtualLight );
+	}
 
-					} else {
+	var coords = new Float32Array( numEdges * 2 * 3 );
 
-						virtualLight = light.shadowCascadeArray[ n ];
+	var index = 0;
 
-					}
+	for ( var key in hash ) {
 
-					updateVirtualLight( light, n );
+		var h = hash[ key ];
 
-					lights[ k ] = virtualLight;
-					k ++;
+		if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) {
+
+			var vertex = vertices[ h.vert1 ];
+			coords[ index ++ ] = vertex.x;
+			coords[ index ++ ] = vertex.y;
+			coords[ index ++ ] = vertex.z;
+
+			vertex = vertices[ h.vert2 ];
+			coords[ index ++ ] = vertex.x;
+			coords[ index ++ ] = vertex.y;
+			coords[ index ++ ] = vertex.z;
+
+		}
 
-				}
+	}
 
-			} else {
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
-				lights[ k ] = light;
-				k ++;
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
 
-			}
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		}
+};
 
-		// render depth map
+THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper;
 
-		for ( i = 0, il = lights.length; i < il; i ++ ) {
+// File:src/extras/helpers/FaceNormalsHelper.js
 
-			light = lights[ i ];
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-			if ( ! light.shadowMap ) {
+THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
 
-				var shadowFilter = THREE.LinearFilter;
+	this.object = object;
 
-				if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
+	this.size = ( size !== undefined ) ? size : 1;
 
-					shadowFilter = THREE.NearestFilter;
+	var color = ( hex !== undefined ) ? hex : 0xffff00;
 
-				}
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
+	var geometry = new THREE.Geometry();
 
-				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
-				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
+	var faces = this.object.geometry.faces;
 
-				light.shadowMatrix = new THREE.Matrix4();
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-			}
+		geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
 
-			if ( ! light.shadowCamera ) {
+	}
 
-				if ( light instanceof THREE.SpotLight ) {
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
+	this.matrixAutoUpdate = false;
 
-				} else if ( light instanceof THREE.DirectionalLight ) {
+	this.normalMatrix = new THREE.Matrix3();
 
-					light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
+	this.update();
 
-				} else {
+};
 
-					console.error( "Unsupported light type for shadow" );
-					continue;
+THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper;
 
-				}
+THREE.FaceNormalsHelper.prototype.update = function () {
 
-				scene.add( light.shadowCamera );
+	var vertices = this.geometry.vertices;
 
-				if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+	var object = this.object;
+	var objectVertices = object.geometry.vertices;
+	var objectFaces = object.geometry.faces;
+	var objectWorldMatrix = object.matrixWorld;
 
-			}
+	object.updateMatrixWorld( true );
 
-			if ( light.shadowCameraVisible && ! light.cameraHelper ) {
+	this.normalMatrix.getNormalMatrix( objectWorldMatrix );
 
-				light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
-				light.shadowCamera.add( light.cameraHelper );
+	for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) {
 
-			}
+		var face = objectFaces[ i ];
 
-			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
+		vertices[ i2 ].copy( objectVertices[ face.a ] )
+			.add( objectVertices[ face.b ] )
+			.add( objectVertices[ face.c ] )
+			.divideScalar( 3 )
+			.applyMatrix4( objectWorldMatrix );
 
-				updateShadowCamera( camera, light );
+		vertices[ i2 + 1 ].copy( face.normal )
+			.applyMatrix3( this.normalMatrix )
+			.normalize()
+			.multiplyScalar( this.size )
+			.add( vertices[ i2 ] );
 
-			}
+	}
 
-			shadowMap = light.shadowMap;
-			shadowMatrix = light.shadowMatrix;
-			shadowCamera = light.shadowCamera;
+	this.geometry.verticesNeedUpdate = true;
 
-			shadowCamera.position.getPositionFromMatrix( light.matrixWorld );
-			_matrixPosition.getPositionFromMatrix( light.target.matrixWorld );
-			shadowCamera.lookAt( _matrixPosition );
-			shadowCamera.updateMatrixWorld();
+	return this;
 
-			shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
+};
 
-			if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
-			if ( light.shadowCameraVisible ) light.cameraHelper.update();
 
-			// compute shadow matrix
+// File:src/extras/helpers/GridHelper.js
 
-			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 );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			shadowMatrix.multiply( shadowCamera.projectionMatrix );
-			shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+THREE.GridHelper = function ( size, step ) {
 
-			// update camera matrices and frustum
+	var geometry = new THREE.Geometry();
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
-			_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
-			_frustum.setFromMatrix( _projScreenMatrix );
+	this.color1 = new THREE.Color( 0x444444 );
+	this.color2 = new THREE.Color( 0x888888 );
 
-			// render shadow map
+	for ( var i = - size; i <= size; i += step ) {
 
-			_renderer.setRenderTarget( shadowMap );
-			_renderer.clear();
+		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 )
+		);
 
-			// set object matrices & frustum culling
+		var color = i === 0 ? this.color1 : this.color2;
 
-			renderList = scene.__webglObjects;
+		geometry.colors.push( color, color, color, color );
 
-			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+	}
 
-				webglObject = renderList[ j ];
-				object = webglObject.object;
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-				webglObject.render = false;
+};
 
-				if ( object.visible && object.castShadow ) {
+THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.GridHelper.prototype.constructor = THREE.GridHelper;
 
-					if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
 
-						object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+	this.color1.set( colorCenterLine );
+	this.color2.set( colorGrid );
 
-						webglObject.render = true;
+	this.geometry.colorsNeedUpdate = true;
 
-					}
+}
 
-				}
+// File:src/extras/helpers/HemisphereLightHelper.js
 
-			}
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			// render regular objects
+THREE.HemisphereLightHelper = function ( light, sphereSize ) {
 
-			var objectMaterial, useMorphing, useSkinning;
+	THREE.Object3D.call( this );
 
-			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-				webglObject = renderList[ j ];
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-				if ( webglObject.render ) {
+	this.colors = [ new THREE.Color(), new THREE.Color() ];
 
-					object = webglObject.object;
-					buffer = webglObject.buffer;
+	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
+	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
 
-					// culling is overriden globally for all objects
-					// while rendering depth map
+	for ( var i = 0, il = 8; i < il; i ++ ) {
 
-					// 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)
+		geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
 
-					objectMaterial = getObjectMaterial( object );
+	}
 
-					useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
-					useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+	var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
 
-					if ( object.customDepthMaterial ) {
+	this.lightSphere = new THREE.Mesh( geometry, material );
+	this.add( this.lightSphere );
 
-						material = object.customDepthMaterial;
+	this.update();
 
-					} else if ( useSkinning ) {
+};
 
-						material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper;
 
-					} else if ( useMorphing ) {
+THREE.HemisphereLightHelper.prototype.dispose = function () {
+	this.lightSphere.geometry.dispose();
+	this.lightSphere.material.dispose();
+};
 
-						material = _depthMaterialMorph;
+THREE.HemisphereLightHelper.prototype.update = function () {
 
-					} else {
+	var vector = new THREE.Vector3();
 
-						material = _depthMaterial;
+	return function () {
 
-					}
+		this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
+		this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
 
-					if ( buffer instanceof THREE.BufferGeometry ) {
+		this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
+		this.lightSphere.geometry.colorsNeedUpdate = true;
 
-						_renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object );
+	}
 
-					} else {
+}();
 
-						_renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object );
+// File:src/extras/helpers/PointLightHelper.js
 
-					}
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				}
+THREE.PointLightHelper = function ( light, sphereSize ) {
 
-			}
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-			// set matrices and render immediate objects
+	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 );
 
-			renderList = scene.__webglObjectsImmediate;
+	THREE.Mesh.call( this, geometry, material );
 
-			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+	this.matrix = this.light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-				webglObject = renderList[ j ];
-				object = webglObject.object;
+	/*
+	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
+	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
 
-				if ( object.visible && object.castShadow ) {
+	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
 
-					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+	var d = light.distance;
 
-					_renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object );
+	if ( d === 0.0 ) {
 
-				}
+		this.lightDistance.visible = false;
 
-			}
+	} else {
 
-		}
+		this.lightDistance.scale.set( d, d, d );
 
-		// restore GL state
+	}
 
-		var clearColor = _renderer.getClearColor(),
-		clearAlpha = _renderer.getClearAlpha();
+	this.add( this.lightDistance );
+	*/
 
-		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
-		_gl.enable( _gl.BLEND );
+};
 
-		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
+THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper;
 
-			_gl.cullFace( _gl.BACK );
+THREE.PointLightHelper.prototype.dispose = function () {
 
-		}
+	this.geometry.dispose();
+	this.material.dispose();
+};
 
-	};
+THREE.PointLightHelper.prototype.update = function () {
 
-	function createVirtualLight( light, cascade ) {
+	this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		var virtualLight = new THREE.DirectionalLight();
+	/*
+	var d = this.light.distance;
 
-		virtualLight.isVirtual = true;
+	if ( d === 0.0 ) {
 
-		virtualLight.onlyShadow = true;
-		virtualLight.castShadow = true;
+		this.lightDistance.visible = false;
 
-		virtualLight.shadowCameraNear = light.shadowCameraNear;
-		virtualLight.shadowCameraFar = light.shadowCameraFar;
+	} else {
 
-		virtualLight.shadowCameraLeft = light.shadowCameraLeft;
-		virtualLight.shadowCameraRight = light.shadowCameraRight;
-		virtualLight.shadowCameraBottom = light.shadowCameraBottom;
-		virtualLight.shadowCameraTop = light.shadowCameraTop;
+		this.lightDistance.visible = true;
+		this.lightDistance.scale.set( d, d, d );
 
-		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
+	}
+	*/
 
-		virtualLight.shadowDarkness = light.shadowDarkness;
+};
 
-		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
-		virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
-		virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
+// File:src/extras/helpers/SkeletonHelper.js
 
-		virtualLight.pointsWorld = [];
-		virtualLight.pointsFrustum = [];
+/**
+ * @author Sean Griffin / http://twitter.com/sgrif
+ * @author Michael Guerrero / http://realitymeltdown.com
+ * @author mrdoob / http://mrdoob.com/
+ * @author ikerr / http://verold.com
+ */
 
-		var pointsWorld = virtualLight.pointsWorld,
-			pointsFrustum = virtualLight.pointsFrustum;
+THREE.SkeletonHelper = function ( object ) {
 
-		for ( var i = 0; i < 8; i ++ ) {
+	this.bones = this.getBoneList( object );
 
-			pointsWorld[ i ] = new THREE.Vector3();
-			pointsFrustum[ i ] = new THREE.Vector3();
+	var geometry = new THREE.Geometry();
 
-		}
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-		var nearZ = light.shadowCascadeNearZ[ cascade ];
-		var farZ = light.shadowCascadeFarZ[ cascade ];
+		var bone = this.bones[ i ];
 
-		pointsFrustum[ 0 ].set( -1, -1, nearZ );
-		pointsFrustum[ 1 ].set(  1, -1, nearZ );
-		pointsFrustum[ 2 ].set( -1,  1, nearZ );
-		pointsFrustum[ 3 ].set(  1,  1, nearZ );
+		if ( bone.parent instanceof THREE.Bone ) {
 
-		pointsFrustum[ 4 ].set( -1, -1, farZ );
-		pointsFrustum[ 5 ].set(  1, -1, farZ );
-		pointsFrustum[ 6 ].set( -1,  1, farZ );
-		pointsFrustum[ 7 ].set(  1,  1, farZ );
+			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 ) );
 
-		return virtualLight;
+		}
 
 	}
 
-	// Synchronize virtual light with the original light
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
 
-	function updateVirtualLight( light, cascade ) {
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-		var virtualLight = light.shadowCascadeArray[ cascade ];
+	this.root = object;
 
-		virtualLight.position.copy( light.position );
-		virtualLight.target.position.copy( light.target.position );
-		virtualLight.lookAt( virtualLight.target );
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
+
+	this.update();
+
+};
 
-		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
-		virtualLight.shadowDarkness = light.shadowDarkness;
 
-		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper;
 
-		var nearZ = light.shadowCascadeNearZ[ cascade ];
-		var farZ = light.shadowCascadeFarZ[ cascade ];
+THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
 
-		var pointsFrustum = virtualLight.pointsFrustum;
+	var boneList = [];
 
-		pointsFrustum[ 0 ].z = nearZ;
-		pointsFrustum[ 1 ].z = nearZ;
-		pointsFrustum[ 2 ].z = nearZ;
-		pointsFrustum[ 3 ].z = nearZ;
+	if ( object instanceof THREE.Bone ) {
 
-		pointsFrustum[ 4 ].z = farZ;
-		pointsFrustum[ 5 ].z = farZ;
-		pointsFrustum[ 6 ].z = farZ;
-		pointsFrustum[ 7 ].z = farZ;
+		boneList.push( object );
 
 	}
 
-	// Fit shadow camera's ortho frustum to camera frustum
+	for ( var i = 0; i < object.children.length; i ++ ) {
 
-	function updateShadowCamera( camera, light ) {
+		boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
 
-		var shadowCamera = light.shadowCamera,
-			pointsFrustum = light.pointsFrustum,
-			pointsWorld = light.pointsWorld;
+	}
 
-		_min.set( Infinity, Infinity, Infinity );
-		_max.set( -Infinity, -Infinity, -Infinity );
+	return boneList;
 
-		for ( var i = 0; i < 8; i ++ ) {
+};
 
-			var p = pointsWorld[ i ];
+THREE.SkeletonHelper.prototype.update = function () {
 
-			p.copy( pointsFrustum[ i ] );
-			THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera );
+	var geometry = this.geometry;
 
-			p.applyMatrix4( shadowCamera.matrixWorldInverse );
+	var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
 
-			if ( p.x < _min.x ) _min.x = p.x;
-			if ( p.x > _max.x ) _max.x = p.x;
+	var boneMatrix = new THREE.Matrix4();
 
-			if ( p.y < _min.y ) _min.y = p.y;
-			if ( p.y > _max.y ) _max.y = p.y;
+	var j = 0;
 
-			if ( p.z < _min.z ) _min.z = p.z;
-			if ( p.z > _max.z ) _max.z = p.z;
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-		}
+		var bone = this.bones[ i ];
 
-		shadowCamera.left = _min.x;
-		shadowCamera.right = _max.x;
-		shadowCamera.top = _max.y;
-		shadowCamera.bottom = _min.y;
+		if ( bone.parent instanceof THREE.Bone ) {
 
-		// can't really fit near/far
-		//shadowCamera.near = _min.z;
-		//shadowCamera.far = _max.z;
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
+			geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
 
-		shadowCamera.updateProjectionMatrix();
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
+			geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
 
-	}
+			j += 2;
 
-	// 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
+		}
 
-	function getObjectMaterial( object ) {
+	}
 
-		return object.material instanceof THREE.MeshFaceMaterial
-			? object.material.materials[ 0 ]
-			: object.material;
+	geometry.verticesNeedUpdate = true;
 
-	};
+	geometry.computeBoundingSphere();
 
 };
 
-THREE.ShadowMapPlugin.__projector = new THREE.Projector();
+// File:src/extras/helpers/SpotLightHelper.js
 
 /**
- * @author mikael emtinger / http://gomo.se/
  * @author alteredq / http://alteredqualia.com/
- */
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-THREE.SpritePlugin = function () {
+THREE.SpotLightHelper = function ( light ) {
 
-	var _gl, _renderer, _precision, _sprite = {};
+	THREE.Object3D.call( this );
 
-	this.init = function ( renderer ) {
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-		_gl = renderer.context;
-		_renderer = renderer;
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		_precision = renderer.getPrecision();
+	var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
 
-		_sprite.vertices = new Float32Array( 8 + 8 );
-		_sprite.faces    = new Uint16Array( 6 );
+	geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
+	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
 
-		var i = 0;
+	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
 
-		_sprite.vertices[ i++ ] = -0.5; _sprite.vertices[ i++ ] = -0.5;	// vertex 0
-		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 0;	// uv 0
+	this.cone = new THREE.Mesh( geometry, material );
+	this.add( this.cone );
 
-		_sprite.vertices[ i++ ] = 0.5;  _sprite.vertices[ i++ ] = -0.5;	// vertex 1
-		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 0;	// uv 1
+	this.update();
 
-		_sprite.vertices[ i++ ] = 0.5;  _sprite.vertices[ i++ ] = 0.5;	// vertex 2
-		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;	// uv 2
+};
 
-		_sprite.vertices[ i++ ] = -0.5; _sprite.vertices[ i++ ] = 0.5;	// vertex 3
-		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 1;	// uv 3
+THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper;
 
-		i = 0;
+THREE.SpotLightHelper.prototype.dispose = function () {
+	this.cone.geometry.dispose();
+	this.cone.material.dispose();
+};
 
-		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2;
-		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3;
+THREE.SpotLightHelper.prototype.update = function () {
 
-		_sprite.vertexBuffer  = _gl.createBuffer();
-		_sprite.elementBuffer = _gl.createBuffer();
+	var vector = new THREE.Vector3();
+	var vector2 = new THREE.Vector3();
 
-		_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
-		_gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW );
+	return function () {
 
-		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
-		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW );
+		var coneLength = this.light.distance ? this.light.distance : 10000;
+		var coneWidth = coneLength * Math.tan( this.light.angle );
 
-		_sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ], _precision );
+		this.cone.scale.set( coneWidth, coneWidth, coneLength );
 
-		_sprite.attributes = {};
-		_sprite.uniforms = {};
+		vector.setFromMatrixPosition( this.light.matrixWorld );
+		vector2.setFromMatrixPosition( this.light.target.matrixWorld );
 
-		_sprite.attributes.position           = _gl.getAttribLocation ( _sprite.program, "position" );
-		_sprite.attributes.uv                 = _gl.getAttribLocation ( _sprite.program, "uv" );
+		this.cone.lookAt( vector2.sub( vector ) );
 
-		_sprite.uniforms.uvOffset             = _gl.getUniformLocation( _sprite.program, "uvOffset" );
-		_sprite.uniforms.uvScale              = _gl.getUniformLocation( _sprite.program, "uvScale" );
+		this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		_sprite.uniforms.rotation             = _gl.getUniformLocation( _sprite.program, "rotation" );
-		_sprite.uniforms.scale                = _gl.getUniformLocation( _sprite.program, "scale" );
-		_sprite.uniforms.alignment            = _gl.getUniformLocation( _sprite.program, "alignment" );
-		_sprite.uniforms.halfViewport         = _gl.getUniformLocation( _sprite.program, "halfViewport" );
+	};
 
-		_sprite.uniforms.color                = _gl.getUniformLocation( _sprite.program, "color" );
-		_sprite.uniforms.map                  = _gl.getUniformLocation( _sprite.program, "map" );
-		_sprite.uniforms.opacity              = _gl.getUniformLocation( _sprite.program, "opacity" );
+}();
 
-		_sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" );
-		_sprite.uniforms.sizeAttenuation   	  = _gl.getUniformLocation( _sprite.program, "sizeAttenuation" );
-		_sprite.uniforms.screenPosition    	  = _gl.getUniformLocation( _sprite.program, "screenPosition" );
-		_sprite.uniforms.modelViewMatrix      = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" );
-		_sprite.uniforms.projectionMatrix     = _gl.getUniformLocation( _sprite.program, "projectionMatrix" );
+// File:src/extras/helpers/VertexNormalsHelper.js
 
-		_sprite.uniforms.fogType 		  	  = _gl.getUniformLocation( _sprite.program, "fogType" );
-		_sprite.uniforms.fogDensity 		  = _gl.getUniformLocation( _sprite.program, "fogDensity" );
-		_sprite.uniforms.fogNear 		  	  = _gl.getUniformLocation( _sprite.program, "fogNear" );
-		_sprite.uniforms.fogFar 		  	  = _gl.getUniformLocation( _sprite.program, "fogFar" );
-		_sprite.uniforms.fogColor 		  	  = _gl.getUniformLocation( _sprite.program, "fogColor" );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-		_sprite.uniforms.alphaTest 		  	  = _gl.getUniformLocation( _sprite.program, "alphaTest" );
+THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
 
-	};
+	this.object = object;
 
-	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+	this.size = ( size !== undefined ) ? size : 1;
 
-		var sprites = scene.__webglSprites,
-			nSprites = sprites.length;
+	var color = ( hex !== undefined ) ? hex : 0xff0000;
 
-		if ( ! nSprites ) return;
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-		var attributes = _sprite.attributes,
-			uniforms = _sprite.uniforms;
+	var geometry = new THREE.Geometry();
 
-		var halfViewportWidth = viewportWidth * 0.5,
-			halfViewportHeight = viewportHeight * 0.5;
+	var faces = object.geometry.faces;
 
-		// setup gl
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		_gl.useProgram( _sprite.program );
+		var face = faces[ i ];
 
-		_gl.enableVertexAttribArray( attributes.position );
-		_gl.enableVertexAttribArray( attributes.uv );
+		for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
 
-		_gl.disable( _gl.CULL_FACE );
-		_gl.enable( _gl.BLEND );
+			geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
 
-		_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.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, _sprite.elementBuffer );
+	}
 
-		_gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-		_gl.activeTexture( _gl.TEXTURE0 );
-		_gl.uniform1i( uniforms.map, 0 );
+	this.matrixAutoUpdate = false;
 
-		var oldFogType = 0;
-		var sceneFogType = 0;
-		var fog = scene.fog;
+	this.normalMatrix = new THREE.Matrix3();
 
-		if ( fog ) {
+	this.update();
 
-			_gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
+};
 
-			if ( fog instanceof THREE.Fog ) {
+THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper;
 
-				_gl.uniform1f( uniforms.fogNear, fog.near );
-				_gl.uniform1f( uniforms.fogFar, fog.far );
+THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
 
-				_gl.uniform1i( uniforms.fogType, 1 );
-				oldFogType = 1;
-				sceneFogType = 1;
+	var v1 = new THREE.Vector3();
 
-			} else if ( fog instanceof THREE.FogExp2 ) {
+	return function( object ) {
 
-				_gl.uniform1f( uniforms.fogDensity, fog.density );
+		var keys = [ 'a', 'b', 'c', 'd' ];
 
-				_gl.uniform1i( uniforms.fogType, 2 );
-				oldFogType = 2;
-				sceneFogType = 2;
+		this.object.updateMatrixWorld( true );
 
-			}
+		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
 
-		} else {
+		var vertices = this.geometry.vertices;
 
-			_gl.uniform1i( uniforms.fogType, 0 );
-			oldFogType = 0;
-			sceneFogType = 0;
+		var verts = this.object.geometry.vertices;
 
-		}
+		var faces = this.object.geometry.faces;
 
+		var worldMatrix = this.object.matrixWorld;
 
-		// update positions and sort
+		var idx = 0;
 
-		var i, sprite, material, screenPosition, fogType, scale = [];
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		for( i = 0; i < nSprites; i ++ ) {
+			var face = faces[ i ];
 
-			sprite = sprites[ i ];
-			material = sprite.material;
+			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
 
-			if ( ! sprite.visible || material.opacity === 0 ) continue;
+				var vertexId = face[ keys[ j ] ];
+				var vertex = verts[ vertexId ];
 
-			if ( ! material.useScreenCoordinates ) {
+				var normal = face.vertexNormals[ j ];
 
-				sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
-				sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
+				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
 
-			} else {
+				v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
 
-				sprite.z = - sprite.position.z;
+				v1.add( vertices[ idx ] );
+				idx = idx + 1;
+
+				vertices[ idx ].copy( v1 );
+				idx = idx + 1;
 
 			}
 
 		}
 
-		sprites.sort( painterSortStable );
+		this.geometry.verticesNeedUpdate = true;
 
-		// render all sprites
+		return this;
 
-		for( i = 0; i < nSprites; i ++ ) {
+	}
 
-			sprite = sprites[ i ];
-			material = sprite.material;
+}());
 
-			if ( ! sprite.visible || material.opacity === 0 ) continue;
+// File:src/extras/helpers/VertexTangentsHelper.js
 
-			if ( material.map && material.map.image && material.map.image.width ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-				_gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
 
-				if ( material.useScreenCoordinates === true ) {
+	this.object = object;
 
-					_gl.uniform1i( uniforms.useScreenCoordinates, 1 );
-					_gl.uniform3f(
-						uniforms.screenPosition,
-						( ( sprite.position.x * _renderer.devicePixelRatio ) - halfViewportWidth  ) / halfViewportWidth,
-						( halfViewportHeight - ( sprite.position.y * _renderer.devicePixelRatio ) ) / halfViewportHeight,
-						Math.max( 0, Math.min( 1, sprite.position.z ) )
-					);
+	this.size = ( size !== undefined ) ? size : 1;
 
-					scale[ 0 ] = _renderer.devicePixelRatio * sprite.scale.x;
-					scale[ 1 ] = _renderer.devicePixelRatio * sprite.scale.y;
+	var color = ( hex !== undefined ) ? hex : 0x0000ff;
 
-				} else {
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-					_gl.uniform1i( uniforms.useScreenCoordinates, 0 );
-					_gl.uniform1i( uniforms.sizeAttenuation, material.sizeAttenuation ? 1 : 0 );
-					_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
+	var geometry = new THREE.Geometry();
 
-					scale[ 0 ] = sprite.scale.x;
-					scale[ 1 ] = sprite.scale.y;
+	var faces = object.geometry.faces;
 
-				}
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-				if ( scene.fog && material.fog ) {
+		var face = faces[ i ];
 
-					fogType = sceneFogType;
+		for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
 
-				} else {
+			geometry.vertices.push( new THREE.Vector3() );
+			geometry.vertices.push( new THREE.Vector3() );
 
-					fogType = 0;
+		}
 
-				}
+	}
 
-				if ( oldFogType !== fogType ) {
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-					_gl.uniform1i( uniforms.fogType, fogType );
-					oldFogType = fogType;
+	this.matrixAutoUpdate = false;
 
-				}
+	this.update();
 
-				_gl.uniform2f( uniforms.uvScale, material.uvScale.x, material.uvScale.y );
-				_gl.uniform2f( uniforms.uvOffset, material.uvOffset.x, material.uvOffset.y );
-				_gl.uniform2f( uniforms.alignment, material.alignment.x, material.alignment.y );
+};
 
-				_gl.uniform1f( uniforms.opacity, material.opacity );
-				_gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
+THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.VertexTangentsHelper.prototype.constructor = THREE.VertexTangentsHelper;
 
-				_gl.uniform1f( uniforms.rotation, sprite.rotation );
-				_gl.uniform2fv( uniforms.scale, scale );
-				_gl.uniform2f( uniforms.halfViewport, halfViewportWidth, halfViewportHeight );
+THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
 
-				_renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
-				_renderer.setDepthTest( material.depthTest );
-				_renderer.setDepthWrite( material.depthWrite );
-				_renderer.setTexture( material.map, 0 );
+	var v1 = new THREE.Vector3();
 
-				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+	return function( object ) {
 
-			}
+		var keys = [ 'a', 'b', 'c', 'd' ];
 
-		}
+		this.object.updateMatrixWorld( true );
 
-		// restore gl
+		var vertices = this.geometry.vertices;
 
-		_gl.enable( _gl.CULL_FACE );
+		var verts = this.object.geometry.vertices;
 
-	};
+		var faces = this.object.geometry.faces;
 
-	function createProgram ( shader, precision ) {
+		var worldMatrix = this.object.matrixWorld;
 
-		var program = _gl.createProgram();
+		var idx = 0;
 
-		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
-		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		var prefix = "precision " + precision + " float;\n";
+			var face = faces[ i ];
 
-		_gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
-		_gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+			for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
 
-		_gl.compileShader( fragmentShader );
-		_gl.compileShader( vertexShader );
+				var vertexId = face[ keys[ j ] ];
+				var vertex = verts[ vertexId ];
 
-		_gl.attachShader( program, fragmentShader );
-		_gl.attachShader( program, vertexShader );
+				var tangent = face.vertexTangents[ j ];
 
-		_gl.linkProgram( program );
+				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
 
-		return program;
+				v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
 
-	};
+				v1.add( vertices[ idx ] );
+				idx = idx + 1;
 
-	function painterSortStable ( a, b ) {
+				vertices[ idx ].copy( v1 );
+				idx = idx + 1;
 
-		if ( a.z !== b.z ) {
+			}
 
-			return b.z - a.z;
+		}
 
-		} else {
+		this.geometry.verticesNeedUpdate = true;
 
-			return b.id - a.id;
+		return this;
 
-		}
+	}
 
-	};
+}());
 
-};
+// File:src/extras/helpers/WireframeHelper.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.DepthPassPlugin = function () {
+THREE.WireframeHelper = function ( object, hex ) {
 
-	this.enabled = false;
-	this.renderTarget = null;
+	var color = ( hex !== undefined ) ? hex : 0xffffff;
 
-	var _gl,
-	_renderer,
-	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+	var edge = [ 0, 0 ], hash = {};
+	var sortFunction = function ( a, b ) { return a - b };
 
-	_frustum = new THREE.Frustum(),
-	_projScreenMatrix = new THREE.Matrix4();
+	var keys = [ 'a', 'b', 'c' ];
+	var geometry = new THREE.BufferGeometry();
 
-	this.init = function ( renderer ) {
+	if ( object.geometry instanceof THREE.Geometry ) {
 
-		_gl = renderer.context;
-		_renderer = renderer;
+		var vertices = object.geometry.vertices;
+		var faces = object.geometry.faces;
+		var numEdges = 0;
 
-		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
-		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+		// allocate maximal size
+		var edges = new Uint32Array( 6 * faces.length );
 
-		_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 } );
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		_depthMaterial._shadowPass = true;
-		_depthMaterialMorph._shadowPass = true;
-		_depthMaterialSkin._shadowPass = true;
-		_depthMaterialMorphSkin._shadowPass = true;
+			var face = faces[ i ];
 
-	};
+			for ( var j = 0; j < 3; j ++ ) {
 
-	this.render = function ( scene, camera ) {
+				edge[ 0 ] = face[ keys[ j ] ];
+				edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+				edge.sort( sortFunction );
 
-		if ( ! this.enabled ) return;
+				var key = edge.toString();
 
-		this.update( scene, camera );
+				if ( hash[ key ] === undefined ) {
 
-	};
+					edges[ 2 * numEdges ] = edge[ 0 ];
+					edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+					hash[ key ] = true;
+					numEdges ++;
 
-	this.update = function ( scene, camera ) {
+				}
 
-		var i, il, j, jl, n,
+			}
 
-		program, buffer, material,
-		webglObject, object, light,
-		renderList,
+		}
 
-		fog = null;
+		var coords = new Float32Array( numEdges * 2 * 3 );
+
+		for ( var i = 0, l = numEdges; i < l; i ++ ) {
+
+			for ( var j = 0; j < 2; j ++ ) {
+
+				var vertex = vertices[ edges [ 2 * i + j] ];
+
+				var index = 6 * i + 3 * j;
+				coords[ index + 0 ] = vertex.x;
+				coords[ index + 1 ] = vertex.y;
+				coords[ index + 2 ] = vertex.z;
+
+			}
+
+		}
+
+		geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+
+	} else if ( object.geometry instanceof THREE.BufferGeometry ) {
+
+		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 ) {
 
-		// set GL state for depth map
+				drawcalls = [ { count : indices.length, index : 0, start : 0 } ];
 
-		_gl.clearColor( 1, 1, 1, 1 );
-		_gl.disable( _gl.BLEND );
+			}
 
-		_renderer.setDepthTest( true );
+			// allocate maximal size
+			var edges = new Uint32Array( 2 * indices.length );
 
-		// update scene
+			for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+				var start = drawcalls[ o ].start;
+				var count = drawcalls[ o ].count;
+				var index = drawcalls[ o ].index;
 
-		// update camera matrices and frustum
+				for ( var i = start, il = start + count; i < il; i += 3 ) {
 
-		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+					for ( var j = 0; j < 3; j ++ ) {
 
-		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-		_frustum.setFromMatrix( _projScreenMatrix );
+						edge[ 0 ] = index + indices[ i + j ];
+						edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ];
+						edge.sort( sortFunction );
 
-		// render depth map
+						var key = edge.toString();
 
-		_renderer.setRenderTarget( this.renderTarget );
-		_renderer.clear();
+						if ( hash[ key ] === undefined ) {
 
-		// set object matrices & frustum culling
+							edges[ 2 * numEdges ] = edge[ 0 ];
+							edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+							hash[ key ] = true;
+							numEdges ++;
 
-		renderList = scene.__webglObjects;
+						}
 
-		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+					}
 
-			webglObject = renderList[ j ];
-			object = webglObject.object;
+				}
 
-			webglObject.render = false;
+			}
 
-			if ( object.visible ) {
+			var coords = new Float32Array( numEdges * 2 * 3 );
 
-				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+			for ( var i = 0, l = numEdges; i < l; i ++ ) {
 
-					object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+				for ( var j = 0; j < 2; j ++ ) {
 
-					webglObject.render = true;
+					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 ];
 
 				}
 
 			}
 
-		}
+			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
-		// render regular objects
+		} else { // non-indexed BufferGeometry
 
-		var objectMaterial, useMorphing, useSkinning;
+			var vertices = object.geometry.attributes.position.array;
+			var numEdges = vertices.length / 3;
+			var numTris = numEdges / 3;
 
-		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+			var coords = new Float32Array( numEdges * 2 * 3 );
 
-			webglObject = renderList[ j ];
+			for ( var i = 0, l = numTris; i < l; i ++ ) {
 
-			if ( webglObject.render ) {
+				for ( var j = 0; j < 3; j ++ ) {
 
-				object = webglObject.object;
-				buffer = webglObject.buffer;
+					var index = 18 * i + 6 * j;
 
-				// todo: create proper depth material for particles
+					var index1 = 9 * i + 3 * j;
+					coords[ index + 0 ] = vertices[ index1 ];
+					coords[ index + 1 ] = vertices[ index1 + 1 ];
+					coords[ index + 2 ] = vertices[ index1 + 2 ];
 
-				if ( object instanceof THREE.ParticleSystem && !object.customDepthMaterial ) continue;
+					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 ];
 
-				objectMaterial = getObjectMaterial( object );
+				}
 
-				if ( objectMaterial ) _renderer.setMaterialFaces( object.material );
+			}
 
-				useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
-				useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
-				if ( object.customDepthMaterial ) {
+		}
 
-					material = object.customDepthMaterial;
+	}
 
-				} else if ( useSkinning ) {
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
 
-					material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-				} else if ( useMorphing ) {
+};
 
-					material = _depthMaterialMorph;
+THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper;
 
-				} else {
+// File:src/extras/objects/ImmediateRenderObject.js
 
-					material = _depthMaterial;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-				}
+THREE.ImmediateRenderObject = function () {
 
-				if ( buffer instanceof THREE.BufferGeometry ) {
+	THREE.Object3D.call( this );
 
-					_renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object );
+	this.render = function ( renderCallback ) {};
 
-				} else {
+};
 
-					_renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object );
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
+THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject;
 
-				}
+// File:src/extras/objects/MorphBlendMesh.js
 
-			}
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		}
+THREE.MorphBlendMesh = function( geometry, material ) {
 
-		// set matrices and render immediate objects
+	THREE.Mesh.call( this, geometry, material );
 
-		renderList = scene.__webglObjectsImmediate;
+	this.animationsMap = {};
+	this.animationsList = [];
 
-		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+	// prepare default animation
+	// (all frames played together in 1 second)
 
-			webglObject = renderList[ j ];
-			object = webglObject.object;
+	var numFrames = this.geometry.morphTargets.length;
 
-			if ( object.visible ) {
+	var name = "__default";
 
-				object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+	var startFrame = 0;
+	var endFrame = numFrames - 1;
 
-				_renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object );
+	var fps = numFrames / 1;
 
-			}
+	this.createAnimation( name, startFrame, endFrame, fps );
+	this.setAnimationWeight( name, 1 );
 
-		}
+};
 
-		// restore GL state
+THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh;
 
-		var clearColor = _renderer.getClearColor(),
-		clearAlpha = _renderer.getClearAlpha();
+THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
 
-		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
-		_gl.enable( _gl.BLEND );
+	var animation = {
 
-	};
+		startFrame: start,
+		endFrame: end,
 
-	// 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
+		length: end - start + 1,
 
-	function getObjectMaterial( object ) {
+		fps: fps,
+		duration: ( end - start ) / fps,
 
-		return object.material instanceof THREE.MeshFaceMaterial
-			? object.material.materials[ 0 ]
-			: object.material;
+		lastFrame: 0,
+		currentFrame: 0,
+
+		active: false,
+
+		time: 0,
+		direction: 1,
+		weight: 1,
+
+		directionBackwards: false,
+		mirroredLoop: false
 
 	};
 
+	this.animationsMap[ name ] = animation;
+	this.animationsList.push( animation );
+
 };
 
+THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
 
-/**
- * @author mikael emtinger / http://gomo.se/
- */
+	var pattern = /([a-z]+)_?(\d+)/;
 
-THREE.ShaderFlares = {
+	var firstAnimation, frameRanges = {};
 
-	'lensFlareVertexTexture': {
+	var geometry = this.geometry;
 
-		vertexShader: [
+	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
 
-			"uniform lowp int renderType;",
+		var morph = geometry.morphTargets[ i ];
+		var chunks = morph.name.match( pattern );
 
-			"uniform vec3 screenPosition;",
-			"uniform vec2 scale;",
-			"uniform float rotation;",
+		if ( chunks && chunks.length > 1 ) {
 
-			"uniform sampler2D occlusionMap;",
+			var name = chunks[ 1 ];
 
-			"attribute vec2 position;",
-			"attribute vec2 uv;",
+			if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
 
-			"varying vec2 vUV;",
-			"varying float vVisibility;",
+			var range = frameRanges[ name ];
 
-			"void main() {",
+			if ( i < range.start ) range.start = i;
+			if ( i > range.end ) range.end = i;
 
-				"vUV = uv;",
+			if ( ! firstAnimation ) firstAnimation = name;
 
-				"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 ) );",
+	for ( var name in frameRanges ) {
 
-					"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 range = frameRanges[ name ];
+		this.createAnimation( name, range.start, range.end, fps );
 
-					"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
-					"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+	}
 
-				"}",
+	this.firstAnimation = firstAnimation;
 
-				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+};
 
-			"}"
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
 
-		].join( "\n" ),
+	var animation = this.animationsMap[ name ];
 
-		fragmentShader: [
+	if ( animation ) {
 
-			"uniform lowp int renderType;",
+		animation.direction = 1;
+		animation.directionBackwards = false;
 
-			"uniform sampler2D map;",
-			"uniform float opacity;",
-			"uniform vec3 color;",
+	}
 
-			"varying vec2 vUV;",
-			"varying float vVisibility;",
+};
 
-			"void main() {",
+THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
 
-				// pink square
+	var animation = this.animationsMap[ name ];
 
-				"if( renderType == 0 ) {",
+	if ( animation ) {
 
-					"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
+		animation.direction = - 1;
+		animation.directionBackwards = true;
 
-				// restore
+	}
 
-				"} else if( renderType == 1 ) {",
+};
 
-					"gl_FragColor = texture2D( map, vUV );",
+THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
 
-				// flare
+	var animation = this.animationsMap[ name ];
 
-				"} else {",
+	if ( animation ) {
 
-					"vec4 texture = texture2D( map, vUV );",
-					"texture.a *= opacity * vVisibility;",
-					"gl_FragColor = texture;",
-					"gl_FragColor.rgb *= color;",
+		animation.fps = fps;
+		animation.duration = ( animation.end - animation.start ) / animation.fps;
 
-				"}",
+	}
 
-			"}"
-		].join( "\n" )
+};
 
-	},
+THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
 
+	var animation = this.animationsMap[ name ];
 
-	'lensFlare': {
+	if ( animation ) {
 
-		vertexShader: [
+		animation.duration = duration;
+		animation.fps = ( animation.end - animation.start ) / animation.duration;
 
-			"uniform lowp int renderType;",
+	}
 
-			"uniform vec3 screenPosition;",
-			"uniform vec2 scale;",
-			"uniform float rotation;",
+};
 
-			"attribute vec2 position;",
-			"attribute vec2 uv;",
+THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
 
-			"varying vec2 vUV;",
+	var animation = this.animationsMap[ name ];
 
-			"void main() {",
+	if ( animation ) {
 
-				"vUV = uv;",
+		animation.weight = weight;
 
-				"vec2 pos = position;",
+	}
 
-				"if( renderType == 2 ) {",
+};
 
-					"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
-					"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
 
-				"}",
+	var animation = this.animationsMap[ name ];
 
-				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+	if ( animation ) {
 
-			"}"
+		animation.time = time;
 
-		].join( "\n" ),
+	}
 
-		fragmentShader: [
+};
 
-			"precision mediump float;",
+THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
 
-			"uniform lowp int renderType;",
+	var time = 0;
 
-			"uniform sampler2D map;",
-			"uniform sampler2D occlusionMap;",
-			"uniform float opacity;",
-			"uniform vec3 color;",
+	var animation = this.animationsMap[ name ];
 
-			"varying vec2 vUV;",
+	if ( animation ) {
 
-			"void main() {",
+		time = animation.time;
 
-				// pink square
+	}
 
-				"if( renderType == 0 ) {",
+	return time;
 
-					"gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
+};
 
-				// restore
+THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
 
-				"} else if( renderType == 1 ) {",
+	var duration = - 1;
 
-					"gl_FragColor = texture2D( map, vUV );",
+	var animation = this.animationsMap[ name ];
 
-				// flare
+	if ( animation ) {
 
-				"} else {",
+		duration = animation.duration;
 
-					"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;",
+	return duration;
 
-				"}",
+};
 
-			"}"
+THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
 
-		].join( "\n" )
+	var animation = this.animationsMap[ name ];
 
-	}
+	if ( animation ) {
 
-};
+		animation.time = 0;
+		animation.active = true;
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- *
- */
+	} else {
 
-THREE.ShaderSprite = {
+		THREE.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" );
 
-	'sprite': {
+	}
 
-		vertexShader: [
+};
 
-			"uniform int useScreenCoordinates;",
-			"uniform int sizeAttenuation;",
-			"uniform vec3 screenPosition;",
-			"uniform mat4 modelViewMatrix;",
-			"uniform mat4 projectionMatrix;",
-			"uniform float rotation;",
-			"uniform vec2 scale;",
-			"uniform vec2 alignment;",
-			"uniform vec2 uvOffset;",
-			"uniform vec2 uvScale;",
-			"uniform vec2 halfViewport;",
+THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
 
-			"attribute vec2 position;",
-			"attribute vec2 uv;",
+	var animation = this.animationsMap[ name ];
 
-			"varying vec2 vUV;",
+	if ( animation ) {
 
-			"void main() {",
+		animation.active = false;
 
-				"vUV = uvOffset + uv * uvScale;",
+	}
 
-				"vec2 alignedPosition = ( position + alignment ) * scale;",
+};
 
-				"vec2 rotatedPosition;",
-				"rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;",
-				"rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;",
+THREE.MorphBlendMesh.prototype.update = function ( delta ) {
 
-				"vec4 finalPosition;",
+	for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
 
-				"if( useScreenCoordinates != 0 ) {",
+		var animation = this.animationsList[ i ];
 
-					"finalPosition = vec4( screenPosition.xy + ( rotatedPosition / halfViewport ), screenPosition.z, 1.0 );",
+		if ( ! animation.active ) continue;
 
-				"} else {",
+		var frameTime = animation.duration / animation.length;
 
-					"finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );",
-					"finalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );",
-					"finalPosition = projectionMatrix * finalPosition;",
+		animation.time += animation.direction * delta;
 
-				"}",
+		if ( animation.mirroredLoop ) {
 
-				"gl_Position = finalPosition;",
+			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;
 
-			"uniform vec3 color;",
-			"uniform sampler2D map;",
-			"uniform float opacity;",
+				}
 
-			"uniform int fogType;",
-			"uniform vec3 fogColor;",
-			"uniform float fogDensity;",
-			"uniform float fogNear;",
-			"uniform float fogFar;",
-			"uniform float alphaTest;",
+				if ( animation.time < 0 ) {
 
-			"varying vec2 vUV;",
+					animation.time = 0;
+					animation.directionBackwards = false;
 
-			"void main() {",
+				}
 
-				"vec4 texture = texture2D( map, vUV );",
+			}
 
-				"if ( texture.a < alphaTest ) discard;",
+		} else {
 
-				"gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );",
+			animation.time = animation.time % animation.duration;
 
-				"if ( fogType > 0 ) {",
+			if ( animation.time < 0 ) animation.time += animation.duration;
 
-					"float depth = gl_FragCoord.z / gl_FragCoord.w;",
-					"float fogFactor = 0.0;",
+		}
 
-					"if ( fogType == 1 ) {",
+		var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
+		var weight = animation.weight;
 
-						"fogFactor = smoothstep( fogNear, fogFar, depth );",
+		if ( keyframe !== animation.currentFrame ) {
 
-					"} else {",
+			this.morphTargetInfluences[ animation.lastFrame ] = 0;
+			this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
 
-						"const float LOG2 = 1.442695;",
-						"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
-						"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
+			this.morphTargetInfluences[ keyframe ] = 0;
 
-					"}",
+			animation.lastFrame = animation.currentFrame;
+			animation.currentFrame = keyframe;
 
-					"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
+		}
 
-				"}",
+		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;
 
 	}
 
 };
+
diff --git a/gz3d/client/js/include/three.min.js b/gz3d/client/js/include/three.min.js
index db4f8f5..85e5596 100644
--- a/gz3d/client/js/include/three.min.js
+++ b/gz3d/client/js/include/three.min.js
@@ -1,705 +1,835 @@
-// three.js - http://github.com/mrdoob/three.js
-'use strict';var THREE={REVISION:"62"};self.console=self.console||{info:function(){},log:function(){},debug:function(){},warn:function(){},error:function(){}};String.prototype.trim=String.prototype.trim||function(){return this.replace(/^\s+|\s+$/g,"")};THREE.extend=function(a,b){if(Object.keys)for(var c=Object.keys(b),d=0,e=c.length;d<e;d++){var f=c[d];Object.defineProperty(a,f,Object.getOwnPropertyDescriptor(b,f))}else for(f in c={}.hasOwnProperty,b)c.call(b,f)&&(a[f]=b[f]);return a};
-(function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!self.requestAnimationFrame;++c)self.requestAnimationFrame=self[b[c]+"RequestAnimationFrame"],self.cancelAnimationFrame=self[b[c]+"CancelAnimationFrame"]||self[b[c]+"CancelRequestAnimationFrame"];void 0===self.requestAnimationFrame&&void 0!==self.setTimeout&&(self.requestAnimationFrame=function(b){var c=Date.now(),f=Math.max(0,16-(c-a)),h=self.setTimeout(function(){b(c+f)},f);a=c+f;return h});void 0===self.cancelAnimationFrame&&void 0!==
-self.clearTimeout&&(self.cancelAnimationFrame=function(a){self.clearTimeout(a)})})();THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;
-THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;
-THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=function(){};THREE.CubeReflectionMapping=function(){};THREE.CubeRefractionMapping=function(){};THREE.SphericalReflectionMapping=function(){};THREE.SphericalRefractionMapping=function(){};THREE.RepeatWrapping=1E3;THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;
-THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;
-THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.Color=function(a){void 0!==a&&this.set(a);return this};
+// threejs.org/license
+'use strict';var THREE={REVISION:"71"};"object"===typeof module&&(module.exports=THREE);void 0===Math.sign&&(Math.sign=function(a){return 0>a?-1:0<a?1:+a});THREE.log=function(){console.log.apply(console,arguments)};THREE.warn=function(){console.warn.apply(console,arguments)};THREE.error=function(){console.error.apply(console,arguments)};THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2};THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;
+THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;
+THREE.MinEquation=103;THREE.MaxEquation=104;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=300;THREE.CubeReflectionMapping=301;THREE.CubeRefractionMapping=302;
+THREE.EquirectangularReflectionMapping=303;THREE.EquirectangularRefractionMapping=304;THREE.SphericalReflectionMapping=305;THREE.RepeatWrapping=1E3;THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;
+THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.HalfFloatType=1025;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;THREE.RGBEFormat=THREE.RGBAFormat;THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;
+THREE.RGBA_S3TC_DXT5_Format=2004;THREE.RGB_PVRTC_4BPPV1_Format=2100;THREE.RGB_PVRTC_2BPPV1_Format=2101;THREE.RGBA_PVRTC_4BPPV1_Format=2102;THREE.RGBA_PVRTC_2BPPV1_Format=2103;
+THREE.Projector=function(){THREE.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js.");this.projectVector=function(a,b){THREE.warn("THREE.Projector: .projectVector() is now vector.project().");a.project(b)};this.unprojectVector=function(a,b){THREE.warn("THREE.Projector: .unprojectVector() is now vector.unproject().");a.unproject(b)};this.pickingRay=function(a,b){THREE.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}};
+THREE.CanvasRenderer=function(){THREE.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js");this.domElement=document.createElement("canvas");this.clear=function(){};this.render=function(){};this.setClearColor=function(){};this.setSize=function(){}};THREE.Color=function(a){return 3===arguments.length?this.setRGB(arguments[0],arguments[1],arguments[2]):this.set(a)};
 THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){if(0===b)this.r=this.g=this.b=c;else{var d=function(a,b,c){0>c&&(c+=1);1<c&&(c-=1);return c<1/6?a+6*(b-a)*
-c:0.5>c?b:c<2/3?a+6*(b-a)*(2/3-c):a},b=0.5>=c?c*(1+b):c+b-c*b,c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r=
+c:.5>c?b:c<2/3?a+6*(b-a)*(2/3-c):a};b=.5>=c?c*(1+b):c+b-c*b;c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r=
 Math.min(100,parseInt(a[1],10))/100,this.g=Math.min(100,parseInt(a[2],10))/100,this.b=Math.min(100,parseInt(a[3],10))/100,this;if(/^\#([0-9a-f]{6})$/i.test(a))return a=/^\#([0-9a-f]{6})$/i.exec(a),this.setHex(parseInt(a[1],16)),this;if(/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a))return a=/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a),this.setHex(parseInt(a[1]+a[1]+a[2]+a[2]+a[3]+a[3],16)),this;if(/^(\w+)$/i.test(a))return this.setHex(THREE.ColorKeywords[a]),this},copy:function(a){this.r=a.r;this.g=
-a.g;this.b=a.b;return this},copyGammaToLinear:function(a){this.r=a.r*a.r;this.g=a.g*a.g;this.b=a.b*a.b;return this},copyLinearToGamma:function(a){this.r=Math.sqrt(a.r);this.g=Math.sqrt(a.g);this.b=Math.sqrt(a.b);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;return this},convertLinearToGamma:function(){this.r=Math.sqrt(this.r);this.g=Math.sqrt(this.g);this.b=Math.sqrt(this.b);return this},getHex:function(){return 255*this.r<<16^255*this.g<<
-8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(){var a={h:0,s:0,l:0};return function(){var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),h,g=(f+e)/2;if(f===e)f=h=0;else{var i=e-f,f=0.5>=g?i/(e+f):i/(2-e-f);switch(e){case b:h=(c-d)/i+(c<d?6:0);break;case c:h=(d-b)/i+2;break;case d:h=(b-c)/i+4}h/=6}a.h=h;a.s=f;a.l=g;return a}}(),getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},
-offsetHSL:function(a,b,c){var d=this.getHSL();d.h+=a;d.s+=b;d.l+=c;this.setHSL(d.h,d.s,d.l);return this},add:function(a){this.r+=a.r;this.g+=a.g;this.b+=a.b;return this},addColors:function(a,b){this.r=a.r+b.r;this.g=a.g+b.g;this.b=a.b+b.b;return this},addScalar:function(a){this.r+=a;this.g+=a;this.b+=a;return this},multiply:function(a){this.r*=a.r;this.g*=a.g;this.b*=a.b;return this},multiplyScalar:function(a){this.r*=a;this.g*=a;this.b*=a;return this},lerp:function(a,b){this.r+=(a.r-this.r)*b;this.g+=
-(a.g-this.g)*b;this.b+=(a.b-this.b)*b;return this},equals:function(a){return a.r===this.r&&a.g===this.g&&a.b===this.b},fromArray:function(a){this.r=a[0];this.g=a[1];this.b=a[2];return this},toArray:function(){return[this.r,this.g,this.b]},clone:function(){return(new THREE.Color).setRGB(this.r,this.g,this.b)}};
+a.g;this.b=a.b;return this},copyGammaToLinear:function(a,b){void 0===b&&(b=2);this.r=Math.pow(a.r,b);this.g=Math.pow(a.g,b);this.b=Math.pow(a.b,b);return this},copyLinearToGamma:function(a,b){void 0===b&&(b=2);var c=0<b?1/b:1;this.r=Math.pow(a.r,c);this.g=Math.pow(a.g,c);this.b=Math.pow(a.b,c);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;return this},convertLinearToGamma:function(){this.r=Math.sqrt(this.r);this.g=Math.sqrt(this.g);this.b=
+Math.sqrt(this.b);return this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a=a||{h:0,s:0,l:0};var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f,f=.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c-d)/k+(c<d?6:0);break;case c:g=(d-b)/k+2;break;case d:g=(b-c)/k+4}g/=6}a.h=g;a.s=f;a.l=h;return a},getStyle:function(){return"rgb("+
+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(a,b,c){var d=this.getHSL();d.h+=a;d.s+=b;d.l+=c;this.setHSL(d.h,d.s,d.l);return this},add:function(a){this.r+=a.r;this.g+=a.g;this.b+=a.b;return this},addColors:function(a,b){this.r=a.r+b.r;this.g=a.g+b.g;this.b=a.b+b.b;return this},addScalar:function(a){this.r+=a;this.g+=a;this.b+=a;return this},multiply:function(a){this.r*=a.r;this.g*=a.g;this.b*=a.b;return this},multiplyScalar:function(a){this.r*=a;this.g*=a;this.b*=a;
+return this},lerp:function(a,b){this.r+=(a.r-this.r)*b;this.g+=(a.g-this.g)*b;this.b+=(a.b-this.b)*b;return this},equals:function(a){return a.r===this.r&&a.g===this.g&&a.b===this.b},fromArray:function(a){this.r=a[0];this.g=a[1];this.b=a[2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.r;a[b+1]=this.g;a[b+2]=this.b;return a},clone:function(){return(new THREE.Color).setRGB(this.r,this.g,this.b)}};
 THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,
 darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,
 grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,
 lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,
 palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,
 tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};THREE.Quaternion=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1};
-THREE.Quaternion.prototype={constructor:THREE.Quaternion,_x:0,_y:0,_z:0,_w:0,_euler:void 0,_updateEuler:function(){void 0!==this._euler&&this._euler.setFromQuaternion(this,void 0,!1)},get x(){return this._x},set x(a){this._x=a;this._updateEuler()},get y(){return this._y},set y(a){this._y=a;this._updateEuler()},get z(){return this._z},set z(a){this._z=a;this._updateEuler()},get w(){return this._w},set w(a){this._w=a;this._updateEuler()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;
-this._updateEuler();return this},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._w=a._w;this._updateEuler();return this},setFromEuler:function(a,b){if(!1===a instanceof THREE.Euler)throw Error("ERROR: Quaternion's .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),h=Math.sin(a._y/2),g=Math.sin(a._z/2);"XYZ"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*
-e-f*d*g,this._z=c*d*g+f*h*e,this._w=c*d*e-f*h*g):"YXZ"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*e-f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e+f*h*g):"ZXY"===a.order?(this._x=f*d*e-c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g+f*h*e,this._w=c*d*e-f*h*g):"ZYX"===a.order?(this._x=f*d*e-c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e+f*h*g):"YZX"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e-f*h*g):"XZY"===a.order&&(this._x=f*d*e-c*h*g,this._y=c*h*e-f*d*g,this._z=
-c*d*g+f*h*e,this._w=c*d*e+f*h*g);!1!==b&&this._updateEuler();return this},setFromAxisAngle:function(a,b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this._updateEuler();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0],a=b[4],d=b[8],e=b[1],f=b[5],h=b[9],g=b[2],i=b[6],b=b[10],k=c+f+b;0<k?(c=0.5/Math.sqrt(k+1),this._w=0.25/c,this._x=(i-h)*c,this._y=(d-g)*c,this._z=(e-a)*c):c>f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(i-h)/c,this._x=0.25*c,
-this._y=(a+e)/c,this._z=(d+g)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-g)/c,this._x=(a+e)/c,this._y=0.25*c,this._z=(h+i)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+g)/c,this._y=(h+i)/c,this._z=0.25*c);this._updateEuler();return this},inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this._updateEuler();return this},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*
-this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);return this},multiply:function(a,b){return void 0!==b?(console.warn("DEPRECATED: Quaternion's .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=
-a._w,h=b._x,g=b._y,i=b._z,k=b._w;this._x=c*k+f*h+d*i-e*g;this._y=d*k+f*g+e*h-c*i;this._z=e*k+f*i+c*g-d*h;this._w=f*k-c*h-d*g-e*i;this._updateEuler();return this},multiplyVector3:function(a){console.warn("DEPRECATED: Quaternion's .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");return a.applyQuaternion(this)},slerp:function(a,b){var c=this._x,d=this._y,e=this._z,f=this._w,h=f*a._w+c*a._x+d*a._y+e*a._z;0>h?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=
--a._z,h=-h):this.copy(a);if(1<=h)return this._w=f,this._x=c,this._y=d,this._z=e,this;var g=Math.acos(h),i=Math.sqrt(1-h*h);if(0.001>Math.abs(i))return this._w=0.5*(f+this._w),this._x=0.5*(c+this._x),this._y=0.5*(d+this._y),this._z=0.5*(e+this._z),this;h=Math.sin((1-b)*g)/i;g=Math.sin(b*g)/i;this._w=f*h+this._w*g;this._x=c*h+this._x*g;this._y=d*h+this._y*g;this._z=e*h+this._z*g;this._updateEuler();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},
-fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];this._w=a[3];this._updateEuler();return this},toArray:function(){return[this._x,this._y,this._z,this._w]},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0};
+THREE.Quaternion.prototype={constructor:THREE.Quaternion,_x:0,_y:0,_z:0,_w:0,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get w(){return this._w},set w(a){this._w=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;
+this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!1===a instanceof THREE.Euler)throw Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),g=Math.sin(a._y/2),h=Math.sin(a._z/2);"XYZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"YXZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=
+c*d*h-f*g*e,this._w=c*d*e+f*g*h):"ZXY"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"ZYX"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e+f*g*h):"YZX"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e-f*g*h):"XZY"===a.order&&(this._x=f*d*e-c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e+f*g*h);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a,
+b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],h=b[2],k=b[6],b=b[10],l=c+f+b;0<l?(c=.5/Math.sqrt(l+1),this._w=.25/c,this._x=(k-g)*c,this._y=(d-h)*c,this._z=(e-a)*c):c>f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y=
+.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3);b=c.dot(d)+1;1E-6>b?(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;this.normalize();return this}}(),inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*=
+-1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this},
+multiply:function(a,b){return void 0!==b?(THREE.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=a._w,g=b._x,h=b._y,k=b._z,l=b._w;this._x=c*l+f*g+d*k-e*h;this._y=d*l+f*h+e*g-c*k;this._z=e*l+f*k+c*h-d*g;this._w=f*l-c*g-d*h-e*k;this.onChangeCallback();return this},multiplyVector3:function(a){THREE.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");
+return a.applyQuaternion(this)},slerp:function(a,b){if(0===b)return this;if(1===b)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;var h=Math.acos(g),k=Math.sqrt(1-g*g);if(.001>Math.abs(k))return this._w=.5*(f+this._w),this._x=.5*(c+this._x),this._y=.5*(d+this._y),this._z=.5*(e+this._z),this;g=Math.sin((1-b)*h)/k;h=
+Math.sin(b*h)/k;this._w=f*g+this._w*h;this._x=c*g+this._x*h;this._y=d*g+this._y*h;this._z=e*g+this._z*h;this.onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){void 0===b&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},onChange:function(a){this.onChangeCallback=
+a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0};
 THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(a,b){this.x=a;this.y=b;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,
-b){if(void 0!==b)return console.warn("DEPRECATED: Vector2's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},sub:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector2's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=
-a.y;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);
-return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/
-b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a){this.x=a[0];this.y=a[1];return this},toArray:function(){return[this.x,this.y]},clone:function(){return new THREE.Vector2(this.x,this.y)}};THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};
+b){if(void 0!==b)return THREE.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},sub:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},
+subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.x<a.x&&(this.x=a.x);
+this.y<a.y&&(this.y=a.y);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector2,b=new THREE.Vector2);a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=
+Math.round(this.x);this.y=Math.round(this.y);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},
+distanceToSquared:function(a){var b=this.x-a.x;a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){this.subVectors(b,a).multiplyScalar(c).add(a);return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){void 0===
+a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];return this},clone:function(){return new THREE.Vector2(this.x,this.y)}};THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};
 THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+
-a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},sub:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
-this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*
-b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements,e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);
-this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,h=a.z,a=a.w,g=a*b+f*d-h*c,i=a*c+h*b-e*d,k=a*d+e*c-f*b,b=-e*b-f*c-h*d;this.x=g*a+b*-e+i*-h-k*-f;this.y=i*a+b*-f+k*-e-g*-h;this.z=k*a+b*-h+g*-f-i*-e;return this},transformDirection:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*
-b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<
-a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},
-setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},cross:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y=e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=
-a.x,d=a.y,e=a.z,f=b.x,h=b.y,g=b.z;this.x=d*g-e*h;this.y=e*f-c*g;this.z=c*h-d*f;return this},angleTo:function(a){a=this.dot(a)/(this.length()*a.length());return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y,a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(){console.error("REMOVED: Vector3's setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code.")},
-setEulerFromQuaternion:function(){console.error("REMOVED: Vector3's setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code.")},getPositionFromMatrix:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},getScaleFromMatrix:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length(),a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();
-this.x=b;this.y=c;this.z=a;return this},getColumnFromMatrix:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];return this},toArray:function(){return[this.x,this.y,this.z]},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};
-THREE.extend(THREE.Vector3.prototype,{applyEuler:function(){var a=new THREE.Quaternion;return function(b){!1===b instanceof THREE.Euler&&console.error("ERROR: Vector3's .applyEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a=new THREE.Quaternion;return function(b,c){this.applyQuaternion(a.setFromAxisAngle(b,c));return this}}(),projectOnVector:function(){var a=new THREE.Vector3;
-return function(b){a.copy(b).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a=new THREE.Vector3;return function(b){a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a=new THREE.Vector3;return function(b){a.copy(this).projectOnVector(b).multiplyScalar(2);return this.subVectors(a,this)}}()});THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1};
+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},sub:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
+this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=
+a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a;return function(b){!1===b instanceof THREE.Euler&&THREE.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.");void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a;return function(b,c){void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromAxisAngle(b,
+c));return this}}(),applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*
+c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,l=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-l*-f;this.y=k*a+b*-f+l*-e-h*-g;this.z=l*a+b*-g+h*-f-k*-e;return this},project:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.projectionMatrix,a.getInverse(b.matrixWorld));return this.applyProjection(a)}}(),
+unproject:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.matrixWorld,a.getInverse(b.projectionMatrix));return this.applyProjection(a)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=
+a):this.z=this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3,
+b=new THREE.Vector3);a.set(c,c,c);b.set(d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):
+Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},
+setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},lerpVectors:function(a,b,c){this.subVectors(b,a).multiplyScalar(c).add(a);return this},cross:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y=
+e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=a.x,d=a.y,e=a.z,f=b.x,g=b.y,h=b.z;this.x=d*h-e*g;this.y=e*f-c*h;this.z=c*g-d*f;return this},projectOnVector:function(){var a,b;return function(c){void 0===a&&(a=new THREE.Vector3);a.copy(c).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a;return function(b){void 0===
+a&&(a=new THREE.Vector3);return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/(this.length()*a.length());return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(a,b){THREE.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},
+setEulerFromQuaternion:function(a,b){THREE.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(a){THREE.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().");return this.setFromMatrixPosition(a)},getScaleFromMatrix:function(a){THREE.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().");return this.setFromMatrixScale(a)},getColumnFromMatrix:function(a,
+b){THREE.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().");return this.setFromMatrixColumn(a,b)},setFromMatrixPosition:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},setFromMatrixScale:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length();a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();this.x=b;this.y=c;this.z=a;
+return this},setFromMatrixColumn:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=
+a.array[b+2];return this},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1};
 THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;
-case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector4's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},
-addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},sub:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector4's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},
-applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,
-this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){var b,c,d,a=a.elements,e=a[0];d=a[4];var f=a[8],h=a[1],g=a[5],i=a[9];c=a[2];b=a[6];var k=a[10];if(0.01>Math.abs(d-h)&&0.01>Math.abs(f-c)&&0.01>Math.abs(i-b)){if(0.1>Math.abs(d+h)&&0.1>Math.abs(f+c)&&0.1>Math.abs(i+b)&&0.1>Math.abs(e+g+k-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;g=(g+1)/2;k=(k+1)/2;d=(d+h)/4;f=(f+c)/4;i=(i+b)/4;e>g&&e>k?0.01>e?(b=0,d=c=0.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):g>k?0.01>g?
-(b=0.707106781,c=0,d=0.707106781):(c=Math.sqrt(g),b=d/c,d=i/c):0.01>k?(c=b=0.707106781,d=0):(d=Math.sqrt(k),b=f/d,c=i/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-i)*(b-i)+(f-c)*(f-c)+(h-d)*(h-d));0.001>Math.abs(a)&&(a=1);this.x=(b-i)/a;this.y=(f-c)/a;this.z=(h-d)/a;this.w=Math.acos((e+g+k-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=
-a.y);this.z<a.z&&(this.z=a.z);this.w<a.w&&(this.w=a.w);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);this.w<a.w?this.w=a.w:this.w>b.w&&(this.w=b.w);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*
-this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&
-a.w===this.w},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];this.w=a[3];return this},toArray:function(){return[this.x,this.y,this.z,this.w]},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)}};THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ";
-THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,_quaternion:void 0,_updateQuaternion:function(){void 0!==this._quaternion&&this._quaternion.setFromEuler(this,!1)},get x(){return this._x},set x(a){this._x=a;this._updateQuaternion()},get y(){return this._y},set y(a){this._y=a;this._updateQuaternion()},get z(){return this._z},set z(a){this._z=a;this._updateQuaternion()},get order(){return this._order},set order(a){this._order=a;this._updateQuaternion()},
-set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this._updateQuaternion();return this},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._order=a._order;this._updateQuaternion();return this},setFromRotationMatrix:function(a,b){function c(a){return Math.min(Math.max(a,-1),1)}var d=a.elements,e=d[0],f=d[4],h=d[8],g=d[1],i=d[5],k=d[9],m=d[2],l=d[6],d=d[10],b=b||this._order;"XYZ"===b?(this._y=Math.asin(c(h)),0.99999>Math.abs(h)?(this._x=Math.atan2(-k,d),this._z=
-Math.atan2(-f,e)):(this._x=Math.atan2(l,i),this._z=0)):"YXZ"===b?(this._x=Math.asin(-c(k)),0.99999>Math.abs(k)?(this._y=Math.atan2(h,d),this._z=Math.atan2(g,i)):(this._y=Math.atan2(-m,e),this._z=0)):"ZXY"===b?(this._x=Math.asin(c(l)),0.99999>Math.abs(l)?(this._y=Math.atan2(-m,d),this._z=Math.atan2(-f,i)):(this._y=0,this._z=Math.atan2(g,e))):"ZYX"===b?(this._y=Math.asin(-c(m)),0.99999>Math.abs(m)?(this._x=Math.atan2(l,d),this._z=Math.atan2(g,e)):(this._x=0,this._z=Math.atan2(-f,i))):"YZX"===b?(this._z=
-Math.asin(c(g)),0.99999>Math.abs(g)?(this._x=Math.atan2(-k,i),this._y=Math.atan2(-m,e)):(this._x=0,this._y=Math.atan2(h,d))):"XZY"===b?(this._z=Math.asin(-c(f)),0.99999>Math.abs(f)?(this._x=Math.atan2(l,i),this._y=Math.atan2(h,e)):(this._x=Math.atan2(-k,d),this._y=0)):console.warn("WARNING: Euler.setFromRotationMatrix() given unsupported order: "+b);this._order=b;this._updateQuaternion();return this},setFromQuaternion:function(a,b,c){function d(a){return Math.min(Math.max(a,-1),1)}var e=a.x*a.x,f=
-a.y*a.y,h=a.z*a.z,g=a.w*a.w,b=b||this._order;"XYZ"===b?(this._x=Math.atan2(2*(a.x*a.w-a.y*a.z),g-e-f+h),this._y=Math.asin(d(2*(a.x*a.z+a.y*a.w))),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),g+e-f-h)):"YXZ"===b?(this._x=Math.asin(d(2*(a.x*a.w-a.y*a.z))),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),g-e-f+h),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),g-e+f-h)):"ZXY"===b?(this._x=Math.asin(d(2*(a.x*a.w+a.y*a.z))),this._y=Math.atan2(2*(a.y*a.w-a.z*a.x),g-e-f+h),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),g-e+f-h)):"ZYX"===
-b?(this._x=Math.atan2(2*(a.x*a.w+a.z*a.y),g-e-f+h),this._y=Math.asin(d(2*(a.y*a.w-a.x*a.z))),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),g+e-f-h)):"YZX"===b?(this._x=Math.atan2(2*(a.x*a.w-a.z*a.y),g-e+f-h),this._y=Math.atan2(2*(a.y*a.w-a.x*a.z),g+e-f-h),this._z=Math.asin(d(2*(a.x*a.y+a.z*a.w)))):"XZY"===b?(this._x=Math.atan2(2*(a.x*a.w+a.y*a.z),g-e+f-h),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),g+e-f-h),this._z=Math.asin(d(2*(a.z*a.w-a.x*a.y)))):console.warn("WARNING: Euler.setFromQuaternion() given unsupported order: "+
-b);this._order=b;!1!==c&&this._updateQuaternion();return this},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this);this.setFromQuaternion(a,b)}}(),fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this._updateQuaternion();return this},toArray:function(){return[this._x,this._y,this._z,this._order]},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},clone:function(){return new THREE.Euler(this._x,
-this._y,this._z,this._order)}};THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
-THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(0.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,
+case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},addVectors:function(a,
+b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},sub:function(a,b){if(void 0!==b)return THREE.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;this.w-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=
+a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>
+b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){var b,c,d;a=a.elements;var e=a[0];d=a[4];var f=a[8],g=a[1],h=a[5],k=a[9];c=a[2];b=a[6];var l=a[10];if(.01>Math.abs(d-g)&&.01>Math.abs(f-c)&&.01>Math.abs(k-b)){if(.1>Math.abs(d+g)&&.1>Math.abs(f+c)&&.1>Math.abs(k+b)&&.1>Math.abs(e+h+l-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;h=(h+1)/2;l=(l+1)/2;d=(d+g)/4;f=(f+c)/4;k=(k+b)/4;e>h&&e>l?.01>e?(b=0,d=c=.707106781):(b=
+Math.sqrt(e),c=d/b,d=f/b):h>l?.01>h?(b=.707106781,c=0,d=.707106781):(c=Math.sqrt(h),b=d/c,d=k/c):.01>l?(c=b=.707106781,d=0):(d=Math.sqrt(l),b=f/d,c=k/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-k)*(b-k)+(f-c)*(f-c)+(g-d)*(g-d));.001>Math.abs(a)&&(a=1);this.x=(b-k)/a;this.y=(f-c)/a;this.z=(g-d)/a;this.w=Math.acos((e+h+l-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.x<a.x&&
+(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);this.w<a.w&&(this.w=a.w);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);this.w<a.w?this.w=a.w:this.w>b.w&&(this.w=b.w);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector4,b=new THREE.Vector4);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=
+Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);this.w=Math.floor(this.w);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);
+this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w):Math.floor(this.w);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+
+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},lerpVectors:function(a,b,c){this.subVectors(b,a).multiplyScalar(c).add(a);return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},
+fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;a[b+3]=this.w;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=a.array[b+2];this.w=a.array[b+3];return this},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)}};
+THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ";
+THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get order(){return this._order},set order(a){this._order=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this.onChangeCallback();return this},copy:function(a){this._x=
+a._x;this._y=a._y;this._z=a._z;this._order=a._order;this.onChangeCallback();return this},setFromRotationMatrix:function(a,b,c){var d=THREE.Math.clamp,e=a.elements;a=e[0];var f=e[4],g=e[8],h=e[1],k=e[5],l=e[9],p=e[2],q=e[6],e=e[10];b=b||this._order;"XYZ"===b?(this._y=Math.asin(d(g,-1,1)),.99999>Math.abs(g)?(this._x=Math.atan2(-l,e),this._z=Math.atan2(-f,a)):(this._x=Math.atan2(q,k),this._z=0)):"YXZ"===b?(this._x=Math.asin(-d(l,-1,1)),.99999>Math.abs(l)?(this._y=Math.atan2(g,e),this._z=Math.atan2(h,
+k)):(this._y=Math.atan2(-p,a),this._z=0)):"ZXY"===b?(this._x=Math.asin(d(q,-1,1)),.99999>Math.abs(q)?(this._y=Math.atan2(-p,e),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,a))):"ZYX"===b?(this._y=Math.asin(-d(p,-1,1)),.99999>Math.abs(p)?(this._x=Math.atan2(q,e),this._z=Math.atan2(h,a)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(d(h,-1,1)),.99999>Math.abs(h)?(this._x=Math.atan2(-l,k),this._y=Math.atan2(-p,a)):(this._x=0,this._y=Math.atan2(g,e))):"XZY"===b?(this._z=
+Math.asin(-d(f,-1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(q,k),this._y=Math.atan2(g,a)):(this._x=Math.atan2(-l,e),this._y=0)):THREE.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},setFromQuaternion:function(){var a;return function(b,c,d){void 0===a&&(a=new THREE.Matrix4);a.makeRotationFromQuaternion(b);this.setFromRotationMatrix(a,c,d);return this}}(),setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,
+b||this._order)},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this);this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){return a?
+a.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}};THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
+THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,
 b){var c=b||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=THREE.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);
 this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)},clone:function(){return(new THREE.Line3).copy(this)}};THREE.Box2=function(a,b){this.min=void 0!==a?a:new THREE.Vector2(Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector2(-Infinity,-Infinity)};
-THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){if(0<a.length){var b=a[0];this.min.copy(b);this.max.copy(b);for(var c=1,d=a.length;c<d;c++)b=a[c],b.x<this.min.x?this.min.x=b.x:b.x>this.max.x&&(this.max.x=b.x),b.y<this.min.y?this.min.y=b.y:b.y>this.max.y&&(this.max.y=b.y)}else this.makeEmpty();return this},setFromCenterAndSize:function(){var a=new THREE.Vector2;return function(b,c){var d=a.copy(c).multiplyScalar(0.5);
-this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=this.min.y=Infinity;this.max.x=this.max.y=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(a){return(a||new THREE.Vector2).addVectors(this.min,this.max).multiplyScalar(0.5)},size:function(a){return(a||new THREE.Vector2).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);
-this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a){return new THREE.Vector2((a.x-this.min.x)/(this.max.x-this.min.x),
-(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);
-return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)};
-THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},addPoint:function(a){a.x<this.min.x?this.min.x=a.x:a.x>this.max.x&&(this.max.x=a.x);a.y<this.min.y?this.min.y=a.y:a.y>this.max.y&&(this.max.y=a.y);a.z<this.min.z?this.min.z=a.z:a.z>this.max.z&&(this.max.z=a.z)},setFromPoints:function(a){if(0<a.length){var b=a[0];this.min.copy(b);this.max.copy(b);for(var b=1,c=a.length;b<c;b++)this.addPoint(a[b])}else this.makeEmpty();return this},setFromCenterAndSize:function(){var a=
-new THREE.Vector3;return function(b,c){var d=a.copy(c).multiplyScalar(0.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),setFromObject:function(){var a=new THREE.Vector3;return function(b){var c=this;b.updateMatrixWorld(!0);this.makeEmpty();b.traverse(function(b){if(void 0!==b.geometry&&void 0!==b.geometry.vertices)for(var e=b.geometry.vertices,f=0,h=e.length;f<h;f++)a.copy(e[f]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a)});return this}}(),copy:function(a){this.min.copy(a.min);
-this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=this.min.y=this.min.z=Infinity;this.max.x=this.max.y=this.max.z=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(a){return(a||new THREE.Vector3).addVectors(this.min,this.max).multiplyScalar(0.5)},size:function(a){return(a||new THREE.Vector3).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);
-this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y||a.z<this.min.z||a.z>this.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a){return new THREE.Vector3((a.x-this.min.x)/(this.max.x-this.min.x),
-(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y||a.max.z<this.min.z||a.min.z>this.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=
-new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=0.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(b){a[0].set(this.min.x,this.min.y,
+THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector2;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=
+this.min.y=Infinity;this.max.x=this.max.y=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(a){return(a||new THREE.Vector2).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector2).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);
+this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector2).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>
+this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&
+a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)};
+THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector3;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),setFromObject:function(){var a=new THREE.Vector3;return function(b){var c=this;b.updateMatrixWorld(!0);
+this.makeEmpty();b.traverse(function(b){var e=b.geometry;if(void 0!==e)if(e instanceof THREE.Geometry)for(var f=e.vertices,e=0,g=f.length;e<g;e++)a.copy(f[e]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a);else if(e instanceof THREE.BufferGeometry&&void 0!==e.attributes.position)for(f=e.attributes.position.array,e=0,g=f.length;e<g;e+=3)a.set(f[e],f[e+1],f[e+2]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a)});return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},
+makeEmpty:function(){this.min.x=this.min.y=this.min.z=Infinity;this.max.x=this.max.y=this.max.z=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(a){return(a||new THREE.Vector3).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector3).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);
+this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y||a.z<this.min.z||a.z>this.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector3).set((a.x-this.min.x)/(this.max.x-
+this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y||a.max.z<this.min.z||a.min.z>this.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=
+new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(b){a[0].set(this.min.x,this.min.y,
 this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b);a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.makeEmpty();this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);
-this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}};THREE.Matrix3=function(a,b,c,d,e,f,h,g,i){this.elements=new Float32Array(9);this.set(void 0!==a?a:1,b||0,c||0,d||0,void 0!==e?e:1,f||0,h||0,g||0,void 0!==i?i:1)};
-THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,h,g,i){var k=this.elements;k[0]=a;k[3]=b;k[6]=c;k[1]=d;k[4]=e;k[7]=f;k[2]=h;k[5]=g;k[8]=i;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){console.warn("DEPRECATED: Matrix3's .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)},
-multiplyVector3Array:function(){var a=new THREE.Vector3;return function(b){for(var c=0,d=b.length;c<d;c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix3(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],h=a[5],g=a[6],i=a[7],a=a[8];return b*f*a-b*h*i-c*e*a+c*h*g+d*e*i-d*f*g},getInverse:function(a,
-b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/
-c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},clone:function(){var a=this.elements;return new THREE.Matrix3(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8])}};THREE.Matrix4=function(a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n){var r=this.elements=new Float32Array(16);r[0]=void 0!==a?a:1;r[4]=b||0;r[8]=c||0;r[12]=d||0;r[1]=e||0;r[5]=void 0!==f?f:1;r[9]=h||0;r[13]=g||0;r[2]=i||0;r[6]=k||0;r[10]=void 0!==m?m:1;r[14]=l||0;r[3]=p||0;r[7]=s||0;r[11]=t||0;r[15]=void 0!==n?n:1};
-THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n){var r=this.elements;r[0]=a;r[4]=b;r[8]=c;r[12]=d;r[1]=e;r[5]=f;r[9]=h;r[13]=g;r[2]=i;r[6]=k;r[10]=m;r[14]=l;r[3]=p;r[7]=s;r[11]=t;r[15]=n;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},copy:function(a){this.elements.set(a.elements);return this},extractPosition:function(a){console.warn("DEPRECATED: Matrix4's .extractPosition() has been renamed to .copyPosition().");
-return this.copyPosition(a)},copyPosition:function(a){var b=this.elements,a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractRotation:function(){var a=new THREE.Vector3;return function(b){var c=this.elements,b=b.elements,d=1/a.set(b[0],b[1],b[2]).length(),e=1/a.set(b[4],b[5],b[6]).length(),f=1/a.set(b[8],b[9],b[10]).length();c[0]=b[0]*d;c[1]=b[1]*d;c[2]=b[2]*d;c[4]=b[4]*e;c[5]=b[5]*e;c[6]=b[6]*e;c[8]=b[8]*f;c[9]=b[9]*f;c[10]=b[10]*f;return this}}(),makeRotationFromEuler:function(a){!1===
-a instanceof THREE.Euler&&console.error("ERROR: Matrix's .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),h=Math.cos(d),d=Math.sin(d),g=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){var a=f*g,i=f*e,k=c*g,m=c*e;b[0]=h*g;b[4]=-h*e;b[8]=d;b[1]=i+k*d;b[5]=a-m*d;b[9]=-c*h;b[2]=m-a*d;b[6]=k+i*d;b[10]=f*h}else"YXZ"===a.order?(a=h*g,i=h*e,k=d*g,m=d*e,b[0]=a+m*c,b[4]=k*c-i,b[8]=
-f*d,b[1]=f*e,b[5]=f*g,b[9]=-c,b[2]=i*c-k,b[6]=m+a*c,b[10]=f*h):"ZXY"===a.order?(a=h*g,i=h*e,k=d*g,m=d*e,b[0]=a-m*c,b[4]=-f*e,b[8]=k+i*c,b[1]=i+k*c,b[5]=f*g,b[9]=m-a*c,b[2]=-f*d,b[6]=c,b[10]=f*h):"ZYX"===a.order?(a=f*g,i=f*e,k=c*g,m=c*e,b[0]=h*g,b[4]=k*d-i,b[8]=a*d+m,b[1]=h*e,b[5]=m*d+a,b[9]=i*d-k,b[2]=-d,b[6]=c*h,b[10]=f*h):"YZX"===a.order?(a=f*h,i=f*d,k=c*h,m=c*d,b[0]=h*g,b[4]=m-a*e,b[8]=k*e+i,b[1]=e,b[5]=f*g,b[9]=-c*g,b[2]=-d*g,b[6]=i*e+k,b[10]=a-m*e):"XZY"===a.order&&(a=f*h,i=f*d,k=c*h,m=c*d,b[0]=
-h*g,b[4]=-e,b[8]=d*g,b[1]=a*e+m,b[5]=f*g,b[9]=i*e-k,b[2]=k*e-i,b[6]=c*g,b[10]=m*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},setRotationFromQuaternion:function(a){console.warn("DEPRECATED: Matrix4's .setRotationFromQuaternion() has been deprecated in favor of makeRotationFromQuaternion.  Please update your code.");return this.makeRotationFromQuaternion(a)},makeRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,h=c+c,g=d+d,i=e+e,a=c*h,k=c*g,c=
-c*i,m=d*g,d=d*i,e=e*i,h=f*h,g=f*g,f=f*i;b[0]=1-(m+e);b[4]=k-f;b[8]=c+g;b[1]=k+f;b[5]=1-(a+e);b[9]=d-h;b[2]=c-g;b[6]=d+h;b[10]=1-(a+m);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f){var h=this.elements;c.subVectors(d,e).normalize();0===c.length()&&(c.z=1);a.crossVectors(f,c).normalize();0===a.length()&&(c.x+=1E-4,a.crossVectors(f,c).normalize());b.crossVectors(c,a);h[0]=a.x;
-h[4]=b.x;h[8]=c.x;h[1]=a.y;h[5]=b.y;h[9]=c.y;h[2]=a.z;h[6]=b.z;h[10]=c.z;return this}}(),multiply:function(a,b){return void 0!==b?(console.warn("DEPRECATED: Matrix4's .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements,f=c[0],h=c[4],g=c[8],i=c[12],k=c[1],m=c[5],l=c[9],p=c[13],s=c[2],t=c[6],n=c[10],r=c[14],q=c[3],u=c[7],w=c[11],c=c[15],
-z=d[0],B=d[4],D=d[8],x=d[12],F=d[1],A=d[5],O=d[9],C=d[13],E=d[2],I=d[6],y=d[10],v=d[14],G=d[3],R=d[7],J=d[11],d=d[15];e[0]=f*z+h*F+g*E+i*G;e[4]=f*B+h*A+g*I+i*R;e[8]=f*D+h*O+g*y+i*J;e[12]=f*x+h*C+g*v+i*d;e[1]=k*z+m*F+l*E+p*G;e[5]=k*B+m*A+l*I+p*R;e[9]=k*D+m*O+l*y+p*J;e[13]=k*x+m*C+l*v+p*d;e[2]=s*z+t*F+n*E+r*G;e[6]=s*B+t*A+n*I+r*R;e[10]=s*D+t*O+n*y+r*J;e[14]=s*x+t*C+n*v+r*d;e[3]=q*z+u*F+w*E+c*G;e[7]=q*B+u*A+w*I+c*R;e[11]=q*D+u*O+w*y+c*J;e[15]=q*x+u*C+w*v+c*d;return this},multiplyToArray:function(a,b,
-c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},multiplyVector3:function(a){console.warn("DEPRECATED: Matrix4's .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");
-return a.applyProjection(this)},multiplyVector4:function(a){console.warn("DEPRECATED: Matrix4's .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},multiplyVector3Array:function(){var a=new THREE.Vector3;return function(b){for(var c=0,d=b.length;c<d;c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyProjection(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),rotateAxis:function(a){console.warn("DEPRECATED: Matrix4's .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");
-a.transformDirection(this)},crossVector:function(a){console.warn("DEPRECATED: Matrix4's .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},determinant:function(){var a=this.elements,b=a[0],c=a[4],d=a[8],e=a[12],f=a[1],h=a[5],g=a[9],i=a[13],k=a[2],m=a[6],l=a[10],p=a[14];return a[3]*(+e*g*m-d*i*m-e*h*l+c*i*l+d*h*p-c*g*p)+a[7]*(+b*g*p-b*i*l+e*f*l-d*f*p+d*i*k-e*g*k)+a[11]*(+b*i*m-b*h*p-e*f*m+c*f*p+e*h*k-c*i*k)+a[15]*(-d*h*k-b*g*m+b*h*l+d*f*m-c*f*
-l+c*g*k)},transpose:function(){var a=this.elements,b;b=a[1];a[1]=a[4];a[4]=b;b=a[2];a[2]=a[8];a[8]=b;b=a[6];a[6]=a[9];a[9]=b;b=a[3];a[3]=a[12];a[12]=b;b=a[7];a[7]=a[13];a[13]=b;b=a[11];a[11]=a[14];a[14]=b;return this},flattenToArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[1];a[2]=b[2];a[3]=b[3];a[4]=b[4];a[5]=b[5];a[6]=b[6];a[7]=b[7];a[8]=b[8];a[9]=b[9];a[10]=b[10];a[11]=b[11];a[12]=b[12];a[13]=b[13];a[14]=b[14];a[15]=b[15];return a},flattenToArrayOffset:function(a,b){var c=this.elements;
-a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a},getPosition:function(){var a=new THREE.Vector3;return function(){console.warn("DEPRECATED: Matrix4's .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.");var b=this.elements;return a.set(b[12],b[13],b[14])}}(),setPosition:function(a){var b=this.elements;
-b[12]=a.x;b[13]=a.y;b[14]=a.z;return this},getInverse:function(a,b){var c=this.elements,d=a.elements,e=d[0],f=d[4],h=d[8],g=d[12],i=d[1],k=d[5],m=d[9],l=d[13],p=d[2],s=d[6],t=d[10],n=d[14],r=d[3],q=d[7],u=d[11],d=d[15];c[0]=m*n*q-l*t*q+l*s*u-k*n*u-m*s*d+k*t*d;c[4]=g*t*q-h*n*q-g*s*u+f*n*u+h*s*d-f*t*d;c[8]=h*l*q-g*m*q+g*k*u-f*l*u-h*k*d+f*m*d;c[12]=g*m*s-h*l*s-g*k*t+f*l*t+h*k*n-f*m*n;c[1]=l*t*r-m*n*r-l*p*u+i*n*u+m*p*d-i*t*d;c[5]=h*n*r-g*t*r+g*p*u-e*n*u-h*p*d+e*t*d;c[9]=g*m*r-h*l*r-g*i*u+e*l*u+h*i*d-
-e*m*d;c[13]=h*l*p-g*m*p+g*i*t-e*l*t-h*i*n+e*m*n;c[2]=k*n*r-l*s*r+l*p*q-i*n*q-k*p*d+i*s*d;c[6]=g*s*r-f*n*r-g*p*q+e*n*q+f*p*d-e*s*d;c[10]=f*l*r-g*k*r+g*i*q-e*l*q-f*i*d+e*k*d;c[14]=g*k*p-f*l*p-g*i*s+e*l*s+f*i*n-e*k*n;c[3]=m*s*r-k*t*r-m*p*q+i*t*q+k*p*u-i*s*u;c[7]=f*t*r-h*s*r+h*p*q-e*t*q-f*p*u+e*s*u;c[11]=h*k*r-f*m*r-h*i*q+e*m*q+f*i*u-e*k*u;c[15]=f*m*p-h*k*p+h*i*s-e*m*s-f*i*t+e*k*t;c=e*c[0]+i*c[4]+p*c[8]+r*c[12];if(0==c){if(b)throw Error("Matrix4.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix4.getInverse(): can't invert matrix, determinant is 0");
-this.identity();return this}this.multiplyScalar(1/c);return this},translate:function(){console.warn("DEPRECATED: Matrix4's .translate() has been removed.")},rotateX:function(){console.warn("DEPRECATED: Matrix4's .rotateX() has been removed.")},rotateY:function(){console.warn("DEPRECATED: Matrix4's .rotateY() has been removed.")},rotateZ:function(){console.warn("DEPRECATED: Matrix4's .rotateZ() has been removed.")},rotateByAxis:function(){console.warn("DEPRECATED: Matrix4's .rotateByAxis() has been removed.")},
-scale:function(a){var b=this.elements,c=a.x,d=a.y,a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements;return Math.sqrt(Math.max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],Math.max(a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(1,
-0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b),d=Math.sin(b),e=1-c,f=a.x,h=a.y,g=a.z,i=e*f,k=e*h;this.set(i*f+c,i*h-d*g,i*g+d*h,0,i*h+d*g,k*h+c,k*g-d*f,0,i*g-d*h,k*g+d*f,e*g*g+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,
-0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a=new THREE.Vector3,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements,h=a.set(f[0],f[1],f[2]).length(),g=a.set(f[4],f[5],f[6]).length(),i=a.set(f[8],f[9],f[10]).length();c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);var c=1/h,f=1/g,k=1/i;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=
-f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=k;b.elements[9]*=k;b.elements[10]*=k;d.setFromRotationMatrix(b);e.x=h;e.y=g;e.z=i;return this}}(),makeFrustum:function(a,b,c,d,e,f){var h=this.elements;h[0]=2*e/(b-a);h[4]=0;h[8]=(b+a)/(b-a);h[12]=0;h[1]=0;h[5]=2*e/(d-c);h[9]=(d+c)/(d-c);h[13]=0;h[2]=0;h[6]=0;h[10]=-(f+e)/(f-e);h[14]=-2*f*e/(f-e);h[3]=0;h[7]=0;h[11]=-1;h[15]=0;return this},makePerspective:function(a,b,c,d){var a=c*Math.tan(THREE.Math.degToRad(0.5*a)),e=-a;return this.makeFrustum(e*
-b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var h=this.elements,g=b-a,i=c-d,k=f-e;h[0]=2/g;h[4]=0;h[8]=0;h[12]=-((b+a)/g);h[1]=0;h[5]=2/i;h[9]=0;h[13]=-((c+d)/i);h[2]=0;h[6]=0;h[10]=-2/k;h[14]=-((f+e)/k);h[3]=0;h[7]=0;h[11]=0;h[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){var a=this.elements;return new THREE.Matrix4(a[0],
-a[4],a[8],a[12],a[1],a[5],a[9],a[13],a[2],a[6],a[10],a[14],a[3],a[7],a[11],a[15])}};THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3};
+this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}};THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]);0<arguments.length&&THREE.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")};
+THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,g,h,k){var l=this.elements;l[0]=a;l[3]=b;l[6]=c;l[1]=d;l[4]=e;l[7]=f;l[2]=g;l[5]=h;l[8]=k;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){THREE.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)},
+multiplyVector3Array:function(a){THREE.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix3(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=
+a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],k=a[7],a=a[8];return b*f*a-b*g*k-c*e*a+c*g*h+d*e*k-d*f*h},getInverse:function(a,b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];
+c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");THREE.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},flattenToArrayOffset:function(a,b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];
+a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]]},clone:function(){return(new THREE.Matrix3).fromArray(this.elements)}};
+THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0<arguments.length&&THREE.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")};
+THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(a,b,c,d,e,f,g,h,k,l,p,q,n,t,r,s){var u=this.elements;u[0]=a;u[4]=b;u[8]=c;u[12]=d;u[1]=e;u[5]=f;u[9]=g;u[13]=h;u[2]=k;u[6]=l;u[10]=p;u[14]=q;u[3]=n;u[7]=t;u[11]=r;u[15]=s;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},copy:function(a){this.elements.set(a.elements);return this},extractPosition:function(a){THREE.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().");return this.copyPosition(a)},
+copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){var d=this.elements;a.set(d[0],d[1],d[2]);b.set(d[4],d[5],d[6]);c.set(d[8],d[9],d[10]);return this},makeBasis:function(a,b,c){this.set(a.x,b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(){var a=new THREE.Vector3;return function(b){var c=this.elements;b=b.elements;var d=1/a.set(b[0],b[1],b[2]).length(),e=1/a.set(b[4],b[5],b[6]).length(),
+f=1/a.set(b[8],b[9],b[10]).length();c[0]=b[0]*d;c[1]=b[1]*d;c[2]=b[2]*d;c[4]=b[4]*e;c[5]=b[5]*e;c[6]=b[6]*e;c[8]=b[8]*f;c[9]=b[9]*f;c[10]=b[10]*f;return this}}(),makeRotationFromEuler:function(a){!1===a instanceof THREE.Euler&&THREE.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),g=Math.cos(d),d=Math.sin(d),h=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){a=f*h;var k=f*e,
+l=c*h,p=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=k+l*d;b[5]=a-p*d;b[9]=-c*g;b[2]=p-a*d;b[6]=l+k*d;b[10]=f*g}else"YXZ"===a.order?(a=g*h,k=g*e,l=d*h,p=d*e,b[0]=a+p*c,b[4]=l*c-k,b[8]=f*d,b[1]=f*e,b[5]=f*h,b[9]=-c,b[2]=k*c-l,b[6]=p+a*c,b[10]=f*g):"ZXY"===a.order?(a=g*h,k=g*e,l=d*h,p=d*e,b[0]=a-p*c,b[4]=-f*e,b[8]=l+k*c,b[1]=k+l*c,b[5]=f*h,b[9]=p-a*c,b[2]=-f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*h,k=f*e,l=c*h,p=c*e,b[0]=g*h,b[4]=l*d-k,b[8]=a*d+p,b[1]=g*e,b[5]=p*d+a,b[9]=k*d-l,b[2]=-d,b[6]=c*g,b[10]=f*g):"YZX"===
+a.order?(a=f*g,k=f*d,l=c*g,p=c*d,b[0]=g*h,b[4]=p-a*e,b[8]=l*e+k,b[1]=e,b[5]=f*h,b[9]=-c*h,b[2]=-d*h,b[6]=k*e+l,b[10]=a-p*e):"XZY"===a.order&&(a=f*g,k=f*d,l=c*g,p=c*d,b[0]=g*h,b[4]=-e,b[8]=d*h,b[1]=a*e+p,b[5]=f*h,b[9]=k*e-l,b[2]=l*e-k,b[6]=c*h,b[10]=p*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},setRotationFromQuaternion:function(a){THREE.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().");return this.makeRotationFromQuaternion(a)},
+makeRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,g=c+c,h=d+d,k=e+e;a=c*g;var l=c*h,c=c*k,p=d*h,d=d*k,e=e*k,g=f*g,h=f*h,f=f*k;b[0]=1-(p+e);b[4]=l-f;b[8]=c+h;b[1]=l+f;b[5]=1-(a+e);b[9]=d-g;b[2]=c-h;b[6]=d+g;b[10]=1-(a+p);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f){var g=this.elements;c.subVectors(d,e).normalize();0===c.length()&&(c.z=1);a.crossVectors(f,
+c).normalize();0===a.length()&&(c.x+=1E-4,a.crossVectors(f,c).normalize());b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),multiply:function(a,b){return void 0!==b?(THREE.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements,f=c[0],g=c[4],h=c[8],
+k=c[12],l=c[1],p=c[5],q=c[9],n=c[13],t=c[2],r=c[6],s=c[10],u=c[14],v=c[3],x=c[7],D=c[11],c=c[15],w=d[0],y=d[4],A=d[8],E=d[12],G=d[1],F=d[5],z=d[9],I=d[13],U=d[2],M=d[6],H=d[10],L=d[14],P=d[3],N=d[7],R=d[11],d=d[15];e[0]=f*w+g*G+h*U+k*P;e[4]=f*y+g*F+h*M+k*N;e[8]=f*A+g*z+h*H+k*R;e[12]=f*E+g*I+h*L+k*d;e[1]=l*w+p*G+q*U+n*P;e[5]=l*y+p*F+q*M+n*N;e[9]=l*A+p*z+q*H+n*R;e[13]=l*E+p*I+q*L+n*d;e[2]=t*w+r*G+s*U+u*P;e[6]=t*y+r*F+s*M+u*N;e[10]=t*A+r*z+s*H+u*R;e[14]=t*E+r*I+s*L+u*d;e[3]=v*w+x*G+D*U+c*P;e[7]=v*y+
+x*F+D*M+c*N;e[11]=v*A+x*z+D*H+c*R;e[15]=v*E+x*I+D*L+c*d;return this},multiplyToArray:function(a,b,c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=
+a;b[15]*=a;return this},multiplyVector3:function(a){THREE.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");return a.applyProjection(this)},multiplyVector4:function(a){THREE.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},multiplyVector3Array:function(a){THREE.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");
+return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix4(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),rotateAxis:function(a){THREE.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");a.transformDirection(this)},crossVector:function(a){THREE.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");
+return a.applyMatrix4(this)},determinant:function(){var a=this.elements,b=a[0],c=a[4],d=a[8],e=a[12],f=a[1],g=a[5],h=a[9],k=a[13],l=a[2],p=a[6],q=a[10],n=a[14];return a[3]*(+e*h*p-d*k*p-e*g*q+c*k*q+d*g*n-c*h*n)+a[7]*(+b*h*n-b*k*q+e*f*q-d*f*n+d*k*l-e*h*l)+a[11]*(+b*k*p-b*g*n-e*f*p+c*f*n+e*g*l-c*k*l)+a[15]*(-d*g*l-b*h*p+b*g*q+d*f*p-c*f*q+c*h*l)},transpose:function(){var a=this.elements,b;b=a[1];a[1]=a[4];a[4]=b;b=a[2];a[2]=a[8];a[8]=b;b=a[6];a[6]=a[9];a[9]=b;b=a[3];a[3]=a[12];a[12]=b;b=a[7];a[7]=a[13];
+a[13]=b;b=a[11];a[11]=a[14];a[14]=b;return this},flattenToArrayOffset:function(a,b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a},getPosition:function(){var a=new THREE.Vector3;return function(){THREE.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var b=
+this.elements;return a.set(b[12],b[13],b[14])}}(),setPosition:function(a){var b=this.elements;b[12]=a.x;b[13]=a.y;b[14]=a.z;return this},getInverse:function(a,b){var c=this.elements,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[12],k=d[1],l=d[5],p=d[9],q=d[13],n=d[2],t=d[6],r=d[10],s=d[14],u=d[3],v=d[7],x=d[11],d=d[15];c[0]=p*s*v-q*r*v+q*t*x-l*s*x-p*t*d+l*r*d;c[4]=h*r*v-g*s*v-h*t*x+f*s*x+g*t*d-f*r*d;c[8]=g*q*v-h*p*v+h*l*x-f*q*x-g*l*d+f*p*d;c[12]=h*p*t-g*q*t-h*l*r+f*q*r+g*l*s-f*p*s;c[1]=q*r*u-p*s*u-q*n*x+
+k*s*x+p*n*d-k*r*d;c[5]=g*s*u-h*r*u+h*n*x-e*s*x-g*n*d+e*r*d;c[9]=h*p*u-g*q*u-h*k*x+e*q*x+g*k*d-e*p*d;c[13]=g*q*n-h*p*n+h*k*r-e*q*r-g*k*s+e*p*s;c[2]=l*s*u-q*t*u+q*n*v-k*s*v-l*n*d+k*t*d;c[6]=h*t*u-f*s*u-h*n*v+e*s*v+f*n*d-e*t*d;c[10]=f*q*u-h*l*u+h*k*v-e*q*v-f*k*d+e*l*d;c[14]=h*l*n-f*q*n-h*k*t+e*q*t+f*k*s-e*l*s;c[3]=p*t*u-l*r*u-p*n*v+k*r*v+l*n*x-k*t*x;c[7]=f*r*u-g*t*u+g*n*v-e*r*v-f*n*x+e*t*x;c[11]=g*l*u-f*p*u-g*k*v+e*p*v+f*k*x-e*l*x;c[15]=f*p*n-g*l*n+g*k*t-e*p*t-f*k*r+e*l*r;c=e*c[0]+k*c[4]+n*c[8]+u*c[12];
+if(0==c){if(b)throw Error("THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0");THREE.warn("THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},translate:function(a){THREE.error("THREE.Matrix4: .translate() has been removed.")},rotateX:function(a){THREE.error("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(a){THREE.error("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(a){THREE.error("THREE.Matrix4: .rotateZ() has been removed.")},
+rotateByAxis:function(a,b){THREE.error("THREE.Matrix4: .rotateByAxis() has been removed.")},scale:function(a){var b=this.elements,c=a.x,d=a.y;a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements;return Math.sqrt(Math.max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],Math.max(a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,
+c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(1,0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b),d=Math.sin(b),e=1-c,f=a.x,g=a.y,h=a.z,k=e*f,l=e*g;this.set(k*f+c,k*g-d*h,k*h+d*g,0,k*g+
+d*h,l*g+c,l*h-d*f,0,k*h-d*g,l*h+d*f,e*h*h+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a=new THREE.Vector3,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements,g=a.set(f[0],f[1],f[2]).length(),h=a.set(f[4],f[5],f[6]).length(),k=a.set(f[8],f[9],f[10]).length();0>this.determinant()&&(g=-g);c.x=f[12];
+c.y=f[13];c.z=f[14];b.elements.set(this.elements);c=1/g;var f=1/h,l=1/k;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=l;b.elements[9]*=l;b.elements[10]*=l;d.setFromRotationMatrix(b);e.x=g;e.y=h;e.z=k;return this}}(),makeFrustum:function(a,b,c,d,e,f){var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(d-c);g[9]=(d+c)/(d-c);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;
+g[11]=-1;g[15]=0;return this},makePerspective:function(a,b,c,d){a=c*Math.tan(THREE.Math.degToRad(.5*a));var e=-a;return this.makeFrustum(e*b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=b-a,k=c-d,l=f-e;g[0]=2/h;g[4]=0;g[8]=0;g[12]=-((b+a)/h);g[1]=0;g[5]=2/k;g[9]=0;g[13]=-((c+d)/k);g[2]=0;g[6]=0;g[10]=-2/l;g[14]=-((f+e)/l);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],
+a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)}};THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3};
 THREE.Ray.prototype={constructor:THREE.Ray,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)},recast:function(){var a=new THREE.Vector3;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new THREE.Vector3;c.subVectors(a,this.origin);
-var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(a,b,c,d){var e=a.clone().add(b).multiplyScalar(0.5),f=b.clone().sub(a).normalize(),h=0.5*a.distanceTo(b),
-g=this.origin.clone().sub(e),a=-this.direction.dot(f),b=g.dot(this.direction),i=-g.dot(f),k=g.lengthSq(),m=Math.abs(1-a*a),l,p;0<=m?(g=a*i-b,l=a*b-i,p=h*m,0<=g?l>=-p?l<=p?(h=1/m,g*=h,l*=h,a=g*(g+a*l+2*b)+l*(a*g+l+2*i)+k):(l=h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k):(l=-h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k):l<=-p?(g=Math.max(0,-(-a*h+b)),l=0<g?-h:Math.min(Math.max(-h,-i),h),a=-g*g+l*(l+2*i)+k):l<=p?(g=0,l=Math.min(Math.max(-h,-i),h),a=l*(l+2*i)+k):(g=Math.max(0,-(a*h+b)),l=0<g?h:Math.min(Math.max(-h,
--i),h),a=-g*g+l*(l+2*i)+k)):(l=0<a?-h:h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k);c&&c.copy(this.direction.clone().multiplyScalar(g).add(this.origin));d&&d.copy(f.clone().multiplyScalar(l).add(e));return a},isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?
-0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},isIntersectionBox:function(){var a=new THREE.Vector3;return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,h;d=1/this.direction.x;f=1/this.direction.y;h=1/this.direction.z;var g=this.origin;0<=d?(c=(a.min.x-g.x)*d,d*=a.max.x-g.x):(c=(a.max.x-g.x)*d,d*=a.min.x-g.x);0<=f?(e=(a.min.y-g.y)*f,f*=
-a.max.y-g.y):(e=(a.max.y-g.y)*f,f*=a.min.y-g.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(f<d||d!==d)d=f;0<=h?(e=(a.min.z-g.z)*h,h*=a.max.z-g.z):(e=(a.max.z-g.z)*h,h*=a.min.z-g.z);if(c>h||e>d)return null;if(e>c||c!==c)c=e;if(h<d||d!==d)d=h;return 0>d?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,h,g,i){b.subVectors(f,e);c.subVectors(h,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0<
-f){if(g)return null;g=1}else if(0>f)g=-1,f=-f;else return null;a.subVectors(this.origin,e);e=g*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;h=g*this.direction.dot(b.cross(a));if(0>h||e+h>f)return null;e=-g*a.dot(d);return 0>e?null:this.at(e/f,i)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},
-clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0};
-THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,h=b.length;f<h;f++)e=Math.max(e,d.distanceToSquared(b[f]));this.radius=Math.sqrt(e);return this}}(),copy:function(a){this.center.copy(a.center);this.radius=a.radius;return this},empty:function(){return 0>=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=
+var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,g){a.copy(d).add(e).multiplyScalar(.5);
+b.copy(e).sub(d).normalize();c.copy(this.origin).sub(a);var h=.5*d.distanceTo(e),k=-this.direction.dot(b),l=c.dot(this.direction),p=-c.dot(b),q=c.lengthSq(),n=Math.abs(1-k*k),t;0<n?(d=k*p-l,e=k*l-p,t=h*n,0<=d?e>=-t?e<=t?(h=1/n,d*=h,e*=h,k=d*(d+k*e+2*l)+e*(k*d+e+2*p)+q):(e=h,d=Math.max(0,-(k*e+l)),k=-d*d+e*(e+2*p)+q):(e=-h,d=Math.max(0,-(k*e+l)),k=-d*d+e*(e+2*p)+q):e<=-t?(d=Math.max(0,-(-k*h+l)),e=0<d?-h:Math.min(Math.max(-h,-p),h),k=-d*d+e*(e+2*p)+q):e<=t?(d=0,e=Math.min(Math.max(-h,-p),h),k=e*(e+
+2*p)+q):(d=Math.max(0,-(k*h+l)),e=0<d?h:Math.min(Math.max(-h,-p),h),k=-d*d+e*(e+2*p)+q)):(e=0<k?-h:h,d=Math.max(0,-(k*e+l)),k=-d*d+e*(e+2*p)+q);f&&f.copy(this.direction).multiplyScalar(d).add(this.origin);g&&g.copy(b).multiplyScalar(e).add(a);return k}}(),isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},intersectSphere:function(){var a=new THREE.Vector3;return function(b,c){a.subVectors(b.center,this.origin);var d=a.dot(this.direction),e=a.dot(a)-d*d,f=b.radius*b.radius;
+if(e>f)return null;f=Math.sqrt(f-e);e=d-f;d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},
+isIntersectionBox:function(){var a=new THREE.Vector3;return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var h=this.origin;0<=d?(c=(a.min.x-h.x)*d,d*=a.max.x-h.x):(c=(a.max.x-h.x)*d,d*=a.min.x-h.x);0<=f?(e=(a.min.y-h.y)*f,f*=a.max.y-h.y):(e=(a.max.y-h.y)*f,f*=a.min.y-h.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(f<d||d!==d)d=f;0<=g?(e=(a.min.z-h.z)*g,g*=a.max.z-h.z):(e=(a.max.z-h.z)*
+g,g*=a.min.z-h.z);if(c>g||e>d)return null;if(e>c||c!==c)c=e;if(g<d||d!==d)d=g;return 0>d?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0<f){if(h)return null;h=1}else if(0>f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=h*this.direction.dot(b.cross(a));
+if(0>g||e+g>f)return null;e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0};
+THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,g=b.length;f<g;f++)e=Math.max(e,d.distanceToSquared(b[f]));this.radius=Math.sqrt(e);return this}}(),copy:function(a){this.center.copy(a.center);this.radius=a.radius;return this},empty:function(){return 0>=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=
 this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center));return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius);
-return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}};THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]};
-THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var h=this.planes;h[0].copy(a);h[1].copy(b);h[2].copy(c);h[3].copy(d);h[4].copy(e);h[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements,a=c[0],d=c[1],e=c[2],f=c[3],h=c[4],g=c[5],i=c[6],k=c[7],m=c[8],l=c[9],p=c[10],s=c[11],t=c[12],n=c[13],r=c[14],c=c[15];b[0].setComponents(f-a,k-h,s-m,c-t).normalize();b[1].setComponents(f+
-a,k+h,s+m,c+t).normalize();b[2].setComponents(f+d,k+g,s+l,c+n).normalize();b[3].setComponents(f-d,k-g,s-l,c-n).normalize();b[4].setComponents(f-e,k-i,s-p,c-r).normalize();b[5].setComponents(f+e,k+i,s+p,c+r).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){for(var b=this.planes,
-c=a.center,a=-a.radius,d=0;6>d;d++)if(b[d].distanceToPoint(c)<a)return!1;return!0},intersectsBox:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){for(var d=this.planes,e=0;6>e;e++){var f=d[e];a.x=0<f.normal.x?c.min.x:c.max.x;b.x=0<f.normal.x?c.max.x:c.min.x;a.y=0<f.normal.y?c.min.y:c.max.y;b.y=0<f.normal.y?c.max.y:c.min.y;a.z=0<f.normal.z?c.min.z:c.max.z;b.z=0<f.normal.z?c.max.z:c.min.z;var h=f.distanceToPoint(a),f=f.distanceToPoint(b);if(0>h&&0>f)return!1}return!0}}(),containsPoint:function(a){for(var b=
-this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0};
+return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}};
+THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]};
+THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],l=c[7],p=c[8],q=c[9],n=c[10],t=c[11],r=c[12],s=c[13],u=c[14],c=c[15];b[0].setComponents(f-a,l-g,t-p,c-r).normalize();b[1].setComponents(f+
+a,l+g,t+p,c+r).normalize();b[2].setComponents(f+d,l+h,t+q,c+s).normalize();b[3].setComponents(f-d,l-h,t-q,c-s).normalize();b[4].setComponents(f-e,l-k,t-n,c-u).normalize();b[5].setComponents(f+e,l+k,t+n,c+u).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,
+c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)<a)return!1;return!0},intersectsBox:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){for(var d=this.planes,e=0;6>e;e++){var f=d[e];a.x=0<f.normal.x?c.min.x:c.max.x;b.x=0<f.normal.x?c.max.x:c.min.x;a.y=0<f.normal.y?c.min.y:c.max.y;b.y=0<f.normal.y?c.max.y:c.min.y;a.z=0<f.normal.z?c.min.z:c.max.z;b.z=0<f.normal.z?c.max.z:c.min.z;var g=f.distanceToPoint(a),f=f.distanceToPoint(b);if(0>g&&0>f)return!1}return!0}}(),
+containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0};
 THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,
 c);return this}}(),copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){return this.orthoPoint(a,b).sub(a).negate()},orthoPoint:function(a,
-b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start),a=this.distanceToPoint(a.end);return 0>b&&0<a||0>a&&0<b},intersectLine:function(){var a=new THREE.Vector3;return function(b,c){var d=c||new THREE.Vector3,e=b.delta(a),f=this.normal.dot(e);if(0==f){if(0==this.distanceToPoint(b.start))return d.copy(b.start)}else return f=-(b.start.dot(this.normal)+this.constant)/f,0>f||1<f?void 0:d.copy(e).multiplyScalar(f).add(b.start)}}(),
-coplanarPoint:function(a){return(a||new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){var d=d||(new THREE.Matrix3).getNormalMatrix(c),e=a.copy(this.normal).applyMatrix3(d),f=this.coplanarPoint(b);f.applyMatrix4(c);this.setFromNormalAndCoplanarPoint(e,f);return this}}(),translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&
-a.constant==this.constant},clone:function(){return(new THREE.Plane).copy(this)}};THREE.Math={PI2:2*Math.PI,generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d;return function(){for(var e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return a<b?b:a>c?c:a},clampBottom:function(a,b){return a<b?b:a},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,
-b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(0.5-Math.random())},sign:function(a){return 0>a?-1:0<a?1:0},degToRad:function(){var a=Math.PI/
-180;return function(b){return b*a}}(),radToDeg:function(){var a=180/Math.PI;return function(b){return b*a}}()};THREE.Spline=function(a){function b(a,b,c,d,e,f,h){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)*h+(-3*(b-c)-2*a-d)*f+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,f,h,g,i,k,m,l,p;this.initFromArray=function(a){this.points=[];for(var b=0;b<a.length;b++)this.points[b]={x:a[b][0],y:a[b][1],z:a[b][2]}};this.getPoint=function(a){e=(this.points.length-1)*a;f=Math.floor(e);h=e-f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1:
-f+2;k=this.points[c[0]];m=this.points[c[1]];l=this.points[c[2]];p=this.points[c[3]];g=h*h;i=h*g;d.x=b(k.x,m.x,l.x,p.x,h,g,i);d.y=b(k.y,m.y,l.y,p.y,h,g,i);d.z=b(k.z,m.z,l.z,p.z,h,g,i);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a<c;a++)b=this.points[a],d[a]=[b.x,b.y,b.z];return d};this.getLength=function(a){var b,c,d,e=b=b=0,f=new THREE.Vector3,h=new THREE.Vector3,g=[],i=0;g[0]=0;a||(a=100);c=this.points.length*a;f.copy(this.points[0]);for(a=1;a<c;a++)b=
-a/c,d=this.getPoint(b),h.copy(d),i+=h.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(g[b]=i,e=b);g[g.length]=i;return{chunks:g,total:i}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,h,g=[],i=new THREE.Vector3,k=this.getLength();g.push(i.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=k.chunks[b]-k.chunks[b-1];h=Math.ceil(a*c/k.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<h-1;c++)d=e+c*(1/h)*(f-e),d=this.getPoint(d),
-g.push(i.copy(d).clone());g.push(i.copy(this.points[b]).clone())}this.points=g}};THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3;this.b=void 0!==b?b:new THREE.Vector3;this.c=void 0!==c?c:new THREE.Vector3};THREE.Triangle.normal=function(){var a=new THREE.Vector3;return function(b,c,d,e){e=e||new THREE.Vector3;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0<b?e.multiplyScalar(1/Math.sqrt(b)):e.set(0,0,0)}}();
-THREE.Triangle.barycoordFromPoint=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,h,g){a.subVectors(h,e);b.subVectors(f,e);c.subVectors(d,e);var d=a.dot(a),e=a.dot(b),f=a.dot(c),i=b.dot(b),h=b.dot(c),k=d*i-e*e,g=g||new THREE.Vector3;if(0==k)return g.set(-2,-1,-1);k=1/k;i=(i*f-e*h)*k;d=(d*h-e*f)*k;return g.set(1-i-d,d,i)}}();
+b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return 0>b&&0<a||0>a&&0<b},intersectLine:function(){var a=new THREE.Vector3;return function(b,c){var d=c||new THREE.Vector3,e=b.delta(a),f=this.normal.dot(e);if(0==f){if(0==this.distanceToPoint(b.start))return d.copy(b.start)}else return f=-(b.start.dot(this.normal)+this.constant)/f,0>f||1<f?void 0:d.copy(e).multiplyScalar(f).add(b.start)}}(),
+coplanarPoint:function(a){return(a||new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Matrix3;return function(d,e){var f=e||c.getNormalMatrix(d),f=a.copy(this.normal).applyMatrix3(f),g=this.coplanarPoint(b);g.applyMatrix4(d);this.setFromNormalAndCoplanarPoint(f,g);return this}}(),translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&
+a.constant==this.constant},clone:function(){return(new THREE.Plane).copy(this)}};
+THREE.Math={generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d;return function(){for(var e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return a<b?b:a>c?c:a},clampBottom:function(a,b){return a<b?b:a},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,b,c){if(a<=
+b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return Math.floor(this.randFloat(a,b))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(){var a=Math.PI/180;return function(b){return b*a}}(),radToDeg:function(){var a=
+180/Math.PI;return function(b){return b*a}}(),isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},nextPowerOfTwo:function(a){a--;a|=a>>1;a|=a>>2;a|=a>>4;a|=a>>8;a|=a>>16;a++;return a}};
+THREE.Spline=function(a){function b(a,b,c,d,e,f,g){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*g+(-3*(b-c)-2*a-d)*f+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,f,g,h,k,l,p,q,n;this.initFromArray=function(a){this.points=[];for(var b=0;b<a.length;b++)this.points[b]={x:a[b][0],y:a[b][1],z:a[b][2]}};this.getPoint=function(a){e=(this.points.length-1)*a;f=Math.floor(e);g=e-f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1:f+
+2;l=this.points[c[0]];p=this.points[c[1]];q=this.points[c[2]];n=this.points[c[3]];h=g*g;k=g*h;d.x=b(l.x,p.x,q.x,n.x,g,h,k);d.y=b(l.y,p.y,q.y,n.y,g,h,k);d.z=b(l.z,p.z,q.z,n.z,g,h,k);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a<c;a++)b=this.points[a],d[a]=[b.x,b.y,b.z];return d};this.getLength=function(a){var b,c,d,e=b=b=0,f=new THREE.Vector3,g=new THREE.Vector3,h=[],k=0;h[0]=0;a||(a=100);c=this.points.length*a;f.copy(this.points[0]);for(a=1;a<c;a++)b=
+a/c,d=this.getPoint(b),g.copy(d),k+=g.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(h[b]=k,e=b);h[h.length]=k;return{chunks:h,total:k}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,g,h=[],k=new THREE.Vector3,n=this.getLength();h.push(k.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=n.chunks[b]-n.chunks[b-1];g=Math.ceil(a*c/n.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<g-1;c++)d=e+1/g*c*(f-e),d=this.getPoint(d),h.push(k.copy(d).clone());
+h.push(k.copy(this.points[b]).clone())}this.points=h}};THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3;this.b=void 0!==b?b:new THREE.Vector3;this.c=void 0!==c?c:new THREE.Vector3};THREE.Triangle.normal=function(){var a=new THREE.Vector3;return function(b,c,d,e){e=e||new THREE.Vector3;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0<b?e.multiplyScalar(1/Math.sqrt(b)):e.set(0,0,0)}}();
+THREE.Triangle.barycoordFromPoint=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,g,h){a.subVectors(g,e);b.subVectors(f,e);c.subVectors(d,e);d=a.dot(a);e=a.dot(b);f=a.dot(c);var k=b.dot(b);g=b.dot(c);var l=d*k-e*e;h=h||new THREE.Vector3;if(0==l)return h.set(-2,-1,-1);l=1/l;k=(k*f-e*g)*l;d=(d*g-e*f)*l;return h.set(1-k-d,d,k)}}();
 THREE.Triangle.containsPoint=function(){var a=new THREE.Vector3;return function(b,c,d,e){b=THREE.Triangle.barycoordFromPoint(b,c,d,e,a);return 0<=b.x&&0<=b.y&&1>=b.x+b.y}}();
-THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return 0.5*a.cross(b).length()}}(),midpoint:function(a){return(a||
+THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),midpoint:function(a){return(a||
 new THREE.Vector3).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return THREE.Triangle.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new THREE.Plane).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return THREE.Triangle.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return THREE.Triangle.containsPoint(a,this.a,this.b,this.c)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)},
-clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Vertex=function(a){console.warn("THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.");return a};THREE.UV=function(a,b){console.warn("THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.");return new THREE.Vector2(a,b)};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1};
+clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1};
 THREE.Clock.prototype={constructor:THREE.Clock,start:function(){this.oldTime=this.startTime=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now();this.running=!0},stop:function(){this.getElapsedTime();this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;this.autoStart&&!this.running&&this.start();if(this.running){var b=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now(),
-a=0.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){};
+a=.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){};
 THREE.EventDispatcher.prototype={constructor:THREE.EventDispatcher,apply:function(a){a.addEventListener=THREE.EventDispatcher.prototype.addEventListener;a.hasEventListener=THREE.EventDispatcher.prototype.hasEventListener;a.removeEventListener=THREE.EventDispatcher.prototype.removeEventListener;a.dispatchEvent=THREE.EventDispatcher.prototype.dispatchEvent},addEventListener:function(a,b){void 0===this._listeners&&(this._listeners={});var c=this._listeners;void 0===c[a]&&(c[a]=[]);-1===c[a].indexOf(b)&&
-c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners,d=c[a].indexOf(b);-1!==d&&c[a].splice(d,1)}},dispatchEvent:function(){var a=[];return function(b){if(void 0!==this._listeners){var c=this._listeners[b.type];if(void 0!==c){b.target=this;for(var d=c.length,e=0;e<d;e++)a[e]=c[e];for(e=0;e<d;e++)a[e].call(this,b)}}}}()};(function(a){a.Raycaster=function(b,c,d,e){this.ray=new a.Ray(b,c);this.near=d||0;this.far=e||Infinity};var b=new a.Sphere,c=new a.Ray;new a.Plane;new a.Vector3;var d=new a.Vector3,e=new a.Matrix4,f=function(a,b){return a.distance-b.distance},h=new a.Vector3,g=new a.Vector3,i=new a.Vector3,k=function(f,m,s){if(f instanceof a.Sprite){d.getPositionFromMatrix(f.matrixWorld);var t=m.ray.distanceToPoint(d);if(t>f.scale.x)return s;s.push({distance:t,point:f.position,face:null,object:f})}else if(f instanceof
-a.LOD)d.getPositionFromMatrix(f.matrixWorld),t=m.ray.origin.distanceTo(d),k(f.getObjectForDistance(t),m,s);else if(f instanceof a.Mesh){var n=f.geometry;null===n.boundingSphere&&n.computeBoundingSphere();b.copy(n.boundingSphere);b.applyMatrix4(f.matrixWorld);if(!1===m.ray.isIntersectionSphere(b))return s;e.getInverse(f.matrixWorld);c.copy(m.ray).applyMatrix4(e);if(null!==n.boundingBox&&!1===c.isIntersectionBox(n.boundingBox))return s;if(n instanceof a.BufferGeometry){var r=f.material;if(void 0===
-r||!1===n.dynamic)return s;var q,u,w=m.precision;if(void 0!==n.attributes.index)for(var z=n.offsets,B=n.attributes.index.array,D=n.attributes.position.array,x=n.offsets.length,F=n.attributes.index.array.length/3,F=0;F<x;++F)for(var t=z[F].start,A=z[F].index,n=t,O=t+z[F].count;n<O;n+=3)t=A+B[n],q=A+B[n+1],u=A+B[n+2],h.set(D[3*t],D[3*t+1],D[3*t+2]),g.set(D[3*q],D[3*q+1],D[3*q+2]),i.set(D[3*u],D[3*u+1],D[3*u+2]),q=r.side===a.BackSide?c.intersectTriangle(i,g,h,!0):c.intersectTriangle(h,g,i,r.side!==a.DoubleSide),
-null!==q&&(q.applyMatrix4(f.matrixWorld),t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:null,faceIndex:null,object:f}));else{D=n.attributes.position.array;F=n.attributes.position.array.length;for(n=0;n<F;n+=3)t=n,q=n+1,u=n+2,h.set(D[3*t],D[3*t+1],D[3*t+2]),g.set(D[3*q],D[3*q+1],D[3*q+2]),i.set(D[3*u],D[3*u+1],D[3*u+2]),q=r.side===a.BackSide?c.intersectTriangle(i,g,h,!0):c.intersectTriangle(h,g,i,r.side!==a.DoubleSide),null!==q&&(q.applyMatrix4(f.matrixWorld),
-t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:null,faceIndex:null,object:f}))}}else if(n instanceof a.Geometry){B=f.material instanceof a.MeshFaceMaterial;D=!0===B?f.material.materials:null;w=m.precision;z=n.vertices;x=0;for(F=n.faces.length;x<F;x++)A=n.faces[x],r=!0===B?D[A.materialIndex]:f.material,void 0!==r&&(t=z[A.a],q=z[A.b],u=z[A.c],q=r.side===a.BackSide?c.intersectTriangle(u,q,t,!0):c.intersectTriangle(t,q,u,r.side!==a.DoubleSide),null!==q&&(q.applyMatrix4(f.matrixWorld),
-t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:A,faceIndex:x,object:f})))}}else if(f instanceof a.Line){w=m.linePrecision;r=w*w;n=f.geometry;null===n.boundingSphere&&n.computeBoundingSphere();b.copy(n.boundingSphere);b.applyMatrix4(f.matrixWorld);if(!1===m.ray.isIntersectionSphere(b))return s;e.getInverse(f.matrixWorld);c.copy(m.ray).applyMatrix4(e);if(n instanceof a.Geometry){z=n.vertices;w=z.length;q=new a.Vector3;u=new a.Vector3;F=f.type===a.LineStrip?1:
-2;for(n=0;n<w-1;n+=F)c.distanceSqToSegment(z[n],z[n+1],u,q)>r||(t=c.origin.distanceTo(u),t<m.near||t>m.far||s.push({distance:t,point:q.clone().applyMatrix4(f.matrixWorld),face:null,faceIndex:null,object:f}))}}},m=function(a,b,c){for(var a=a.getDescendants(),d=0,e=a.length;d<e;d++)k(a[d],b,c)};a.Raycaster.prototype.precision=1E-4;a.Raycaster.prototype.linePrecision=1;a.Raycaster.prototype.set=function(a,b){this.ray.set(a,b)};a.Raycaster.prototype.intersectObject=function(a,b){var c=[];!0===b&&m(a,
-this,c);k(a,this,c);c.sort(f);return c};a.Raycaster.prototype.intersectObjects=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++)k(a[d],this,c),!0===b&&m(a[d],this,c);c.sort(f);return c}})(THREE);THREE.Object3D=function(){this.id=THREE.Object3DIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.parent=void 0;this.children=[];this.up=new THREE.Vector3(0,1,0);this.position=new THREE.Vector3;this.rotation=new THREE.Euler;this.quaternion=new THREE.Quaternion;this.scale=new THREE.Vector3(1,1,1);this.rotation._quaternion=this.quaternion;this.quaternion._euler=this.rotation;this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;
-this.visible=this.matrixWorldNeedsUpdate=this.matrixAutoUpdate=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this.userData={}};
-THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){console.warn("DEPRECATED: Object3D's .eulerOrder has been moved to Object3D's .rotation.order.");return this.rotation.order},set eulerOrder(a){console.warn("DEPRECATED: Object3D's .eulerOrder has been moved to Object3D's .rotation.order.");this.rotation.order=a},get useQuaternion(){console.warn("DEPRECATED: Object3D's .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(a){console.warn("DEPRECATED: Object3D's .useQuaternion has been removed. The library now uses quaternions by default.")},
-applyMatrix:function(){var a=new THREE.Matrix4;return function(b){this.matrix.multiplyMatrices(b,this.matrix);this.position.getPositionFromMatrix(this.matrix);this.scale.getScaleFromMatrix(this.matrix);a.extractRotation(this.matrix);this.quaternion.setFromRotationMatrix(a)}}(),setRotationFromAxisAngle:function(a,b){this.quaternion.setFromAxisAngle(a,b)},setRotationFromEuler:function(a){this.quaternion.setFromEuler(a,!0)},setRotationFromMatrix:function(a){this.quaternion.setFromRotationMatrix(a)},
-setRotationFromQuaternion:function(a){this.quaternion.copy(a)},rotateOnAxis:function(){var a=new THREE.Quaternion;return function(b,c){a.setFromAxisAngle(b,c);this.quaternion.multiply(a);return this}}(),rotateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.rotateOnAxis(a,
-b)}}(),translateOnAxis:function(){var a=new THREE.Vector3;return function(b,c){a.copy(b);a.applyQuaternion(this.quaternion);this.position.add(a.multiplyScalar(c));return this}}(),translate:function(a,b){console.warn("DEPRECATED: Object3D's .translate() has been removed. Use .translateOnAxis( axis, distance ) instead. Note args have been changed.");return this.translateOnAxis(b,a)},translateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.translateOnAxis(a,b)}}(),translateY:function(){var a=
-new THREE.Vector3(0,1,0);return function(b){return this.translateOnAxis(a,b)}}(),translateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.translateOnAxis(a,b)}}(),localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var a=new THREE.Matrix4;return function(b){return b.applyMatrix4(a.getInverse(this.matrixWorld))}}(),lookAt:function(){var a=new THREE.Matrix4;return function(b){a.lookAt(b,this.position,this.up);this.quaternion.setFromRotationMatrix(a)}}(),
-add:function(a){if(a===this)console.warn("THREE.Object3D.add: An object can't be added as a child of itself.");else if(a instanceof THREE.Object3D){void 0!==a.parent&&a.parent.remove(a);a.parent=this;a.dispatchEvent({type:"added"});this.children.push(a);for(var b=this;void 0!==b.parent;)b=b.parent;void 0!==b&&b instanceof THREE.Scene&&b.__addObject(a)}},remove:function(a){var b=this.children.indexOf(a);if(-1!==b){a.parent=void 0;a.dispatchEvent({type:"removed"});this.children.splice(b,1);for(b=this;void 0!==
-b.parent;)b=b.parent;void 0!==b&&b instanceof THREE.Scene&&b.__removeObject(a)}},traverse:function(a){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverse(a)},getObjectById:function(a,b){for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c];if(e.id===a||!0===b&&(e=e.getObjectById(a,b),void 0!==e))return e}},getObjectByName:function(a,b){for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c];if(e.name===a||!0===b&&(e=e.getObjectByName(a,b),void 0!==
-e))return e}},getChildByName:function(a,b){console.warn("DEPRECATED: Object3D's .getChildByName() has been renamed to .getObjectByName().");return this.getObjectByName(a,b)},getDescendants:function(a){void 0===a&&(a=[]);Array.prototype.push.apply(a,this.children);for(var b=0,c=this.children.length;b<c;b++)this.children[b].getDescendants(a);return a},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(a){!0===
-this.matrixAutoUpdate&&this.updateMatrix();if(!0===this.matrixWorldNeedsUpdate||!0===a)void 0===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)},clone:function(a,b){void 0===a&&(a=new THREE.Object3D);void 0===b&&(b=!0);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.quaternion.copy(this.quaternion);
-a.scale.copy(this.scale);a.renderDepth=this.renderDepth;a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);a.matrixWorld.copy(this.matrixWorld);a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;a.userData=JSON.parse(JSON.stringify(this.userData));if(!0===b)for(var c=0;c<this.children.length;c++)a.add(this.children[c].clone());
-return a}};THREE.EventDispatcher.prototype.apply(THREE.Object3D.prototype);THREE.Object3DIdCount=0;THREE.Projector=function(){function a(){if(i===m){var a=new THREE.RenderableVertex;k.push(a);m++;i++;return a}return k[i++]}function b(a,b){return a.z!==b.z?b.z-a.z:a.id!==b.id?a.id-b.id:0}function c(a,b){var c=0,d=1,e=a.z+a.w,f=b.z+b.w,h=-a.z+a.w,g=-b.z+b.w;if(0<=e&&0<=f&&0<=h&&0<=g)return!0;if(0>e&&0>f||0>h&&0>g)return!1;0>e?c=Math.max(c,e/(e-f)):0>f&&(d=Math.min(d,e/(e-f)));0>h?c=Math.max(c,h/(h-g)):0>g&&(d=Math.min(d,h/(h-g)));if(d<c)return!1;a.lerp(b,c);b.lerp(a,1-d);return!0}var d,e,f=[],h=
-0,g,i,k=[],m=0,l,p,s=[],t=0,n,r,q=[],u=0,w,z,B=[],D=0,x={objects:[],sprites:[],lights:[],elements:[]},F=new THREE.Vector3,A=new THREE.Vector4,O=new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)),C=new THREE.Box3,E=Array(3),I=new THREE.Matrix4,y=new THREE.Matrix4,v,G=new THREE.Matrix4,R=new THREE.Matrix3,J=new THREE.Matrix3,ba=new THREE.Vector3,oa=new THREE.Frustum,pa=new THREE.Vector4,N=new THREE.Vector4;this.projectVector=function(a,b){b.matrixWorldInverse.getInverse(b.matrixWorld);
-y.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);return a.applyProjection(y)};this.unprojectVector=function(a,b){b.projectionMatrixInverse.getInverse(b.projectionMatrix);y.multiplyMatrices(b.matrixWorld,b.projectionMatrixInverse);return a.applyProjection(y)};this.pickingRay=function(a,b){a.z=-1;var c=new THREE.Vector3(a.x,a.y,1);this.unprojectVector(a,b);this.unprojectVector(c,b);c.sub(a).normalize();return new THREE.Raycaster(a,c)};var M=function(a){if(e===h){var b=new THREE.RenderableObject;
-f.push(b);h++;e++;d=b}else d=f[e++];d.id=a.id;d.object=a;null!==a.renderDepth?d.z=a.renderDepth:(F.getPositionFromMatrix(a.matrixWorld),F.applyProjection(y),d.z=F.z);return d},Q=function(a){if(!1!==a.visible){a instanceof THREE.Light?x.lights.push(a):a instanceof THREE.Mesh||a instanceof THREE.Line?(!1===a.frustumCulled||!0===oa.intersectsObject(a))&&x.objects.push(M(a)):a instanceof THREE.Sprite&&x.sprites.push(M(a));for(var b=0,c=a.children.length;b<c;b++)Q(a.children[b])}};this.projectScene=function(d,
-f,h,m){var da=!1,F,M,ea,V,P,Z,U,ka,ta,ia,La,Ga;z=r=p=0;x.elements.length=0;!0===d.autoUpdate&&d.updateMatrixWorld();void 0===f.parent&&f.updateMatrixWorld();I.copy(f.matrixWorldInverse.getInverse(f.matrixWorld));y.multiplyMatrices(f.projectionMatrix,I);J.getNormalMatrix(I);oa.setFromMatrix(y);e=0;x.objects.length=0;x.sprites.length=0;x.lights.length=0;Q(d);!0===h&&x.objects.sort(b);d=0;for(h=x.objects.length;d<h;d++)if(U=x.objects[d].object,v=U.matrixWorld,i=0,U instanceof THREE.Mesh){ka=U.geometry;
-ea=ka.vertices;ta=ka.faces;ka=ka.faceVertexUvs;R.getNormalMatrix(v);La=U.material instanceof THREE.MeshFaceMaterial;Ga=!0===La?U.material:null;F=0;for(M=ea.length;F<M;F++){g=a();g.positionWorld.copy(ea[F]).applyMatrix4(v);g.positionScreen.copy(g.positionWorld).applyMatrix4(y);var fa=1/g.positionScreen.w;g.positionScreen.x*=fa;g.positionScreen.y*=fa;g.positionScreen.z*=fa;g.visible=!(-1>g.positionScreen.x||1<g.positionScreen.x||-1>g.positionScreen.y||1<g.positionScreen.y||-1>g.positionScreen.z||1<
-g.positionScreen.z)}ea=0;for(F=ta.length;ea<F;ea++)if(M=ta[ea],fa=!0===La?Ga.materials[M.materialIndex]:U.material,void 0!==fa&&(Z=fa.side,V=k[M.a],P=k[M.b],ia=k[M.c],E[0]=V.positionScreen,E[1]=P.positionScreen,E[2]=ia.positionScreen,!0===V.visible||!0===P.visible||!0===ia.visible||O.isIntersectionBox(C.setFromPoints(E))))if(da=0>(ia.positionScreen.x-V.positionScreen.x)*(P.positionScreen.y-V.positionScreen.y)-(ia.positionScreen.y-V.positionScreen.y)*(P.positionScreen.x-V.positionScreen.x),Z===THREE.DoubleSide||
-da===(Z===THREE.FrontSide)){if(p===t){var Da=new THREE.RenderableFace3;s.push(Da);t++;p++;l=Da}else l=s[p++];l.id=U.id;l.v1.copy(V);l.v2.copy(P);l.v3.copy(ia);l.normalModel.copy(M.normal);!1===da&&(Z===THREE.BackSide||Z===THREE.DoubleSide)&&l.normalModel.negate();l.normalModel.applyMatrix3(R).normalize();l.normalModelView.copy(l.normalModel).applyMatrix3(J);l.centroidModel.copy(M.centroid).applyMatrix4(v);ia=M.vertexNormals;V=0;for(P=Math.min(ia.length,3);V<P;V++)Da=l.vertexNormalsModel[V],Da.copy(ia[V]),
-!1===da&&(Z===THREE.BackSide||Z===THREE.DoubleSide)&&Da.negate(),Da.applyMatrix3(R).normalize(),l.vertexNormalsModelView[V].copy(Da).applyMatrix3(J);l.vertexNormalsLength=ia.length;da=0;for(V=Math.min(ka.length,3);da<V;da++)if(ia=ka[da][ea],void 0!==ia){P=0;for(Z=ia.length;P<Z;P++)l.uvs[da][P]=ia[P]}l.color=M.color;l.material=fa;ba.copy(l.centroidModel).applyProjection(y);l.z=ba.z;x.elements.push(l)}}else if(U instanceof THREE.Line){G.multiplyMatrices(y,v);ea=U.geometry.vertices;V=a();V.positionScreen.copy(ea[0]).applyMatrix4(G);
-ta=U.type===THREE.LinePieces?2:1;F=1;for(M=ea.length;F<M;F++)V=a(),V.positionScreen.copy(ea[F]).applyMatrix4(G),0<(F+1)%ta||(P=k[i-2],pa.copy(V.positionScreen),N.copy(P.positionScreen),!0===c(pa,N)&&(pa.multiplyScalar(1/pa.w),N.multiplyScalar(1/N.w),r===u?(ka=new THREE.RenderableLine,q.push(ka),u++,r++,n=ka):n=q[r++],n.id=U.id,n.v1.positionScreen.copy(pa),n.v2.positionScreen.copy(N),n.z=Math.max(pa.z,N.z),n.material=U.material,U.material.vertexColors===THREE.VertexColors&&(n.vertexColors[0].copy(U.geometry.colors[F]),
-n.vertexColors[1].copy(U.geometry.colors[F-1])),x.elements.push(n)))}d=0;for(h=x.sprites.length;d<h;d++)U=x.sprites[d].object,v=U.matrixWorld,U instanceof THREE.Sprite&&(A.set(v.elements[12],v.elements[13],v.elements[14],1),A.applyMatrix4(y),fa=1/A.w,A.z*=fa,-1<A.z&&1>A.z&&(z===D?(ta=new THREE.RenderableSprite,B.push(ta),D++,z++,w=ta):w=B[z++],w.id=U.id,w.x=A.x*fa,w.y=A.y*fa,w.z=A.z,w.object=U,w.rotation=U.rotation,w.scale.x=U.scale.x*Math.abs(w.x-(A.x+f.projectionMatrix.elements[0])/(A.w+f.projectionMatrix.elements[12])),
-w.scale.y=U.scale.y*Math.abs(w.y-(A.y+f.projectionMatrix.elements[5])/(A.w+f.projectionMatrix.elements[13])),w.material=U.material,x.elements.push(w)));!0===m&&x.elements.sort(b);return x}};THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0;this.centroid=new THREE.Vector3};
-THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.centroid.copy(this.centroid);a.materialIndex=this.materialIndex;var b,c;b=0;for(c=this.vertexNormals.length;b<c;b++)a.vertexNormals[b]=this.vertexNormals[b].clone();b=0;for(c=this.vertexColors.length;b<c;b++)a.vertexColors[b]=this.vertexColors[b].clone();b=0;for(c=this.vertexTangents.length;b<c;b++)a.vertexTangents[b]=this.vertexTangents[b].clone();
-return a}};THREE.Face4=function(a,b,c,d,e,f,h){console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead.");return new THREE.Face3(a,b,c,e,f,h)};THREE.Geometry=function(){this.id=THREE.GeometryIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.dynamic=!0;this.buffersNeedUpdate=this.lineDistancesNeedUpdate=this.colorsNeedUpdate=this.tangentsNeedUpdate=this.normalsNeedUpdate=this.uvsNeedUpdate=
-this.elementsNeedUpdate=this.verticesNeedUpdate=!1};
-THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for(var b=(new THREE.Matrix3).getNormalMatrix(a),c=0,d=this.vertices.length;c<d;c++)this.vertices[c].applyMatrix4(a);c=0;for(d=this.faces.length;c<d;c++){var e=this.faces[c];e.normal.applyMatrix3(b).normalize();for(var f=0,h=e.vertexNormals.length;f<h;f++)e.vertexNormals[f].applyMatrix3(b).normalize();e.centroid.applyMatrix4(a)}this.boundingBox instanceof THREE.Box3&&this.computeBoundingBox();this.boundingSphere instanceof
-THREE.Sphere&&this.computeBoundingSphere()},computeCentroids:function(){var a,b,c;a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],c.centroid.set(0,0,0),c.centroid.add(this.vertices[c.a]),c.centroid.add(this.vertices[c.b]),c.centroid.add(this.vertices[c.c]),c.centroid.divideScalar(3)},computeFaceNormals:function(){for(var a=new THREE.Vector3,b=new THREE.Vector3,c=0,d=this.faces.length;c<d;c++){var e=this.faces[c],f=this.vertices[e.a],h=this.vertices[e.b];a.subVectors(this.vertices[e.c],h);b.subVectors(f,
-h);a.cross(b);a.normalize();e.normal.copy(a)}},computeVertexNormals:function(a){var b,c,d,e;if(void 0===this.__tmpVertices){e=this.__tmpVertices=Array(this.vertices.length);b=0;for(c=this.vertices.length;b<c;b++)e[b]=new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d.vertexNormals=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3]}else{e=this.__tmpVertices;b=0;for(c=this.vertices.length;b<c;b++)e[b].set(0,0,0)}if(a){var f,h,g=new THREE.Vector3,i=new THREE.Vector3;new THREE.Vector3;
-new THREE.Vector3;new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],a=this.vertices[d.a],f=this.vertices[d.b],h=this.vertices[d.c],g.subVectors(h,f),i.subVectors(a,f),g.cross(i),e[d.a].add(g),e[d.b].add(g),e[d.c].add(g)}else{b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],e[d.a].add(d.normal),e[d.b].add(d.normal),e[d.c].add(d.normal)}b=0;for(c=this.vertices.length;b<c;b++)e[b].normalize();b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d.vertexNormals[0].copy(e[d.a]),d.vertexNormals[1].copy(e[d.b]),
-d.vertexNormals[2].copy(e[d.c])},computeMorphNormals:function(){var a,b,c,d,e;c=0;for(d=this.faces.length;c<d;c++){e=this.faces[c];e.__originalFaceNormal?e.__originalFaceNormal.copy(e.normal):e.__originalFaceNormal=e.normal.clone();e.__originalVertexNormals||(e.__originalVertexNormals=[]);a=0;for(b=e.vertexNormals.length;a<b;a++)e.__originalVertexNormals[a]?e.__originalVertexNormals[a].copy(e.vertexNormals[a]):e.__originalVertexNormals[a]=e.vertexNormals[a].clone()}var f=new THREE.Geometry;f.faces=
-this.faces;a=0;for(b=this.morphTargets.length;a<b;a++){if(!this.morphNormals[a]){this.morphNormals[a]={};this.morphNormals[a].faceNormals=[];this.morphNormals[a].vertexNormals=[];e=this.morphNormals[a].faceNormals;var h=this.morphNormals[a].vertexNormals,g,i;c=0;for(d=this.faces.length;c<d;c++)g=new THREE.Vector3,i={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},e.push(g),h.push(i)}h=this.morphNormals[a];f.vertices=this.morphTargets[a].vertices;f.computeFaceNormals();f.computeVertexNormals();
-c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],g=h.faceNormals[c],i=h.vertexNormals[c],g.copy(e.normal),i.a.copy(e.vertexNormals[0]),i.b.copy(e.vertexNormals[1]),i.c.copy(e.vertexNormals[2])}c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],e.normal=e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){var a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n,r,q=[],u=[];c=new THREE.Vector3;var w=new THREE.Vector3,z=new THREE.Vector3,B=new THREE.Vector3,D=new THREE.Vector3;
-a=0;for(b=this.vertices.length;a<b;a++)q[a]=new THREE.Vector3,u[a]=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)e=this.faces[a],f=this.faceVertexUvs[0][a],d=e.a,r=e.b,e=e.c,h=this.vertices[d],g=this.vertices[r],i=this.vertices[e],k=f[0],m=f[1],l=f[2],f=g.x-h.x,p=i.x-h.x,s=g.y-h.y,t=i.y-h.y,g=g.z-h.z,h=i.z-h.z,i=m.x-k.x,n=l.x-k.x,m=m.y-k.y,k=l.y-k.y,l=1/(i*k-n*m),c.set((k*f-m*p)*l,(k*s-m*t)*l,(k*g-m*h)*l),w.set((i*p-n*f)*l,(i*t-n*s)*l,(i*h-n*g)*l),q[d].add(c),q[r].add(c),q[e].add(c),u[d].add(w),
-u[r].add(w),u[e].add(w);w=["a","b","c","d"];a=0;for(b=this.faces.length;a<b;a++){e=this.faces[a];for(c=0;c<Math.min(e.vertexNormals.length,3);c++)D.copy(e.vertexNormals[c]),d=e[w[c]],r=q[d],z.copy(r),z.sub(D.multiplyScalar(D.dot(r))).normalize(),B.crossVectors(e.vertexNormals[c],r),d=B.dot(u[d]),d=0>d?-1:1,e.vertexTangents[c]=new THREE.Vector4(z.x,z.y,z.z,d)}this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;c<d;c++)0<c&&(a+=b[c].distanceTo(b[c-1])),this.lineDistances[c]=
-a},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);this.boundingSphere.setFromPoints(this.vertices)},mergeVertices:function(){var a={},b=[],c=[],d,e=Math.pow(10,4),f,h;this.__tmpVertices=void 0;f=0;for(h=this.vertices.length;f<h;f++)d=this.vertices[f],d=Math.round(d.x*e)+"_"+Math.round(d.y*e)+"_"+Math.round(d.z*
-e),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];a=[];f=0;for(h=this.faces.length;f<h;f++){e=this.faces[f];e.a=c[e.a];e.b=c[e.b];e.c=c[e.c];e=[e.a,e.b,e.c];for(d=0;3>d;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}}for(f=a.length-1;0<=f;f--){e=a[f];this.faces.splice(e,1);c=0;for(h=this.faceVertexUvs.length;c<h;c++)this.faceVertexUvs[c].splice(e,1)}f=this.vertices.length-b.length;this.vertices=b;return f},clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=
-b.length;c<d;c++)a.vertices.push(b[c].clone());b=this.faces;c=0;for(d=b.length;c<d;c++)a.faces.push(b[c].clone());b=this.faceVertexUvs[0];c=0;for(d=b.length;c<d;c++){for(var e=b[c],f=[],h=0,g=e.length;h<g;h++)f.push(new THREE.Vector2(e[h].x,e[h].y));a.faceVertexUvs[0].push(f)}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype);THREE.GeometryIdCount=0;THREE.BufferGeometry=function(){this.id=THREE.GeometryIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.attributes={};this.dynamic=!0;this.offsets=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.morphTargets=[]};
-THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addAttribute:function(a,b,c,d){this.attributes[a]={itemSize:d,array:new b(c*d)}},applyMatrix:function(a){var b,c;this.attributes.position&&(b=this.attributes.position.array);this.attributes.normal&&(c=this.attributes.normal.array);void 0!==b&&(a.multiplyVector3Array(b),this.verticesNeedUpdate=!0);void 0!==c&&((new THREE.Matrix3).getNormalMatrix(a).multiplyVector3Array(c),this.normalizeNormals(),this.normalsNeedUpdate=!0)},computeBoundingBox:function(){null===
-this.boundingBox&&(this.boundingBox=new THREE.Box3);var a=this.attributes.position.array;if(a){var b=this.boundingBox,c,d,e;3<=a.length&&(b.min.x=b.max.x=a[0],b.min.y=b.max.y=a[1],b.min.z=b.max.z=a[2]);for(var f=3,h=a.length;f<h;f+=3)c=a[f],d=a[f+1],e=a[f+2],c<b.min.x?b.min.x=c:c>b.max.x&&(b.max.x=c),d<b.min.y?b.min.y=d:d>b.max.y&&(b.max.y=d),e<b.min.z?b.min.z=e:e>b.max.z&&(b.max.z=e)}if(void 0===a||0===a.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0)},computeBoundingSphere:function(){var a=
-new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;if(c){for(var d=this.boundingSphere.center,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),a.addPoint(b);a.center(d);for(var h=0,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),h=Math.max(h,d.distanceToSquared(b));this.boundingSphere.radius=Math.sqrt(h)}}}(),computeVertexNormals:function(){if(this.attributes.position){var a,b,c,d;a=this.attributes.position.array.length;
-if(void 0===this.attributes.normal)this.attributes.normal={itemSize:3,array:new Float32Array(a)};else{a=0;for(b=this.attributes.normal.array.length;a<b;a++)this.attributes.normal.array[a]=0}var e=this.attributes.position.array,f=this.attributes.normal.array,h,g,i,k,m,l,p=new THREE.Vector3,s=new THREE.Vector3,t=new THREE.Vector3,n=new THREE.Vector3,r=new THREE.Vector3;if(this.attributes.index){var q=this.attributes.index.array,u=this.offsets;c=0;for(d=u.length;c<d;++c){b=u[c].start;h=u[c].count;var w=
-u[c].index;a=b;for(b+=h;a<b;a+=3)h=w+q[a],g=w+q[a+1],i=w+q[a+2],k=e[3*h],m=e[3*h+1],l=e[3*h+2],p.set(k,m,l),k=e[3*g],m=e[3*g+1],l=e[3*g+2],s.set(k,m,l),k=e[3*i],m=e[3*i+1],l=e[3*i+2],t.set(k,m,l),n.subVectors(t,s),r.subVectors(p,s),n.cross(r),f[3*h]+=n.x,f[3*h+1]+=n.y,f[3*h+2]+=n.z,f[3*g]+=n.x,f[3*g+1]+=n.y,f[3*g+2]+=n.z,f[3*i]+=n.x,f[3*i+1]+=n.y,f[3*i+2]+=n.z}}else{a=0;for(b=e.length;a<b;a+=9)k=e[a],m=e[a+1],l=e[a+2],p.set(k,m,l),k=e[a+3],m=e[a+4],l=e[a+5],s.set(k,m,l),k=e[a+6],m=e[a+7],l=e[a+8],
-t.set(k,m,l),n.subVectors(t,s),r.subVectors(p,s),n.cross(r),f[a]=n.x,f[a+1]=n.y,f[a+2]=n.z,f[a+3]=n.x,f[a+4]=n.y,f[a+5]=n.z,f[a+6]=n.x,f[a+7]=n.y,f[a+8]=n.z}this.normalizeNormals();this.normalsNeedUpdate=!0}},normalizeNormals:function(){for(var a=this.attributes.normal.array,b,c,d,e=0,f=a.length;e<f;e+=3)b=a[e],c=a[e+1],d=a[e+2],b=1/Math.sqrt(b*b+c*c+d*d),a[e]*=b,a[e+1]*=b,a[e+2]*=b},computeTangents:function(){function a(a){oa.x=d[3*a];oa.y=d[3*a+1];oa.z=d[3*a+2];pa.copy(oa);M=g[a];J.copy(M);J.sub(oa.multiplyScalar(oa.dot(M))).normalize();
-ba.crossVectors(pa,M);Q=ba.dot(i[a]);N=0>Q?-1:1;h[4*a]=J.x;h[4*a+1]=J.y;h[4*a+2]=J.z;h[4*a+3]=N}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||void 0===this.attributes.uv)console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var b=this.attributes.index.array,c=this.attributes.position.array,d=this.attributes.normal.array,e=this.attributes.uv.array,f=c.length/3;void 0===this.attributes.tangent&&
-(this.attributes.tangent={itemSize:4,array:new Float32Array(4*f)});for(var h=this.attributes.tangent.array,g=[],i=[],k=0;k<f;k++)g[k]=new THREE.Vector3,i[k]=new THREE.Vector3;var m,l,p,s,t,n,r,q,u,w,z,B,D,x,F,f=new THREE.Vector3,k=new THREE.Vector3,A,O,C,E,I,y,v,G=this.offsets;C=0;for(E=G.length;C<E;++C){O=G[C].start;I=G[C].count;var R=G[C].index;A=O;for(O+=I;A<O;A+=3)I=R+b[A],y=R+b[A+1],v=R+b[A+2],m=c[3*I],l=c[3*I+1],p=c[3*I+2],s=c[3*y],t=c[3*y+1],n=c[3*y+2],r=c[3*v],q=c[3*v+1],u=c[3*v+2],w=e[2*
-I],z=e[2*I+1],B=e[2*y],D=e[2*y+1],x=e[2*v],F=e[2*v+1],s-=m,m=r-m,t-=l,l=q-l,n-=p,p=u-p,B-=w,w=x-w,D-=z,z=F-z,F=1/(B*z-w*D),f.set((z*s-D*m)*F,(z*t-D*l)*F,(z*n-D*p)*F),k.set((B*m-w*s)*F,(B*l-w*t)*F,(B*p-w*n)*F),g[I].add(f),g[y].add(f),g[v].add(f),i[I].add(k),i[y].add(k),i[v].add(k)}var J=new THREE.Vector3,ba=new THREE.Vector3,oa=new THREE.Vector3,pa=new THREE.Vector3,N,M,Q;C=0;for(E=G.length;C<E;++C){O=G[C].start;I=G[C].count;R=G[C].index;A=O;for(O+=I;A<O;A+=3)I=R+b[A],y=R+b[A+1],v=R+b[A+2],a(I),a(y),
-a(v)}this.tangentsNeedUpdate=this.hasTangents=!0}},clone:function(){var a=new THREE.BufferGeometry,b=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array],c;for(c in this.attributes){for(var d=this.attributes[c],e=d.array,f={itemSize:d.itemSize,numItems:d.numItems,array:null},d=0,h=b.length;d<h;d++){var g=b[d];if(e instanceof g){f.array=new g(e);break}}a.attributes[c]=f}d=0;for(h=this.offsets.length;d<h;d++)b=this.offsets[d],a.offsets.push({start:b.start,
-index:b.index,count:b.count});return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype);THREE.Camera=function(){THREE.Object3D.call(this);this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=new THREE.Matrix4;this.projectionMatrixInverse=new THREE.Matrix4};THREE.Camera.prototype=Object.create(THREE.Object3D.prototype);THREE.Camera.prototype.lookAt=function(){var a=new THREE.Matrix4;return function(b){a.lookAt(this.position,b,this.up);this.quaternion.setFromRotationMatrix(a)}}();
-THREE.Camera.prototype.clone=function(a){void 0===a&&(a=new THREE.Camera);THREE.Object3D.prototype.clone.call(this,a);a.matrixWorldInverse.copy(this.matrixWorldInverse);a.projectionMatrix.copy(this.projectionMatrix);a.projectionMatrixInverse.copy(this.projectionMatrixInverse);return a};THREE.OrthographicCamera=function(a,b,c,d,e,f){THREE.Camera.call(this);this.left=a;this.right=b;this.top=c;this.bottom=d;this.near=void 0!==e?e:0.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()};THREE.OrthographicCamera.prototype=Object.create(THREE.Camera.prototype);THREE.OrthographicCamera.prototype.updateProjectionMatrix=function(){this.projectionMatrix.makeOrthographic(this.left,this.right,this.top,this.bottom,this.near,this.far)};
-THREE.OrthographicCamera.prototype.clone=function(){var a=new THREE.OrthographicCamera;THREE.Camera.prototype.clone.call(this,a);a.left=this.left;a.right=this.right;a.top=this.top;a.bottom=this.bottom;a.near=this.near;a.far=this.far;return a};THREE.PerspectiveCamera=function(a,b,c,d){THREE.Camera.call(this);this.fov=void 0!==a?a:50;this.aspect=void 0!==b?b:1;this.near=void 0!==c?c:0.1;this.far=void 0!==d?d:2E3;this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype=Object.create(THREE.Camera.prototype);THREE.PerspectiveCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);this.fov=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.setViewOffset=function(a,b,c,d,e,f){this.fullWidth=a;this.fullHeight=b;this.x=c;this.y=d;this.width=e;this.height=f;this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.updateProjectionMatrix=function(){if(this.fullWidth){var a=this.fullWidth/this.fullHeight,b=Math.tan(THREE.Math.degToRad(0.5*this.fov))*this.near,c=-b,d=a*c,a=Math.abs(a*b-d),c=Math.abs(b-c);this.projectionMatrix.makeFrustum(d+this.x*a/this.fullWidth,d+(this.x+this.width)*a/this.fullWidth,b-(this.y+this.height)*c/this.fullHeight,b-this.y*c/this.fullHeight,this.near,this.far)}else this.projectionMatrix.makePerspective(this.fov,this.aspect,this.near,this.far)};
-THREE.PerspectiveCamera.prototype.clone=function(){var a=new THREE.PerspectiveCamera;THREE.Camera.prototype.clone.call(this,a);a.fov=this.fov;a.aspect=this.aspect;a.near=this.near;a.far=this.far;return a};THREE.Light=function(a){THREE.Object3D.call(this);this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);THREE.Light.prototype.clone=function(a){void 0===a&&(a=new THREE.Light);THREE.Object3D.prototype.clone.call(this,a);a.color.copy(this.color);return a};THREE.AmbientLight=function(a){THREE.Light.call(this,a)};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);THREE.AmbientLight.prototype.clone=function(){var a=new THREE.AmbientLight;THREE.Light.prototype.clone.call(this,a);return a};THREE.AreaLight=function(a,b){THREE.Light.call(this,a);this.normal=new THREE.Vector3(0,-1,0);this.right=new THREE.Vector3(1,0,0);this.intensity=void 0!==b?b:1;this.height=this.width=1;this.constantAttenuation=1.5;this.linearAttenuation=0.5;this.quadraticAttenuation=0.1};THREE.AreaLight.prototype=Object.create(THREE.Light.prototype);THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraLeft=-500;this.shadowCameraTop=this.shadowCameraRight=500;this.shadowCameraBottom=-500;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=0.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowCascade=!1;this.shadowCascadeOffset=new THREE.Vector3(0,
-0,-1E3);this.shadowCascadeCount=2;this.shadowCascadeBias=[0,0,0];this.shadowCascadeWidth=[512,512,512];this.shadowCascadeHeight=[512,512,512];this.shadowCascadeNearZ=[-1,0.99,0.998];this.shadowCascadeFarZ=[0.99,0.998,1];this.shadowCascadeArray=[];this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.DirectionalLight.prototype=Object.create(THREE.Light.prototype);
-THREE.DirectionalLight.prototype.clone=function(){var a=new THREE.DirectionalLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;return a};THREE.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.position.set(0,100,0);this.groundColor=new THREE.Color(b);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);THREE.HemisphereLight.prototype.clone=function(){var a=new THREE.HemisphereLight;THREE.Light.prototype.clone.call(this,a);a.groundColor.copy(this.groundColor);a.intensity=this.intensity;return a};THREE.PointLight=function(a,b,c){THREE.Light.call(this,a);this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);THREE.PointLight.prototype.clone=function(){var a=new THREE.PointLight;THREE.Light.prototype.clone.call(this,a);a.intensity=this.intensity;a.distance=this.distance;return a};THREE.SpotLight=function(a,b,c,d,e){THREE.Light.call(this,a);this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.exponent=void 0!==e?e:10;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraFov=50;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=0.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowMatrix=this.shadowCamera=this.shadowMapSize=
-this.shadowMap=null};THREE.SpotLight.prototype=Object.create(THREE.Light.prototype);THREE.SpotLight.prototype.clone=function(){var a=new THREE.SpotLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.distance=this.distance;a.angle=this.angle;a.exponent=this.exponent;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;return a};THREE.Loader=function(a){this.statusDomElement=(this.showStatus=a)?THREE.Loader.prototype.addStatusElement():null;this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}};
-THREE.Loader.prototype={constructor:THREE.Loader,crossOrigin:"anonymous",addStatusElement:function(){var a=document.createElement("div");a.style.position="absolute";a.style.right="0px";a.style.top="0px";a.style.fontSize="0.8em";a.style.textAlign="left";a.style.background="rgba(0,0,0,0.25)";a.style.color="#fff";a.style.width="120px";a.style.padding="0.5em 0.5em 0.5em 0.5em";a.style.zIndex=1E3;a.innerHTML="Loading ...";return a},updateProgress:function(a){var b="Loaded ",b=a.total?b+((100*a.loaded/
-a.total).toFixed(0)+"%"):b+((a.loaded/1E3).toFixed(2)+" KB");this.statusDomElement.innerHTML=b},extractUrlBase:function(a){a=a.split("/");a.pop();return(1>a.length?".":a.join("/"))+"/"},initMaterials:function(a,b){for(var c=[],d=0;d<a.length;++d)c[d]=THREE.Loader.prototype.createMaterial(a[d],b);return c},needsTangents:function(a){for(var b=0,c=a.length;b<c;b++)if(a[b]instanceof THREE.ShaderMaterial)return!0;return!1},createMaterial:function(a,b){function c(a){a=Math.log(a)/Math.LN2;return Math.floor(a)==
-a}function d(a){a=Math.log(a)/Math.LN2;return Math.pow(2,Math.round(a))}function e(a,e,f,g,i,k,r){var q=/\.dds$/i.test(f),u=b+"/"+f;if(q){var w=THREE.ImageUtils.loadCompressedTexture(u);a[e]=w}else w=document.createElement("canvas"),a[e]=new THREE.Texture(w);a[e].sourceFile=f;g&&(a[e].repeat.set(g[0],g[1]),1!==g[0]&&(a[e].wrapS=THREE.RepeatWrapping),1!==g[1]&&(a[e].wrapT=THREE.RepeatWrapping));i&&a[e].offset.set(i[0],i[1]);k&&(f={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==
-f[k[0]]&&(a[e].wrapS=f[k[0]]),void 0!==f[k[1]]&&(a[e].wrapT=f[k[1]]));r&&(a[e].anisotropy=r);if(!q){var z=a[e],a=new Image;a.onload=function(){if(!c(this.width)||!c(this.height)){var a=d(this.width),b=d(this.height);z.image.width=a;z.image.height=b;z.image.getContext("2d").drawImage(this,0,0,a,b)}else z.image=this;z.needsUpdate=!0};a.crossOrigin=h.crossOrigin;a.src=u}}function f(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var h=this,g="MeshLambertMaterial",i={color:15658734,opacity:1,map:null,
-lightMap:null,normalMap:null,bumpMap:null,wireframe:!1};if(a.shading){var k=a.shading.toLowerCase();"phong"===k?g="MeshPhongMaterial":"basic"===k&&(g="MeshBasicMaterial")}void 0!==a.blending&&void 0!==THREE[a.blending]&&(i.blending=THREE[a.blending]);if(void 0!==a.transparent||1>a.opacity)i.transparent=a.transparent;void 0!==a.depthTest&&(i.depthTest=a.depthTest);void 0!==a.depthWrite&&(i.depthWrite=a.depthWrite);void 0!==a.visible&&(i.visible=a.visible);void 0!==a.flipSided&&(i.side=THREE.BackSide);
-void 0!==a.doubleSided&&(i.side=THREE.DoubleSide);void 0!==a.wireframe&&(i.wireframe=a.wireframe);void 0!==a.vertexColors&&("face"===a.vertexColors?i.vertexColors=THREE.FaceColors:a.vertexColors&&(i.vertexColors=THREE.VertexColors));a.colorDiffuse?i.color=f(a.colorDiffuse):a.DbgColor&&(i.color=a.DbgColor);a.colorSpecular&&(i.specular=f(a.colorSpecular));a.colorAmbient&&(i.ambient=f(a.colorAmbient));a.transparency&&(i.opacity=a.transparency);a.specularCoef&&(i.shininess=a.specularCoef);a.mapDiffuse&&
-b&&e(i,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap,a.mapDiffuseAnisotropy);a.mapLight&&b&&e(i,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&e(i,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&e(i,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&e(i,"specularMap",a.mapSpecular,a.mapSpecularRepeat,
-a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapBumpScale&&(i.bumpScale=a.mapBumpScale);a.mapNormal?(g=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(g.uniforms),k.tNormal.value=i.normalMap,a.mapNormalFactor&&k.uNormalScale.value.set(a.mapNormalFactor,a.mapNormalFactor),i.map&&(k.tDiffuse.value=i.map,k.enableDiffuse.value=!0),i.specularMap&&(k.tSpecular.value=i.specularMap,k.enableSpecular.value=!0),i.lightMap&&(k.tAO.value=i.lightMap,k.enableAO.value=!0),k.uDiffuseColor.value.setHex(i.color),
-k.uSpecularColor.value.setHex(i.specular),k.uAmbientColor.value.setHex(i.ambient),k.uShininess.value=i.shininess,void 0!==i.opacity&&(k.uOpacity.value=i.opacity),g=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:k,lights:!0,fog:!0}),i.transparent&&(g.transparent=!0)):g=new THREE[g](i);void 0!==a.DbgName&&(g.name=a.DbgName);return g}};THREE.XHRLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(a,b,c,d){var e=this,f=new XMLHttpRequest;void 0!==b&&f.addEventListener("load",function(c){b(c.target.responseText);e.manager.itemEnd(a)},!1);void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1);void 0!==d&&f.addEventListener("error",function(a){d(a)},!1);void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin);f.open("GET",a,!0);f.send(null);e.manager.itemStart(a)},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.ImageLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d){var e=this,f=document.createElement("img");void 0!==b&&f.addEventListener("load",function(){e.manager.itemEnd(a);b(this)},!1);void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1);void 0!==d&&f.addEventListener("error",function(a){d(a)},!1);void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin);f.src=a;e.manager.itemStart(a);return f},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.JSONLoader=function(a){THREE.Loader.call(this,a);this.withCredentials=!1};THREE.JSONLoader.prototype=Object.create(THREE.Loader.prototype);THREE.JSONLoader.prototype.load=function(a,b,c){c=c&&"string"===typeof c?c:this.extractUrlBase(a);this.onLoadStart();this.loadAjaxJSON(this,a,b,c)};
-THREE.JSONLoader.prototype.loadAjaxJSON=function(a,b,c,d,e){var f=new XMLHttpRequest,h=0;f.onreadystatechange=function(){if(f.readyState===f.DONE)if(200===f.status||0===f.status){if(f.responseText){var g=JSON.parse(f.responseText),g=a.parse(g,d);c(g.geometry,g.materials)}else console.warn("THREE.JSONLoader: ["+b+"] seems to be unreachable or file there is empty");a.onLoadComplete()}else console.error("THREE.JSONLoader: Couldn't load ["+b+"] ["+f.status+"]");else f.readyState===f.LOADING?e&&(0===h&&
-(h=f.getResponseHeader("Content-Length")),e({total:h,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&void 0!==e&&(h=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
-THREE.JSONLoader.prototype.parse=function(a,b){var c=new THREE.Geometry,d=void 0!==a.scale?1/a.scale:1,e,f,h,g,i,k,m,l,p,s,t,n,r,q,u=a.faces;p=a.vertices;var w=a.normals,z=a.colors,B=0;if(void 0!==a.uvs){for(e=0;e<a.uvs.length;e++)a.uvs[e].length&&B++;for(e=0;e<B;e++)c.faceVertexUvs[e]=[]}g=0;for(i=p.length;g<i;)k=new THREE.Vector3,k.x=p[g++]*d,k.y=p[g++]*d,k.z=p[g++]*d,c.vertices.push(k);g=0;for(i=u.length;g<i;)if(p=u[g++],s=p&1,h=p&2,e=p&8,m=p&16,t=p&32,k=p&64,p&=128,s){s=new THREE.Face3;s.a=u[g];
-s.b=u[g+1];s.c=u[g+3];n=new THREE.Face3;n.a=u[g+1];n.b=u[g+2];n.c=u[g+3];g+=4;h&&(h=u[g++],s.materialIndex=h,n.materialIndex=h);h=c.faces.length;if(e)for(e=0;e<B;e++){r=a.uvs[e];c.faceVertexUvs[e][h]=[];c.faceVertexUvs[e][h+1]=[];for(f=0;4>f;f++)l=u[g++],q=r[2*l],l=r[2*l+1],q=new THREE.Vector2(q,l),2!==f&&c.faceVertexUvs[e][h].push(q),0!==f&&c.faceVertexUvs[e][h+1].push(q)}m&&(m=3*u[g++],s.normal.set(w[m++],w[m++],w[m]),n.normal.copy(s.normal));if(t)for(e=0;4>e;e++)m=3*u[g++],t=new THREE.Vector3(w[m++],
-w[m++],w[m]),2!==e&&s.vertexNormals.push(t),0!==e&&n.vertexNormals.push(t);k&&(k=u[g++],k=z[k],s.color.setHex(k),n.color.setHex(k));if(p)for(e=0;4>e;e++)k=u[g++],k=z[k],2!==e&&s.vertexColors.push(new THREE.Color(k)),0!==e&&n.vertexColors.push(new THREE.Color(k));c.faces.push(s);c.faces.push(n)}else{s=new THREE.Face3;s.a=u[g++];s.b=u[g++];s.c=u[g++];h&&(h=u[g++],s.materialIndex=h);h=c.faces.length;if(e)for(e=0;e<B;e++){r=a.uvs[e];c.faceVertexUvs[e][h]=[];for(f=0;3>f;f++)l=u[g++],q=r[2*l],l=r[2*l+1],
-q=new THREE.Vector2(q,l),c.faceVertexUvs[e][h].push(q)}m&&(m=3*u[g++],s.normal.set(w[m++],w[m++],w[m]));if(t)for(e=0;3>e;e++)m=3*u[g++],t=new THREE.Vector3(w[m++],w[m++],w[m]),s.vertexNormals.push(t);k&&(k=u[g++],s.color.setHex(z[k]));if(p)for(e=0;3>e;e++)k=u[g++],s.vertexColors.push(new THREE.Color(z[k]));c.faces.push(s)}if(a.skinWeights){g=0;for(i=a.skinWeights.length;g<i;g+=2)u=a.skinWeights[g],w=a.skinWeights[g+1],c.skinWeights.push(new THREE.Vector4(u,w,0,0))}if(a.skinIndices){g=0;for(i=a.skinIndices.length;g<
-i;g+=2)u=a.skinIndices[g],w=a.skinIndices[g+1],c.skinIndices.push(new THREE.Vector4(u,w,0,0))}c.bones=a.bones;c.animation=a.animation;c.animations=a.animations;if(void 0!==a.morphTargets){g=0;for(i=a.morphTargets.length;g<i;g++){c.morphTargets[g]={};c.morphTargets[g].name=a.morphTargets[g].name;c.morphTargets[g].vertices=[];z=c.morphTargets[g].vertices;B=a.morphTargets[g].vertices;u=0;for(w=B.length;u<w;u+=3)p=new THREE.Vector3,p.x=B[u]*d,p.y=B[u+1]*d,p.z=B[u+2]*d,z.push(p)}}if(void 0!==a.morphColors){g=
-0;for(i=a.morphColors.length;g<i;g++){c.morphColors[g]={};c.morphColors[g].name=a.morphColors[g].name;c.morphColors[g].colors=[];w=c.morphColors[g].colors;z=a.morphColors[g].colors;d=0;for(u=z.length;d<u;d+=3)B=new THREE.Color(16755200),B.setRGB(z[d],z[d+1],z[d+2]),w.push(B)}}c.computeCentroids();c.computeFaceNormals();c.computeBoundingSphere();if(void 0===a.materials)return{geometry:c};d=this.initMaterials(a.materials,b);this.needsTangents(d)&&c.computeTangents();return{geometry:c,materials:d}};THREE.LoadingManager=function(a,b,c){var d=this,e=0,f=0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(){f++};this.itemEnd=function(a){e++;if(void 0!==d.onProgress)d.onProgress(a,e,f);if(e===f&&void 0!==d.onLoad)d.onLoad()}};THREE.DefaultLoadingManager=new THREE.LoadingManager;THREE.BufferGeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE.BufferGeometry,c=a.attributes,d=a.offsets,a=a.boundingSphere,e;for(e in c){var f=c[e];b.attributes[e]={itemSize:f.itemSize,array:new self[f.type](f.array)}}void 0!==d&&(b.offsets=JSON.parse(JSON.stringify(d)));
-void 0!==a&&(b.boundingSphere=new THREE.Sphere((new THREE.Vector3).fromArray(void 0!==a.center?a.center:[0,0,0]),a.radius));return b}};THREE.GeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};THREE.GeometryLoader.prototype={constructor:THREE.GeometryLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(){}};THREE.MaterialLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE[a.type];void 0!==a.color&&b.color.setHex(a.color);void 0!==a.ambient&&b.ambient.setHex(a.ambient);void 0!==a.emissive&&b.emissive.setHex(a.emissive);void 0!==a.specular&&b.specular.setHex(a.specular);void 0!==a.shininess&&
-(b.shininess=a.shininess);void 0!==a.vertexColors&&(b.vertexColors=a.vertexColors);void 0!==a.blending&&(b.blending=a.blending);void 0!==a.opacity&&(b.opacity=a.opacity);void 0!==a.transparent&&(b.transparent=a.transparent);void 0!==a.wireframe&&(b.wireframe=a.wireframe);if(void 0!==a.materials)for(var c=0,d=a.materials.length;c<d;c++)b.materials.push(this.parse(a.materials[c]));return b}};THREE.ObjectLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader(c.manager);d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=this.parseGeometries(a.geometries),c=this.parseMaterials(a.materials);return this.parseObject(a.object,b,c)},parseGeometries:function(a){var b={};if(void 0!==a)for(var c=new THREE.JSONLoader,d=new THREE.BufferGeometryLoader,
-e=0,f=a.length;e<f;e++){var h,g=a[e];switch(g.type){case "PlaneGeometry":h=new THREE.PlaneGeometry(g.width,g.height,g.widthSegments,g.heightSegments);break;case "CircleGeometry":h=new THREE.CircleGeometry(g.radius,g.segments);break;case "CubeGeometry":h=new THREE.CubeGeometry(g.width,g.height,g.depth,g.widthSegments,g.heightSegments,g.depthSegments);break;case "CylinderGeometry":h=new THREE.CylinderGeometry(g.radiusTop,g.radiusBottom,g.height,g.radiusSegments,g.heightSegments,g.openEnded);break;case "SphereGeometry":h=
-new THREE.SphereGeometry(g.radius,g.widthSegments,g.heightSegments,g.phiStart,g.phiLength,g.thetaStart,g.thetaLength);break;case "IcosahedronGeometry":h=new THREE.IcosahedronGeometry(g.radius,g.detail);break;case "TorusGeometry":h=new THREE.TorusGeometry(g.radius,g.tube,g.radialSegments,g.tubularSegments,g.arc);break;case "TorusKnotGeometry":h=new THREE.TorusKnotGeometry(g.radius,g.tube,g.radialSegments,g.tubularSegments,g.p,g.q,g.heightScale);break;case "BufferGeometry":h=d.parse(g.data);break;case "Geometry":h=
-c.parse(g.data).geometry}h.uuid=g.uuid;void 0!==g.name&&(h.name=g.name);b[g.uuid]=h}return b},parseMaterials:function(a){var b={};if(void 0!==a)for(var c=new THREE.MaterialLoader,d=0,e=a.length;d<e;d++){var f=a[d],h=c.parse(f);h.uuid=f.uuid;void 0!==f.name&&(h.name=f.name);b[f.uuid]=h}return b},parseObject:function(){var a=new THREE.Matrix4;return function(b,c,d){var e;switch(b.type){case "Scene":e=new THREE.Scene;break;case "PerspectiveCamera":e=new THREE.PerspectiveCamera(b.fov,b.aspect,b.near,
-b.far);break;case "OrthographicCamera":e=new THREE.OrthographicCamera(b.left,b.right,b.top,b.bottom,b.near,b.far);break;case "AmbientLight":e=new THREE.AmbientLight(b.color);break;case "DirectionalLight":e=new THREE.DirectionalLight(b.color,b.intensity);break;case "PointLight":e=new THREE.PointLight(b.color,b.intensity,b.distance);break;case "SpotLight":e=new THREE.SpotLight(b.color,b.intensity,b.distance,b.angle,b.exponent);break;case "HemisphereLight":e=new THREE.HemisphereLight(b.color,b.groundColor,
-b.intensity);break;case "Mesh":e=c[b.geometry];var f=d[b.material];void 0===e&&console.error("THREE.ObjectLoader: Undefined geometry "+b.geometry);void 0===f&&console.error("THREE.ObjectLoader: Undefined material "+b.material);e=new THREE.Mesh(e,f);break;default:e=new THREE.Object3D}e.uuid=b.uuid;void 0!==b.name&&(e.name=b.name);void 0!==b.matrix?(a.fromArray(b.matrix),a.decompose(e.position,e.quaternion,e.scale)):(void 0!==b.position&&e.position.fromArray(b.position),void 0!==b.rotation&&e.rotation.fromArray(b.rotation),
-void 0!==b.scale&&e.scale.fromArray(b.scale));void 0!==b.visible&&(e.visible=b.visible);void 0!==b.userData&&(e.userData=b.userData);if(void 0!==b.children)for(var h in b.children)e.add(this.parseObject(b.children[h],c,d));return e}}()};THREE.SceneLoader=function(){this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){};this.callbackSync=function(){};this.callbackProgress=function(){};this.geometryHandlers={};this.hierarchyHandlers={};this.addGeometryHandler("ascii",THREE.JSONLoader)};
-THREE.SceneLoader.prototype={constructor:THREE.SceneLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader(c.manager);d.setCrossOrigin(this.crossOrigin);d.load(a,function(d){c.parse(JSON.parse(d),b,a)})},setCrossOrigin:function(a){this.crossOrigin=a},addGeometryHandler:function(a,b){this.geometryHandlers[a]={loaderClass:b}},addHierarchyHandler:function(a,b){this.hierarchyHandlers[a]={loaderClass:b}},parse:function(a,b,c){function d(a,b){return"relativeToHTML"==b?a:p+"/"+a}function e(){f(A.scene,
-C.objects)}function f(a,b){var c,e,h,i,k,m,p;for(p in b){var r=A.objects[p],q=b[p];if(void 0===r){if(q.type&&q.type in l.hierarchyHandlers){if(void 0===q.loading){e={type:1,url:1,material:1,position:1,rotation:1,scale:1,visible:1,children:1,userData:1,skin:1,morph:1,mirroredLoop:1,duration:1};h={};for(var B in q)B in e||(h[B]=q[B]);t=A.materials[q.material];q.loading=!0;e=l.hierarchyHandlers[q.type].loaderObject;e.options?e.load(d(q.url,C.urlBaseType),g(p,a,t,q)):e.load(d(q.url,C.urlBaseType),g(p,
-a,t,q),h)}}else if(void 0!==q.geometry){if(s=A.geometries[q.geometry]){r=!1;t=A.materials[q.material];r=t instanceof THREE.ShaderMaterial;h=q.position;i=q.rotation;k=q.scale;c=q.matrix;m=q.quaternion;q.material||(t=new THREE.MeshFaceMaterial(A.face_materials[q.geometry]));t instanceof THREE.MeshFaceMaterial&&0===t.materials.length&&(t=new THREE.MeshFaceMaterial(A.face_materials[q.geometry]));if(t instanceof THREE.MeshFaceMaterial)for(e=0;e<t.materials.length;e++)r=r||t.materials[e]instanceof THREE.ShaderMaterial;
-r&&s.computeTangents();q.skin?r=new THREE.SkinnedMesh(s,t):q.morph?(r=new THREE.MorphAnimMesh(s,t),void 0!==q.duration&&(r.duration=q.duration),void 0!==q.time&&(r.time=q.time),void 0!==q.mirroredLoop&&(r.mirroredLoop=q.mirroredLoop),t.morphNormals&&s.computeMorphNormals()):r=new THREE.Mesh(s,t);r.name=p;c?(r.matrixAutoUpdate=!1,r.matrix.set(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9],c[10],c[11],c[12],c[13],c[14],c[15])):(r.position.fromArray(h),m?r.quaternion.fromArray(m):r.rotation.fromArray(i),
-r.scale.fromArray(k));r.visible=q.visible;r.castShadow=q.castShadow;r.receiveShadow=q.receiveShadow;a.add(r);A.objects[p]=r}}else"DirectionalLight"===q.type||"PointLight"===q.type||"AmbientLight"===q.type?(w=void 0!==q.color?q.color:16777215,z=void 0!==q.intensity?q.intensity:1,"DirectionalLight"===q.type?(h=q.direction,u=new THREE.DirectionalLight(w,z),u.position.fromArray(h),q.target&&(O.push({object:u,targetName:q.target}),u.target=null)):"PointLight"===q.type?(h=q.position,e=q.distance,u=new THREE.PointLight(w,
-z,e),u.position.fromArray(h)):"AmbientLight"===q.type&&(u=new THREE.AmbientLight(w)),a.add(u),u.name=p,A.lights[p]=u,A.objects[p]=u):"PerspectiveCamera"===q.type||"OrthographicCamera"===q.type?(h=q.position,i=q.rotation,m=q.quaternion,"PerspectiveCamera"===q.type?n=new THREE.PerspectiveCamera(q.fov,q.aspect,q.near,q.far):"OrthographicCamera"===q.type&&(n=new THREE.OrthographicCamera(q.left,q.right,q.top,q.bottom,q.near,q.far)),n.name=p,n.position.fromArray(h),void 0!==m?n.quaternion.fromArray(m):
-void 0!==i&&n.rotation.fromArray(i),a.add(n),A.cameras[p]=n,A.objects[p]=n):(h=q.position,i=q.rotation,k=q.scale,m=q.quaternion,r=new THREE.Object3D,r.name=p,r.position.fromArray(h),m?r.quaternion.fromArray(m):r.rotation.fromArray(i),r.scale.fromArray(k),r.visible=void 0!==q.visible?q.visible:!1,a.add(r),A.objects[p]=r,A.empties[p]=r);if(r){if(void 0!==q.userData)for(var D in q.userData)r.userData[D]=q.userData[D];if(void 0!==q.groups)for(e=0;e<q.groups.length;e++)h=q.groups[e],void 0===A.groups[h]&&
-(A.groups[h]=[]),A.groups[h].push(p)}}void 0!==r&&void 0!==q.children&&f(r,q.children)}}function h(a){return function(b,c){b.name=a;A.geometries[a]=b;A.face_materials[a]=c;e();B-=1;l.onLoadComplete();k()}}function g(a,b,c,d){return function(f){var f=f.content?f.content:f.dae?f.scene:f,h=d.rotation,g=d.quaternion,i=d.scale;f.position.fromArray(d.position);g?f.quaternion.fromArray(g):f.rotation.fromArray(h);f.scale.fromArray(i);c&&f.traverse(function(a){a.material=c});var m=void 0!==d.visible?d.visible:
-!0;f.traverse(function(a){a.visible=m});b.add(f);f.name=a;A.objects[a]=f;e();B-=1;l.onLoadComplete();k()}}function i(a){return function(b,c){b.name=a;A.geometries[a]=b;A.face_materials[a]=c}}function k(){l.callbackProgress({totalModels:x,totalTextures:F,loadedModels:x-B,loadedTextures:F-D},A);l.onLoadProgress();if(0===B&&0===D){for(var a=0;a<O.length;a++){var c=O[a],d=A.objects[c.targetName];d?c.object.target=d:(c.object.target=new THREE.Object3D,A.scene.add(c.object.target));c.object.target.userData.targetInverse=
-c.object}b(A)}}function m(a,b){b(a);if(void 0!==a.children)for(var c in a.children)m(a.children[c],b)}var l=this,p=THREE.Loader.prototype.extractUrlBase(c),s,t,n,r,q,u,w,z,B,D,x,F,A,O=[],C=a,E;for(E in this.geometryHandlers)a=this.geometryHandlers[E].loaderClass,this.geometryHandlers[E].loaderObject=new a;for(E in this.hierarchyHandlers)a=this.hierarchyHandlers[E].loaderClass,this.hierarchyHandlers[E].loaderObject=new a;D=B=0;A={scene:new THREE.Scene,geometries:{},face_materials:{},materials:{},textures:{},
-objects:{},cameras:{},lights:{},fogs:{},empties:{},groups:{}};if(C.transform&&(E=C.transform.position,a=C.transform.rotation,c=C.transform.scale,E&&A.scene.position.fromArray(E),a&&A.scene.rotation.fromArray(a),c&&A.scene.scale.fromArray(c),E||a||c))A.scene.updateMatrix(),A.scene.updateMatrixWorld();E=function(a){return function(){D-=a;k();l.onLoadComplete()}};for(var I in C.fogs)a=C.fogs[I],"linear"===a.type?r=new THREE.Fog(0,a.near,a.far):"exp2"===a.type&&(r=new THREE.FogExp2(0,a.density)),a=a.color,
-r.color.setRGB(a[0],a[1],a[2]),A.fogs[I]=r;for(var y in C.geometries)r=C.geometries[y],r.type in this.geometryHandlers&&(B+=1,l.onLoadStart());for(var v in C.objects)m(C.objects[v],function(a){a.type&&a.type in l.hierarchyHandlers&&(B+=1,l.onLoadStart())});x=B;for(y in C.geometries)if(r=C.geometries[y],"cube"===r.type)s=new THREE.CubeGeometry(r.width,r.height,r.depth,r.widthSegments,r.heightSegments,r.depthSegments),s.name=y,A.geometries[y]=s;else if("plane"===r.type)s=new THREE.PlaneGeometry(r.width,
-r.height,r.widthSegments,r.heightSegments),s.name=y,A.geometries[y]=s;else if("sphere"===r.type)s=new THREE.SphereGeometry(r.radius,r.widthSegments,r.heightSegments),s.name=y,A.geometries[y]=s;else if("cylinder"===r.type)s=new THREE.CylinderGeometry(r.topRad,r.botRad,r.height,r.radSegs,r.heightSegs),s.name=y,A.geometries[y]=s;else if("torus"===r.type)s=new THREE.TorusGeometry(r.radius,r.tube,r.segmentsR,r.segmentsT),s.name=y,A.geometries[y]=s;else if("icosahedron"===r.type)s=new THREE.IcosahedronGeometry(r.radius,
-r.subdivisions),s.name=y,A.geometries[y]=s;else if(r.type in this.geometryHandlers){v={};for(q in r)"type"!==q&&"url"!==q&&(v[q]=r[q]);this.geometryHandlers[r.type].loaderObject.load(d(r.url,C.urlBaseType),h(y),v)}else"embedded"===r.type&&(v=C.embeds[r.id],v.metadata=C.metadata,v&&(v=this.geometryHandlers.ascii.loaderObject.parse(v,""),i(y)(v.geometry,v.materials)));for(var G in C.textures)if(y=C.textures[G],y.url instanceof Array){D+=y.url.length;for(q=0;q<y.url.length;q++)l.onLoadStart()}else D+=
-1,l.onLoadStart();F=D;for(G in C.textures){y=C.textures[G];void 0!==y.mapping&&void 0!==THREE[y.mapping]&&(y.mapping=new THREE[y.mapping]);if(y.url instanceof Array){v=y.url.length;r=[];for(q=0;q<v;q++)r[q]=d(y.url[q],C.urlBaseType);q=(q=/\.dds$/i.test(r[0]))?THREE.ImageUtils.loadCompressedTextureCube(r,y.mapping,E(v)):THREE.ImageUtils.loadTextureCube(r,y.mapping,E(v))}else q=/\.dds$/i.test(y.url),v=d(y.url,C.urlBaseType),r=E(1),q=q?THREE.ImageUtils.loadCompressedTexture(v,y.mapping,r):THREE.ImageUtils.loadTexture(v,
-y.mapping,r),void 0!==THREE[y.minFilter]&&(q.minFilter=THREE[y.minFilter]),void 0!==THREE[y.magFilter]&&(q.magFilter=THREE[y.magFilter]),y.anisotropy&&(q.anisotropy=y.anisotropy),y.repeat&&(q.repeat.set(y.repeat[0],y.repeat[1]),1!==y.repeat[0]&&(q.wrapS=THREE.RepeatWrapping),1!==y.repeat[1]&&(q.wrapT=THREE.RepeatWrapping)),y.offset&&q.offset.set(y.offset[0],y.offset[1]),y.wrap&&(v={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==v[y.wrap[0]]&&(q.wrapS=v[y.wrap[0]]),void 0!==
-v[y.wrap[1]]&&(q.wrapT=v[y.wrap[1]]));A.textures[G]=q}var R,J;for(R in C.materials){G=C.materials[R];for(J in G.parameters)"envMap"===J||"map"===J||"lightMap"===J||"bumpMap"===J?G.parameters[J]=A.textures[G.parameters[J]]:"shading"===J?G.parameters[J]="flat"===G.parameters[J]?THREE.FlatShading:THREE.SmoothShading:"side"===J?G.parameters[J]="double"==G.parameters[J]?THREE.DoubleSide:"back"==G.parameters[J]?THREE.BackSide:THREE.FrontSide:"blending"===J?G.parameters[J]=G.parameters[J]in THREE?THREE[G.parameters[J]]:
-THREE.NormalBlending:"combine"===J?G.parameters[J]=G.parameters[J]in THREE?THREE[G.parameters[J]]:THREE.MultiplyOperation:"vertexColors"===J?"face"==G.parameters[J]?G.parameters[J]=THREE.FaceColors:G.parameters[J]&&(G.parameters[J]=THREE.VertexColors):"wrapRGB"===J&&(E=G.parameters[J],G.parameters[J]=new THREE.Vector3(E[0],E[1],E[2]));void 0!==G.parameters.opacity&&1>G.parameters.opacity&&(G.parameters.transparent=!0);G.parameters.normalMap?(E=THREE.ShaderLib.normalmap,y=THREE.UniformsUtils.clone(E.uniforms),
-q=G.parameters.color,v=G.parameters.specular,r=G.parameters.ambient,I=G.parameters.shininess,y.tNormal.value=A.textures[G.parameters.normalMap],G.parameters.normalScale&&y.uNormalScale.value.set(G.parameters.normalScale[0],G.parameters.normalScale[1]),G.parameters.map&&(y.tDiffuse.value=G.parameters.map,y.enableDiffuse.value=!0),G.parameters.envMap&&(y.tCube.value=G.parameters.envMap,y.enableReflection.value=!0,y.uReflectivity.value=G.parameters.reflectivity),G.parameters.lightMap&&(y.tAO.value=G.parameters.lightMap,
-y.enableAO.value=!0),G.parameters.specularMap&&(y.tSpecular.value=A.textures[G.parameters.specularMap],y.enableSpecular.value=!0),G.parameters.displacementMap&&(y.tDisplacement.value=A.textures[G.parameters.displacementMap],y.enableDisplacement.value=!0,y.uDisplacementBias.value=G.parameters.displacementBias,y.uDisplacementScale.value=G.parameters.displacementScale),y.uDiffuseColor.value.setHex(q),y.uSpecularColor.value.setHex(v),y.uAmbientColor.value.setHex(r),y.uShininess.value=I,G.parameters.opacity&&
-(y.uOpacity.value=G.parameters.opacity),t=new THREE.ShaderMaterial({fragmentShader:E.fragmentShader,vertexShader:E.vertexShader,uniforms:y,lights:!0,fog:!0})):t=new THREE[G.type](G.parameters);t.name=R;A.materials[R]=t}for(R in C.materials)if(G=C.materials[R],G.parameters.materials){J=[];for(q=0;q<G.parameters.materials.length;q++)J.push(A.materials[G.parameters.materials[q]]);A.materials[R].materials=J}e();A.cameras&&C.defaults.camera&&(A.currentCamera=A.cameras[C.defaults.camera]);A.fogs&&C.defaults.fog&&
-(A.scene.fog=A.fogs[C.defaults.fog]);l.callbackSync(A);k()}};THREE.TextureLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a,b){var c=new THREE.ImageLoader(this.manager);c.setCrossOrigin(this.crossOrigin);c.load(a,function(a){a=new THREE.Texture(a);a.needsUpdate=!0;void 0!==b&&b(a)})},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.Material=function(){this.id=THREE.MaterialIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.side=THREE.FrontSide;this.opacity=1;this.transparent=!1;this.blending=THREE.NormalBlending;this.blendSrc=THREE.SrcAlphaFactor;this.blendDst=THREE.OneMinusSrcAlphaFactor;this.blendEquation=THREE.AddEquation;this.depthWrite=this.depthTest=!0;this.polygonOffset=!1;this.overdraw=this.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=0;this.needsUpdate=this.visible=!0};
-THREE.Material.prototype={constructor:THREE.Material,setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else if(b in this){var d=this[b];d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]="overdraw"==b?Number(c):c}}},clone:function(a){void 0===a&&(a=new THREE.Material);a.name=this.name;a.side=this.side;a.opacity=this.opacity;a.transparent=this.transparent;
-a.blending=this.blending;a.blendSrc=this.blendSrc;a.blendDst=this.blendDst;a.blendEquation=this.blendEquation;a.depthTest=this.depthTest;a.depthWrite=this.depthWrite;a.polygonOffset=this.polygonOffset;a.polygonOffsetFactor=this.polygonOffsetFactor;a.polygonOffsetUnits=this.polygonOffsetUnits;a.alphaTest=this.alphaTest;a.overdraw=this.overdraw;a.visible=this.visible;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Material.prototype);
-THREE.MaterialIdCount=0;THREE.LineBasicMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.linewidth=1;this.linejoin=this.linecap="round";this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineBasicMaterial.prototype.clone=function(){var a=new THREE.LineBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.linecap=this.linecap;a.linejoin=this.linejoin;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.LineDashedMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineDashedMaterial.prototype.clone=function(){var a=new THREE.LineDashedMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.scale=this.scale;a.dashSize=this.dashSize;a.gapSize=this.gapSize;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.MeshBasicMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.envMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphTargets=this.skinning=!1;this.setValues(a)};
-THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshBasicMaterial.prototype.clone=function(){var a=new THREE.MeshBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=
-this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;return a};THREE.MeshLambertMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.envMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap=
-"round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshLambertMaterial.prototype.clone=function(){var a=new THREE.MeshLambertMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;
-a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.MeshPhongMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.specular=new THREE.Color(1118481);this.shininess=30;this.metal=!1;this.perPixel=!0;this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.bumpMap=this.lightMap=this.map=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new THREE.Vector2(1,1);this.envMap=this.specularMap=null;this.combine=THREE.MultiplyOperation;
-this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshPhongMaterial.prototype.clone=function(){var a=new THREE.MeshPhongMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.specular.copy(this.specular);a.shininess=this.shininess;a.metal=this.metal;a.perPixel=this.perPixel;a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.bumpMap=this.bumpMap;a.bumpScale=this.bumpScale;a.normalMap=this.normalMap;a.normalScale.copy(this.normalScale);
-a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.MeshDepthMaterial=function(a){THREE.Material.call(this);this.wireframe=!1;this.wireframeLinewidth=1;this.setValues(a)};THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshDepthMaterial.prototype.clone=function(){var a=new THREE.MeshDepthMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshNormalMaterial=function(a){THREE.Material.call(this,a);this.shading=THREE.FlatShading;this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=!1;this.setValues(a)};THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshNormalMaterial.prototype.clone=function(){var a=new THREE.MeshNormalMaterial;THREE.Material.prototype.clone.call(this,a);a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshFaceMaterial=function(a){this.materials=a instanceof Array?a:[]};THREE.MeshFaceMaterial.prototype.clone=function(){for(var a=new THREE.MeshFaceMaterial,b=0;b<this.materials.length;b++)a.materials.push(this.materials[b].clone());return a};THREE.ParticleSystemMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.map=null;this.size=1;this.sizeAttenuation=!0;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.ParticleSystemMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.ParticleSystemMaterial.prototype.clone=function(){var a=new THREE.ParticleSystemMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.size=this.size;a.sizeAttenuation=this.sizeAttenuation;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.ParticleBasicMaterial=THREE.ParticleSystemMaterial;THREE.ShaderMaterial=function(a){THREE.Material.call(this);this.vertexShader=this.fragmentShader="void main() {}";this.uniforms={};this.defines={};this.attributes=null;this.shading=THREE.SmoothShading;this.linewidth=1;this.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv2:[0,0]};this.index0AttributeName="position";this.setValues(a)};
-THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);
+c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners[a];if(void 0!==c){var d=c.indexOf(b);-1!==d&&c.splice(d,1)}}},dispatchEvent:function(a){if(void 0!==this._listeners){var b=this._listeners[a.type];if(void 0!==b){a.target=this;for(var c=[],d=b.length,e=0;e<d;e++)c[e]=b[e];for(e=0;e<d;e++)c[e].call(this,a)}}}};
+(function(a){a.Raycaster=function(b,c,f,g){this.ray=new a.Ray(b,c);this.near=f||0;this.far=g||Infinity;this.params={Sprite:{},Mesh:{},PointCloud:{threshold:1},LOD:{},Line:{}}};var b=function(a,b){return a.distance-b.distance},c=function(a,b,f,g){a.raycast(b,f);if(!0===g){a=a.children;g=0;for(var h=a.length;g<h;g++)c(a[g],b,f,!0)}};a.Raycaster.prototype={constructor:a.Raycaster,precision:1E-4,linePrecision:1,set:function(a,b){this.ray.set(a,b)},setFromCamera:function(b,c){c instanceof a.PerspectiveCamera?
+(this.ray.origin.copy(c.position),this.ray.direction.set(b.x,b.y,.5).unproject(c).sub(c.position).normalize()):c instanceof a.OrthographicCamera?(this.ray.origin.set(b.x,b.y,-1).unproject(c),this.ray.direction.set(0,0,-1).transformDirection(c.matrixWorld)):a.error("THREE.Raycaster: Unsupported camera type.")},intersectObject:function(a,e){var f=[];c(a,this,f,e);f.sort(b);return f},intersectObjects:function(d,e){var f=[];if(!1===d instanceof Array)return a.warn("THREE.Raycaster.intersectObjects: objects is not an Array."),
+f;for(var g=0,h=d.length;g<h;g++)c(d[g],this,f,e);f.sort(b);return f}}})(THREE);
+THREE.Object3D=function(){Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Object3D";this.parent=void 0;this.children=[];this.up=THREE.Object3D.DefaultUp.clone();var a=new THREE.Vector3,b=new THREE.Euler,c=new THREE.Quaternion,d=new THREE.Vector3(1,1,1);b.onChange(function(){c.setFromEuler(b,!1)});c.onChange(function(){b.setFromQuaternion(c,void 0,!1)});Object.defineProperties(this,{position:{enumerable:!0,value:a},rotation:{enumerable:!0,
+value:b},quaternion:{enumerable:!0,value:c},scale:{enumerable:!0,value:d}});this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;this.matrixAutoUpdate=!0;this.matrixWorldNeedsUpdate=!1;this.visible=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this.renderOrder=0;this.userData={}};THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0);
+THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){THREE.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");return this.rotation.order},set eulerOrder(a){THREE.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");this.rotation.order=a},get useQuaternion(){THREE.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(a){THREE.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},
+applyMatrix:function(a){this.matrix.multiplyMatrices(a,this.matrix);this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(a,b){this.quaternion.setFromAxisAngle(a,b)},setRotationFromEuler:function(a){this.quaternion.setFromEuler(a,!0)},setRotationFromMatrix:function(a){this.quaternion.setFromRotationMatrix(a)},setRotationFromQuaternion:function(a){this.quaternion.copy(a)},rotateOnAxis:function(){var a=new THREE.Quaternion;return function(b,c){a.setFromAxisAngle(b,
+c);this.quaternion.multiply(a);return this}}(),rotateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.rotateOnAxis(a,b)}}(),translateOnAxis:function(){var a=new THREE.Vector3;return function(b,c){a.copy(b).applyQuaternion(this.quaternion);this.position.add(a.multiplyScalar(c));
+return this}}(),translate:function(a,b){THREE.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.");return this.translateOnAxis(b,a)},translateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.translateOnAxis(a,b)}}(),translateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.translateOnAxis(a,b)}}(),translateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.translateOnAxis(a,
+b)}}(),localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var a=new THREE.Matrix4;return function(b){return b.applyMatrix4(a.getInverse(this.matrixWorld))}}(),lookAt:function(){var a=new THREE.Matrix4;return function(b){a.lookAt(b,this.position,this.up);this.quaternion.setFromRotationMatrix(a)}}(),add:function(a){if(1<arguments.length){for(var b=0;b<arguments.length;b++)this.add(arguments[b]);return this}if(a===this)return THREE.error("THREE.Object3D.add: object can't be added as a child of itself.",
+a),this;a instanceof THREE.Object3D?(void 0!==a.parent&&a.parent.remove(a),a.parent=this,a.dispatchEvent({type:"added"}),this.children.push(a)):THREE.error("THREE.Object3D.add: object not an instance of THREE.Object3D.",a);return this},remove:function(a){if(1<arguments.length)for(var b=0;b<arguments.length;b++)this.remove(arguments[b]);b=this.children.indexOf(a);-1!==b&&(a.parent=void 0,a.dispatchEvent({type:"removed"}),this.children.splice(b,1))},getChildByName:function(a){THREE.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().");
+return this.getObjectByName(a)},getObjectById:function(a){return this.getObjectByProperty("id",a)},getObjectByName:function(a){return this.getObjectByProperty("name",a)},getObjectByProperty:function(a,b){if(this[a]===b)return this;for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c].getObjectByProperty(a,b);if(void 0!==e)return e}},getWorldPosition:function(a){a=a||new THREE.Vector3;this.updateMatrixWorld(!0);return a.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var a=
+new THREE.Vector3,b=new THREE.Vector3;return function(c){c=c||new THREE.Quaternion;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,c,b);return c}}(),getWorldRotation:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Euler;this.getWorldQuaternion(a);return b.setFromQuaternion(a,this.rotation.order,!1)}}(),getWorldScale:function(){var a=new THREE.Vector3,b=new THREE.Quaternion;return function(c){c=c||new THREE.Vector3;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,
+b,c);return c}}(),getWorldDirection:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,1).applyQuaternion(a)}}(),raycast:function(){},traverse:function(a){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverse(a)},traverseVisible:function(a){if(!1!==this.visible){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverseVisible(a)}},traverseAncestors:function(a){this.parent&&(a(this.parent),
+this.parent.traverseAncestors(a))},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(a){!0===this.matrixAutoUpdate&&this.updateMatrix();if(!0===this.matrixWorldNeedsUpdate||!0===a)void 0===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)},
+toJSON:function(){var a={metadata:{version:4.3,type:"Object",generator:"ObjectExporter"}},b={},c={},d=function(b){void 0===a.materials&&(a.materials=[]);if(void 0===c[b.uuid]){var d=b.toJSON();delete d.metadata;c[b.uuid]=d;a.materials.push(d)}return b.uuid},e=function(c){var g={};g.uuid=c.uuid;g.type=c.type;""!==c.name&&(g.name=c.name);"{}"!==JSON.stringify(c.userData)&&(g.userData=c.userData);!0!==c.visible&&(g.visible=c.visible);if(c instanceof THREE.PerspectiveCamera)g.fov=c.fov,g.aspect=c.aspect,
+g.near=c.near,g.far=c.far;else if(c instanceof THREE.OrthographicCamera)g.left=c.left,g.right=c.right,g.top=c.top,g.bottom=c.bottom,g.near=c.near,g.far=c.far;else if(c instanceof THREE.AmbientLight)g.color=c.color.getHex();else if(c instanceof THREE.DirectionalLight)g.color=c.color.getHex(),g.intensity=c.intensity;else if(c instanceof THREE.PointLight)g.color=c.color.getHex(),g.intensity=c.intensity,g.distance=c.distance,g.decay=c.decay;else if(c instanceof THREE.SpotLight)g.color=c.color.getHex(),
+g.intensity=c.intensity,g.distance=c.distance,g.angle=c.angle,g.exponent=c.exponent,g.decay=c.decay;else if(c instanceof THREE.HemisphereLight)g.color=c.color.getHex(),g.groundColor=c.groundColor.getHex();else if(c instanceof THREE.Mesh||c instanceof THREE.Line||c instanceof THREE.PointCloud){var h=c.geometry;void 0===a.geometries&&(a.geometries=[]);if(void 0===b[h.uuid]){var k=h.toJSON();delete k.metadata;b[h.uuid]=k;a.geometries.push(k)}g.geometry=h.uuid;g.material=d(c.material);c instanceof THREE.Line&&
+(g.mode=c.mode)}else c instanceof THREE.Sprite&&(g.material=d(c.material));g.matrix=c.matrix.toArray();if(0<c.children.length)for(g.children=[],h=0;h<c.children.length;h++)g.children.push(e(c.children[h]));return g};a.object=e(this);return a},clone:function(a,b){void 0===a&&(a=new THREE.Object3D);void 0===b&&(b=!0);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.quaternion.copy(this.quaternion);a.scale.copy(this.scale);a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);
+a.matrixWorld.copy(this.matrixWorld);a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;a.userData=JSON.parse(JSON.stringify(this.userData));if(!0===b)for(var c=0;c<this.children.length;c++)a.add(this.children[c].clone());return a}};THREE.EventDispatcher.prototype.apply(THREE.Object3D.prototype);THREE.Object3DIdCount=0;
+THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0};
+THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.materialIndex=this.materialIndex;for(var b=0,c=this.vertexNormals.length;b<c;b++)a.vertexNormals[b]=this.vertexNormals[b].clone();b=0;for(c=this.vertexColors.length;b<c;b++)a.vertexColors[b]=this.vertexColors[b].clone();b=0;for(c=this.vertexTangents.length;b<c;b++)a.vertexTangents[b]=this.vertexTangents[b].clone();return a}};
+THREE.Face4=function(a,b,c,d,e,f,g){THREE.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead.");return new THREE.Face3(a,b,c,e,f,g)};THREE.BufferAttribute=function(a,b){this.array=a;this.itemSize=b;this.needsUpdate=!1};
+THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return this.array.length},copyAt:function(a,b,c){a*=this.itemSize;c*=b.itemSize;for(var d=0,e=this.itemSize;d<e;d++)this.array[a+d]=b.array[c+d];return this},set:function(a,b){void 0===b&&(b=0);this.array.set(a,b);return this},setX:function(a,b){this.array[a*this.itemSize]=b;return this},setY:function(a,b){this.array[a*this.itemSize+1]=b;return this},setZ:function(a,b){this.array[a*this.itemSize+2]=b;return this},setXY:function(a,
+b,c){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;return this},setXYZ:function(a,b,c,d){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;return this},setXYZW:function(a,b,c,d,e){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;this.array[a+3]=e;return this},clone:function(){return new THREE.BufferAttribute(new this.array.constructor(this.array),this.itemSize)}};
+THREE.Int8Attribute=function(a,b){THREE.warn("THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Uint8Attribute=function(a,b){THREE.warn("THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
+THREE.Uint8ClampedAttribute=function(a,b){THREE.warn("THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int16Attribute=function(a,b){THREE.warn("THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
+THREE.Uint16Attribute=function(a,b){THREE.warn("THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int32Attribute=function(a,b){THREE.warn("THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
+THREE.Uint32Attribute=function(a,b){THREE.warn("THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Float32Attribute=function(a,b){THREE.warn("THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
+THREE.Float64Attribute=function(a,b){THREE.warn("THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.DynamicBufferAttribute=function(a,b){THREE.BufferAttribute.call(this,a,b);this.updateRange={offset:0,count:-1}};THREE.DynamicBufferAttribute.prototype=Object.create(THREE.BufferAttribute.prototype);THREE.DynamicBufferAttribute.prototype.constructor=THREE.DynamicBufferAttribute;
+THREE.DynamicBufferAttribute.prototype.clone=function(){return new THREE.DynamicBufferAttribute(new this.array.constructor(this.array),this.itemSize)};THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="BufferGeometry";this.attributes={};this.attributesKeys=[];this.offsets=this.drawcalls=[];this.boundingSphere=this.boundingBox=null};
+THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addAttribute:function(a,b,c){!1===b instanceof THREE.BufferAttribute?(THREE.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),this.attributes[a]={array:b,itemSize:c}):(this.attributes[a]=b,this.attributesKeys=Object.keys(this.attributes))},getAttribute:function(a){return this.attributes[a]},addDrawCall:function(a,b,c){this.drawcalls.push({start:a,count:b,index:void 0!==c?c:0})},applyMatrix:function(a){var b=
+this.attributes.position;void 0!==b&&(a.applyToVector3Array(b.array),b.needsUpdate=!0);b=this.attributes.normal;void 0!==b&&((new THREE.Matrix3).getNormalMatrix(a).applyToVector3Array(b.array),b.needsUpdate=!0);null!==this.boundingBox&&this.computeBoundingBox();null!==this.boundingSphere&&this.computeBoundingSphere()},center:function(){this.computeBoundingBox();var a=this.boundingBox.center().negate();this.applyMatrix((new THREE.Matrix4).setPosition(a));return a},fromGeometry:function(a,b){b=b||{vertexColors:THREE.NoColors};
+var c=a.vertices,d=a.faces,e=a.faceVertexUvs,f=b.vertexColors,g=0<e[0].length,h=3==d[0].vertexNormals.length,k=new Float32Array(9*d.length);this.addAttribute("position",new THREE.BufferAttribute(k,3));var l=new Float32Array(9*d.length);this.addAttribute("normal",new THREE.BufferAttribute(l,3));if(f!==THREE.NoColors){var p=new Float32Array(9*d.length);this.addAttribute("color",new THREE.BufferAttribute(p,3))}if(!0===g){var q=new Float32Array(6*d.length);this.addAttribute("uv",new THREE.BufferAttribute(q,
+2))}for(var n=0,t=0,r=0;n<d.length;n++,t+=6,r+=9){var s=d[n],u=c[s.a],v=c[s.b],x=c[s.c];k[r]=u.x;k[r+1]=u.y;k[r+2]=u.z;k[r+3]=v.x;k[r+4]=v.y;k[r+5]=v.z;k[r+6]=x.x;k[r+7]=x.y;k[r+8]=x.z;!0===h?(u=s.vertexNormals[0],v=s.vertexNormals[1],x=s.vertexNormals[2],l[r]=u.x,l[r+1]=u.y,l[r+2]=u.z,l[r+3]=v.x,l[r+4]=v.y,l[r+5]=v.z,l[r+6]=x.x,l[r+7]=x.y,l[r+8]=x.z):(u=s.normal,l[r]=u.x,l[r+1]=u.y,l[r+2]=u.z,l[r+3]=u.x,l[r+4]=u.y,l[r+5]=u.z,l[r+6]=u.x,l[r+7]=u.y,l[r+8]=u.z);f===THREE.FaceColors?(s=s.color,p[r]=
+s.r,p[r+1]=s.g,p[r+2]=s.b,p[r+3]=s.r,p[r+4]=s.g,p[r+5]=s.b,p[r+6]=s.r,p[r+7]=s.g,p[r+8]=s.b):f===THREE.VertexColors&&(u=s.vertexColors[0],v=s.vertexColors[1],s=s.vertexColors[2],p[r]=u.r,p[r+1]=u.g,p[r+2]=u.b,p[r+3]=v.r,p[r+4]=v.g,p[r+5]=v.b,p[r+6]=s.r,p[r+7]=s.g,p[r+8]=s.b);!0===g&&(s=e[0][n][0],u=e[0][n][1],v=e[0][n][2],q[t]=s.x,q[t+1]=s.y,q[t+2]=u.x,q[t+3]=u.y,q[t+4]=v.x,q[t+5]=v.y)}this.computeBoundingSphere();return this},computeBoundingBox:function(){var a=new THREE.Vector3;return function(){null===
+this.boundingBox&&(this.boundingBox=new THREE.Box3);var b=this.attributes.position.array;if(b){var c=this.boundingBox;c.makeEmpty();for(var d=0,e=b.length;d<e;d+=3)a.set(b[d],b[d+1],b[d+2]),c.expandByPoint(a)}if(void 0===b||0===b.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0);(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&THREE.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.')}}(),
+computeBoundingSphere:function(){var a=new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;if(c){a.makeEmpty();for(var d=this.boundingSphere.center,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),a.expandByPoint(b);a.center(d);for(var g=0,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),g=Math.max(g,d.distanceToSquared(b));this.boundingSphere.radius=Math.sqrt(g);isNaN(this.boundingSphere.radius)&&
+THREE.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.')}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var a=this.attributes;if(a.position){var b=a.position.array;if(void 0===a.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(b.length),3));else for(var c=a.normal.array,d=0,e=c.length;d<e;d++)c[d]=0;var c=a.normal.array,f,g,h,k=new THREE.Vector3,l=new THREE.Vector3,
+p=new THREE.Vector3,q=new THREE.Vector3,n=new THREE.Vector3;if(a.index)for(var t=a.index.array,r=0<this.offsets.length?this.offsets:[{start:0,count:t.length,index:0}],s=0,u=r.length;s<u;++s){e=r[s].start;f=r[s].count;for(var v=r[s].index,d=e,e=e+f;d<e;d+=3)f=3*(v+t[d]),g=3*(v+t[d+1]),h=3*(v+t[d+2]),k.fromArray(b,f),l.fromArray(b,g),p.fromArray(b,h),q.subVectors(p,l),n.subVectors(k,l),q.cross(n),c[f]+=q.x,c[f+1]+=q.y,c[f+2]+=q.z,c[g]+=q.x,c[g+1]+=q.y,c[g+2]+=q.z,c[h]+=q.x,c[h+1]+=q.y,c[h+2]+=q.z}else for(d=
+0,e=b.length;d<e;d+=9)k.fromArray(b,d),l.fromArray(b,d+3),p.fromArray(b,d+6),q.subVectors(p,l),n.subVectors(k,l),q.cross(n),c[d]=q.x,c[d+1]=q.y,c[d+2]=q.z,c[d+3]=q.x,c[d+4]=q.y,c[d+5]=q.z,c[d+6]=q.x,c[d+7]=q.y,c[d+8]=q.z;this.normalizeNormals();a.normal.needsUpdate=!0}},computeTangents:function(){function a(a,b,c){q.fromArray(d,3*a);n.fromArray(d,3*b);t.fromArray(d,3*c);r.fromArray(f,2*a);s.fromArray(f,2*b);u.fromArray(f,2*c);v=n.x-q.x;x=t.x-q.x;D=n.y-q.y;w=t.y-q.y;y=n.z-q.z;A=t.z-q.z;E=s.x-r.x;G=
+u.x-r.x;F=s.y-r.y;z=u.y-r.y;I=1/(E*z-G*F);U.set((z*v-F*x)*I,(z*D-F*w)*I,(z*y-F*A)*I);M.set((E*x-G*v)*I,(E*w-G*D)*I,(E*A-G*y)*I);k[a].add(U);k[b].add(U);k[c].add(U);l[a].add(M);l[b].add(M);l[c].add(M)}function b(a){ha.fromArray(e,3*a);O.copy(ha);ba=k[a];oa.copy(ba);oa.sub(ha.multiplyScalar(ha.dot(ba))).normalize();ja.crossVectors(O,ba);qa=ja.dot(l[a]);ca=0>qa?-1:1;h[4*a]=oa.x;h[4*a+1]=oa.y;h[4*a+2]=oa.z;h[4*a+3]=ca}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||
+void 0===this.attributes.uv)THREE.warn("THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var c=this.attributes.index.array,d=this.attributes.position.array,e=this.attributes.normal.array,f=this.attributes.uv.array,g=d.length/3;void 0===this.attributes.tangent&&this.addAttribute("tangent",new THREE.BufferAttribute(new Float32Array(4*g),4));for(var h=this.attributes.tangent.array,k=[],l=[],p=0;p<g;p++)k[p]=new THREE.Vector3,
+l[p]=new THREE.Vector3;var q=new THREE.Vector3,n=new THREE.Vector3,t=new THREE.Vector3,r=new THREE.Vector2,s=new THREE.Vector2,u=new THREE.Vector2,v,x,D,w,y,A,E,G,F,z,I,U=new THREE.Vector3,M=new THREE.Vector3,H,L,P,N,R;0===this.drawcalls.length&&this.addDrawCall(0,c.length,0);var V=this.drawcalls,p=0;for(L=V.length;p<L;++p){H=V[p].start;P=V[p].count;var J=V[p].index,g=H;for(H+=P;g<H;g+=3)P=J+c[g],N=J+c[g+1],R=J+c[g+2],a(P,N,R)}var oa=new THREE.Vector3,ja=new THREE.Vector3,ha=new THREE.Vector3,O=new THREE.Vector3,
+ca,ba,qa,p=0;for(L=V.length;p<L;++p)for(H=V[p].start,P=V[p].count,J=V[p].index,g=H,H+=P;g<H;g+=3)P=J+c[g],N=J+c[g+1],R=J+c[g+2],b(P),b(N),b(R)}},computeOffsets:function(a){void 0===a&&(a=65535);for(var b=this.attributes.index.array,c=this.attributes.position.array,d=b.length/3,e=new Uint16Array(b.length),f=0,g=0,h=[{start:0,count:0,index:0}],k=h[0],l=0,p=0,q=new Int32Array(6),n=new Int32Array(c.length),t=new Int32Array(c.length),r=0;r<c.length;r++)n[r]=-1,t[r]=-1;for(c=0;c<d;c++){for(var s=p=0;3>
+s;s++)r=b[3*c+s],-1==n[r]?(q[2*s]=r,q[2*s+1]=-1,p++):n[r]<k.index?(q[2*s]=r,q[2*s+1]=-1,l++):(q[2*s]=r,q[2*s+1]=n[r]);if(g+p>k.index+a)for(k={start:f,count:0,index:g},h.push(k),p=0;6>p;p+=2)s=q[p+1],-1<s&&s<k.index&&(q[p+1]=-1);for(p=0;6>p;p+=2)r=q[p],s=q[p+1],-1===s&&(s=g++),n[r]=s,t[s]=r,e[f++]=s-k.index,k.count++}this.reorderBuffers(e,t,g);return this.drawcalls=this.offsets=h},merge:function(a,b){if(!1===a instanceof THREE.BufferGeometry)THREE.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.",
+a);else{void 0===b&&(b=0);var c=this.attributes,d;for(d in c)if(void 0!==a.attributes[d])for(var e=c[d].array,f=a.attributes[d],g=f.array,h=0,f=f.itemSize*b;h<g.length;h++,f++)e[f]=g[h];return this}},normalizeNormals:function(){for(var a=this.attributes.normal.array,b,c,d,e=0,f=a.length;e<f;e+=3)b=a[e],c=a[e+1],d=a[e+2],b=1/Math.sqrt(b*b+c*c+d*d),a[e]*=b,a[e+1]*=b,a[e+2]*=b},reorderBuffers:function(a,b,c){var d={},e;for(e in this.attributes)"index"!=e&&(d[e]=new this.attributes[e].array.constructor(this.attributes[e].itemSize*
+c));for(var f=0;f<c;f++){var g=b[f];for(e in this.attributes)if("index"!=e)for(var h=this.attributes[e].array,k=this.attributes[e].itemSize,l=d[e],p=0;p<k;p++)l[f*k+p]=h[g*k+p]}this.attributes.index.array=a;for(e in this.attributes)"index"!=e&&(this.attributes[e].array=d[e],this.attributes[e].numItems=this.attributes[e].itemSize*c)},toJSON:function(){var a={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type,data:{attributes:{}}},b=this.attributes,
+c=this.offsets,d=this.boundingSphere,e;for(e in b){var f=b[e],g=Array.prototype.slice.call(f.array);a.data.attributes[e]={itemSize:f.itemSize,type:f.array.constructor.name,array:g}}0<c.length&&(a.data.offsets=JSON.parse(JSON.stringify(c)));null!==d&&(a.data.boundingSphere={center:d.center.toArray(),radius:d.radius});return a},clone:function(){var a=new THREE.BufferGeometry,b;for(b in this.attributes)a.addAttribute(b,this.attributes[b].clone());b=0;for(var c=this.offsets.length;b<c;b++){var d=this.offsets[b];
+a.offsets.push({start:d.start,index:d.index,count:d.count})}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype);
+THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Geometry";this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.dynamic=!0;this.groupsNeedUpdate=this.lineDistancesNeedUpdate=this.colorsNeedUpdate=
+this.tangentsNeedUpdate=this.normalsNeedUpdate=this.uvsNeedUpdate=this.elementsNeedUpdate=this.verticesNeedUpdate=!1};
+THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for(var b=(new THREE.Matrix3).getNormalMatrix(a),c=0,d=this.vertices.length;c<d;c++)this.vertices[c].applyMatrix4(a);c=0;for(d=this.faces.length;c<d;c++){a=this.faces[c];a.normal.applyMatrix3(b).normalize();for(var e=0,f=a.vertexNormals.length;e<f;e++)a.vertexNormals[e].applyMatrix3(b).normalize()}null!==this.boundingBox&&this.computeBoundingBox();null!==this.boundingSphere&&this.computeBoundingSphere();this.normalsNeedUpdate=
+this.verticesNeedUpdate=!0},fromBufferGeometry:function(a){for(var b=this,c=a.attributes,d=c.position.array,e=void 0!==c.index?c.index.array:void 0,f=void 0!==c.normal?c.normal.array:void 0,g=void 0!==c.color?c.color.array:void 0,h=void 0!==c.uv?c.uv.array:void 0,k=[],l=[],p=c=0;c<d.length;c+=3,p+=2)b.vertices.push(new THREE.Vector3(d[c],d[c+1],d[c+2])),void 0!==f&&k.push(new THREE.Vector3(f[c],f[c+1],f[c+2])),void 0!==g&&b.colors.push(new THREE.Color(g[c],g[c+1],g[c+2])),void 0!==h&&l.push(new THREE.Vector2(h[p],
+h[p+1]));var q=function(a,c,d){var e=void 0!==f?[k[a].clone(),k[c].clone(),k[d].clone()]:[],n=void 0!==g?[b.colors[a].clone(),b.colors[c].clone(),b.colors[d].clone()]:[];b.faces.push(new THREE.Face3(a,c,d,e,n));void 0!==h&&b.faceVertexUvs[0].push([l[a].clone(),l[c].clone(),l[d].clone()])};if(void 0!==e)if(d=a.drawcalls,0<d.length)for(c=0;c<d.length;c++)for(var p=d[c],n=p.start,t=p.count,r=p.index,p=n,n=n+t;p<n;p+=3)q(r+e[p],r+e[p+1],r+e[p+2]);else for(c=0;c<e.length;c+=3)q(e[c],e[c+1],e[c+2]);else for(c=
+0;c<d.length/3;c+=3)q(c,c+1,c+2);this.computeFaceNormals();null!==a.boundingBox&&(this.boundingBox=a.boundingBox.clone());null!==a.boundingSphere&&(this.boundingSphere=a.boundingSphere.clone());return this},center:function(){this.computeBoundingBox();var a=this.boundingBox.center().negate();this.applyMatrix((new THREE.Matrix4).setPosition(a));return a},computeFaceNormals:function(){for(var a=new THREE.Vector3,b=new THREE.Vector3,c=0,d=this.faces.length;c<d;c++){var e=this.faces[c],f=this.vertices[e.a],
+g=this.vertices[e.b];a.subVectors(this.vertices[e.c],g);b.subVectors(f,g);a.cross(b);a.normalize();e.normal.copy(a)}},computeVertexNormals:function(a){var b,c,d;d=Array(this.vertices.length);b=0;for(c=this.vertices.length;b<c;b++)d[b]=new THREE.Vector3;if(a){var e,f,g,h=new THREE.Vector3,k=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],e=this.vertices[c.a],f=this.vertices[c.b],g=this.vertices[c.c],h.subVectors(g,f),k.subVectors(e,f),h.cross(k),d[c.a].add(h),d[c.b].add(h),d[c.c].add(h)}else for(a=
+0,b=this.faces.length;a<b;a++)c=this.faces[a],d[c.a].add(c.normal),d[c.b].add(c.normal),d[c.c].add(c.normal);b=0;for(c=this.vertices.length;b<c;b++)d[b].normalize();a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],c.vertexNormals[0]=d[c.a].clone(),c.vertexNormals[1]=d[c.b].clone(),c.vertexNormals[2]=d[c.c].clone()},computeMorphNormals:function(){var a,b,c,d,e;c=0;for(d=this.faces.length;c<d;c++)for(e=this.faces[c],e.__originalFaceNormal?e.__originalFaceNormal.copy(e.normal):e.__originalFaceNormal=
+e.normal.clone(),e.__originalVertexNormals||(e.__originalVertexNormals=[]),a=0,b=e.vertexNormals.length;a<b;a++)e.__originalVertexNormals[a]?e.__originalVertexNormals[a].copy(e.vertexNormals[a]):e.__originalVertexNormals[a]=e.vertexNormals[a].clone();var f=new THREE.Geometry;f.faces=this.faces;a=0;for(b=this.morphTargets.length;a<b;a++){if(!this.morphNormals[a]){this.morphNormals[a]={};this.morphNormals[a].faceNormals=[];this.morphNormals[a].vertexNormals=[];e=this.morphNormals[a].faceNormals;var g=
+this.morphNormals[a].vertexNormals,h,k;c=0;for(d=this.faces.length;c<d;c++)h=new THREE.Vector3,k={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},e.push(h),g.push(k)}g=this.morphNormals[a];f.vertices=this.morphTargets[a].vertices;f.computeFaceNormals();f.computeVertexNormals();c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],h=g.faceNormals[c],k=g.vertexNormals[c],h.copy(e.normal),k.a.copy(e.vertexNormals[0]),k.b.copy(e.vertexNormals[1]),k.c.copy(e.vertexNormals[2])}c=0;for(d=this.faces.length;c<
+d;c++)e=this.faces[c],e.normal=e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){var a,b,c,d,e,f,g,h,k,l,p,q,n,t,r,s,u,v=[],x=[];c=new THREE.Vector3;var D=new THREE.Vector3,w=new THREE.Vector3,y=new THREE.Vector3,A=new THREE.Vector3;a=0;for(b=this.vertices.length;a<b;a++)v[a]=new THREE.Vector3,x[a]=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)e=this.faces[a],f=this.faceVertexUvs[0][a],d=e.a,u=e.b,e=e.c,g=this.vertices[d],h=this.vertices[u],k=this.vertices[e],
+l=f[0],p=f[1],q=f[2],f=h.x-g.x,n=k.x-g.x,t=h.y-g.y,r=k.y-g.y,h=h.z-g.z,g=k.z-g.z,k=p.x-l.x,s=q.x-l.x,p=p.y-l.y,l=q.y-l.y,q=1/(k*l-s*p),c.set((l*f-p*n)*q,(l*t-p*r)*q,(l*h-p*g)*q),D.set((k*n-s*f)*q,(k*r-s*t)*q,(k*g-s*h)*q),v[d].add(c),v[u].add(c),v[e].add(c),x[d].add(D),x[u].add(D),x[e].add(D);D=["a","b","c","d"];a=0;for(b=this.faces.length;a<b;a++)for(e=this.faces[a],c=0;c<Math.min(e.vertexNormals.length,3);c++)A.copy(e.vertexNormals[c]),d=e[D[c]],u=v[d],w.copy(u),w.sub(A.multiplyScalar(A.dot(u))).normalize(),
+y.crossVectors(e.vertexNormals[c],u),d=y.dot(x[d]),d=0>d?-1:1,e.vertexTangents[c]=new THREE.Vector4(w.x,w.y,w.z,d);this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;c<d;c++)0<c&&(a+=b[c].distanceTo(b[c-1])),this.lineDistances[c]=a},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);
+this.boundingSphere.setFromPoints(this.vertices)},merge:function(a,b,c){if(!1===a instanceof THREE.Geometry)THREE.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",a);else{var d,e=this.vertices.length,f=this.vertices,g=a.vertices,h=this.faces,k=a.faces,l=this.faceVertexUvs[0];a=a.faceVertexUvs[0];void 0===c&&(c=0);void 0!==b&&(d=(new THREE.Matrix3).getNormalMatrix(b));for(var p=0,q=g.length;p<q;p++){var n=g[p].clone();void 0!==b&&n.applyMatrix4(b);f.push(n)}p=0;for(q=k.length;p<
+q;p++){var g=k[p],t,r=g.vertexNormals,s=g.vertexColors,n=new THREE.Face3(g.a+e,g.b+e,g.c+e);n.normal.copy(g.normal);void 0!==d&&n.normal.applyMatrix3(d).normalize();b=0;for(f=r.length;b<f;b++)t=r[b].clone(),void 0!==d&&t.applyMatrix3(d).normalize(),n.vertexNormals.push(t);n.color.copy(g.color);b=0;for(f=s.length;b<f;b++)t=s[b],n.vertexColors.push(t.clone());n.materialIndex=g.materialIndex+c;h.push(n)}p=0;for(q=a.length;p<q;p++)if(c=a[p],d=[],void 0!==c){b=0;for(f=c.length;b<f;b++)d.push(c[b].clone());
+l.push(d)}}},mergeMesh:function(a){!1===a instanceof THREE.Mesh?THREE.error("THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.",a):(a.matrixAutoUpdate&&a.updateMatrix(),this.merge(a.geometry,a.matrix))},mergeVertices:function(){var a={},b=[],c=[],d,e=Math.pow(10,4),f,g;f=0;for(g=this.vertices.length;f<g;f++)d=this.vertices[f],d=Math.round(d.x*e)+"_"+Math.round(d.y*e)+"_"+Math.round(d.z*e),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];a=[];f=0;for(g=this.faces.length;f<
+g;f++)for(e=this.faces[f],e.a=c[e.a],e.b=c[e.b],e.c=c[e.c],e=[e.a,e.b,e.c],d=0;3>d;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0,g=this.faceVertexUvs.length;c<g;c++)this.faceVertexUvs[c].splice(e,1);f=this.vertices.length-b.length;this.vertices=b;return f},toJSON:function(){function a(a,b,c){return c?a|1<<b:a&~(1<<b)}function b(a){var b=a.x.toString()+a.y.toString()+a.z.toString();if(void 0!==l[b])return l[b];l[b]=k.length/3;k.push(a.x,a.y,
+a.z);return l[b]}function c(a){var b=a.r.toString()+a.g.toString()+a.b.toString();if(void 0!==q[b])return q[b];q[b]=p.length;p.push(a.getHex());return q[b]}function d(a){var b=a.x.toString()+a.y.toString();if(void 0!==t[b])return t[b];t[b]=n.length/2;n.push(a.x,a.y);return t[b]}var e={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type};""!==this.name&&(e.name=this.name);if(void 0!==this.parameters){var f=this.parameters,g;for(g in f)void 0!==
+f[g]&&(e[g]=f[g]);return e}f=[];for(g=0;g<this.vertices.length;g++){var h=this.vertices[g];f.push(h.x,h.y,h.z)}var h=[],k=[],l={},p=[],q={},n=[],t={};for(g=0;g<this.faces.length;g++){var r=this.faces[g],s=void 0!==this.faceVertexUvs[0][g],u=0<r.normal.length(),v=0<r.vertexNormals.length,x=1!==r.color.r||1!==r.color.g||1!==r.color.b,D=0<r.vertexColors.length,w=0,w=a(w,0,0),w=a(w,1,!1),w=a(w,2,!1),w=a(w,3,s),w=a(w,4,u),w=a(w,5,v),w=a(w,6,x),w=a(w,7,D);h.push(w);h.push(r.a,r.b,r.c);s&&(s=this.faceVertexUvs[0][g],
+h.push(d(s[0]),d(s[1]),d(s[2])));u&&h.push(b(r.normal));v&&(u=r.vertexNormals,h.push(b(u[0]),b(u[1]),b(u[2])));x&&h.push(c(r.color));D&&(r=r.vertexColors,h.push(c(r[0]),c(r[1]),c(r[2])))}e.data={};e.data.vertices=f;e.data.normals=k;0<p.length&&(e.data.colors=p);0<n.length&&(e.data.uvs=[n]);e.data.faces=h;return e},clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=b.length;c<d;c++)a.vertices.push(b[c].clone());b=this.faces;c=0;for(d=b.length;c<d;c++)a.faces.push(b[c].clone());c=0;
+for(d=this.faceVertexUvs.length;c<d;c++){b=this.faceVertexUvs[c];void 0===a.faceVertexUvs[c]&&(a.faceVertexUvs[c]=[]);for(var e=0,f=b.length;e<f;e++){for(var g=b[e],h=[],k=0,l=g.length;k<l;k++)h.push(g[k].clone());a.faceVertexUvs[c].push(h)}}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype);THREE.GeometryIdCount=0;
+THREE.Camera=function(){THREE.Object3D.call(this);this.type="Camera";this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=new THREE.Matrix4};THREE.Camera.prototype=Object.create(THREE.Object3D.prototype);THREE.Camera.prototype.constructor=THREE.Camera;THREE.Camera.prototype.getWorldDirection=function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,-1).applyQuaternion(a)}}();
+THREE.Camera.prototype.lookAt=function(){var a=new THREE.Matrix4;return function(b){a.lookAt(this.position,b,this.up);this.quaternion.setFromRotationMatrix(a)}}();THREE.Camera.prototype.clone=function(a){void 0===a&&(a=new THREE.Camera);THREE.Object3D.prototype.clone.call(this,a);a.matrixWorldInverse.copy(this.matrixWorldInverse);a.projectionMatrix.copy(this.projectionMatrix);return a};
+THREE.CubeCamera=function(a,b,c){THREE.Object3D.call(this);this.type="CubeCamera";var d=new THREE.PerspectiveCamera(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new THREE.Vector3(1,0,0));this.add(d);var e=new THREE.PerspectiveCamera(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new THREE.Vector3(-1,0,0));this.add(e);var f=new THREE.PerspectiveCamera(90,1,a,b);f.up.set(0,0,1);f.lookAt(new THREE.Vector3(0,1,0));this.add(f);var g=new THREE.PerspectiveCamera(90,1,a,b);g.up.set(0,0,-1);g.lookAt(new THREE.Vector3(0,-1,0));
+this.add(g);var h=new THREE.PerspectiveCamera(90,1,a,b);h.up.set(0,-1,0);h.lookAt(new THREE.Vector3(0,0,1));this.add(h);var k=new THREE.PerspectiveCamera(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new THREE.Vector3(0,0,-1));this.add(k);this.renderTarget=new THREE.WebGLRenderTargetCube(c,c,{format:THREE.RGBFormat,magFilter:THREE.LinearFilter,minFilter:THREE.LinearFilter});this.updateCubeMap=function(a,b){var c=this.renderTarget,n=c.generateMipmaps;c.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=
+1;a.render(b,e,c);c.activeCubeFace=2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,g,c);c.activeCubeFace=4;a.render(b,h,c);c.generateMipmaps=n;c.activeCubeFace=5;a.render(b,k,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.CubeCamera.prototype.constructor=THREE.CubeCamera;
+THREE.OrthographicCamera=function(a,b,c,d,e,f){THREE.Camera.call(this);this.type="OrthographicCamera";this.zoom=1;this.left=a;this.right=b;this.top=c;this.bottom=d;this.near=void 0!==e?e:.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()};THREE.OrthographicCamera.prototype=Object.create(THREE.Camera.prototype);THREE.OrthographicCamera.prototype.constructor=THREE.OrthographicCamera;
+THREE.OrthographicCamera.prototype.updateProjectionMatrix=function(){var a=(this.right-this.left)/(2*this.zoom),b=(this.top-this.bottom)/(2*this.zoom),c=(this.right+this.left)/2,d=(this.top+this.bottom)/2;this.projectionMatrix.makeOrthographic(c-a,c+a,d+b,d-b,this.near,this.far)};
+THREE.OrthographicCamera.prototype.clone=function(){var a=new THREE.OrthographicCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.left=this.left;a.right=this.right;a.top=this.top;a.bottom=this.bottom;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};
+THREE.PerspectiveCamera=function(a,b,c,d){THREE.Camera.call(this);this.type="PerspectiveCamera";this.zoom=1;this.fov=void 0!==a?a:50;this.aspect=void 0!==b?b:1;this.near=void 0!==c?c:.1;this.far=void 0!==d?d:2E3;this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype=Object.create(THREE.Camera.prototype);THREE.PerspectiveCamera.prototype.constructor=THREE.PerspectiveCamera;
+THREE.PerspectiveCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);this.fov=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype.setViewOffset=function(a,b,c,d,e,f){this.fullWidth=a;this.fullHeight=b;this.x=c;this.y=d;this.width=e;this.height=f;this.updateProjectionMatrix()};
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix=function(){var a=THREE.Math.radToDeg(2*Math.atan(Math.tan(.5*THREE.Math.degToRad(this.fov))/this.zoom));if(this.fullWidth){var b=this.fullWidth/this.fullHeight,a=Math.tan(THREE.Math.degToRad(.5*a))*this.near,c=-a,d=b*c,b=Math.abs(b*a-d),c=Math.abs(a-c);this.projectionMatrix.makeFrustum(d+this.x*b/this.fullWidth,d+(this.x+this.width)*b/this.fullWidth,a-(this.y+this.height)*c/this.fullHeight,a-this.y*c/this.fullHeight,this.near,this.far)}else this.projectionMatrix.makePerspective(a,
+this.aspect,this.near,this.far)};THREE.PerspectiveCamera.prototype.clone=function(){var a=new THREE.PerspectiveCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.fov=this.fov;a.aspect=this.aspect;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};THREE.Light=function(a){THREE.Object3D.call(this);this.type="Light";this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);THREE.Light.prototype.constructor=THREE.Light;
+THREE.Light.prototype.clone=function(a){void 0===a&&(a=new THREE.Light);THREE.Object3D.prototype.clone.call(this,a);a.color.copy(this.color);return a};THREE.AmbientLight=function(a){THREE.Light.call(this,a);this.type="AmbientLight"};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);THREE.AmbientLight.prototype.constructor=THREE.AmbientLight;THREE.AmbientLight.prototype.clone=function(){var a=new THREE.AmbientLight;THREE.Light.prototype.clone.call(this,a);return a};
+THREE.AreaLight=function(a,b){THREE.Light.call(this,a);this.type="AreaLight";this.normal=new THREE.Vector3(0,-1,0);this.right=new THREE.Vector3(1,0,0);this.intensity=void 0!==b?b:1;this.height=this.width=1;this.constantAttenuation=1.5;this.linearAttenuation=.5;this.quadraticAttenuation=.1};THREE.AreaLight.prototype=Object.create(THREE.Light.prototype);THREE.AreaLight.prototype.constructor=THREE.AreaLight;
+THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.type="DirectionalLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraLeft=-500;this.shadowCameraTop=this.shadowCameraRight=500;this.shadowCameraBottom=-500;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowCascade=!1;
+this.shadowCascadeOffset=new THREE.Vector3(0,0,-1E3);this.shadowCascadeCount=2;this.shadowCascadeBias=[0,0,0];this.shadowCascadeWidth=[512,512,512];this.shadowCascadeHeight=[512,512,512];this.shadowCascadeNearZ=[-1,.99,.998];this.shadowCascadeFarZ=[.99,.998,1];this.shadowCascadeArray=[];this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.DirectionalLight.prototype=Object.create(THREE.Light.prototype);THREE.DirectionalLight.prototype.constructor=THREE.DirectionalLight;
+THREE.DirectionalLight.prototype.clone=function(){var a=new THREE.DirectionalLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraLeft=this.shadowCameraLeft;a.shadowCameraRight=this.shadowCameraRight;a.shadowCameraTop=this.shadowCameraTop;a.shadowCameraBottom=this.shadowCameraBottom;a.shadowCameraVisible=
+this.shadowCameraVisible;a.shadowBias=this.shadowBias;a.shadowDarkness=this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;a.shadowCascade=this.shadowCascade;a.shadowCascadeOffset.copy(this.shadowCascadeOffset);a.shadowCascadeCount=this.shadowCascadeCount;a.shadowCascadeBias=this.shadowCascadeBias.slice(0);a.shadowCascadeWidth=this.shadowCascadeWidth.slice(0);a.shadowCascadeHeight=this.shadowCascadeHeight.slice(0);a.shadowCascadeNearZ=this.shadowCascadeNearZ.slice(0);
+a.shadowCascadeFarZ=this.shadowCascadeFarZ.slice(0);return a};THREE.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.type="HemisphereLight";this.position.set(0,100,0);this.groundColor=new THREE.Color(b);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);THREE.HemisphereLight.prototype.constructor=THREE.HemisphereLight;
+THREE.HemisphereLight.prototype.clone=function(){var a=new THREE.HemisphereLight;THREE.Light.prototype.clone.call(this,a);a.groundColor.copy(this.groundColor);a.intensity=this.intensity;return a};THREE.PointLight=function(a,b,c,d){THREE.Light.call(this,a);this.type="PointLight";this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.decay=void 0!==d?d:1};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);THREE.PointLight.prototype.constructor=THREE.PointLight;
+THREE.PointLight.prototype.clone=function(){var a=new THREE.PointLight;THREE.Light.prototype.clone.call(this,a);a.intensity=this.intensity;a.distance=this.distance;a.decay=this.decay;return a};
+THREE.SpotLight=function(a,b,c,d,e,f){THREE.Light.call(this,a);this.type="SpotLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.exponent=void 0!==e?e:10;this.decay=void 0!==f?f:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraFov=50;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=
+512;this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.SpotLight.prototype=Object.create(THREE.Light.prototype);THREE.SpotLight.prototype.constructor=THREE.SpotLight;
+THREE.SpotLight.prototype.clone=function(){var a=new THREE.SpotLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.distance=this.distance;a.angle=this.angle;a.exponent=this.exponent;a.decay=this.decay;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraFov=this.shadowCameraFov;a.shadowCameraVisible=this.shadowCameraVisible;a.shadowBias=this.shadowBias;
+a.shadowDarkness=this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;return a};THREE.Cache={files:{},add:function(a,b){this.files[a]=b},get:function(a){return this.files[a]},remove:function(a){delete this.files[a]},clear:function(){this.files={}}};
+THREE.Loader=function(a){this.statusDomElement=(this.showStatus=a)?THREE.Loader.prototype.addStatusElement():null;this.imageLoader=new THREE.ImageLoader;this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}};
+THREE.Loader.prototype={constructor:THREE.Loader,crossOrigin:void 0,addStatusElement:function(){var a=document.createElement("div");a.style.position="absolute";a.style.right="0px";a.style.top="0px";a.style.fontSize="0.8em";a.style.textAlign="left";a.style.background="rgba(0,0,0,0.25)";a.style.color="#fff";a.style.width="120px";a.style.padding="0.5em 0.5em 0.5em 0.5em";a.style.zIndex=1E3;a.innerHTML="Loading ...";return a},updateProgress:function(a){var b="Loaded ",b=a.total?b+((100*a.loaded/a.total).toFixed(0)+
+"%"):b+((a.loaded/1024).toFixed(2)+" KB");this.statusDomElement.innerHTML=b},extractUrlBase:function(a){a=a.split("/");if(1===a.length)return"./";a.pop();return a.join("/")+"/"},initMaterials:function(a,b){for(var c=[],d=0;d<a.length;++d)c[d]=this.createMaterial(a[d],b);return c},needsTangents:function(a){for(var b=0,c=a.length;b<c;b++)if(a[b]instanceof THREE.ShaderMaterial)return!0;return!1},createMaterial:function(a,b){function c(a){a=Math.log(a)/Math.LN2;return Math.pow(2,Math.round(a))}function d(a,
+d,e,g,h,k,s){var u=b+e,v,x=THREE.Loader.Handlers.get(u);null!==x?v=x.load(u):(v=new THREE.Texture,x=f.imageLoader,x.crossOrigin=f.crossOrigin,x.load(u,function(a){if(!1===THREE.Math.isPowerOfTwo(a.width)||!1===THREE.Math.isPowerOfTwo(a.height)){var b=c(a.width),d=c(a.height),e=document.createElement("canvas");e.width=b;e.height=d;e.getContext("2d").drawImage(a,0,0,b,d);v.image=e}else v.image=a;v.needsUpdate=!0}));v.sourceFile=e;g&&(v.repeat.set(g[0],g[1]),1!==g[0]&&(v.wrapS=THREE.RepeatWrapping),
+1!==g[1]&&(v.wrapT=THREE.RepeatWrapping));h&&v.offset.set(h[0],h[1]);k&&(e={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==e[k[0]]&&(v.wrapS=e[k[0]]),void 0!==e[k[1]]&&(v.wrapT=e[k[1]]));s&&(v.anisotropy=s);a[d]=v}function e(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var f=this,g="MeshLambertMaterial",h={color:15658734,opacity:1,map:null,lightMap:null,normalMap:null,bumpMap:null,wireframe:!1};if(a.shading){var k=a.shading.toLowerCase();"phong"===k?g="MeshPhongMaterial":
+"basic"===k&&(g="MeshBasicMaterial")}void 0!==a.blending&&void 0!==THREE[a.blending]&&(h.blending=THREE[a.blending]);void 0!==a.transparent&&(h.transparent=a.transparent);void 0!==a.opacity&&1>a.opacity&&(h.transparent=!0);void 0!==a.depthTest&&(h.depthTest=a.depthTest);void 0!==a.depthWrite&&(h.depthWrite=a.depthWrite);void 0!==a.visible&&(h.visible=a.visible);void 0!==a.flipSided&&(h.side=THREE.BackSide);void 0!==a.doubleSided&&(h.side=THREE.DoubleSide);void 0!==a.wireframe&&(h.wireframe=a.wireframe);
+void 0!==a.vertexColors&&("face"===a.vertexColors?h.vertexColors=THREE.FaceColors:a.vertexColors&&(h.vertexColors=THREE.VertexColors));a.colorDiffuse?h.color=e(a.colorDiffuse):a.DbgColor&&(h.color=a.DbgColor);a.colorSpecular&&(h.specular=e(a.colorSpecular));a.colorEmissive&&(h.emissive=e(a.colorEmissive));void 0!==a.transparency&&(console.warn("THREE.Loader: transparency has been renamed to opacity"),a.opacity=a.transparency);void 0!==a.opacity&&(h.opacity=a.opacity);a.specularCoef&&(h.shininess=
+a.specularCoef);a.mapDiffuse&&b&&d(h,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap,a.mapDiffuseAnisotropy);a.mapLight&&b&&d(h,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&d(h,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&d(h,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&d(h,"specularMap",
+a.mapSpecular,a.mapSpecularRepeat,a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapAlpha&&b&&d(h,"alphaMap",a.mapAlpha,a.mapAlphaRepeat,a.mapAlphaOffset,a.mapAlphaWrap,a.mapAlphaAnisotropy);a.mapBumpScale&&(h.bumpScale=a.mapBumpScale);a.mapNormalFactor&&(h.normalScale=new THREE.Vector2(a.mapNormalFactor,a.mapNormalFactor));g=new THREE[g](h);void 0!==a.DbgName&&(g.name=a.DbgName);return g}};
+THREE.Loader.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=0,c=this.handlers.length;b<c;b+=2){var d=this.handlers[b+1];if(this.handlers[b].test(a))return d}return null}};THREE.XHRLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(a,b,c,d){var e=this,f=THREE.Cache.get(a);void 0!==f?b&&b(f):(f=new XMLHttpRequest,f.open("GET",a,!0),f.addEventListener("load",function(c){THREE.Cache.add(a,this.response);b&&b(this.response);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(f.responseType=
+this.responseType),f.send(null),e.manager.itemStart(a))},setResponseType:function(a){this.responseType=a},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.ImageLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d){var e=this,f=THREE.Cache.get(a);if(void 0!==f)b(f);else return f=document.createElement("img"),f.addEventListener("load",function(c){THREE.Cache.add(a,this);b&&b(this);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),f.src=a,e.manager.itemStart(a),f},setCrossOrigin:function(a){this.crossOrigin=
+a}};THREE.JSONLoader=function(a){THREE.Loader.call(this,a);this.withCredentials=!1};THREE.JSONLoader.prototype=Object.create(THREE.Loader.prototype);THREE.JSONLoader.prototype.constructor=THREE.JSONLoader;THREE.JSONLoader.prototype.load=function(a,b,c){c=c&&"string"===typeof c?c:this.extractUrlBase(a);this.onLoadStart();this.loadAjaxJSON(this,a,b,c)};
+THREE.JSONLoader.prototype.loadAjaxJSON=function(a,b,c,d,e){var f=new XMLHttpRequest,g=0;f.onreadystatechange=function(){if(f.readyState===f.DONE)if(200===f.status||0===f.status){if(f.responseText){var h=JSON.parse(f.responseText),k=h.metadata;if(void 0!==k){if("object"===k.type){THREE.error("THREE.JSONLoader: "+b+" should be loaded with THREE.ObjectLoader instead.");return}if("scene"===k.type){THREE.error("THREE.JSONLoader: "+b+" seems to be a Scene. Use THREE.SceneLoader instead.");return}}h=a.parse(h,
+d);c(h.geometry,h.materials)}else THREE.error("THREE.JSONLoader: "+b+" seems to be unreachable or the file is empty.");a.onLoadComplete()}else THREE.error("THREE.JSONLoader: Couldn't load "+b+" ("+f.status+")");else f.readyState===f.LOADING?e&&(0===g&&(g=f.getResponseHeader("Content-Length")),e({total:g,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&void 0!==e&&(g=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
+THREE.JSONLoader.prototype.parse=function(a,b){var c=new THREE.Geometry,d=void 0!==a.scale?1/a.scale:1;(function(b){var d,g,h,k,l,p,q,n,t,r,s,u,v,x=a.faces;p=a.vertices;var D=a.normals,w=a.colors,y=0;if(void 0!==a.uvs){for(d=0;d<a.uvs.length;d++)a.uvs[d].length&&y++;for(d=0;d<y;d++)c.faceVertexUvs[d]=[]}k=0;for(l=p.length;k<l;)d=new THREE.Vector3,d.x=p[k++]*b,d.y=p[k++]*b,d.z=p[k++]*b,c.vertices.push(d);k=0;for(l=x.length;k<l;)if(b=x[k++],t=b&1,h=b&2,d=b&8,q=b&16,r=b&32,p=b&64,b&=128,t){t=new THREE.Face3;
+t.a=x[k];t.b=x[k+1];t.c=x[k+3];s=new THREE.Face3;s.a=x[k+1];s.b=x[k+2];s.c=x[k+3];k+=4;h&&(h=x[k++],t.materialIndex=h,s.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<y;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],c.faceVertexUvs[d][h+1]=[],g=0;4>g;g++)n=x[k++],v=u[2*n],n=u[2*n+1],v=new THREE.Vector2(v,n),2!==g&&c.faceVertexUvs[d][h].push(v),0!==g&&c.faceVertexUvs[d][h+1].push(v);q&&(q=3*x[k++],t.normal.set(D[q++],D[q++],D[q]),s.normal.copy(t.normal));if(r)for(d=0;4>d;d++)q=3*x[k++],r=new THREE.Vector3(D[q++],
+D[q++],D[q]),2!==d&&t.vertexNormals.push(r),0!==d&&s.vertexNormals.push(r);p&&(p=x[k++],p=w[p],t.color.setHex(p),s.color.setHex(p));if(b)for(d=0;4>d;d++)p=x[k++],p=w[p],2!==d&&t.vertexColors.push(new THREE.Color(p)),0!==d&&s.vertexColors.push(new THREE.Color(p));c.faces.push(t);c.faces.push(s)}else{t=new THREE.Face3;t.a=x[k++];t.b=x[k++];t.c=x[k++];h&&(h=x[k++],t.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<y;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],g=0;3>g;g++)n=x[k++],v=u[2*n],n=u[2*n+1],
+v=new THREE.Vector2(v,n),c.faceVertexUvs[d][h].push(v);q&&(q=3*x[k++],t.normal.set(D[q++],D[q++],D[q]));if(r)for(d=0;3>d;d++)q=3*x[k++],r=new THREE.Vector3(D[q++],D[q++],D[q]),t.vertexNormals.push(r);p&&(p=x[k++],t.color.setHex(w[p]));if(b)for(d=0;3>d;d++)p=x[k++],t.vertexColors.push(new THREE.Color(w[p]));c.faces.push(t)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;d<g;d+=b)c.skinWeights.push(new THREE.Vector4(a.skinWeights[d],
+1<b?a.skinWeights[d+1]:0,2<b?a.skinWeights[d+2]:0,3<b?a.skinWeights[d+3]:0));if(a.skinIndices)for(d=0,g=a.skinIndices.length;d<g;d+=b)c.skinIndices.push(new THREE.Vector4(a.skinIndices[d],1<b?a.skinIndices[d+1]:0,2<b?a.skinIndices[d+2]:0,3<b?a.skinIndices[d+3]:0));c.bones=a.bones;c.bones&&0<c.bones.length&&(c.skinWeights.length!==c.skinIndices.length||c.skinIndices.length!==c.vertices.length)&&THREE.warn("THREE.JSONLoader: When skinning, number of vertices ("+c.vertices.length+"), skinIndices ("+
+c.skinIndices.length+"), and skinWeights ("+c.skinWeights.length+") should match.");c.animation=a.animation;c.animations=a.animations})();(function(b){if(void 0!==a.morphTargets){var d,g,h,k,l,p;d=0;for(g=a.morphTargets.length;d<g;d++)for(c.morphTargets[d]={},c.morphTargets[d].name=a.morphTargets[d].name,c.morphTargets[d].vertices=[],l=c.morphTargets[d].vertices,p=a.morphTargets[d].vertices,h=0,k=p.length;h<k;h+=3){var q=new THREE.Vector3;q.x=p[h]*b;q.y=p[h+1]*b;q.z=p[h+2]*b;l.push(q)}}if(void 0!==
+a.morphColors)for(d=0,g=a.morphColors.length;d<g;d++)for(c.morphColors[d]={},c.morphColors[d].name=a.morphColors[d].name,c.morphColors[d].colors=[],k=c.morphColors[d].colors,l=a.morphColors[d].colors,b=0,h=l.length;b<h;b+=3)p=new THREE.Color(16755200),p.setRGB(l[b],l[b+1],l[b+2]),k.push(p)})(d);c.computeFaceNormals();c.computeBoundingSphere();if(void 0===a.materials||0===a.materials.length)return{geometry:c};d=this.initMaterials(a.materials,b);this.needsTangents(d)&&c.computeTangents();return{geometry:c,
+materials:d}};THREE.LoadingManager=function(a,b,c){var d=this,e=0,f=0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(a){f++};this.itemEnd=function(a){e++;if(void 0!==d.onProgress)d.onProgress(a,e,f);if(e===f&&void 0!==d.onLoad)d.onLoad()}};THREE.DefaultLoadingManager=new THREE.LoadingManager;THREE.BufferGeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader(e.manager);f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE.BufferGeometry,c=a.data.attributes,d;for(d in c){var e=c[d],f=new self[e.type](e.array);b.addAttribute(d,new THREE.BufferAttribute(f,e.itemSize))}c=a.data.offsets;void 0!==c&&(b.offsets=JSON.parse(JSON.stringify(c)));
+a=a.data.boundingSphere;void 0!==a&&(c=new THREE.Vector3,void 0!==a.center&&c.fromArray(a.center),b.boundingSphere=new THREE.Sphere(c,a.radius));return b}};THREE.MaterialLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader(e.manager);f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE[a.type];void 0!==a.color&&b.color.setHex(a.color);void 0!==a.emissive&&b.emissive.setHex(a.emissive);void 0!==a.specular&&b.specular.setHex(a.specular);void 0!==a.shininess&&(b.shininess=a.shininess);
+void 0!==a.uniforms&&(b.uniforms=a.uniforms);void 0!==a.vertexShader&&(b.vertexShader=a.vertexShader);void 0!==a.fragmentShader&&(b.fragmentShader=a.fragmentShader);void 0!==a.vertexColors&&(b.vertexColors=a.vertexColors);void 0!==a.shading&&(b.shading=a.shading);void 0!==a.blending&&(b.blending=a.blending);void 0!==a.side&&(b.side=a.side);void 0!==a.opacity&&(b.opacity=a.opacity);void 0!==a.transparent&&(b.transparent=a.transparent);void 0!==a.wireframe&&(b.wireframe=a.wireframe);void 0!==a.size&&
+(b.size=a.size);void 0!==a.sizeAttenuation&&(b.sizeAttenuation=a.sizeAttenuation);if(void 0!==a.materials)for(var c=0,d=a.materials.length;c<d;c++)b.materials.push(this.parse(a.materials[c]));return b}};THREE.ObjectLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager;this.texturePath=""};
+THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(a,b,c,d){""===this.texturePath&&(this.texturePath=a.substring(0,a.lastIndexOf("/")+1));var e=this,f=new THREE.XHRLoader(e.manager);f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){e.parse(JSON.parse(a),b)},c,d)},setTexturePath:function(a){this.texturePath=a},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a,b){var c=this.parseGeometries(a.geometries),d=this.parseImages(a.images,function(){void 0!==b&&b(e)}),
+d=this.parseTextures(a.textures,d),d=this.parseMaterials(a.materials,d),e=this.parseObject(a.object,c,d);void 0!==a.images&&0!==a.images.length||void 0===b||b(e);return e},parseGeometries:function(a){var b={};if(void 0!==a)for(var c=new THREE.JSONLoader,d=new THREE.BufferGeometryLoader,e=0,f=a.length;e<f;e++){var g,h=a[e];switch(h.type){case "PlaneGeometry":case "PlaneBufferGeometry":g=new THREE[h.type](h.width,h.height,h.widthSegments,h.heightSegments);break;case "BoxGeometry":case "CubeGeometry":g=
+new THREE.BoxGeometry(h.width,h.height,h.depth,h.widthSegments,h.heightSegments,h.depthSegments);break;case "CircleGeometry":g=new THREE.CircleGeometry(h.radius,h.segments);break;case "CylinderGeometry":g=new THREE.CylinderGeometry(h.radiusTop,h.radiusBottom,h.height,h.radialSegments,h.heightSegments,h.openEnded);break;case "SphereGeometry":g=new THREE.SphereGeometry(h.radius,h.widthSegments,h.heightSegments,h.phiStart,h.phiLength,h.thetaStart,h.thetaLength);break;case "IcosahedronGeometry":g=new THREE.IcosahedronGeometry(h.radius,
+h.detail);break;case "TorusGeometry":g=new THREE.TorusGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.arc);break;case "TorusKnotGeometry":g=new THREE.TorusKnotGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.p,h.q,h.heightScale);break;case "BufferGeometry":g=d.parse(h);break;case "Geometry":g=c.parse(h.data).geometry}g.uuid=h.uuid;void 0!==h.name&&(g.name=h.name);b[h.uuid]=g}return b},parseMaterials:function(a,b){var c={};if(void 0!==a)for(var d=function(a){void 0===b[a]&&
+THREE.warn("THREE.ObjectLoader: Undefined texture",a);return b[a]},e=new THREE.MaterialLoader,f=0,g=a.length;f<g;f++){var h=a[f],k=e.parse(h);k.uuid=h.uuid;void 0!==h.name&&(k.name=h.name);void 0!==h.map&&(k.map=d(h.map));void 0!==h.bumpMap&&(k.bumpMap=d(h.bumpMap),h.bumpScale&&(k.bumpScale=new THREE.Vector2(h.bumpScale,h.bumpScale)));void 0!==h.alphaMap&&(k.alphaMap=d(h.alphaMap));void 0!==h.envMap&&(k.envMap=d(h.envMap));void 0!==h.normalMap&&(k.normalMap=d(h.normalMap),h.normalScale&&(k.normalScale=
+new THREE.Vector2(h.normalScale,h.normalScale)));void 0!==h.lightMap&&(k.lightMap=d(h.lightMap));void 0!==h.specularMap&&(k.specularMap=d(h.specularMap));c[h.uuid]=k}return c},parseImages:function(a,b){var c=this,d={};if(void 0!==a&&0<a.length){var e=new THREE.LoadingManager(b),f=new THREE.ImageLoader(e);f.setCrossOrigin(this.crossOrigin);for(var e=function(a){c.manager.itemStart(a);return f.load(a,function(){c.manager.itemEnd(a)})},g=0,h=a.length;g<h;g++){var k=a[g],l=/^(\/\/)|([a-z]+:(\/\/)?)/i.test(k.url)?
+k.url:c.texturePath+k.url;d[k.uuid]=e(l)}}return d},parseTextures:function(a,b){var c={};if(void 0!==a)for(var d=0,e=a.length;d<e;d++){var f=a[d];void 0===f.image&&THREE.warn('THREE.ObjectLoader: No "image" speficied for',f.uuid);void 0===b[f.image]&&THREE.warn("THREE.ObjectLoader: Undefined image",f.image);var g=new THREE.Texture(b[f.image]);g.needsUpdate=!0;g.uuid=f.uuid;void 0!==f.name&&(g.name=f.name);void 0!==f.repeat&&(g.repeat=new THREE.Vector2(f.repeat[0],f.repeat[1]));void 0!==f.minFilter&&
+(g.minFilter=THREE[f.minFilter]);void 0!==f.magFilter&&(g.magFilter=THREE[f.magFilter]);void 0!==f.anisotropy&&(g.anisotropy=f.anisotropy);f.wrap instanceof Array&&(g.wrapS=THREE[f.wrap[0]],g.wrapT=THREE[f.wrap[1]]);c[f.uuid]=g}return c},parseObject:function(){var a=new THREE.Matrix4;return function(b,c,d){var e;e=function(a){void 0===c[a]&&THREE.warn("THREE.ObjectLoader: Undefined geometry",a);return c[a]};var f=function(a){void 0===d[a]&&THREE.warn("THREE.ObjectLoader: Undefined material",a);return d[a]};
+switch(b.type){case "Scene":e=new THREE.Scene;break;case "PerspectiveCamera":e=new THREE.PerspectiveCamera(b.fov,b.aspect,b.near,b.far);break;case "OrthographicCamera":e=new THREE.OrthographicCamera(b.left,b.right,b.top,b.bottom,b.near,b.far);break;case "AmbientLight":e=new THREE.AmbientLight(b.color);break;case "DirectionalLight":e=new THREE.DirectionalLight(b.color,b.intensity);break;case "PointLight":e=new THREE.PointLight(b.color,b.intensity,b.distance,b.decay);break;case "SpotLight":e=new THREE.SpotLight(b.color,
+b.intensity,b.distance,b.angle,b.exponent,b.decay);break;case "HemisphereLight":e=new THREE.HemisphereLight(b.color,b.groundColor,b.intensity);break;case "Mesh":e=new THREE.Mesh(e(b.geometry),f(b.material));break;case "Line":e=new THREE.Line(e(b.geometry),f(b.material),b.mode);break;case "PointCloud":e=new THREE.PointCloud(e(b.geometry),f(b.material));break;case "Sprite":e=new THREE.Sprite(f(b.material));break;case "Group":e=new THREE.Group;break;default:e=new THREE.Object3D}e.uuid=b.uuid;void 0!==
+b.name&&(e.name=b.name);void 0!==b.matrix?(a.fromArray(b.matrix),a.decompose(e.position,e.quaternion,e.scale)):(void 0!==b.position&&e.position.fromArray(b.position),void 0!==b.rotation&&e.rotation.fromArray(b.rotation),void 0!==b.scale&&e.scale.fromArray(b.scale));void 0!==b.visible&&(e.visible=b.visible);void 0!==b.userData&&(e.userData=b.userData);if(void 0!==b.children)for(var g in b.children)e.add(this.parseObject(b.children[g],c,d));return e}}()};
+THREE.TextureLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a,b,c,d){var e=new THREE.ImageLoader(this.manager);e.setCrossOrigin(this.crossOrigin);e.load(a,function(a){a=new THREE.Texture(a);a.needsUpdate=!0;void 0!==b&&b(a)},c,d)},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.DataTextureLoader=THREE.BinaryTextureLoader=function(){this._parser=null};
+THREE.BinaryTextureLoader.prototype={constructor:THREE.BinaryTextureLoader,load:function(a,b,c,d){var e=this,f=new THREE.DataTexture,g=new THREE.XHRLoader;g.setResponseType("arraybuffer");g.load(a,function(a){if(a=e._parser(a))void 0!==a.image?f.image=a.image:void 0!==a.data&&(f.image.width=a.width,f.image.height=a.height,f.image.data=a.data),f.wrapS=void 0!==a.wrapS?a.wrapS:THREE.ClampToEdgeWrapping,f.wrapT=void 0!==a.wrapT?a.wrapT:THREE.ClampToEdgeWrapping,f.magFilter=void 0!==a.magFilter?a.magFilter:
+THREE.LinearFilter,f.minFilter=void 0!==a.minFilter?a.minFilter:THREE.LinearMipMapLinearFilter,f.anisotropy=void 0!==a.anisotropy?a.anisotropy:1,void 0!==a.format&&(f.format=a.format),void 0!==a.type&&(f.type=a.type),void 0!==a.mipmaps&&(f.mipmaps=a.mipmaps),1===a.mipmapCount&&(f.minFilter=THREE.LinearFilter),f.needsUpdate=!0,b&&b(f,a)},c,d);return f}};THREE.CompressedTextureLoader=function(){this._parser=null};
+THREE.CompressedTextureLoader.prototype={constructor:THREE.CompressedTextureLoader,load:function(a,b,c){var d=this,e=[],f=new THREE.CompressedTexture;f.image=e;var g=new THREE.XHRLoader;g.setResponseType("arraybuffer");if(a instanceof Array){var h=0;c=function(c){g.load(a[c],function(a){a=d._parser(a,!0);e[c]={width:a.width,height:a.height,format:a.format,mipmaps:a.mipmaps};h+=1;6===h&&(1==a.mipmapCount&&(f.minFilter=THREE.LinearFilter),f.format=a.format,f.needsUpdate=!0,b&&b(f))})};for(var k=0,l=
+a.length;k<l;++k)c(k)}else g.load(a,function(a){a=d._parser(a,!0);if(a.isCubemap)for(var c=a.mipmaps.length/a.mipmapCount,g=0;g<c;g++){e[g]={mipmaps:[]};for(var h=0;h<a.mipmapCount;h++)e[g].mipmaps.push(a.mipmaps[g*a.mipmapCount+h]),e[g].format=a.format,e[g].width=a.width,e[g].height=a.height}else f.image.width=a.width,f.image.height=a.height,f.mipmaps=a.mipmaps;1===a.mipmapCount&&(f.minFilter=THREE.LinearFilter);f.format=a.format;f.needsUpdate=!0;b&&b(f)});return f}};
+THREE.Material=function(){Object.defineProperty(this,"id",{value:THREE.MaterialIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Material";this.side=THREE.FrontSide;this.opacity=1;this.transparent=!1;this.blending=THREE.NormalBlending;this.blendSrc=THREE.SrcAlphaFactor;this.blendDst=THREE.OneMinusSrcAlphaFactor;this.blendEquation=THREE.AddEquation;this.blendEquationAlpha=this.blendDstAlpha=this.blendSrcAlpha=null;this.colorWrite=this.depthWrite=this.depthTest=!0;this.polygonOffset=
+!1;this.overdraw=this.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=0;this._needsUpdate=this.visible=!0};
+THREE.Material.prototype={constructor:THREE.Material,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)THREE.warn("THREE.Material: '"+b+"' parameter is undefined.");else if(b in this){var d=this[b];d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]="overdraw"==b?Number(c):c}}},toJSON:function(){var a={metadata:{version:4.2,
+type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type};""!==this.name&&(a.name=this.name);this instanceof THREE.MeshBasicMaterial?(a.color=this.color.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshLambertMaterial?(a.color=this.color.getHex(),a.emissive=this.emissive.getHex(),this.vertexColors!==THREE.NoColors&&
+(a.vertexColors=this.vertexColors),this.shading!==THREE.SmoothShading&&(a.shading=this.shading),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshPhongMaterial?(a.color=this.color.getHex(),a.emissive=this.emissive.getHex(),a.specular=this.specular.getHex(),a.shininess=this.shininess,this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.shading!==THREE.SmoothShading&&(a.shading=this.shading),
+this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshNormalMaterial?(this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshDepthMaterial?(this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.PointCloudMaterial?(a.size=this.size,a.sizeAttenuation=
+this.sizeAttenuation,a.color=this.color.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending)):this instanceof THREE.ShaderMaterial?(a.uniforms=this.uniforms,a.vertexShader=this.vertexShader,a.fragmentShader=this.fragmentShader):this instanceof THREE.SpriteMaterial&&(a.color=this.color.getHex());1>this.opacity&&(a.opacity=this.opacity);!1!==this.transparent&&(a.transparent=this.transparent);!1!==this.wireframe&&
+(a.wireframe=this.wireframe);return a},clone:function(a){void 0===a&&(a=new THREE.Material);a.name=this.name;a.side=this.side;a.opacity=this.opacity;a.transparent=this.transparent;a.blending=this.blending;a.blendSrc=this.blendSrc;a.blendDst=this.blendDst;a.blendEquation=this.blendEquation;a.blendSrcAlpha=this.blendSrcAlpha;a.blendDstAlpha=this.blendDstAlpha;a.blendEquationAlpha=this.blendEquationAlpha;a.depthTest=this.depthTest;a.depthWrite=this.depthWrite;a.polygonOffset=this.polygonOffset;a.polygonOffsetFactor=
+this.polygonOffsetFactor;a.polygonOffsetUnits=this.polygonOffsetUnits;a.alphaTest=this.alphaTest;a.overdraw=this.overdraw;a.visible=this.visible;return a},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Material.prototype);THREE.MaterialIdCount=0;
+THREE.LineBasicMaterial=function(a){THREE.Material.call(this);this.type="LineBasicMaterial";this.color=new THREE.Color(16777215);this.linewidth=1;this.linejoin=this.linecap="round";this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype);THREE.LineBasicMaterial.prototype.constructor=THREE.LineBasicMaterial;
+THREE.LineBasicMaterial.prototype.clone=function(){var a=new THREE.LineBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.linecap=this.linecap;a.linejoin=this.linejoin;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
+THREE.LineDashedMaterial=function(a){THREE.Material.call(this);this.type="LineDashedMaterial";this.color=new THREE.Color(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype);THREE.LineDashedMaterial.prototype.constructor=THREE.LineDashedMaterial;
+THREE.LineDashedMaterial.prototype.clone=function(){var a=new THREE.LineDashedMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.scale=this.scale;a.dashSize=this.dashSize;a.gapSize=this.gapSize;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
+THREE.MeshBasicMaterial=function(a){THREE.Material.call(this);this.type="MeshBasicMaterial";this.color=new THREE.Color(16777215);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphTargets=this.skinning=!1;this.setValues(a)};
+THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshBasicMaterial.prototype.constructor=THREE.MeshBasicMaterial;
+THREE.MeshBasicMaterial.prototype.clone=function(){var a=new THREE.MeshBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;
+a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;return a};
+THREE.MeshLambertMaterial=function(a){THREE.Material.call(this);this.type="MeshLambertMaterial";this.color=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap=
+"round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshLambertMaterial.prototype.constructor=THREE.MeshLambertMaterial;
+THREE.MeshLambertMaterial.prototype.clone=function(){var a=new THREE.MeshLambertMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.emissive.copy(this.emissive);a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=
+this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
+THREE.MeshPhongMaterial=function(a){THREE.Material.call(this);this.type="MeshPhongMaterial";this.color=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.specular=new THREE.Color(1118481);this.shininess=30;this.wrapAround=this.metal=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.bumpMap=this.lightMap=this.map=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new THREE.Vector2(1,1);this.envMap=this.alphaMap=this.specularMap=null;this.combine=THREE.MultiplyOperation;this.reflectivity=
+1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshPhongMaterial.prototype.constructor=THREE.MeshPhongMaterial;
+THREE.MeshPhongMaterial.prototype.clone=function(){var a=new THREE.MeshPhongMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.emissive.copy(this.emissive);a.specular.copy(this.specular);a.shininess=this.shininess;a.metal=this.metal;a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.bumpMap=this.bumpMap;a.bumpScale=this.bumpScale;a.normalMap=this.normalMap;a.normalScale.copy(this.normalScale);a.specularMap=this.specularMap;
+a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
+THREE.MeshDepthMaterial=function(a){THREE.Material.call(this);this.type="MeshDepthMaterial";this.wireframe=this.morphTargets=!1;this.wireframeLinewidth=1;this.setValues(a)};THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshDepthMaterial.prototype.constructor=THREE.MeshDepthMaterial;
+THREE.MeshDepthMaterial.prototype.clone=function(){var a=new THREE.MeshDepthMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshNormalMaterial=function(a){THREE.Material.call(this,a);this.type="MeshNormalMaterial";this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=!1;this.setValues(a)};THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.MeshNormalMaterial.prototype.constructor=THREE.MeshNormalMaterial;THREE.MeshNormalMaterial.prototype.clone=function(){var a=new THREE.MeshNormalMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshFaceMaterial=function(a){this.uuid=THREE.Math.generateUUID();this.type="MeshFaceMaterial";this.materials=a instanceof Array?a:[]};
+THREE.MeshFaceMaterial.prototype={constructor:THREE.MeshFaceMaterial,toJSON:function(){for(var a={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},b=0,c=this.materials.length;b<c;b++)a.materials.push(this.materials[b].toJSON());return a},clone:function(){for(var a=new THREE.MeshFaceMaterial,b=0;b<this.materials.length;b++)a.materials.push(this.materials[b].clone());return a}};
+THREE.PointCloudMaterial=function(a){THREE.Material.call(this);this.type="PointCloudMaterial";this.color=new THREE.Color(16777215);this.map=null;this.size=1;this.sizeAttenuation=!0;this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.PointCloudMaterial.prototype=Object.create(THREE.Material.prototype);THREE.PointCloudMaterial.prototype.constructor=THREE.PointCloudMaterial;
+THREE.PointCloudMaterial.prototype.clone=function(){var a=new THREE.PointCloudMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.size=this.size;a.sizeAttenuation=this.sizeAttenuation;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.ParticleBasicMaterial=function(a){THREE.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
+THREE.ParticleSystemMaterial=function(a){THREE.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
+THREE.ShaderMaterial=function(a){THREE.Material.call(this);this.type="ShaderMaterial";this.defines={};this.uniforms={};this.attributes=null;this.vertexShader="void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";this.fragmentShader="void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}";this.shading=THREE.SmoothShading;this.linewidth=1;this.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=
+this.morphTargets=this.skinning=!1;this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv2:[0,0]};this.index0AttributeName=void 0;this.setValues(a)};THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);THREE.ShaderMaterial.prototype.constructor=THREE.ShaderMaterial;
 THREE.ShaderMaterial.prototype.clone=function(){var a=new THREE.ShaderMaterial;THREE.Material.prototype.clone.call(this,a);a.fragmentShader=this.fragmentShader;a.vertexShader=this.vertexShader;a.uniforms=THREE.UniformsUtils.clone(this.uniforms);a.attributes=this.attributes;a.defines=this.defines;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.fog=this.fog;a.lights=this.lights;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=
-this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.SpriteMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.map=new THREE.Texture;this.useScreenCoordinates=!0;this.depthTest=!this.useScreenCoordinates;this.sizeAttenuation=!this.useScreenCoordinates;this.alignment=THREE.SpriteAlignment.center.clone();this.fog=!1;this.uvOffset=new THREE.Vector2(0,0);this.uvScale=new THREE.Vector2(1,1);this.setValues(a);a=a||{};void 0===a.depthTest&&(this.depthTest=!this.useScreenCoordinates);void 0===a.sizeAttenuation&&(this.sizeAttenuation=
-!this.useScreenCoordinates)};THREE.SpriteMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteMaterial.prototype.clone=function(){var a=new THREE.SpriteMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.useScreenCoordinates=this.useScreenCoordinates;a.sizeAttenuation=this.sizeAttenuation;a.alignment.copy(this.alignment);a.uvOffset.copy(this.uvOffset);a.uvScale.copy(this.uvScale);a.fog=this.fog;return a};THREE.SpriteAlignment={};
-THREE.SpriteAlignment.topLeft=new THREE.Vector2(0.5,-0.5);THREE.SpriteAlignment.topCenter=new THREE.Vector2(0,-0.5);THREE.SpriteAlignment.topRight=new THREE.Vector2(-0.5,-0.5);THREE.SpriteAlignment.centerLeft=new THREE.Vector2(0.5,0);THREE.SpriteAlignment.center=new THREE.Vector2(0,0);THREE.SpriteAlignment.centerRight=new THREE.Vector2(-0.5,0);THREE.SpriteAlignment.bottomLeft=new THREE.Vector2(0.5,0.5);THREE.SpriteAlignment.bottomCenter=new THREE.Vector2(0,0.5);
-THREE.SpriteAlignment.bottomRight=new THREE.Vector2(-0.5,0.5);THREE.SpriteCanvasMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.program=function(){};this.setValues(a)};THREE.SpriteCanvasMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteCanvasMaterial.prototype.clone=function(){var a=new THREE.SpriteCanvasMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.program=this.program;return a};THREE.ParticleCanvasMaterial=THREE.SpriteCanvasMaterial;THREE.Texture=function(a,b,c,d,e,f,h,g,i){this.id=THREE.TextureIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.image=a;this.mipmaps=[];this.mapping=void 0!==b?b:new THREE.UVMapping;this.wrapS=void 0!==c?c:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==d?d:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==e?e:THREE.LinearFilter;this.minFilter=void 0!==f?f:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==i?i:1;this.format=void 0!==h?h:THREE.RGBAFormat;this.type=void 0!==g?g:THREE.UnsignedByteType;
-this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this.needsUpdate=!1;this.onUpdate=null};
-THREE.Texture.prototype={constructor:THREE.Texture,clone:function(a){void 0===a&&(a=new THREE.Texture);a.image=this.image;a.mipmaps=this.mipmaps.slice(0);a.mapping=this.mapping;a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.format=this.format;a.type=this.type;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.generateMipmaps=this.generateMipmaps;a.premultiplyAlpha=this.premultiplyAlpha;a.flipY=this.flipY;a.unpackAlignment=
-this.unpackAlignment;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype);THREE.TextureIdCount=0;THREE.CompressedTexture=function(a,b,c,d,e,f,h,g,i,k,m){THREE.Texture.call(this,null,f,h,g,i,k,d,e,m);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=!1};THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype);THREE.CompressedTexture.prototype.clone=function(){var a=new THREE.CompressedTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.DataTexture=function(a,b,c,d,e,f,h,g,i,k,m){THREE.Texture.call(this,null,f,h,g,i,k,d,e,m);this.image={data:a,width:b,height:c}};THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype);THREE.DataTexture.prototype.clone=function(){var a=new THREE.DataTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.ParticleSystemMaterial({color:16777215*Math.random()});this.frustumCulled=this.sortParticles=!1};THREE.ParticleSystem.prototype=Object.create(THREE.Object3D.prototype);
-THREE.ParticleSystem.prototype.clone=function(a){void 0===a&&(a=new THREE.ParticleSystem(this.geometry,this.material));a.sortParticles=this.sortParticles;THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Line=function(a,b,c){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.type=void 0!==c?c:THREE.LineStrip};THREE.LineStrip=0;THREE.LinePieces=1;THREE.Line.prototype=Object.create(THREE.Object3D.prototype);THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.type));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Mesh.prototype.updateMorphTargets=function(){if(0<this.geometry.morphTargets.length){this.morphTargetBase=-1;this.morphTargetForcedOrder=[];this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var a=0,b=this.geometry.morphTargets.length;a<b;a++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[a].name]=a}};
-THREE.Mesh.prototype.getMorphTargetIndexByName=function(a){if(void 0!==this.morphTargetDictionary[a])return this.morphTargetDictionary[a];console.log("THREE.Mesh.getMorphTargetIndexByName: morph target "+a+" does not exist. Returning 0.");return 0};THREE.Mesh.prototype.clone=function(a){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.skin=a;this.skinMatrix=new THREE.Matrix4};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype);THREE.Bone.prototype.update=function(a,b){this.matrixAutoUpdate&&(b|=this.updateMatrix());if(b||this.matrixWorldNeedsUpdate)a?this.skinMatrix.multiplyMatrices(a,this.matrix):this.skinMatrix.copy(this.matrix),this.matrixWorldNeedsUpdate=!1,b=!0;var c,d=this.children.length;for(c=0;c<d;c++)this.children[c].update(this.skinMatrix,b)};THREE.SkinnedMesh=function(a,b,c){THREE.Mesh.call(this,a,b);this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;this.bones=[];this.boneMatrices=[];var d,e,f;if(this.geometry&&void 0!==this.geometry.bones){for(a=0;a<this.geometry.bones.length;a++)c=this.geometry.bones[a],d=c.pos,e=c.rotq,f=c.scl,b=this.addBone(),b.name=c.name,b.position.set(d[0],d[1],d[2]),b.quaternion.set(e[0],e[1],e[2],e[3]),void 0!==f?b.scale.set(f[0],f[1],f[2]):b.scale.set(1,1,1);for(a=0;a<this.bones.length;a++)c=
-this.geometry.bones[a],b=this.bones[a],-1===c.parent?this.add(b):this.bones[c.parent].add(b);a=this.bones.length;this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256<a?64:64<a?32:16<a?16:8,this.boneMatrices=new Float32Array(4*this.boneTextureWidth*this.boneTextureHeight),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType),this.boneTexture.minFilter=THREE.NearestFilter,this.boneTexture.magFilter=
-THREE.NearestFilter,this.boneTexture.generateMipmaps=!1,this.boneTexture.flipY=!1):this.boneMatrices=new Float32Array(16*a);this.pose()}};THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.SkinnedMesh.prototype.addBone=function(a){void 0===a&&(a=new THREE.Bone(this));this.bones.push(a);return a};
-THREE.SkinnedMesh.prototype.updateMatrixWorld=function(){var a=new THREE.Matrix4;return function(b){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||b)this.parent?this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=!1;for(var b=0,c=this.children.length;b<c;b++){var d=this.children[b];d instanceof THREE.Bone?d.update(this.identityMatrix,!1):d.updateMatrixWorld(!0)}if(void 0==this.boneInverses){this.boneInverses=
-[];b=0;for(c=this.bones.length;b<c;b++)d=new THREE.Matrix4,d.getInverse(this.bones[b].skinMatrix),this.boneInverses.push(d)}b=0;for(c=this.bones.length;b<c;b++)a.multiplyMatrices(this.bones[b].skinMatrix,this.boneInverses[b]),a.flattenToArrayOffset(this.boneMatrices,16*b);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}();THREE.SkinnedMesh.prototype.pose=function(){this.updateMatrixWorld(!0);this.normalizeSkinWeights()};
-THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var a=0;a<this.geometry.skinIndices.length;a++){var b=this.geometry.skinWeights[a],c=1/b.lengthManhattan();Infinity!==c?b.multiplyScalar(c):b.set(1)}};THREE.SkinnedMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.SkinnedMesh(this.geometry,this.material,this.useVertexTexture));THREE.Mesh.prototype.clone.call(this,a);return a};THREE.MorphAnimMesh=function(a,b){THREE.Mesh.call(this,a,b);this.duration=1E3;this.mirroredLoop=!1;this.currentKeyframe=this.lastKeyframe=this.time=0;this.direction=1;this.directionBackwards=!1;this.setFrameRange(0,this.geometry.morphTargets.length-1)};THREE.MorphAnimMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.MorphAnimMesh.prototype.setFrameRange=function(a,b){this.startKeyframe=a;this.endKeyframe=b;this.length=this.endKeyframe-this.startKeyframe+1};
-THREE.MorphAnimMesh.prototype.setDirectionForward=function(){this.direction=1;this.directionBackwards=!1};THREE.MorphAnimMesh.prototype.setDirectionBackward=function(){this.direction=-1;this.directionBackwards=!0};
-THREE.MorphAnimMesh.prototype.parseAnimations=function(){var a=this.geometry;a.animations||(a.animations={});for(var b,c=a.animations,d=/([a-z]+)(\d+)/,e=0,f=a.morphTargets.length;e<f;e++){var h=a.morphTargets[e].name.match(d);if(h&&1<h.length){h=h[1];c[h]||(c[h]={start:Infinity,end:-Infinity});var g=c[h];e<g.start&&(g.start=e);e>g.end&&(g.end=e);b||(b=h)}}a.firstAnimation=b};
-THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=1E3*((c.end-c.start)/b),this.time=0):console.warn("animation["+a+"] undefined")};
+this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.RawShaderMaterial=function(a){THREE.ShaderMaterial.call(this,a);this.type="RawShaderMaterial"};THREE.RawShaderMaterial.prototype=Object.create(THREE.ShaderMaterial.prototype);THREE.RawShaderMaterial.prototype.constructor=THREE.RawShaderMaterial;THREE.RawShaderMaterial.prototype.clone=function(){var a=new THREE.RawShaderMaterial;THREE.ShaderMaterial.prototype.clone.call(this,a);return a};
+THREE.SpriteMaterial=function(a){THREE.Material.call(this);this.type="SpriteMaterial";this.color=new THREE.Color(16777215);this.map=null;this.rotation=0;this.fog=!1;this.setValues(a)};THREE.SpriteMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteMaterial.prototype.constructor=THREE.SpriteMaterial;
+THREE.SpriteMaterial.prototype.clone=function(){var a=new THREE.SpriteMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.rotation=this.rotation;a.fog=this.fog;return a};
+THREE.Texture=function(a,b,c,d,e,f,g,h,k){Object.defineProperty(this,"id",{value:THREE.TextureIdCount++});this.uuid=THREE.Math.generateUUID();this.sourceFile=this.name="";this.image=void 0!==a?a:THREE.Texture.DEFAULT_IMAGE;this.mipmaps=[];this.mapping=void 0!==b?b:THREE.Texture.DEFAULT_MAPPING;this.wrapS=void 0!==c?c:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==d?d:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==e?e:THREE.LinearFilter;this.minFilter=void 0!==f?f:THREE.LinearMipMapLinearFilter;
+this.anisotropy=void 0!==k?k:1;this.format=void 0!==g?g:THREE.RGBAFormat;this.type=void 0!==h?h:THREE.UnsignedByteType;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this._needsUpdate=!1;this.onUpdate=null};THREE.Texture.DEFAULT_IMAGE=void 0;THREE.Texture.DEFAULT_MAPPING=THREE.UVMapping;
+THREE.Texture.prototype={constructor:THREE.Texture,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},clone:function(a){void 0===a&&(a=new THREE.Texture);a.image=this.image;a.mipmaps=this.mipmaps.slice(0);a.mapping=this.mapping;a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.format=this.format;a.type=this.type;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.generateMipmaps=
+this.generateMipmaps;a.premultiplyAlpha=this.premultiplyAlpha;a.flipY=this.flipY;a.unpackAlignment=this.unpackAlignment;return a},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype);THREE.TextureIdCount=0;THREE.CubeTexture=function(a,b,c,d,e,f,g,h,k){b=void 0!==b?b:THREE.CubeReflectionMapping;THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.images=a};
+THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype);THREE.CubeTexture.prototype.constructor=THREE.CubeTexture;THREE.CubeTexture.clone=function(a){void 0===a&&(a=new THREE.CubeTexture);THREE.Texture.prototype.clone.call(this,a);a.images=this.images;return a};THREE.CompressedTexture=function(a,b,c,d,e,f,g,h,k,l,p){THREE.Texture.call(this,null,f,g,h,k,l,d,e,p);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=this.flipY=!1};THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype);
+THREE.CompressedTexture.prototype.constructor=THREE.CompressedTexture;THREE.CompressedTexture.prototype.clone=function(){var a=new THREE.CompressedTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.DataTexture=function(a,b,c,d,e,f,g,h,k,l,p){THREE.Texture.call(this,null,f,g,h,k,l,d,e,p);this.image={data:a,width:b,height:c}};THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype);THREE.DataTexture.prototype.constructor=THREE.DataTexture;
+THREE.DataTexture.prototype.clone=function(){var a=new THREE.DataTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.VideoTexture=function(a,b,c,d,e,f,g,h,k){THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.generateMipmaps=!1;var l=this,p=function(){requestAnimationFrame(p);a.readyState===a.HAVE_ENOUGH_DATA&&(l.needsUpdate=!0)};p()};THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype);THREE.VideoTexture.prototype.constructor=THREE.VideoTexture;
+THREE.Group=function(){THREE.Object3D.call(this);this.type="Group"};THREE.Group.prototype=Object.create(THREE.Object3D.prototype);THREE.Group.prototype.constructor=THREE.Group;THREE.PointCloud=function(a,b){THREE.Object3D.call(this);this.type="PointCloud";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.PointCloudMaterial({color:16777215*Math.random()})};THREE.PointCloud.prototype=Object.create(THREE.Object3D.prototype);THREE.PointCloud.prototype.constructor=THREE.PointCloud;
+THREE.PointCloud.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray;return function(c,d){var e=this,f=e.geometry,g=c.params.PointCloud.threshold;a.getInverse(this.matrixWorld);b.copy(c.ray).applyMatrix4(a);if(null===f.boundingBox||!1!==b.isIntersectionBox(f.boundingBox)){var h=g/((this.scale.x+this.scale.y+this.scale.z)/3),k=new THREE.Vector3,g=function(a,f){var g=b.distanceToPoint(a);if(g<h){var k=b.closestPointToPoint(a);k.applyMatrix4(e.matrixWorld);var n=c.ray.origin.distanceTo(k);
+d.push({distance:n,distanceToRay:g,point:k.clone(),index:f,face:null,object:e})}};if(f instanceof THREE.BufferGeometry){var l=f.attributes,p=l.position.array;if(void 0!==l.index){var l=l.index.array,q=f.offsets;0===q.length&&(q=[{start:0,count:l.length,index:0}]);for(var n=0,t=q.length;n<t;++n)for(var r=q[n].start,s=q[n].index,f=r,r=r+q[n].count;f<r;f++){var u=s+l[f];k.fromArray(p,3*u);g(k,u)}}else for(l=p.length/3,f=0;f<l;f++)k.set(p[3*f],p[3*f+1],p[3*f+2]),g(k,f)}else for(k=this.geometry.vertices,
+f=0;f<k.length;f++)g(k[f],f)}}}();THREE.PointCloud.prototype.clone=function(a){void 0===a&&(a=new THREE.PointCloud(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){THREE.warn("THREE.ParticleSystem has been renamed to THREE.PointCloud.");return new THREE.PointCloud(a,b)};
+THREE.Line=function(a,b,c){THREE.Object3D.call(this);this.type="Line";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.mode=void 0!==c?c:THREE.LineStrip};THREE.LineStrip=0;THREE.LinePieces=1;THREE.Line.prototype=Object.create(THREE.Object3D.prototype);THREE.Line.prototype.constructor=THREE.Line;
+THREE.Line.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere;return function(d,e){var f=d.linePrecision,f=f*f,g=this.geometry;null===g.boundingSphere&&g.computeBoundingSphere();c.copy(g.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==d.ray.isIntersectionSphere(c)){a.getInverse(this.matrixWorld);b.copy(d.ray).applyMatrix4(a);var h=new THREE.Vector3,k=new THREE.Vector3,l=new THREE.Vector3,p=new THREE.Vector3,q=this.mode===THREE.LineStrip?1:2;if(g instanceof
+THREE.BufferGeometry){var n=g.attributes;if(void 0!==n.index){var t=n.index.array,n=n.position.array,r=g.offsets;0===r.length&&(r=[{start:0,count:t.length,index:0}]);for(var s=0;s<r.length;s++)for(var u=r[s].start,v=r[s].count,x=r[s].index,g=u;g<u+v-1;g+=q){var D=x+t[g+1];h.fromArray(n,3*(x+t[g]));k.fromArray(n,3*D);D=b.distanceSqToSegment(h,k,p,l);D>f||(D=b.origin.distanceTo(p),D<d.near||D>d.far||e.push({distance:D,point:l.clone().applyMatrix4(this.matrixWorld),index:g,offsetIndex:s,face:null,faceIndex:null,
+object:this}))}}else for(n=n.position.array,g=0;g<n.length/3-1;g+=q)h.fromArray(n,3*g),k.fromArray(n,3*g+3),D=b.distanceSqToSegment(h,k,p,l),D>f||(D=b.origin.distanceTo(p),D<d.near||D>d.far||e.push({distance:D,point:l.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else if(g instanceof THREE.Geometry)for(h=g.vertices,k=h.length,g=0;g<k-1;g+=q)D=b.distanceSqToSegment(h[g],h[g+1],p,l),D>f||(D=b.origin.distanceTo(p),D<d.near||D>d.far||e.push({distance:D,point:l.clone().applyMatrix4(this.matrixWorld),
+index:g,face:null,faceIndex:null,object:this}))}}}();THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.mode));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.type="Mesh";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);
+THREE.Mesh.prototype.constructor=THREE.Mesh;THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&0<this.geometry.morphTargets.length){this.morphTargetBase=-1;this.morphTargetForcedOrder=[];this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var a=0,b=this.geometry.morphTargets.length;a<b;a++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[a].name]=a}};
+THREE.Mesh.prototype.getMorphTargetIndexByName=function(a){if(void 0!==this.morphTargetDictionary[a])return this.morphTargetDictionary[a];THREE.warn("THREE.Mesh.getMorphTargetIndexByName: morph target "+a+" does not exist. Returning 0.");return 0};
+THREE.Mesh.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g,h){var k=this.geometry;null===k.boundingSphere&&k.computeBoundingSphere();c.copy(k.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==g.ray.isIntersectionSphere(c)&&(a.getInverse(this.matrixWorld),b.copy(g.ray).applyMatrix4(a),null===k.boundingBox||!1!==b.isIntersectionBox(k.boundingBox)))if(k instanceof THREE.BufferGeometry){var l=
+this.material;if(void 0!==l){var p=k.attributes,q,n,t=g.precision;if(void 0!==p.index){var r=p.index.array,s=p.position.array,u=k.offsets;0===u.length&&(u=[{start:0,count:r.length,index:0}]);for(var v=0,x=u.length;v<x;++v)for(var p=u[v].start,D=u[v].index,k=p,w=p+u[v].count;k<w;k+=3){p=D+r[k];q=D+r[k+1];n=D+r[k+2];d.fromArray(s,3*p);e.fromArray(s,3*q);f.fromArray(s,3*n);var y=l.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,l.side!==THREE.DoubleSide);if(null!==y){y.applyMatrix4(this.matrixWorld);
+var A=g.ray.origin.distanceTo(y);A<t||A<g.near||A>g.far||h.push({distance:A,point:y,face:new THREE.Face3(p,q,n,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this})}}}else for(s=p.position.array,r=k=0,w=s.length;k<w;k+=3,r+=9)p=k,q=k+1,n=k+2,d.fromArray(s,r),e.fromArray(s,r+3),f.fromArray(s,r+6),y=l.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,l.side!==THREE.DoubleSide),null!==y&&(y.applyMatrix4(this.matrixWorld),A=g.ray.origin.distanceTo(y),A<t||A<g.near||A>
+g.far||h.push({distance:A,point:y,face:new THREE.Face3(p,q,n,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this}))}}else if(k instanceof THREE.Geometry)for(r=this.material instanceof THREE.MeshFaceMaterial,s=!0===r?this.material.materials:null,t=g.precision,u=k.vertices,v=0,x=k.faces.length;v<x;v++)if(D=k.faces[v],l=!0===r?s[D.materialIndex]:this.material,void 0!==l){p=u[D.a];q=u[D.b];n=u[D.c];if(!0===l.morphTargets){y=k.morphTargets;A=this.morphTargetInfluences;d.set(0,0,0);e.set(0,0,0);f.set(0,
+0,0);for(var w=0,E=y.length;w<E;w++){var G=A[w];if(0!==G){var F=y[w].vertices;d.x+=(F[D.a].x-p.x)*G;d.y+=(F[D.a].y-p.y)*G;d.z+=(F[D.a].z-p.z)*G;e.x+=(F[D.b].x-q.x)*G;e.y+=(F[D.b].y-q.y)*G;e.z+=(F[D.b].z-q.z)*G;f.x+=(F[D.c].x-n.x)*G;f.y+=(F[D.c].y-n.y)*G;f.z+=(F[D.c].z-n.z)*G}}d.add(p);e.add(q);f.add(n);p=d;q=e;n=f}y=l.side===THREE.BackSide?b.intersectTriangle(n,q,p,!0):b.intersectTriangle(p,q,n,l.side!==THREE.DoubleSide);null!==y&&(y.applyMatrix4(this.matrixWorld),A=g.ray.origin.distanceTo(y),A<t||
+A<g.near||A>g.far||h.push({distance:A,point:y,face:D,faceIndex:v,object:this}))}}}();THREE.Mesh.prototype.clone=function(a,b){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a,b);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.type="Bone";this.skin=a};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype);THREE.Bone.prototype.constructor=THREE.Bone;
+THREE.Skeleton=function(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256<this.bones.length?64:64<this.bones.length?32:16<this.bones.length?16:8,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType),
+this.boneTexture.minFilter=THREE.NearestFilter,this.boneTexture.magFilter=THREE.NearestFilter,this.boneTexture.generateMipmaps=!1,this.boneTexture.flipY=!1):this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(THREE.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[],b=0,a=this.bones.length;b<a;b++)this.boneInverses.push(new THREE.Matrix4)};
+THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var a=0,b=this.bones.length;a<b;a++){var c=new THREE.Matrix4;this.bones[a]&&c.getInverse(this.bones[a].matrixWorld);this.boneInverses.push(c)}};
+THREE.Skeleton.prototype.pose=function(){for(var a,b=0,c=this.bones.length;b<c;b++)(a=this.bones[b])&&a.matrixWorld.getInverse(this.boneInverses[b]);b=0;for(c=this.bones.length;b<c;b++)if(a=this.bones[b])a.parent?(a.matrix.getInverse(a.parent.matrixWorld),a.matrix.multiply(a.matrixWorld)):a.matrix.copy(a.matrixWorld),a.matrix.decompose(a.position,a.quaternion,a.scale)};
+THREE.Skeleton.prototype.update=function(){var a=new THREE.Matrix4;return function(){for(var b=0,c=this.bones.length;b<c;b++)a.multiplyMatrices(this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix,this.boneInverses[b]),a.flattenToArrayOffset(this.boneMatrices,16*b);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}();
+THREE.SkinnedMesh=function(a,b,c){THREE.Mesh.call(this,a,b);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new THREE.Matrix4;this.bindMatrixInverse=new THREE.Matrix4;a=[];if(this.geometry&&void 0!==this.geometry.bones){for(var d,e,f,g,h=0,k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],e=d.pos,f=d.rotq,g=d.scl,b=new THREE.Bone(this),a.push(b),b.name=d.name,b.position.set(e[0],e[1],e[2]),b.quaternion.set(f[0],f[1],f[2],f[3]),void 0!==g?b.scale.set(g[0],g[1],g[2]):b.scale.set(1,
+1,1);h=0;for(k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],-1!==d.parent?a[d.parent].add(a[h]):this.add(a[h])}this.normalizeSkinWeights();this.updateMatrixWorld(!0);this.bind(new THREE.Skeleton(a,void 0,c))};THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.SkinnedMesh.prototype.constructor=THREE.SkinnedMesh;THREE.SkinnedMesh.prototype.bind=function(a,b){this.skeleton=a;void 0===b&&(this.updateMatrixWorld(!0),b=this.matrixWorld);this.bindMatrix.copy(b);this.bindMatrixInverse.getInverse(b)};
+THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()};THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var a=0;a<this.geometry.skinIndices.length;a++){var b=this.geometry.skinWeights[a],c=1/b.lengthManhattan();Infinity!==c?b.multiplyScalar(c):b.set(1)}};
+THREE.SkinnedMesh.prototype.updateMatrixWorld=function(a){THREE.Mesh.prototype.updateMatrixWorld.call(this,!0);"attached"===this.bindMode?this.bindMatrixInverse.getInverse(this.matrixWorld):"detached"===this.bindMode?this.bindMatrixInverse.getInverse(this.bindMatrix):THREE.warn("THREE.SkinnedMesh unreckognized bindMode: "+this.bindMode)};
+THREE.SkinnedMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.SkinnedMesh(this.geometry,this.material,this.useVertexTexture));THREE.Mesh.prototype.clone.call(this,a);return a};THREE.MorphAnimMesh=function(a,b){THREE.Mesh.call(this,a,b);this.type="MorphAnimMesh";this.duration=1E3;this.mirroredLoop=!1;this.currentKeyframe=this.lastKeyframe=this.time=0;this.direction=1;this.directionBackwards=!1;this.setFrameRange(0,this.geometry.morphTargets.length-1)};THREE.MorphAnimMesh.prototype=Object.create(THREE.Mesh.prototype);
+THREE.MorphAnimMesh.prototype.constructor=THREE.MorphAnimMesh;THREE.MorphAnimMesh.prototype.setFrameRange=function(a,b){this.startKeyframe=a;this.endKeyframe=b;this.length=this.endKeyframe-this.startKeyframe+1};THREE.MorphAnimMesh.prototype.setDirectionForward=function(){this.direction=1;this.directionBackwards=!1};THREE.MorphAnimMesh.prototype.setDirectionBackward=function(){this.direction=-1;this.directionBackwards=!0};
+THREE.MorphAnimMesh.prototype.parseAnimations=function(){var a=this.geometry;a.animations||(a.animations={});for(var b,c=a.animations,d=/([a-z]+)_?(\d+)/,e=0,f=a.morphTargets.length;e<f;e++){var g=a.morphTargets[e].name.match(d);if(g&&1<g.length){g=g[1];c[g]||(c[g]={start:Infinity,end:-Infinity});var h=c[g];e<h.start&&(h.start=e);e>h.end&&(h.end=e);b||(b=g)}}a.firstAnimation=b};
+THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=(c.end-c.start)/b*1E3,this.time=0):THREE.warn("THREE.MorphAnimMesh: animation["+a+"] undefined in .playAnimation()")};
 THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);a=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a!==this.currentKeyframe&&
 (this.morphTargetInfluences[this.lastKeyframe]=0,this.morphTargetInfluences[this.currentKeyframe]=1,this.morphTargetInfluences[a]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=a);b=this.time%b/b;this.directionBackwards&&(b=1-b);this.morphTargetInfluences[this.currentKeyframe]=b;this.morphTargetInfluences[this.lastKeyframe]=1-b};
-THREE.MorphAnimMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.MorphAnimMesh(this.geometry,this.material));a.duration=this.duration;a.mirroredLoop=this.mirroredLoop;a.time=this.time;a.lastKeyframe=this.lastKeyframe;a.currentKeyframe=this.currentKeyframe;a.direction=this.direction;a.directionBackwards=this.directionBackwards;THREE.Mesh.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.objects=[]};THREE.LOD.prototype=Object.create(THREE.Object3D.prototype);THREE.LOD.prototype.addLevel=function(a,b){void 0===b&&(b=0);for(var b=Math.abs(b),c=0;c<this.objects.length&&!(b<this.objects[c].distance);c++);this.objects.splice(c,0,{distance:b,object:a});this.add(a)};THREE.LOD.prototype.getObjectForDistance=function(a){for(var b=1,c=this.objects.length;b<c&&!(a<this.objects[b].distance);b++);return this.objects[b-1].object};
-THREE.LOD.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){if(1<this.objects.length){a.getPositionFromMatrix(c.matrixWorld);b.getPositionFromMatrix(this.matrixWorld);c=a.distanceTo(b);this.objects[0].object.visible=!0;for(var d=1,e=this.objects.length;d<e;d++)if(c>=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;d<e;d++)this.objects[d].object.visible=!1}}}();THREE.LOD.prototype.clone=function(){};THREE.Sprite=function(a){THREE.Object3D.call(this);this.material=void 0!==a?a:new THREE.SpriteMaterial;this.rotation=0};THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.updateMatrix=function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0};THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Particle=THREE.Sprite;THREE.Scene=function(){THREE.Object3D.call(this);this.overrideMaterial=this.fog=null;this.autoUpdate=!0;this.matrixAutoUpdate=!1;this.__lights=[];this.__objectsAdded=[];this.__objectsRemoved=[]};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Scene.prototype.__addObject=function(a){if(a instanceof THREE.Light)-1===this.__lights.indexOf(a)&&this.__lights.push(a),a.target&&void 0===a.target.parent&&this.add(a.target);else if(!(a instanceof THREE.Camera||a instanceof THREE.Bone)){this.__objectsAdded.push(a);var b=this.__objectsRemoved.indexOf(a);-1!==b&&this.__objectsRemoved.splice(b,1)}for(b=0;b<a.children.length;b++)this.__addObject(a.children[b])};
-THREE.Scene.prototype.__removeObject=function(a){if(a instanceof THREE.Light){var b=this.__lights.indexOf(a);-1!==b&&this.__lights.splice(b,1);if(a.shadowCascadeArray)for(b=0;b<a.shadowCascadeArray.length;b++)this.__removeObject(a.shadowCascadeArray[b])}else a instanceof THREE.Camera||(this.__objectsRemoved.push(a),b=this.__objectsAdded.indexOf(a),-1!==b&&this.__objectsAdded.splice(b,1));for(b=0;b<a.children.length;b++)this.__removeObject(a.children[b])};
-THREE.Scene.prototype.clone=function(a){void 0===a&&(a=new THREE.Scene);THREE.Object3D.prototype.clone.call(this,a);null!==this.fog&&(a.fog=this.fog.clone());null!==this.overrideMaterial&&(a.overrideMaterial=this.overrideMaterial.clone());a.autoUpdate=this.autoUpdate;a.matrixAutoUpdate=this.matrixAutoUpdate;return a};THREE.Fog=function(a,b,c){this.name="";this.color=new THREE.Color(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3};THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)};THREE.FogExp2=function(a,b){this.name="";this.color=new THREE.Color(a);this.density=void 0!==b?b:2.5E-4};THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)};THREE.CanvasRenderer=function(a){function b(a,b,c){for(var d=0,e=z.length;d<e;d++){var f=z[d];La.copy(f.color);if(f instanceof THREE.DirectionalLight){var h=ua.getPositionFromMatrix(f.matrixWorld).normalize(),g=b.dot(h);0>=g||(g*=f.intensity,c.add(La.multiplyScalar(g)))}else f instanceof THREE.PointLight&&(h=ua.getPositionFromMatrix(f.matrixWorld),g=b.dot(ua.subVectors(h,a).normalize()),0>=g||(g*=0==f.distance?1:1-Math.min(a.distanceTo(h)/f.distance,1),0!=g&&(g*=f.intensity,c.add(La.multiplyScalar(g)))))}}
-function c(a,b,c,d){m(b);l(c);p(d);s(a.getStyle());C.stroke();ra.expandByScalar(2*b)}function d(a){t(a.getStyle());C.fill()}function e(a,b,c,e,f,h,g,j,i,k,m,l,p){if(!(p instanceof THREE.DataTexture||void 0===p.image||0==p.image.width)){if(!0===p.needsUpdate){var n=p.wrapS==THREE.RepeatWrapping,r=p.wrapT==THREE.RepeatWrapping;Ga[p.id]=C.createPattern(p.image,!0===n&&!0===r?"repeat":!0===n&&!1===r?"repeat-x":!1===n&&!0===r?"repeat-y":"no-repeat");p.needsUpdate=!1}void 0===Ga[p.id]?t("rgba(0,0,0,1)"):
-t(Ga[p.id]);var n=p.offset.x/p.repeat.x,r=p.offset.y/p.repeat.y,s=p.image.width*p.repeat.x,q=p.image.height*p.repeat.y,g=(g+n)*s,j=(1-j+r)*q,c=c-a,e=e-b,f=f-a,h=h-b,i=(i+n)*s-g,k=(1-k+r)*q-j,m=(m+n)*s-g,l=(1-l+r)*q-j,n=i*l-m*k;0===n?(void 0===fa[p.id]&&(b=document.createElement("canvas"),b.width=p.image.width,b.height=p.image.height,b=b.getContext("2d"),b.drawImage(p.image,0,0),fa[p.id]=b.getImageData(0,0,p.image.width,p.image.height).data),b=fa[p.id],g=4*(Math.floor(g)+Math.floor(j)*p.image.width),
-V.setRGB(b[g]/255,b[g+1]/255,b[g+2]/255),d(V)):(n=1/n,p=(l*c-k*f)*n,k=(l*e-k*h)*n,c=(i*f-m*c)*n,e=(i*h-m*e)*n,a=a-p*g-c*j,g=b-k*g-e*j,C.save(),C.transform(p,k,c,e,a,g),C.fill(),C.restore())}}function f(a,b,c,d,e,f,h,g,j,i,k,m,l){var p,n;p=l.width-1;n=l.height-1;h*=p;g*=n;c-=a;d-=b;e-=a;f-=b;j=j*p-h;i=i*n-g;k=k*p-h;m=m*n-g;n=1/(j*m-k*i);p=(m*c-i*e)*n;i=(m*d-i*f)*n;c=(j*e-k*c)*n;d=(j*f-k*d)*n;a=a-p*h-c*g;b=b-i*h-d*g;C.save();C.transform(p,i,c,d,a,b);C.clip();C.drawImage(l,0,0);C.restore()}function h(a,
-b,c,d){va[0]=255*a.r|0;va[1]=255*a.g|0;va[2]=255*a.b|0;va[4]=255*b.r|0;va[5]=255*b.g|0;va[6]=255*b.b|0;va[8]=255*c.r|0;va[9]=255*c.g|0;va[10]=255*c.b|0;va[12]=255*d.r|0;va[13]=255*d.g|0;va[14]=255*d.b|0;j.putImageData(Oa,0,0);Ea.drawImage(Pa,0,0);return wa}function g(a,b,c){var d=b.x-a.x,e=b.y-a.y,f=d*d+e*e;0!==f&&(c/=Math.sqrt(f),d*=c,e*=c,b.x+=d,b.y+=e,a.x-=d,a.y-=e)}function i(a){y!==a&&(y=C.globalAlpha=a)}function k(a){v!==a&&(a===THREE.NormalBlending?C.globalCompositeOperation="source-over":
-a===THREE.AdditiveBlending?C.globalCompositeOperation="lighter":a===THREE.SubtractiveBlending&&(C.globalCompositeOperation="darker"),v=a)}function m(a){J!==a&&(J=C.lineWidth=a)}function l(a){ba!==a&&(ba=C.lineCap=a)}function p(a){oa!==a&&(oa=C.lineJoin=a)}function s(a){G!==a&&(G=C.strokeStyle=a)}function t(a){R!==a&&(R=C.fillStyle=a)}function n(a,b){if(pa!==a||N!==b)C.setLineDash([a,b]),pa=a,N=b}console.log("THREE.CanvasRenderer",THREE.REVISION);var r=THREE.Math.smoothstep,a=a||{},q=this,u,w,z,B=
-new THREE.Projector,D=void 0!==a.canvas?a.canvas:document.createElement("canvas"),x=D.width,F=D.height,A=Math.floor(x/2),O=Math.floor(F/2),C=D.getContext("2d"),E=new THREE.Color(0),I=0,y=1,v=0,G=null,R=null,J=null,ba=null,oa=null,pa=null,N=0,M,Q,K,ca;new THREE.RenderableVertex;new THREE.RenderableVertex;var Fa,Ba,da,Aa,$,ea,V=new THREE.Color,P=new THREE.Color,Z=new THREE.Color,U=new THREE.Color,ka=new THREE.Color,ta=new THREE.Color,ia=new THREE.Color,La=new THREE.Color,Ga={},fa={},Da,Ua,Qa,xa,bb,
-cb,Ma,fb,sb,pb,Ha=new THREE.Box2,la=new THREE.Box2,ra=new THREE.Box2,gb=new THREE.Color,sa=new THREE.Color,ga=new THREE.Color,ua=new THREE.Vector3,Pa,j,Oa,va,wa,Ea,Ra=16;Pa=document.createElement("canvas");Pa.width=Pa.height=2;j=Pa.getContext("2d");j.fillStyle="rgba(0,0,0,1)";j.fillRect(0,0,2,2);Oa=j.getImageData(0,0,2,2);va=Oa.data;wa=document.createElement("canvas");wa.width=wa.height=Ra;Ea=wa.getContext("2d");Ea.translate(-Ra/2,-Ra/2);Ea.scale(Ra,Ra);Ra--;void 0===C.setLineDash&&(C.setLineDash=
-void 0!==C.mozDash?function(a){C.mozDash=null!==a[0]?a:null}:function(){});this.domElement=D;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.sortElements=this.sortObjects=this.autoClear=!0;this.info={render:{vertices:0,faces:0}};this.supportsVertexTextures=function(){};this.setFaceCulling=function(){};this.setSize=function(a,b,c){x=a*this.devicePixelRatio;F=b*this.devicePixelRatio;A=Math.floor(x/2);O=Math.floor(F/2);
-D.width=x;D.height=F;1!==this.devicePixelRatio&&!1!==c&&(D.style.width=a+"px",D.style.height=b+"px");Ha.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O));la.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O));y=1;v=0;oa=ba=J=R=G=null};this.setClearColor=function(a,b){E.set(a);I=void 0!==b?b:1;la.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O))};this.setClearColorHex=function(a,b){console.warn("DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,
-b)};this.getMaxAnisotropy=function(){return 0};this.clear=function(){C.setTransform(1,0,0,-1,A,O);!1===la.empty()&&(la.intersect(Ha),la.expandByScalar(2),1>I&&C.clearRect(la.min.x|0,la.min.y|0,la.max.x-la.min.x|0,la.max.y-la.min.y|0),0<I&&(k(THREE.NormalBlending),i(1),t("rgba("+Math.floor(255*E.r)+","+Math.floor(255*E.g)+","+Math.floor(255*E.b)+","+I+")"),C.fillRect(la.min.x|0,la.min.y|0,la.max.x-la.min.x|0,la.max.y-la.min.y|0)),la.makeEmpty())};this.render=function(a,j){if(!1===j instanceof THREE.Camera)console.error("THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.");
-else{!0===this.autoClear&&this.clear();C.setTransform(1,0,0,-1,A,O);q.info.render.vertices=0;q.info.render.faces=0;u=B.projectScene(a,j,this.sortObjects,this.sortElements);w=u.elements;z=u.lights;M=j;gb.setRGB(0,0,0);sa.setRGB(0,0,0);ga.setRGB(0,0,0);for(var D=0,G=z.length;D<G;D++){var y=z[D],F=y.color;y instanceof THREE.AmbientLight?gb.add(F):y instanceof THREE.DirectionalLight?sa.add(F):y instanceof THREE.PointLight&&ga.add(F)}D=0;for(G=w.length;D<G;D++){var x=w[D],v=x.material;if(!(void 0===v||
-!1===v.visible)){ra.makeEmpty();if(x instanceof THREE.RenderableSprite){Q=x;Q.x*=A;Q.y*=O;var y=Q,F=x,J=v;i(J.opacity);k(J.blending);var N=v=x=void 0,E=void 0,I=void 0,R=void 0,ba=void 0;J instanceof THREE.SpriteMaterial||J instanceof THREE.ParticleSystemMaterial?void 0!==J.map.image?(I=J.map.image,R=I.width>>1,ba=I.height>>1,N=F.scale.x*A,E=F.scale.y*O,x=N*R,v=E*ba,ra.min.set(y.x-x,y.y-v),ra.max.set(y.x+x,y.y+v),!1===Ha.isIntersectionBox(ra)?ra.makeEmpty():(C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),
-C.scale(N,-E),C.translate(-R,-ba),C.drawImage(I,0,0),C.restore())):(N=F.object.scale.x,E=F.object.scale.y,N*=F.scale.x*A,E*=F.scale.y*O,ra.min.set(y.x-N,y.y-E),ra.max.set(y.x+N,y.y+E),!1===Ha.isIntersectionBox(ra)?ra.makeEmpty():(t(J.color.getStyle()),C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),C.scale(N,E),C.fillRect(-1,-1,2,2),C.restore())):J instanceof THREE.SpriteCanvasMaterial&&(x=F.scale.x*A,v=F.scale.y*O,ra.min.set(y.x-x,y.y-v),ra.max.set(y.x+x,y.y+v),!1===Ha.isIntersectionBox(ra)?
-ra.makeEmpty():(s(J.color.getStyle()),t(J.color.getStyle()),C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),C.scale(x,v),J.program(C),C.restore()))}else if(x instanceof THREE.RenderableLine){if(Q=x.v1,K=x.v2,Q.positionScreen.x*=A,Q.positionScreen.y*=O,K.positionScreen.x*=A,K.positionScreen.y*=O,ra.setFromPoints([Q.positionScreen,K.positionScreen]),!0===Ha.isIntersectionBox(ra))if(y=Q,F=K,J=x,x=v,i(x.opacity),k(x.blending),C.beginPath(),C.moveTo(y.positionScreen.x,y.positionScreen.y),C.lineTo(F.positionScreen.x,
-F.positionScreen.y),x instanceof THREE.LineBasicMaterial){m(x.linewidth);l(x.linecap);p(x.linejoin);if(x.vertexColors!==THREE.VertexColors)s(x.color.getStyle());else if(v=J.vertexColors[0].getStyle(),J=J.vertexColors[1].getStyle(),v===J)s(v);else{try{var fa=C.createLinearGradient(y.positionScreen.x,y.positionScreen.y,F.positionScreen.x,F.positionScreen.y);fa.addColorStop(0,v);fa.addColorStop(1,J)}catch(oa){fa=v}s(fa)}C.stroke();ra.expandByScalar(2*x.linewidth)}else x instanceof THREE.LineDashedMaterial&&
-(m(x.linewidth),l(x.linecap),p(x.linejoin),s(x.color.getStyle()),n(x.dashSize,x.gapSize),C.stroke(),ra.expandByScalar(2*x.linewidth),n(null,null))}else if(x instanceof THREE.RenderableFace3){Q=x.v1;K=x.v2;ca=x.v3;if(-1>Q.positionScreen.z||1<Q.positionScreen.z)continue;if(-1>K.positionScreen.z||1<K.positionScreen.z)continue;if(-1>ca.positionScreen.z||1<ca.positionScreen.z)continue;Q.positionScreen.x*=A;Q.positionScreen.y*=O;K.positionScreen.x*=A;K.positionScreen.y*=O;ca.positionScreen.x*=A;ca.positionScreen.y*=
-O;0<v.overdraw&&(g(Q.positionScreen,K.positionScreen,v.overdraw),g(K.positionScreen,ca.positionScreen,v.overdraw),g(ca.positionScreen,Q.positionScreen,v.overdraw));ra.setFromPoints([Q.positionScreen,K.positionScreen,ca.positionScreen]);if(!0===Ha.isIntersectionBox(ra)){y=Q;F=K;J=ca;q.info.render.vertices+=3;q.info.render.faces++;i(v.opacity);k(v.blending);Fa=y.positionScreen.x;Ba=y.positionScreen.y;da=F.positionScreen.x;Aa=F.positionScreen.y;$=J.positionScreen.x;ea=J.positionScreen.y;var N=Fa,E=Ba,
-I=da,R=Aa,ba=$,pa=ea;C.beginPath();C.moveTo(N,E);C.lineTo(I,R);C.lineTo(ba,pa);C.closePath();(v instanceof THREE.MeshLambertMaterial||v instanceof THREE.MeshPhongMaterial)&&null===v.map?(ta.copy(v.color),ia.copy(v.emissive),v.vertexColors===THREE.FaceColors&&ta.multiply(x.color),!1===v.wireframe&&v.shading==THREE.SmoothShading&&3==x.vertexNormalsLength?(P.copy(gb),Z.copy(gb),U.copy(gb),b(x.v1.positionWorld,x.vertexNormalsModel[0],P),b(x.v2.positionWorld,x.vertexNormalsModel[1],Z),b(x.v3.positionWorld,
-x.vertexNormalsModel[2],U),P.multiply(ta).add(ia),Z.multiply(ta).add(ia),U.multiply(ta).add(ia),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,1,0,0,1,Qa)):(V.copy(gb),b(x.centroidModel,x.normalModel,V),V.multiply(ta).add(ia),!0===v.wireframe?c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V))):v instanceof THREE.MeshBasicMaterial||v instanceof THREE.MeshLambertMaterial||v instanceof THREE.MeshPhongMaterial?null!==v.map?v.map.mapping instanceof THREE.UVMapping&&
-(xa=x.uvs[0],e(Fa,Ba,da,Aa,$,ea,xa[0].x,xa[0].y,xa[1].x,xa[1].y,xa[2].x,xa[2].y,v.map)):null!==v.envMap?v.envMap.mapping instanceof THREE.SphericalReflectionMapping&&(ua.copy(x.vertexNormalsModelView[0]),bb=0.5*ua.x+0.5,cb=0.5*ua.y+0.5,ua.copy(x.vertexNormalsModelView[1]),Ma=0.5*ua.x+0.5,fb=0.5*ua.y+0.5,ua.copy(x.vertexNormalsModelView[2]),sb=0.5*ua.x+0.5,pb=0.5*ua.y+0.5,e(Fa,Ba,da,Aa,$,ea,bb,cb,Ma,fb,sb,pb,v.envMap)):(V.copy(v.color),v.vertexColors===THREE.FaceColors&&V.multiply(x.color),!0===v.wireframe?
-c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V)):v instanceof THREE.MeshDepthMaterial?(Da=M.near,Ua=M.far,P.r=P.g=P.b=1-r(y.positionScreen.z*y.positionScreen.w,Da,Ua),Z.r=Z.g=Z.b=1-r(F.positionScreen.z*F.positionScreen.w,Da,Ua),U.r=U.g=U.b=1-r(J.positionScreen.z*J.positionScreen.w,Da,Ua),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,1,0,0,1,Qa)):v instanceof THREE.MeshNormalMaterial&&(y=void 0,v.shading==THREE.FlatShading?(y=x.normalModelView,V.setRGB(y.x,
-y.y,y.z).multiplyScalar(0.5).addScalar(0.5),!0===v.wireframe?c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V)):v.shading==THREE.SmoothShading&&(y=x.vertexNormalsModelView[0],P.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),y=x.vertexNormalsModelView[1],Z.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),y=x.vertexNormalsModelView[2],U.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,
-1,0,0,1,Qa)))}}la.union(ra)}}C.setTransform(1,0,0,1,0,0)}}};THREE.ShaderChunk={fog_pars_fragment:"#ifdef USE_FOG\nuniform vec3 fogColor;\n#ifdef FOG_EXP2\nuniform float fogDensity;\n#else\nuniform float fogNear;\nuniform float fogFar;\n#endif\n#endif",fog_fragment:"#ifdef USE_FOG\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n#ifdef FOG_EXP2\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n#else\nfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n#endif\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n#endif",
-envmap_pars_fragment:"#ifdef USE_ENVMAP\nuniform float reflectivity;\nuniform samplerCube envMap;\nuniform float flipEnvMap;\nuniform int combine;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nuniform bool useRefract;\nuniform float refractionRatio;\n#else\nvarying vec3 vReflect;\n#endif\n#endif",envmap_fragment:"#ifdef USE_ENVMAP\nvec3 reflectVec;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nreflectVec = refract( cameraToVertex, normal, refractionRatio );\n} else { \nreflectVec = reflect( cameraToVertex, normal );\n}\n#else\nreflectVec = vReflect;\n#endif\n#ifdef DOUBLE_SIDED\nfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\nvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#else\nvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#endif\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\nif ( combine == 1 ) {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n} else if ( combine == 2 ) {\ngl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n} else {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n}\n#endif",
-envmap_pars_vertex:"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvarying vec3 vReflect;\nuniform float refractionRatio;\nuniform bool useRefract;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n#ifdef USE_SKINNING\nvec4 worldPosition = modelMatrix * skinned;\n#endif\n#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n#endif\n#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n#endif\n#endif",
-envmap_vertex:"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\nworldNormal = normalize( worldNormal );\nvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, worldNormal );\n}\n#endif",map_particle_pars_fragment:"#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
-map_particle_fragment:"#ifdef USE_MAP\ngl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n#endif",map_pars_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\nuniform vec4 offsetRepeat;\n#endif",map_pars_fragment:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
-map_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif",map_fragment:"#ifdef USE_MAP\nvec4 texelColor = texture2D( map, vUv );\n#ifdef GAMMA_INPUT\ntexelColor.xyz *= texelColor.xyz;\n#endif\ngl_FragColor = gl_FragColor * texelColor;\n#endif",lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\nuniform sampler2D lightMap;\n#endif",lightmap_pars_vertex:"#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\n#endif",
-lightmap_fragment:"#ifdef USE_LIGHTMAP\ngl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n#endif",lightmap_vertex:"#ifdef USE_LIGHTMAP\nvUv2 = uv2;\n#endif",bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\nuniform sampler2D bumpMap;\nuniform float bumpScale;\nvec2 dHdxy_fwd() {\nvec2 dSTdx = dFdx( vUv );\nvec2 dSTdy = dFdy( vUv );\nfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\nfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\nfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\nreturn vec2( dBx, dBy );\n}\nvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\nvec3 vSigmaX = dFdx( surf_pos );\nvec3 vSigmaY = dFdy( surf_pos );\nvec3 vN = surf_norm;\nvec3 R1 = cross( vSigmaY, vN );\nvec3 R2 = cross( vN, vSigmaX );\nfloat fDet = dot( vSigmaX, R1 );\nvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\nreturn normalize( abs( fDet ) * surf_norm - vGrad );\n}\n#endif",
-normalmap_pars_fragment:"#ifdef USE_NORMALMAP\nuniform sampler2D normalMap;\nuniform vec2 normalScale;\nvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\nvec3 q0 = dFdx( eye_pos.xyz );\nvec3 q1 = dFdy( eye_pos.xyz );\nvec2 st0 = dFdx( vUv.st );\nvec2 st1 = dFdy( vUv.st );\nvec3 S = normalize(  q0 * st1.t - q1 * st0.t );\nvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\nvec3 N = normalize( surf_norm );\nvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\nmapN.xy = normalScale * mapN.xy;\nmat3 tsn = mat3( S, T, N );\nreturn normalize( tsn * mapN );\n}\n#endif",
-specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\nuniform sampler2D specularMap;\n#endif",specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\nvec4 texelSpecular = texture2D( specularMap, vUv );\nspecularStrength = texelSpecular.r;\n#else\nspecularStrength = 1.0;\n#endif",lights_lambert_pars_vertex:"uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif",
-lights_lambert_vertex:"vLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\nvLightBack = vec3( 0.0 );\n#endif\ntransformedNormal = normalize( transformedNormal );\n#if MAX_DIR_LIGHTS > 0\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, dirVector );\nvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\ndirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\ndirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n#ifdef DOUBLE_SIDED\nvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n#endif\n}\n#endif\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\npointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\npointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n#ifdef DOUBLE_SIDED\nvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\nspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\nspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n#ifdef DOUBLE_SIDED\nvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\nvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n#ifdef DOUBLE_SIDED\nvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n#endif\n}\n#endif\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n#ifdef DOUBLE_SIDED\nvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n#endif",
-lights_phong_pars_vertex:"#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif",
-lights_phong_vertex:"#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nvPointLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nvSpotLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvWorldPosition = worldPosition.xyz;\n#endif",
-lights_phong_pars_fragment:"uniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#else\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#else\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",
-lights_phong_fragment:"vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#ifdef DOUBLE_SIDED\nnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n#endif\n#ifdef USE_NORMALMAP\nnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\nnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse  = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vPointLight[ i ].xyz );\nfloat lDistance = vPointLight[ i ].w;\n#endif\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n#endif\npointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\nvec3 pointHalfVector = normalize( lVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n#else\npointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse  = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vSpotLight[ i ].xyz );\nfloat lDistance = vSpotLight[ i ].w;\n#endif\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n#endif\nspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\nvec3 spotHalfVector = normalize( lVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse  = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, dirVector );\n#ifdef WRAP_AROUND\nfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n#endif\ndirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += diffuse * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n#endif",
-color_pars_fragment:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_fragment:"#ifdef USE_COLOR\ngl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n#endif",color_pars_vertex:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\n#ifdef GAMMA_INPUT\nvColor = color * color;\n#else\nvColor = color;\n#endif\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n#ifdef BONE_TEXTURE\nuniform sampler2D boneTexture;\nuniform int boneTextureWidth;\nuniform int boneTextureHeight;\nmat4 getBoneMatrix( const in float i ) {\nfloat j = i * 4.0;\nfloat x = mod( j, float( boneTextureWidth ) );\nfloat y = floor( j / float( boneTextureWidth ) );\nfloat dx = 1.0 / float( boneTextureWidth );\nfloat dy = 1.0 / float( boneTextureHeight );\ny = dy * ( y + 0.5 );\nvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\nvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\nvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\nvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\nmat4 bone = mat4( v1, v2, v3, v4 );\nreturn bone;\n}\n#else\nuniform mat4 boneGlobalMatrices[ MAX_BONES ];\nmat4 getBoneMatrix( const in float i ) {\nmat4 bone = boneGlobalMatrices[ int(i) ];\nreturn bone;\n}\n#endif\n#endif",
-skinbase_vertex:"#ifdef USE_SKINNING\nmat4 boneMatX = getBoneMatrix( skinIndex.x );\nmat4 boneMatY = getBoneMatrix( skinIndex.y );\n#endif",skinning_vertex:"#ifdef USE_SKINNING\n#ifdef USE_MORPHTARGETS\nvec4 skinVertex = vec4( morphed, 1.0 );\n#else\nvec4 skinVertex = vec4( position, 1.0 );\n#endif\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\n#endif",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n#ifndef USE_MORPHNORMALS\nuniform float morphTargetInfluences[ 8 ];\n#else\nuniform float morphTargetInfluences[ 4 ];\n#endif\n#endif",
-morphtarget_vertex:"#ifdef USE_MORPHTARGETS\nvec3 morphed = vec3( 0.0 );\nmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\nmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\nmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\nmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n#ifndef USE_MORPHNORMALS\nmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\nmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\nmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\nmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n#endif\nmorphed += position;\n#endif",
-default_vertex:"vec4 mvPosition;\n#ifdef USE_SKINNING\nmvPosition = modelViewMatrix * skinned;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( position, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\nvec3 morphedNormal = vec3( 0.0 );\nmorphedNormal +=  ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\nmorphedNormal +=  ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\nmorphedNormal +=  ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\nmorphedNormal +=  ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\nmorphedNormal += normal;\n#endif",
-skinnormal_vertex:"#ifdef USE_SKINNING\nmat4 skinMatrix = skinWeight.x * boneMatX;\nskinMatrix \t+= skinWeight.y * boneMatY;\n#ifdef USE_MORPHNORMALS\nvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n#else\nvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n#endif\n#endif",defaultnormal_vertex:"vec3 objectNormal;\n#ifdef USE_SKINNING\nobjectNormal = skinnedNormal.xyz;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\nobjectNormal = morphedNormal;\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\nobjectNormal = normal;\n#endif\n#ifdef FLIP_SIDED\nobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;",
-shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\nuniform sampler2D shadowMap[ MAX_SHADOWS ];\nuniform vec2 shadowMapSize[ MAX_SHADOWS ];\nuniform float shadowDarkness[ MAX_SHADOWS ];\nuniform float shadowBias[ MAX_SHADOWS ];\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nfloat unpackDepth( const in vec4 rgba_depth ) {\nconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\nfloat depth = dot( rgba_depth, bit_shift );\nreturn depth;\n}\n#endif",shadowmap_fragment:"#ifdef USE_SHADOWMAP\n#ifdef SHADOWMAP_DEBUG\nvec3 frustumColors[3];\nfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\nfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\nfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n#endif\n#ifdef SHADOWMAP_CASCADE\nint inFrustumCount = 0;\n#endif\nfloat fDepth;\nvec3 shadowColor = vec3( 1.0 );\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\nbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\nbool inFrustum = all( inFrustumVec );\n#ifdef SHADOWMAP_CASCADE\ninFrustumCount += int( inFrustum );\nbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n#else\nbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n#endif\nbool frustumTest = all( frustumTestVec );\nif ( frustumTest ) {\nshadowCoord.z += shadowBias[ i ];\n#if defined( SHADOWMAP_TYPE_PCF )\nfloat shadow = 0.0;\nconst float shadowDelta = 1.0 / 9.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.25 * xPixelOffset;\nfloat dy0 = -1.25 * yPixelOffset;\nfloat dx1 = 1.25 * xPixelOffset;\nfloat dy1 = 1.25 * yPixelOffset;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\nfloat shadow = 0.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.0 * xPixelOffset;\nfloat dy0 = -1.0 * yPixelOffset;\nfloat dx1 = 1.0 * xPixelOffset;\nfloat dy1 = 1.0 * yPixelOffset;\nmat3 shadowKernel;\nmat3 depthKernel;\ndepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\ndepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\ndepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\ndepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\ndepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\ndepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\ndepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\ndepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\ndepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nvec3 shadowZ = vec3( shadowCoord.z );\nshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\nshadowKernel[0] *= vec3(0.25);\nshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\nshadowKernel[1] *= vec3(0.25);\nshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\nshadowKernel[2] *= vec3(0.25);\nvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\nshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\nshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\nvec4 shadowValues;\nshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\nshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\nshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\nshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\nshadow = dot( shadowValues, vec4( 1.0 ) );\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#else\nvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\nfloat fDepth = unpackDepth( rgbaDepth );\nif ( fDepth < shadowCoord.z )\nshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n#endif\n}\n#ifdef SHADOWMAP_DEBUG\n#ifdef SHADOWMAP_CASCADE\nif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n#else\nif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n#endif\n#endif\n}\n#ifdef GAMMA_OUTPUT\nshadowColor *= shadowColor;\n#endif\ngl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n#endif",
-shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n#endif",shadowmap_vertex:"#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif",alphatest_fragment:"#ifdef ALPHATEST\nif ( gl_FragColor.a < ALPHATEST ) discard;\n#endif",linear_to_gamma_fragment:"#ifdef GAMMA_OUTPUT\ngl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n#endif"};
-THREE.UniformsUtils={merge:function(a){var b,c,d,e={};for(b=0;b<a.length;b++)for(c in d=this.clone(a[b]),d)e[c]=d[c];return e},clone:function(a){var b,c,d,e={};for(b in a)for(c in e[b]={},a[b])d=a[b][c],e[b][c]=d instanceof THREE.Color||d instanceof THREE.Vector2||d instanceof THREE.Vector3||d instanceof THREE.Vector4||d instanceof THREE.Matrix4||d instanceof THREE.Texture?d.clone():d instanceof Array?d.slice():d;return e}};
-THREE.UniformsLib={common:{diffuse:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},map:{type:"t",value:null},offsetRepeat:{type:"v4",value:new THREE.Vector4(0,0,1,1)},lightMap:{type:"t",value:null},specularMap:{type:"t",value:null},envMap:{type:"t",value:null},flipEnvMap:{type:"f",value:-1},useRefract:{type:"i",value:0},reflectivity:{type:"f",value:1},refractionRatio:{type:"f",value:0.98},combine:{type:"i",value:0},morphTargetInfluences:{type:"f",value:0}},bump:{bumpMap:{type:"t",
-value:null},bumpScale:{type:"f",value:1}},normalmap:{normalMap:{type:"t",value:null},normalScale:{type:"v2",value:new THREE.Vector2(1,1)}},fog:{fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},lights:{ambientLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]},directionalLightColor:{type:"fv",value:[]},hemisphereLightDirection:{type:"fv",value:[]},hemisphereLightSkyColor:{type:"fv",
-value:[]},hemisphereLightGroundColor:{type:"fv",value:[]},pointLightColor:{type:"fv",value:[]},pointLightPosition:{type:"fv",value:[]},pointLightDistance:{type:"fv1",value:[]},spotLightColor:{type:"fv",value:[]},spotLightPosition:{type:"fv",value:[]},spotLightDirection:{type:"fv",value:[]},spotLightDistance:{type:"fv1",value:[]},spotLightAngleCos:{type:"fv1",value:[]},spotLightExponent:{type:"fv1",value:[]}},particle:{psColor:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},size:{type:"f",
-value:1},scale:{type:"f",value:1},map:{type:"t",value:null},fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},shadowmap:{shadowMap:{type:"tv",value:[]},shadowMapSize:{type:"v2v",value:[]},shadowBias:{type:"fv1",value:[]},shadowDarkness:{type:"fv1",value:[]},shadowMatrix:{type:"m4v",value:[]}}};
-THREE.ShaderLib={basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.shadowmap]),vertexShader:[THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,
-THREE.ShaderChunk.skinbase_vertex,"#ifdef USE_ENVMAP",THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"#endif",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,
-THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,"void main() {\ngl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,
-THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},lambert:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif",
-THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_lambert_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,
-THREE.ShaderChunk.defaultnormal_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_lambert_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,
-THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,"void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );",THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,"#ifdef DOUBLE_SIDED\nif ( gl_FrontFacing )\ngl_FragColor.xyz *= vLightFront;\nelse\ngl_FragColor.xyz *= vLightBack;\n#else\ngl_FragColor.xyz *= vLightFront;\n#endif",THREE.ShaderChunk.lightmap_fragment,
-THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},phong:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.bump,THREE.UniformsLib.normalmap,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},specular:{type:"c",
-value:new THREE.Color(1118481)},shininess:{type:"f",value:30},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define PHONG\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_phong_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,
-"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"vNormal = normalize( transformedNormal );",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,"vViewPosition = -mvPosition.xyz;",THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,
-THREE.ShaderChunk.lights_phong_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.lights_phong_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,
-THREE.ShaderChunk.bumpmap_pars_fragment,THREE.ShaderChunk.normalmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,"void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );",THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,THREE.ShaderChunk.lights_phong_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,
-THREE.ShaderChunk.fog_fragment,"}"].join("\n")},particle_basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.particle,THREE.UniformsLib.shadowmap]),vertexShader:["uniform float size;\nuniform float scale;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n#ifdef USE_SIZEATTENUATION\ngl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n#else\ngl_PointSize = size;\n#endif\ngl_Position = projectionMatrix * mvPosition;",
-THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 psColor;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_particle_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,"void main() {\ngl_FragColor = vec4( psColor, opacity );",THREE.ShaderChunk.map_particle_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.shadowmap_fragment,
-THREE.ShaderChunk.fog_fragment,"}"].join("\n")},dashed:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,{scale:{type:"f",value:1},dashSize:{type:"f",value:1},totalSize:{type:"f",value:2}}]),vertexShader:["uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"vLineDistance = scale * lineDistance;\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n}"].join("\n"),
-fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,"void main() {\nif ( mod( vLineDistance, totalSize ) > dashSize ) {\ndiscard;\n}\ngl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",
-value:1}},vertexShader:"void main() {\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform float mNear;\nuniform float mFar;\nuniform float opacity;\nvoid main() {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat color = 1.0 - smoothstep( mNear, mFar, depth );\ngl_FragColor = vec4( vec3( color ), opacity );\n}"},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.morphtarget_pars_vertex,"void main() {\nvNormal = normalize( normalMatrix * normal );",
-THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,"}"].join("\n"),fragmentShader:"uniform float opacity;\nvarying vec3 vNormal;\nvoid main() {\ngl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );\n}"},normalmap:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{enableAO:{type:"i",value:0},enableDiffuse:{type:"i",value:0},enableSpecular:{type:"i",value:0},enableReflection:{type:"i",value:0},enableDisplacement:{type:"i",
-value:0},tDisplacement:{type:"t",value:null},tDiffuse:{type:"t",value:null},tCube:{type:"t",value:null},tNormal:{type:"t",value:null},tSpecular:{type:"t",value:null},tAO:{type:"t",value:null},uNormalScale:{type:"v2",value:new THREE.Vector2(1,1)},uDisplacementBias:{type:"f",value:0},uDisplacementScale:{type:"f",value:1},uDiffuseColor:{type:"c",value:new THREE.Color(16777215)},uSpecularColor:{type:"c",value:new THREE.Color(1118481)},uAmbientColor:{type:"c",value:new THREE.Color(16777215)},uShininess:{type:"f",
-value:30},uOpacity:{type:"f",value:1},useRefract:{type:"i",value:0},uRefractionRatio:{type:"f",value:0.98},uReflectivity:{type:"f",value:0.5},uOffset:{type:"v2",value:new THREE.Vector2(0,0)},uRepeat:{type:"v2",value:new THREE.Vector2(1,1)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),fragmentShader:["uniform vec3 uAmbientColor;\nuniform vec3 uDiffuseColor;\nuniform vec3 uSpecularColor;\nuniform float uShininess;\nuniform float uOpacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float uRefractionRatio;\nuniform float uReflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,"void main() {\ngl_FragColor = vec4( vec3( 1.0 ), uOpacity );\nvec3 specularTex = vec3( 1.0 );\nvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\nnormalTex.xy *= uNormalScale;\nnormalTex = normalize( normalTex );\nif( enableDiffuse ) {\n#ifdef GAMMA_INPUT\nvec4 texelColor = texture2D( tDiffuse, vUv );\ntexelColor.xyz *= texelColor.xyz;\ngl_FragColor = gl_FragColor * texelColor;\n#else\ngl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n#endif\n}\nif( enableAO ) {\n#ifdef GAMMA_INPUT\nvec4 aoColor = texture2D( tAO, vUv );\naoColor.xyz *= aoColor.xyz;\ngl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n#endif\n}\nif( enableSpecular )\nspecularTex = texture2D( tSpecular, vUv ).xyz;\nmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\nvec3 finalNormal = tsb * normalTex;\n#ifdef FLIP_SIDED\nfinalNormal = -finalNormal;\n#endif\nvec3 normal = normalize( finalNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\nfloat pointDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\npointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\npointVector = normalize( pointVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n#endif\npointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;\nvec3 pointHalfVector = normalize( pointVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n#else\npointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\nfloat spotDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\nspotVector = normalize( spotVector );\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n#endif\nspotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;\nvec3 spotHalfVector = normalize( spotVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\n#ifdef WRAP_AROUND\nfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\nfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n#endif\ndirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += uDiffuseColor * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;\n#endif\nif ( enableReflection ) {\nvec3 vReflect;\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, normal, uRefractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, normal );\n}\nvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );\n}",
-THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n"),vertexShader:["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\nuniform sampler2D tDisplacement;\nuniform float uDisplacementScale;\nuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,"#ifdef USE_SKINNING\nvNormal = normalize( normalMatrix * skinnedNormal.xyz );\nvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\nvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n#else\nvNormal = normalize( normalMatrix * normal );\nvTangent = normalize( normalMatrix * tangent.xyz );\n#endif\nvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\nvUv = uv * uRepeat + uOffset;\nvec3 displacedPosition;\n#ifdef VERTEX_TEXTURES\nif ( enableDisplacement ) {\nvec3 dv = texture2D( tDisplacement, uv ).xyz;\nfloat df = uDisplacementScale * dv.x + uDisplacementBias;\ndisplacedPosition = position + normalize( normal ) * df;\n} else {\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n}\n#else\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n#endif\nvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\nvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\nvWorldPosition = worldPosition.xyz;\nvViewPosition = -mvPosition.xyz;\n#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif\n}"].join("\n")},
-cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:"varying vec3 vWorldPosition;\nvoid main() {\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\nvWorldPosition = worldPosition.xyz;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\nvoid main() {\ngl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n}"},
-depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,"}"].join("\n"),fragmentShader:"vec4 pack_depth( const in float depth ) {\nconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\nconst vec4 bit_mask  = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\nvec4 res = fract( depth * bit_shift );\nres -= res.xxyz * bit_mask;\nreturn res;\n}\nvoid main() {\ngl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n}"}};THREE.WebGLRenderer=function(a){function b(a,b){var c=a.vertices.length,d=b.material;if(d.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var e in d.attributes){var f=d.attributes[e];if(!f.__webglInitialized||f.createUniqueBuffers){f.__webglInitialized=!0;var h=1;"v2"===f.type?h=2:"v3"===f.type?h=3:"v4"===f.type?h=4:"c"===f.type&&(h=3);f.size=h;f.array=new Float32Array(c*h);f.buffer=j.createBuffer();f.buffer.belongsToAttribute=e;f.needsUpdate=!0}a.__webglCustomAttributesList.push(f)}}}
-function c(a,b){var c=b.geometry,h=a.faces3,g=3*h.length,i=1*h.length,k=3*h.length,h=d(b,a),m=f(h),l=e(h),p=h.vertexColors?h.vertexColors:!1;a.__vertexArray=new Float32Array(3*g);l&&(a.__normalArray=new Float32Array(3*g));c.hasTangents&&(a.__tangentArray=new Float32Array(4*g));p&&(a.__colorArray=new Float32Array(3*g));m&&(0<c.faceVertexUvs.length&&(a.__uvArray=new Float32Array(2*g)),1<c.faceVertexUvs.length&&(a.__uv2Array=new Float32Array(2*g)));b.geometry.skinWeights.length&&b.geometry.skinIndices.length&&
-(a.__skinIndexArray=new Float32Array(4*g),a.__skinWeightArray=new Float32Array(4*g));a.__faceArray=new Uint16Array(3*i);a.__lineArray=new Uint16Array(2*k);if(a.numMorphTargets){a.__morphTargetsArrays=[];c=0;for(m=a.numMorphTargets;c<m;c++)a.__morphTargetsArrays.push(new Float32Array(3*g))}if(a.numMorphNormals){a.__morphNormalsArrays=[];c=0;for(m=a.numMorphNormals;c<m;c++)a.__morphNormalsArrays.push(new Float32Array(3*g))}a.__webglFaceCount=3*i;a.__webglLineCount=2*k;if(h.attributes){void 0===a.__webglCustomAttributesList&&
-(a.__webglCustomAttributesList=[]);for(var n in h.attributes){var i=h.attributes[n],k={},r;for(r in i)k[r]=i[r];if(!k.__webglInitialized||k.createUniqueBuffers)k.__webglInitialized=!0,c=1,"v2"===k.type?c=2:"v3"===k.type?c=3:"v4"===k.type?c=4:"c"===k.type&&(c=3),k.size=c,k.array=new Float32Array(g*c),k.buffer=j.createBuffer(),k.buffer.belongsToAttribute=n,i.needsUpdate=!0,k.__original=i;a.__webglCustomAttributesList.push(k)}}a.__inittedArrays=!0}function d(a,b){return a.material instanceof THREE.MeshFaceMaterial?
-a.material.materials[b.materialIndex]:a.material}function e(a){return a instanceof THREE.MeshBasicMaterial&&!a.envMap||a instanceof THREE.MeshDepthMaterial?!1:a&&void 0!==a.shading&&a.shading===THREE.SmoothShading?THREE.SmoothShading:THREE.FlatShading}function f(a){return a.map||a.lightMap||a.bumpMap||a.normalMap||a.specularMap||a instanceof THREE.ShaderMaterial?!0:!1}function h(a){Ha[a]||(j.enableVertexAttribArray(a),Ha[a]=!0)}function g(){for(var a in Ha)Ha[a]&&(j.disableVertexAttribArray(a),Ha[a]=
-!1)}function i(a,b){return a.z!==b.z?b.z-a.z:a.id-b.id}function k(a,b){return b[0]-a[0]}function m(a,b,c){if(a.length)for(var d=0,e=a.length;d<e;d++)ea=Ba=null,Aa=$=U=Z=fa=Ga=ka=-1,ua=!0,a[d].render(b,c,sb,pb),ea=Ba=null,Aa=$=U=Z=fa=Ga=ka=-1,ua=!0}function l(a,b,c,d,e,f,h,g){var j,i,k,m;b?(i=a.length-1,m=b=-1):(i=0,b=a.length,m=1);for(var l=i;l!==b;l+=m)if(j=a[l],j.render){i=j.object;k=j.buffer;if(g)j=g;else{j=j[c];if(!j)continue;h&&K.setBlending(j.blending,j.blendEquation,j.blendSrc,j.blendDst);
-K.setDepthTest(j.depthTest);K.setDepthWrite(j.depthWrite);A(j.polygonOffset,j.polygonOffsetFactor,j.polygonOffsetUnits)}K.setMaterialFaces(j);k instanceof THREE.BufferGeometry?K.renderBufferDirect(d,e,f,j,k,i):K.renderBuffer(d,e,f,j,k,i)}}function p(a,b,c,d,e,f,h){for(var g,j,i=0,k=a.length;i<k;i++)if(g=a[i],j=g.object,j.visible){if(h)g=h;else{g=g[b];if(!g)continue;f&&K.setBlending(g.blending,g.blendEquation,g.blendSrc,g.blendDst);K.setDepthTest(g.depthTest);K.setDepthWrite(g.depthWrite);A(g.polygonOffset,
-g.polygonOffsetFactor,g.polygonOffsetUnits)}K.renderImmediateObject(c,d,e,g,j)}}function s(a,d){var e,f,h,g;if(void 0===a.__webglInit&&(a.__webglInit=!0,a._modelViewMatrix=new THREE.Matrix4,a._normalMatrix=new THREE.Matrix3,void 0!==a.geometry&&void 0===a.geometry.__webglInit&&(a.geometry.__webglInit=!0,a.geometry.addEventListener("dispose",Cb)),f=a.geometry,void 0!==f))if(f instanceof THREE.BufferGeometry){var i,k;for(i in f.attributes)k="index"===i?j.ELEMENT_ARRAY_BUFFER:j.ARRAY_BUFFER,g=f.attributes[i],
-void 0===g.numItems&&(g.numItems=g.array.length),g.buffer=j.createBuffer(),j.bindBuffer(k,g.buffer),j.bufferData(k,g.array,j.STATIC_DRAW)}else if(a instanceof THREE.Mesh){h=a.material;if(void 0===f.geometryGroups){i=f;var m,l,p;k={};var n=i.morphTargets.length,r=i.morphNormals.length,s=h instanceof THREE.MeshFaceMaterial;i.geometryGroups={};h=0;for(m=i.faces.length;h<m;h++)l=i.faces[h],l=s?l.materialIndex:0,void 0===k[l]&&(k[l]={hash:l,counter:0}),p=k[l].hash+"_"+k[l].counter,void 0===i.geometryGroups[p]&&
-(i.geometryGroups[p]={faces3:[],materialIndex:l,vertices:0,numMorphTargets:n,numMorphNormals:r}),65535<i.geometryGroups[p].vertices+3&&(k[l].counter+=1,p=k[l].hash+"_"+k[l].counter,void 0===i.geometryGroups[p]&&(i.geometryGroups[p]={faces3:[],materialIndex:l,vertices:0,numMorphTargets:n,numMorphNormals:r})),i.geometryGroups[p].faces3.push(h),i.geometryGroups[p].vertices+=3;i.geometryGroupsList=[];for(g in i.geometryGroups)i.geometryGroups[g].id=V++,i.geometryGroupsList.push(i.geometryGroups[g])}for(e in f.geometryGroups)if(g=
-f.geometryGroups[e],!g.__webglVertexBuffer){i=g;i.__webglVertexBuffer=j.createBuffer();i.__webglNormalBuffer=j.createBuffer();i.__webglTangentBuffer=j.createBuffer();i.__webglColorBuffer=j.createBuffer();i.__webglUVBuffer=j.createBuffer();i.__webglUV2Buffer=j.createBuffer();i.__webglSkinIndicesBuffer=j.createBuffer();i.__webglSkinWeightsBuffer=j.createBuffer();i.__webglFaceBuffer=j.createBuffer();i.__webglLineBuffer=j.createBuffer();n=k=void 0;if(i.numMorphTargets){i.__webglMorphTargetsBuffers=[];
-k=0;for(n=i.numMorphTargets;k<n;k++)i.__webglMorphTargetsBuffers.push(j.createBuffer())}if(i.numMorphNormals){i.__webglMorphNormalsBuffers=[];k=0;for(n=i.numMorphNormals;k<n;k++)i.__webglMorphNormalsBuffers.push(j.createBuffer())}K.info.memory.geometries++;c(g,a);f.verticesNeedUpdate=!0;f.morphTargetsNeedUpdate=!0;f.elementsNeedUpdate=!0;f.uvsNeedUpdate=!0;f.normalsNeedUpdate=!0;f.tangentsNeedUpdate=!0;f.colorsNeedUpdate=!0}}else a instanceof THREE.Line?f.__webglVertexBuffer||(g=f,g.__webglVertexBuffer=
-j.createBuffer(),g.__webglColorBuffer=j.createBuffer(),g.__webglLineDistanceBuffer=j.createBuffer(),K.info.memory.geometries++,g=f,i=g.vertices.length,g.__vertexArray=new Float32Array(3*i),g.__colorArray=new Float32Array(3*i),g.__lineDistanceArray=new Float32Array(1*i),g.__webglLineCount=i,b(g,a),f.verticesNeedUpdate=!0,f.colorsNeedUpdate=!0,f.lineDistancesNeedUpdate=!0):a instanceof THREE.ParticleSystem&&!f.__webglVertexBuffer&&(g=f,g.__webglVertexBuffer=j.createBuffer(),g.__webglColorBuffer=j.createBuffer(),
-K.info.memory.geometries++,g=f,i=g.vertices.length,g.__vertexArray=new Float32Array(3*i),g.__colorArray=new Float32Array(3*i),g.__sortArray=[],g.__webglParticleCount=i,b(g,a),f.verticesNeedUpdate=!0,f.colorsNeedUpdate=!0);if(void 0===a.__webglActive){if(a instanceof THREE.Mesh)if(f=a.geometry,f instanceof THREE.BufferGeometry)t(d.__webglObjects,f,a);else{if(f instanceof THREE.Geometry)for(e in f.geometryGroups)g=f.geometryGroups[e],t(d.__webglObjects,g,a)}else a instanceof THREE.Line||a instanceof
-THREE.ParticleSystem?(f=a.geometry,t(d.__webglObjects,f,a)):a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback?d.__webglObjectsImmediate.push({id:null,object:a,opaque:null,transparent:null,z:0}):a instanceof THREE.Sprite?d.__webglSprites.push(a):a instanceof THREE.LensFlare&&d.__webglFlares.push(a);a.__webglActive=!0}}function t(a,b,c){a.push({id:null,buffer:b,object:c,opaque:null,transparent:null,z:0})}function n(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;
-return!1}function r(a){for(var b in a.attributes)a.attributes[b].needsUpdate=!1}function q(a,b){a instanceof THREE.Mesh||a instanceof THREE.ParticleSystem||a instanceof THREE.Line?u(b.__webglObjects,a):a instanceof THREE.Sprite?w(b.__webglSprites,a):a instanceof THREE.LensFlare?w(b.__webglFlares,a):(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)&&u(b.__webglObjectsImmediate,a);delete a.__webglActive}function u(a,b){for(var c=a.length-1;0<=c;c--)a[c].object===b&&a.splice(c,1)}
-function w(a,b){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1)}function z(a,b,c,d,e){P=0;d.needsUpdate&&(d.program&&Gb(d),K.initMaterial(d,b,c,e),d.needsUpdate=!1);d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(K.maxMorphTargets));var f=!1,g=d.program,h=g.uniforms,i=d.uniforms;g!==Ba&&(j.useProgram(g),Ba=g,f=!0);d.id!==Aa&&(Aa=d.id,f=!0);if(f||a!==ea)j.uniformMatrix4fv(h.projectionMatrix,!1,a.projectionMatrix.elements),a!==ea&&(ea=a);if(d.skinning)if(yb&&
-e.useVertexTexture){if(null!==h.boneTexture){var k=B();j.uniform1i(h.boneTexture,k);K.setTexture(e.boneTexture,k)}null!==h.boneTextureWidth&&j.uniform1i(h.boneTextureWidth,e.boneTextureWidth);null!==h.boneTextureHeight&&j.uniform1i(h.boneTextureHeight,e.boneTextureHeight)}else null!==h.boneGlobalMatrices&&j.uniformMatrix4fv(h.boneGlobalMatrices,!1,e.boneMatrices);if(f){c&&d.fog&&(i.fogColor.value=c.color,c instanceof THREE.Fog?(i.fogNear.value=c.near,i.fogFar.value=c.far):c instanceof THREE.FogExp2&&
-(i.fogDensity.value=c.density));if(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d.lights){if(ua){for(var m,l=k=0,p=0,n,r,s,q=Pa,t=q.directional.colors,u=q.directional.positions,w=q.point.colors,z=q.point.positions,y=q.point.distances,A=q.spot.colors,C=q.spot.positions,F=q.spot.distances,J=q.spot.directions,N=q.spot.anglesCos,O=q.spot.exponents,I=q.hemi.skyColors,V=q.hemi.groundColors,R=q.hemi.positions,M=0,U=0,da=0,Z=0,Fa=0,dc=0,X=0,W=0,Q=m=0,c=s=Q=0,f=b.length;c<
-f;c++)m=b[c],m.onlyShadow||(n=m.color,r=m.intensity,s=m.distance,m instanceof THREE.AmbientLight?m.visible&&(K.gammaInput?(k+=n.r*n.r,l+=n.g*n.g,p+=n.b*n.b):(k+=n.r,l+=n.g,p+=n.b)):m instanceof THREE.DirectionalLight?(Fa+=1,m.visible&&(ga.getPositionFromMatrix(m.matrixWorld),sa.getPositionFromMatrix(m.target.matrixWorld),ga.sub(sa),ga.normalize(),0===ga.x&&0===ga.y&&0===ga.z||(m=3*M,u[m]=ga.x,u[m+1]=ga.y,u[m+2]=ga.z,K.gammaInput?D(t,m,n,r*r):x(t,m,n,r),M+=1))):m instanceof THREE.PointLight?(dc+=1,
-m.visible&&(Q=3*U,K.gammaInput?D(w,Q,n,r*r):x(w,Q,n,r),sa.getPositionFromMatrix(m.matrixWorld),z[Q]=sa.x,z[Q+1]=sa.y,z[Q+2]=sa.z,y[U]=s,U+=1)):m instanceof THREE.SpotLight?(X+=1,m.visible&&(Q=3*da,K.gammaInput?D(A,Q,n,r*r):x(A,Q,n,r),sa.getPositionFromMatrix(m.matrixWorld),C[Q]=sa.x,C[Q+1]=sa.y,C[Q+2]=sa.z,F[da]=s,ga.copy(sa),sa.getPositionFromMatrix(m.target.matrixWorld),ga.sub(sa),ga.normalize(),J[Q]=ga.x,J[Q+1]=ga.y,J[Q+2]=ga.z,N[da]=Math.cos(m.angle),O[da]=m.exponent,da+=1)):m instanceof THREE.HemisphereLight&&
-(W+=1,m.visible&&(ga.getPositionFromMatrix(m.matrixWorld),ga.normalize(),0===ga.x&&0===ga.y&&0===ga.z||(s=3*Z,R[s]=ga.x,R[s+1]=ga.y,R[s+2]=ga.z,n=m.color,m=m.groundColor,K.gammaInput?(r*=r,D(I,s,n,r),D(V,s,m,r)):(x(I,s,n,r),x(V,s,m,r)),Z+=1))));c=3*M;for(f=Math.max(t.length,3*Fa);c<f;c++)t[c]=0;c=3*U;for(f=Math.max(w.length,3*dc);c<f;c++)w[c]=0;c=3*da;for(f=Math.max(A.length,3*X);c<f;c++)A[c]=0;c=3*Z;for(f=Math.max(I.length,3*W);c<f;c++)I[c]=0;c=3*Z;for(f=Math.max(V.length,3*W);c<f;c++)V[c]=0;q.directional.length=
-M;q.point.length=U;q.spot.length=da;q.hemi.length=Z;q.ambient[0]=k;q.ambient[1]=l;q.ambient[2]=p;ua=!1}c=Pa;i.ambientLightColor.value=c.ambient;i.directionalLightColor.value=c.directional.colors;i.directionalLightDirection.value=c.directional.positions;i.pointLightColor.value=c.point.colors;i.pointLightPosition.value=c.point.positions;i.pointLightDistance.value=c.point.distances;i.spotLightColor.value=c.spot.colors;i.spotLightPosition.value=c.spot.positions;i.spotLightDistance.value=c.spot.distances;
-i.spotLightDirection.value=c.spot.directions;i.spotLightAngleCos.value=c.spot.anglesCos;i.spotLightExponent.value=c.spot.exponents;i.hemisphereLightSkyColor.value=c.hemi.skyColors;i.hemisphereLightGroundColor.value=c.hemi.groundColors;i.hemisphereLightDirection.value=c.hemi.positions}if(d instanceof THREE.MeshBasicMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.MeshPhongMaterial){i.opacity.value=d.opacity;K.gammaInput?i.diffuse.value.copyGammaToLinear(d.color):i.diffuse.value=
-d.color;i.map.value=d.map;i.lightMap.value=d.lightMap;i.specularMap.value=d.specularMap;d.bumpMap&&(i.bumpMap.value=d.bumpMap,i.bumpScale.value=d.bumpScale);d.normalMap&&(i.normalMap.value=d.normalMap,i.normalScale.value.copy(d.normalScale));var $;d.map?$=d.map:d.specularMap?$=d.specularMap:d.normalMap?$=d.normalMap:d.bumpMap&&($=d.bumpMap);void 0!==$&&(c=$.offset,$=$.repeat,i.offsetRepeat.value.set(c.x,c.y,$.x,$.y));i.envMap.value=d.envMap;i.flipEnvMap.value=d.envMap instanceof THREE.WebGLRenderTargetCube?
-1:-1;i.reflectivity.value=d.reflectivity;i.refractionRatio.value=d.refractionRatio;i.combine.value=d.combine;i.useRefract.value=d.envMap&&d.envMap.mapping instanceof THREE.CubeRefractionMapping}d instanceof THREE.LineBasicMaterial?(i.diffuse.value=d.color,i.opacity.value=d.opacity):d instanceof THREE.LineDashedMaterial?(i.diffuse.value=d.color,i.opacity.value=d.opacity,i.dashSize.value=d.dashSize,i.totalSize.value=d.dashSize+d.gapSize,i.scale.value=d.scale):d instanceof THREE.ParticleSystemMaterial?
-(i.psColor.value=d.color,i.opacity.value=d.opacity,i.size.value=d.size,i.scale.value=G.height/2,i.map.value=d.map):d instanceof THREE.MeshPhongMaterial?(i.shininess.value=d.shininess,K.gammaInput?(i.ambient.value.copyGammaToLinear(d.ambient),i.emissive.value.copyGammaToLinear(d.emissive),i.specular.value.copyGammaToLinear(d.specular)):(i.ambient.value=d.ambient,i.emissive.value=d.emissive,i.specular.value=d.specular),d.wrapAround&&i.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshLambertMaterial?
-(K.gammaInput?(i.ambient.value.copyGammaToLinear(d.ambient),i.emissive.value.copyGammaToLinear(d.emissive)):(i.ambient.value=d.ambient,i.emissive.value=d.emissive),d.wrapAround&&i.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshDepthMaterial?(i.mNear.value=a.near,i.mFar.value=a.far,i.opacity.value=d.opacity):d instanceof THREE.MeshNormalMaterial&&(i.opacity.value=d.opacity);if(e.receiveShadow&&!d._shadowPass&&i.shadowMatrix){c=$=0;for(f=b.length;c<f;c++)if(k=b[c],k.castShadow&&(k instanceof
-THREE.SpotLight||k instanceof THREE.DirectionalLight&&!k.shadowCascade))i.shadowMap.value[$]=k.shadowMap,i.shadowMapSize.value[$]=k.shadowMapSize,i.shadowMatrix.value[$]=k.shadowMatrix,i.shadowDarkness.value[$]=k.shadowDarkness,i.shadowBias.value[$]=k.shadowBias,$++}b=d.uniformsList;i=0;for($=b.length;i<$;i++)if(f=g.uniforms[b[i][1]])if(c=b[i][0],l=c.type,k=c.value,"i"===l)j.uniform1i(f,k);else if("f"===l)j.uniform1f(f,k);else if("v2"===l)j.uniform2f(f,k.x,k.y);else if("v3"===l)j.uniform3f(f,k.x,
-k.y,k.z);else if("v4"===l)j.uniform4f(f,k.x,k.y,k.z,k.w);else if("c"===l)j.uniform3f(f,k.r,k.g,k.b);else if("iv1"===l)j.uniform1iv(f,k);else if("iv"===l)j.uniform3iv(f,k);else if("fv1"===l)j.uniform1fv(f,k);else if("fv"===l)j.uniform3fv(f,k);else if("v2v"===l){void 0===c._array&&(c._array=new Float32Array(2*k.length));l=0;for(p=k.length;l<p;l++)q=2*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y;j.uniform2fv(f,c._array)}else if("v3v"===l){void 0===c._array&&(c._array=new Float32Array(3*k.length));l=0;for(p=
-k.length;l<p;l++)q=3*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y,c._array[q+2]=k[l].z;j.uniform3fv(f,c._array)}else if("v4v"===l){void 0===c._array&&(c._array=new Float32Array(4*k.length));l=0;for(p=k.length;l<p;l++)q=4*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y,c._array[q+2]=k[l].z,c._array[q+3]=k[l].w;j.uniform4fv(f,c._array)}else if("m4"===l)void 0===c._array&&(c._array=new Float32Array(16)),k.flattenToArray(c._array),j.uniformMatrix4fv(f,!1,c._array);else if("m4v"===l){void 0===c._array&&(c._array=
-new Float32Array(16*k.length));l=0;for(p=k.length;l<p;l++)k[l].flattenToArrayOffset(c._array,16*l);j.uniformMatrix4fv(f,!1,c._array)}else if("t"===l){if(q=k,k=B(),j.uniform1i(f,k),q)if(q.image instanceof Array&&6===q.image.length){if(c=q,f=k,6===c.image.length)if(c.needsUpdate){c.image.__webglTextureCube||(c.addEventListener("dispose",Db),c.image.__webglTextureCube=j.createTexture(),K.info.memory.textures++);j.activeTexture(j.TEXTURE0+f);j.bindTexture(j.TEXTURE_CUBE_MAP,c.image.__webglTextureCube);
-j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL,c.flipY);f=c instanceof THREE.CompressedTexture;k=[];for(l=0;6>l;l++)K.autoScaleCubemaps&&!f?(p=k,q=l,t=c.image[l],w=ac,t.width<=w&&t.height<=w||(z=Math.max(t.width,t.height),u=Math.floor(t.width*w/z),w=Math.floor(t.height*w/z),z=document.createElement("canvas"),z.width=u,z.height=w,z.getContext("2d").drawImage(t,0,0,t.width,t.height,0,0,u,w),t=z),p[q]=t):k[l]=c.image[l];l=k[0];p=0===(l.width&l.width-1)&&0===(l.height&l.height-1);q=v(c.format);t=v(c.type);E(j.TEXTURE_CUBE_MAP,
-c,p);for(l=0;6>l;l++)if(f){w=k[l].mipmaps;z=0;for(y=w.length;z<y;z++)u=w[z],c.format!==THREE.RGBAFormat?j.compressedTexImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,z,q,u.width,u.height,0,u.data):j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,z,q,u.width,u.height,0,q,t,u.data)}else j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,0,q,q,t,k[l]);c.generateMipmaps&&p&&j.generateMipmap(j.TEXTURE_CUBE_MAP);c.needsUpdate=!1;if(c.onUpdate)c.onUpdate()}else j.activeTexture(j.TEXTURE0+f),j.bindTexture(j.TEXTURE_CUBE_MAP,
-c.image.__webglTextureCube)}else q instanceof THREE.WebGLRenderTargetCube?(c=q,j.activeTexture(j.TEXTURE0+k),j.bindTexture(j.TEXTURE_CUBE_MAP,c.__webglTexture)):K.setTexture(q,k)}else if("tv"===l){void 0===c._array&&(c._array=[]);l=0;for(p=c.value.length;l<p;l++)c._array[l]=B();j.uniform1iv(f,c._array);l=0;for(p=c.value.length;l<p;l++)q=c.value[l],k=c._array[l],q&&K.setTexture(q,k)}else console.warn("THREE.WebGLRenderer: Unknown uniform type: "+l);if((d instanceof THREE.ShaderMaterial||d instanceof
-THREE.MeshPhongMaterial||d.envMap)&&null!==h.cameraPosition)sa.getPositionFromMatrix(a.matrixWorld),j.uniform3f(h.cameraPosition,sa.x,sa.y,sa.z);(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.ShaderMaterial||d.skinning)&&null!==h.viewMatrix&&j.uniformMatrix4fv(h.viewMatrix,!1,a.matrixWorldInverse.elements)}j.uniformMatrix4fv(h.modelViewMatrix,!1,e._modelViewMatrix.elements);h.normalMatrix&&j.uniformMatrix3fv(h.normalMatrix,!1,e._normalMatrix.elements);
-null!==h.modelMatrix&&j.uniformMatrix4fv(h.modelMatrix,!1,e.matrixWorld.elements);return g}function B(){var a=P;a>=Mb&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+Mb);P+=1;return a}function D(a,b,c,d){a[b]=c.r*c.r*d;a[b+1]=c.g*c.g*d;a[b+2]=c.b*c.b*d}function x(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function F(a){a!==xa&&(j.lineWidth(a),xa=a)}function A(a,b,c){Da!==a&&(a?j.enable(j.POLYGON_OFFSET_FILL):j.disable(j.POLYGON_OFFSET_FILL),Da=a);
-if(a&&(Ua!==b||Qa!==c))j.polygonOffset(b,c),Ua=b,Qa=c}function O(a){for(var a=a.split("\n"),b=0,c=a.length;b<c;b++)a[b]=b+1+": "+a[b];return a.join("\n")}function C(a,b){var c;"fragment"===a?c=j.createShader(j.FRAGMENT_SHADER):"vertex"===a&&(c=j.createShader(j.VERTEX_SHADER));j.shaderSource(c,b);j.compileShader(c);return!j.getShaderParameter(c,j.COMPILE_STATUS)?(console.error(j.getShaderInfoLog(c)),console.error(O(b)),null):c}function E(a,b,c){c?(j.texParameteri(a,j.TEXTURE_WRAP_S,v(b.wrapS)),j.texParameteri(a,
-j.TEXTURE_WRAP_T,v(b.wrapT)),j.texParameteri(a,j.TEXTURE_MAG_FILTER,v(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,v(b.minFilter))):(j.texParameteri(a,j.TEXTURE_WRAP_S,j.CLAMP_TO_EDGE),j.texParameteri(a,j.TEXTURE_WRAP_T,j.CLAMP_TO_EDGE),j.texParameteri(a,j.TEXTURE_MAG_FILTER,y(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,y(b.minFilter)));if(wa&&b.type!==THREE.FloatType&&(1<b.anisotropy||b.__oldAnisotropy))j.texParameterf(a,wa.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,Nb)),b.__oldAnisotropy=
-b.anisotropy}function I(a,b){j.bindRenderbuffer(j.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(j.renderbufferStorage(j.RENDERBUFFER,j.DEPTH_COMPONENT16,b.width,b.height),j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_ATTACHMENT,j.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(j.renderbufferStorage(j.RENDERBUFFER,j.DEPTH_STENCIL,b.width,b.height),j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_STENCIL_ATTACHMENT,j.RENDERBUFFER,a)):j.renderbufferStorage(j.RENDERBUFFER,j.RGBA4,b.width,b.height)}
-function y(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?j.NEAREST:j.LINEAR}function v(a){if(a===THREE.RepeatWrapping)return j.REPEAT;if(a===THREE.ClampToEdgeWrapping)return j.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return j.MIRRORED_REPEAT;if(a===THREE.NearestFilter)return j.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return j.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return j.NEAREST_MIPMAP_LINEAR;if(a===
-THREE.LinearFilter)return j.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return j.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return j.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return j.UNSIGNED_BYTE;if(a===THREE.UnsignedShort4444Type)return j.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return j.UNSIGNED_SHORT_5_5_5_1;if(a===THREE.UnsignedShort565Type)return j.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return j.BYTE;if(a===THREE.ShortType)return j.SHORT;if(a===
-THREE.UnsignedShortType)return j.UNSIGNED_SHORT;if(a===THREE.IntType)return j.INT;if(a===THREE.UnsignedIntType)return j.UNSIGNED_INT;if(a===THREE.FloatType)return j.FLOAT;if(a===THREE.AlphaFormat)return j.ALPHA;if(a===THREE.RGBFormat)return j.RGB;if(a===THREE.RGBAFormat)return j.RGBA;if(a===THREE.LuminanceFormat)return j.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return j.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return j.FUNC_ADD;if(a===THREE.SubtractEquation)return j.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return j.FUNC_REVERSE_SUBTRACT;
-if(a===THREE.ZeroFactor)return j.ZERO;if(a===THREE.OneFactor)return j.ONE;if(a===THREE.SrcColorFactor)return j.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return j.ONE_MINUS_SRC_COLOR;if(a===THREE.SrcAlphaFactor)return j.SRC_ALPHA;if(a===THREE.OneMinusSrcAlphaFactor)return j.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return j.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return j.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return j.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return j.ONE_MINUS_DST_COLOR;
-if(a===THREE.SrcAlphaSaturateFactor)return j.SRC_ALPHA_SATURATE;if(void 0!==Ea){if(a===THREE.RGB_S3TC_DXT1_Format)return Ea.COMPRESSED_RGB_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT1_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT5_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);var a=a||{},G=void 0!==a.canvas?a.canvas:document.createElement("canvas"),
-R=void 0!==a.precision?a.precision:"highp",J=void 0!==a.alpha?a.alpha:!0,ba=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,oa=void 0!==a.antialias?a.antialias:!1,pa=void 0!==a.stencil?a.stencil:!0,N=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,M=new THREE.Color(0),Q=0;this.domElement=G;this.context=null;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.autoUpdateObjects=this.sortObjects=this.autoClearStencil=
-this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.shadowMapEnabled=this.physicallyBasedShading=this.gammaOutput=this.gammaInput=!1;this.shadowMapAutoUpdate=!0;this.shadowMapType=THREE.PCFShadowMap;this.shadowMapCullFace=THREE.CullFaceFront;this.shadowMapCascade=this.shadowMapDebug=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;this.renderPluginsPre=[];this.renderPluginsPost=[];this.info={memory:{programs:0,geometries:0,textures:0},render:{calls:0,vertices:0,
-faces:0,points:0}};var K=this,ca=[],Fa=0,Ba=null,da=null,Aa=-1,$=null,ea=null,V=0,P=0,Z=-1,U=-1,ka=-1,ta=-1,ia=-1,La=-1,Ga=-1,fa=-1,Da=null,Ua=null,Qa=null,xa=null,bb=0,cb=0,Ma=G.width,fb=G.height,sb=0,pb=0,Ha={},la=new THREE.Frustum,ra=new THREE.Matrix4,gb=new THREE.Matrix4,sa=new THREE.Vector3,ga=new THREE.Vector3,ua=!0,Pa={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],
-anglesCos:[],exponents:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},j,Oa,va,wa,Ea;try{var Ra={alpha:J,premultipliedAlpha:ba,antialias:oa,stencil:pa,preserveDrawingBuffer:N};j=G.getContext("webgl",Ra)||G.getContext("experimental-webgl",Ra);if(null===j)throw"Error creating WebGL context.";}catch(Zb){console.error(Zb)}Oa=j.getExtension("OES_texture_float");j.getExtension("OES_texture_float_linear");va=j.getExtension("OES_standard_derivatives");wa=j.getExtension("EXT_texture_filter_anisotropic")||
-j.getExtension("MOZ_EXT_texture_filter_anisotropic")||j.getExtension("WEBKIT_EXT_texture_filter_anisotropic");Ea=j.getExtension("WEBGL_compressed_texture_s3tc")||j.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||j.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");Oa||console.log("THREE.WebGLRenderer: Float textures not supported.");va||console.log("THREE.WebGLRenderer: Standard derivatives not supported.");wa||console.log("THREE.WebGLRenderer: Anisotropic texture filtering not supported.");
-Ea||console.log("THREE.WebGLRenderer: S3TC compressed textures not supported.");void 0===j.getShaderPrecisionFormat&&(j.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}});j.clearColor(0,0,0,1);j.clearDepth(1);j.clearStencil(0);j.enable(j.DEPTH_TEST);j.depthFunc(j.LEQUAL);j.frontFace(j.CCW);j.cullFace(j.BACK);j.enable(j.CULL_FACE);j.enable(j.BLEND);j.blendEquation(j.FUNC_ADD);j.blendFunc(j.SRC_ALPHA,j.ONE_MINUS_SRC_ALPHA);j.viewport(bb,cb,Ma,fb);j.clearColor(M.r,M.g,M.b,
-Q);this.context=j;var Mb=j.getParameter(j.MAX_TEXTURE_IMAGE_UNITS),$b=j.getParameter(j.MAX_VERTEX_TEXTURE_IMAGE_UNITS);j.getParameter(j.MAX_TEXTURE_SIZE);var ac=j.getParameter(j.MAX_CUBE_MAP_TEXTURE_SIZE),Nb=wa?j.getParameter(wa.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0,Bb=0<$b,yb=Bb&&Oa;Ea&&j.getParameter(j.COMPRESSED_TEXTURE_FORMATS);var bc=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.HIGH_FLOAT),cc=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.MEDIUM_FLOAT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.LOW_FLOAT);
-var qc=j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.HIGH_FLOAT),rc=j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.MEDIUM_FLOAT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.LOW_FLOAT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.HIGH_INT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.MEDIUM_INT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.LOW_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.HIGH_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.MEDIUM_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,
-j.LOW_INT);var sc=0<bc.precision&&0<qc.precision,Ob=0<cc.precision&&0<rc.precision;"highp"===R&&!sc&&(Ob?(R="mediump",console.warn("WebGLRenderer: highp not supported, using mediump")):(R="lowp",console.warn("WebGLRenderer: highp and mediump not supported, using lowp")));"mediump"===R&&!Ob&&(R="lowp",console.warn("WebGLRenderer: mediump not supported, using lowp"));this.getContext=function(){return j};this.supportsVertexTextures=function(){return Bb};this.supportsFloatTextures=function(){return Oa};
-this.supportsStandardDerivatives=function(){return va};this.supportsCompressedTextureS3TC=function(){return Ea};this.getMaxAnisotropy=function(){return Nb};this.getPrecision=function(){return R};this.setSize=function(a,b,c){G.width=a*this.devicePixelRatio;G.height=b*this.devicePixelRatio;1!==this.devicePixelRatio&&!1!==c&&(G.style.width=a+"px",G.style.height=b+"px");this.setViewport(0,0,G.width,G.height)};this.setViewport=function(a,b,c,d){bb=void 0!==a?a:0;cb=void 0!==b?b:0;Ma=void 0!==c?c:G.width;
-fb=void 0!==d?d:G.height;j.viewport(bb,cb,Ma,fb)};this.setScissor=function(a,b,c,d){j.scissor(a,b,c,d)};this.enableScissorTest=function(a){a?j.enable(j.SCISSOR_TEST):j.disable(j.SCISSOR_TEST)};this.setClearColor=function(a,b){M.set(a);Q=void 0!==b?b:1;j.clearColor(M.r,M.g,M.b,Q)};this.setClearColorHex=function(a,b){console.warn("DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,b)};this.getClearColor=function(){return M};this.getClearAlpha=function(){return Q};
-this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=j.COLOR_BUFFER_BIT;if(void 0===b||b)d|=j.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=j.STENCIL_BUFFER_BIT;j.clear(d)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.addPostPlugin=function(a){a.init(this);this.renderPluginsPost.push(a)};this.addPrePlugin=function(a){a.init(this);this.renderPluginsPre.push(a)};this.updateShadowMap=function(a,b){Ba=null;Aa=$=fa=Ga=ka=-1;ua=!0;U=Z=-1;this.shadowMapPlugin.update(a,b)};
-var Cb=function(a){a=a.target;a.removeEventListener("dispose",Cb);a.__webglInit=void 0;if(a instanceof THREE.BufferGeometry){var b=a.attributes,c;for(c in b)void 0!==b[c].buffer&&j.deleteBuffer(b[c].buffer);K.info.memory.geometries--}else if(void 0!==a.geometryGroups)for(b in a.geometryGroups){c=a.geometryGroups[b];if(void 0!==c.numMorphTargets)for(var d=0,e=c.numMorphTargets;d<e;d++)j.deleteBuffer(c.__webglMorphTargetsBuffers[d]);if(void 0!==c.numMorphNormals){d=0;for(e=c.numMorphNormals;d<e;d++)j.deleteBuffer(c.__webglMorphNormalsBuffers[d])}Hb(c)}else Hb(a)},
-Db=function(a){a=a.target;a.removeEventListener("dispose",Db);a.image&&a.image.__webglTextureCube?j.deleteTexture(a.image.__webglTextureCube):a.__webglInit&&(a.__webglInit=!1,j.deleteTexture(a.__webglTexture));K.info.memory.textures--},Eb=function(a){a=a.target;a.removeEventListener("dispose",Eb);if(a&&a.__webglTexture)if(j.deleteTexture(a.__webglTexture),a instanceof THREE.WebGLRenderTargetCube)for(var b=0;6>b;b++)j.deleteFramebuffer(a.__webglFramebuffer[b]),j.deleteRenderbuffer(a.__webglRenderbuffer[b]);
-else j.deleteFramebuffer(a.__webglFramebuffer),j.deleteRenderbuffer(a.__webglRenderbuffer);K.info.memory.textures--},Fb=function(a){a=a.target;a.removeEventListener("dispose",Fb);Gb(a)},Hb=function(a){void 0!==a.__webglVertexBuffer&&j.deleteBuffer(a.__webglVertexBuffer);void 0!==a.__webglNormalBuffer&&j.deleteBuffer(a.__webglNormalBuffer);void 0!==a.__webglTangentBuffer&&j.deleteBuffer(a.__webglTangentBuffer);void 0!==a.__webglColorBuffer&&j.deleteBuffer(a.__webglColorBuffer);void 0!==a.__webglUVBuffer&&
-j.deleteBuffer(a.__webglUVBuffer);void 0!==a.__webglUV2Buffer&&j.deleteBuffer(a.__webglUV2Buffer);void 0!==a.__webglSkinIndicesBuffer&&j.deleteBuffer(a.__webglSkinIndicesBuffer);void 0!==a.__webglSkinWeightsBuffer&&j.deleteBuffer(a.__webglSkinWeightsBuffer);void 0!==a.__webglFaceBuffer&&j.deleteBuffer(a.__webglFaceBuffer);void 0!==a.__webglLineBuffer&&j.deleteBuffer(a.__webglLineBuffer);void 0!==a.__webglLineDistanceBuffer&&j.deleteBuffer(a.__webglLineDistanceBuffer);if(void 0!==a.__webglCustomAttributesList)for(var b in a.__webglCustomAttributesList)j.deleteBuffer(a.__webglCustomAttributesList[b].buffer);
-K.info.memory.geometries--},Gb=function(a){var b=a.program;if(void 0!==b){a.program=void 0;var c,d,e=!1,a=0;for(c=ca.length;a<c;a++)if(d=ca[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=ca.length;a<c;a++)d=ca[a],d.program!==b&&e.push(d);ca=e;j.deleteProgram(b);K.info.memory.programs--}}};this.renderBufferImmediate=function(a,b,c){a.hasPositions&&!a.__webglVertexBuffer&&(a.__webglVertexBuffer=j.createBuffer());a.hasNormals&&!a.__webglNormalBuffer&&(a.__webglNormalBuffer=
-j.createBuffer());a.hasUvs&&!a.__webglUvBuffer&&(a.__webglUvBuffer=j.createBuffer());a.hasColors&&!a.__webglColorBuffer&&(a.__webglColorBuffer=j.createBuffer());a.hasPositions&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglVertexBuffer),j.bufferData(j.ARRAY_BUFFER,a.positionArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.position),j.vertexAttribPointer(b.attributes.position,3,j.FLOAT,!1,0,0));if(a.hasNormals){j.bindBuffer(j.ARRAY_BUFFER,a.__webglNormalBuffer);if(c.shading===THREE.FlatShading){var d,
-e,f,h,g,i,k,l,m,p,n,q=3*a.count;for(n=0;n<q;n+=9)p=a.normalArray,d=p[n],e=p[n+1],f=p[n+2],h=p[n+3],i=p[n+4],l=p[n+5],g=p[n+6],k=p[n+7],m=p[n+8],d=(d+h+g)/3,e=(e+i+k)/3,f=(f+l+m)/3,p[n]=d,p[n+1]=e,p[n+2]=f,p[n+3]=d,p[n+4]=e,p[n+5]=f,p[n+6]=d,p[n+7]=e,p[n+8]=f}j.bufferData(j.ARRAY_BUFFER,a.normalArray,j.DYNAMIC_DRAW);j.enableVertexAttribArray(b.attributes.normal);j.vertexAttribPointer(b.attributes.normal,3,j.FLOAT,!1,0,0)}a.hasUvs&&c.map&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglUvBuffer),j.bufferData(j.ARRAY_BUFFER,
-a.uvArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.uv),j.vertexAttribPointer(b.attributes.uv,2,j.FLOAT,!1,0,0));a.hasColors&&c.vertexColors!==THREE.NoColors&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,a.colorArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.color),j.vertexAttribPointer(b.attributes.color,3,j.FLOAT,!1,0,0));j.drawArrays(j.TRIANGLES,0,a.count);a.count=0};this.renderBufferDirect=function(a,b,c,d,e,f){if(!1!==d.visible){var i,
-k,l,m;i=z(a,b,c,d,f);b=i.attributes;a=e.attributes;c=!1;i=16777215*e.id+2*i.id+(d.wireframe?1:0);i!==$&&($=i,c=!0);c&&g();if(f instanceof THREE.Mesh)if(f=a.index){e=e.offsets;1<e.length&&(c=!0);for(var p=0,n=e.length;p<n;p++){var q=e[p].index;if(c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,m,j.FLOAT,!1,0,4*q*m)):d.defaultAttributeValues&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):
-3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,f.buffer)}j.drawElements(j.TRIANGLES,e[p].count,j.UNSIGNED_SHORT,2*e[p].start);K.info.render.calls++;K.info.render.vertices+=e[p].count;K.info.render.faces+=e[p].count/3}}else{if(c)for(k in b)"index"!==k&&(l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&
-(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k]))));d=e.attributes.position;j.drawArrays(j.TRIANGLES,0,d.numItems/3);K.info.render.calls++;K.info.render.vertices+=d.numItems/3;K.info.render.faces+=d.numItems/3/3}else if(f instanceof THREE.ParticleSystem){if(c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,
-m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));d=a.position;j.drawArrays(j.POINTS,0,d.numItems/3);K.info.render.calls++;K.info.render.points+=d.numItems/3}}else if(f instanceof THREE.Line&&c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,
-m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));k=f.type===THREE.LineStrip?j.LINE_STRIP:j.LINES;F(d.linewidth);d=a.position;j.drawArrays(k,0,d.numItems/3);K.info.render.calls++;K.info.render.points+=d.numItems}}};this.renderBuffer=function(a,b,c,d,e,f){if(!1!==d.visible){var i,l,c=z(a,b,c,d,f),
-a=c.attributes,b=!1,c=16777215*e.id+2*c.id+(d.wireframe?1:0);c!==$&&($=c,b=!0);b&&g();if(!d.morphTargets&&0<=a.position)b&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglVertexBuffer),h(a.position),j.vertexAttribPointer(a.position,3,j.FLOAT,!1,0,0));else if(f.morphTargetBase){c=d.program.attributes;-1!==f.morphTargetBase&&0<=c.position?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[f.morphTargetBase]),h(c.position),j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0)):0<=c.position&&(j.bindBuffer(j.ARRAY_BUFFER,
-e.__webglVertexBuffer),h(c.position),j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0));if(f.morphTargetForcedOrder.length){var m=0;l=f.morphTargetForcedOrder;for(i=f.morphTargetInfluences;m<d.numSupportedMorphTargets&&m<l.length;)0<=c["morphTarget"+m]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[l[m]]),h(c["morphTarget"+m]),j.vertexAttribPointer(c["morphTarget"+m],3,j.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[l[m]]),
-h(c["morphNormal"+m]),j.vertexAttribPointer(c["morphNormal"+m],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[m]=i[l[m]],m++}else{l=[];i=f.morphTargetInfluences;var p,n=i.length;for(p=0;p<n;p++)m=i[p],0<m&&l.push([m,p]);l.length>d.numSupportedMorphTargets?(l.sort(k),l.length=d.numSupportedMorphTargets):l.length>d.numSupportedMorphNormals?l.sort(k):0===l.length&&l.push([0,0]);for(m=0;m<d.numSupportedMorphTargets;)l[m]?(p=l[m][1],0<=c["morphTarget"+m]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[p]),
-h(c["morphTarget"+m]),j.vertexAttribPointer(c["morphTarget"+m],3,j.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[p]),h(c["morphNormal"+m]),j.vertexAttribPointer(c["morphNormal"+m],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[m]=i[p]):f.__webglMorphTargetInfluences[m]=0,m++}null!==d.program.uniforms.morphTargetInfluences&&j.uniform1fv(d.program.uniforms.morphTargetInfluences,f.__webglMorphTargetInfluences)}if(b){if(e.__webglCustomAttributesList){i=
-0;for(l=e.__webglCustomAttributesList.length;i<l;i++)c=e.__webglCustomAttributesList[i],0<=a[c.buffer.belongsToAttribute]&&(j.bindBuffer(j.ARRAY_BUFFER,c.buffer),h(a[c.buffer.belongsToAttribute]),j.vertexAttribPointer(a[c.buffer.belongsToAttribute],c.size,j.FLOAT,!1,0,0))}0<=a.color&&(0<f.geometry.colors.length||0<f.geometry.faces.length?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglColorBuffer),h(a.color),j.vertexAttribPointer(a.color,3,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib3fv(a.color,
-d.defaultAttributeValues.color));0<=a.normal&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglNormalBuffer),h(a.normal),j.vertexAttribPointer(a.normal,3,j.FLOAT,!1,0,0));0<=a.tangent&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglTangentBuffer),h(a.tangent),j.vertexAttribPointer(a.tangent,4,j.FLOAT,!1,0,0));0<=a.uv&&(f.geometry.faceVertexUvs[0]?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUVBuffer),h(a.uv),j.vertexAttribPointer(a.uv,2,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib2fv(a.uv,d.defaultAttributeValues.uv));
-0<=a.uv2&&(f.geometry.faceVertexUvs[1]?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUV2Buffer),h(a.uv2),j.vertexAttribPointer(a.uv2,2,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib2fv(a.uv2,d.defaultAttributeValues.uv2));d.skinning&&(0<=a.skinIndex&&0<=a.skinWeight)&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglSkinIndicesBuffer),h(a.skinIndex),j.vertexAttribPointer(a.skinIndex,4,j.FLOAT,!1,0,0),j.bindBuffer(j.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),h(a.skinWeight),j.vertexAttribPointer(a.skinWeight,
-4,j.FLOAT,!1,0,0));0<=a.lineDistance&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglLineDistanceBuffer),h(a.lineDistance),j.vertexAttribPointer(a.lineDistance,1,j.FLOAT,!1,0,0))}f instanceof THREE.Mesh?(d.wireframe?(F(d.wireframeLinewidth),b&&j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,e.__webglLineBuffer),j.drawElements(j.LINES,e.__webglLineCount,j.UNSIGNED_SHORT,0)):(b&&j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,e.__webglFaceBuffer),j.drawElements(j.TRIANGLES,e.__webglFaceCount,j.UNSIGNED_SHORT,0)),K.info.render.calls++,
-K.info.render.vertices+=e.__webglFaceCount,K.info.render.faces+=e.__webglFaceCount/3):f instanceof THREE.Line?(f=f.type===THREE.LineStrip?j.LINE_STRIP:j.LINES,F(d.linewidth),j.drawArrays(f,0,e.__webglLineCount),K.info.render.calls++):f instanceof THREE.ParticleSystem&&(j.drawArrays(j.POINTS,0,e.__webglParticleCount),K.info.render.calls++,K.info.render.points+=e.__webglParticleCount)}};this.render=function(a,b,c,d){if(!1===b instanceof THREE.Camera)console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");
-else{var e,f,h,g,k=a.__lights,n=a.fog;Aa=-1;ua=!0;!0===a.autoUpdate&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();b.matrixWorldInverse.getInverse(b.matrixWorld);ra.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);la.setFromMatrix(ra);this.autoUpdateObjects&&this.initWebGLObjects(a);m(this.renderPluginsPre,a,b);K.info.render.calls=0;K.info.render.vertices=0;K.info.render.faces=0;K.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,
-this.autoClearDepth,this.autoClearStencil);g=a.__webglObjects;d=0;for(e=g.length;d<e;d++)if(f=g[d],h=f.object,f.id=d,f.render=!1,h.visible&&(!(h instanceof THREE.Mesh||h instanceof THREE.ParticleSystem)||!h.frustumCulled||la.intersectsObject(h))){var q=h;q._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,q.matrixWorld);q._normalMatrix.getNormalMatrix(q._modelViewMatrix);var q=f,r=q.buffer,s=void 0,t=s=void 0,t=q.object.material;if(t instanceof THREE.MeshFaceMaterial)s=r.materialIndex,s=t.materials[s],
-s.transparent?(q.transparent=s,q.opaque=null):(q.opaque=s,q.transparent=null);else if(s=t)s.transparent?(q.transparent=s,q.opaque=null):(q.opaque=s,q.transparent=null);f.render=!0;!0===this.sortObjects&&(null!==h.renderDepth?f.z=h.renderDepth:(sa.getPositionFromMatrix(h.matrixWorld),sa.applyProjection(ra),f.z=sa.z))}this.sortObjects&&g.sort(i);g=a.__webglObjectsImmediate;d=0;for(e=g.length;d<e;d++)f=g[d],h=f.object,h.visible&&(h._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,h.matrixWorld),
-h._normalMatrix.getNormalMatrix(h._modelViewMatrix),h=f.object.material,h.transparent?(f.transparent=h,f.opaque=null):(f.opaque=h,f.transparent=null));a.overrideMaterial?(d=a.overrideMaterial,this.setBlending(d.blending,d.blendEquation,d.blendSrc,d.blendDst),this.setDepthTest(d.depthTest),this.setDepthWrite(d.depthWrite),A(d.polygonOffset,d.polygonOffsetFactor,d.polygonOffsetUnits),l(a.__webglObjects,!1,"",b,k,n,!0,d),p(a.__webglObjectsImmediate,"",b,k,n,!1,d)):(d=null,this.setBlending(THREE.NoBlending),
-l(a.__webglObjects,!0,"opaque",b,k,n,!1,d),p(a.__webglObjectsImmediate,"opaque",b,k,n,!1,d),l(a.__webglObjects,!1,"transparent",b,k,n,!0,d),p(a.__webglObjectsImmediate,"transparent",b,k,n,!0,d));m(this.renderPluginsPost,a,b);c&&(c.generateMipmaps&&c.minFilter!==THREE.NearestFilter&&c.minFilter!==THREE.LinearFilter)&&(c instanceof THREE.WebGLRenderTargetCube?(j.bindTexture(j.TEXTURE_CUBE_MAP,c.__webglTexture),j.generateMipmap(j.TEXTURE_CUBE_MAP),j.bindTexture(j.TEXTURE_CUBE_MAP,null)):(j.bindTexture(j.TEXTURE_2D,
-c.__webglTexture),j.generateMipmap(j.TEXTURE_2D),j.bindTexture(j.TEXTURE_2D,null)));this.setDepthTest(!0);this.setDepthWrite(!0)}};this.renderImmediateObject=function(a,b,c,d,e){var f=z(a,b,c,d,e);$=-1;K.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,j,la):e.render(function(a){K.renderBufferImmediate(a,f,d)})};this.initWebGLObjects=function(a){a.__webglObjects||(a.__webglObjects=[],a.__webglObjectsImmediate=[],a.__webglSprites=[],a.__webglFlares=[]);for(;a.__objectsAdded.length;)s(a.__objectsAdded[0],
-a),a.__objectsAdded.splice(0,1);for(;a.__objectsRemoved.length;)q(a.__objectsRemoved[0],a),a.__objectsRemoved.splice(0,1);for(var b=0,h=a.__webglObjects.length;b<h;b++){var g=a.__webglObjects[b].object;void 0===g.__webglInit&&(void 0!==g.__webglActive&&q(g,a),s(g,a));var i=g,l=i.geometry,m=void 0,p=void 0,t=void 0;if(l instanceof THREE.BufferGeometry){var u=j.DYNAMIC_DRAW,w=!l.dynamic,z=l.attributes,y=void 0,x=void 0;for(y in z)x=z[y],x.needsUpdate&&("index"===y?(j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,
-x.buffer),j.bufferData(j.ELEMENT_ARRAY_BUFFER,x.array,u)):(j.bindBuffer(j.ARRAY_BUFFER,x.buffer),j.bufferData(j.ARRAY_BUFFER,x.array,u)),x.needsUpdate=!1),w&&!x.dynamic&&(x.array=null)}else if(i instanceof THREE.Mesh){for(var A=0,B=l.geometryGroupsList.length;A<B;A++)if(m=l.geometryGroupsList[A],t=d(i,m),l.buffersNeedUpdate&&c(m,i),p=t.attributes&&n(t),l.verticesNeedUpdate||l.morphTargetsNeedUpdate||l.elementsNeedUpdate||l.uvsNeedUpdate||l.normalsNeedUpdate||l.colorsNeedUpdate||l.tangentsNeedUpdate||
-p){var v=m,C=i,D=j.DYNAMIC_DRAW,F=!l.dynamic,G=t;if(v.__inittedArrays){var J=e(G),K=G.vertexColors?G.vertexColors:!1,N=f(G),O=J===THREE.SmoothShading,E=void 0,I=void 0,V=void 0,M=void 0,R=void 0,U=void 0,Q=void 0,da=void 0,Z=void 0,$=void 0,Fa=void 0,P=void 0,X=void 0,W=void 0,Ba=void 0,ea=void 0,Aa=void 0,ba=void 0,ca=void 0,ia=void 0,fa=void 0,ga=void 0,ka=void 0,la=void 0,oa=void 0,pa=void 0,ta=void 0,ua=void 0,va=void 0,Ca=void 0,Da=void 0,Ga=void 0,Ea=void 0,La=void 0,Sa=void 0,Ha=void 0,wa=
-void 0,xa=void 0,Qa=void 0,Ra=void 0,db=0,eb=0,Oa=0,Pa=0,Ua=0,hb=0,Ta=0,tb=0,Za=0,qa=0,ya=0,L=0,Na=void 0,ib=v.__vertexArray,bb=v.__uvArray,cb=v.__uv2Array,Ma=v.__normalArray,Va=v.__tangentArray,jb=v.__colorArray,Wa=v.__skinIndexArray,Xa=v.__skinWeightArray,fb=v.__morphTargetsArrays,sb=v.__morphNormalsArrays,pb=v.__webglCustomAttributesList,H=void 0,Pb=v.__faceArray,vb=v.__lineArray,Ia=C.geometry,Bb=Ia.elementsNeedUpdate,yb=Ia.uvsNeedUpdate,Db=Ia.normalsNeedUpdate,Mb=Ia.tangentsNeedUpdate,Nb=Ia.colorsNeedUpdate,
-Ob=Ia.morphTargetsNeedUpdate,ec=Ia.vertices,aa=v.faces3,kb=Ia.faces,Cb=Ia.faceVertexUvs[0],Eb=Ia.faceVertexUvs[1],fc=Ia.skinIndices,Qb=Ia.skinWeights,Rb=Ia.morphTargets,Fb=Ia.morphNormals;if(Ia.verticesNeedUpdate){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=ec[M.a],X=ec[M.b],W=ec[M.c],ib[eb]=P.x,ib[eb+1]=P.y,ib[eb+2]=P.z,ib[eb+3]=X.x,ib[eb+4]=X.y,ib[eb+5]=X.z,ib[eb+6]=W.x,ib[eb+7]=W.y,ib[eb+8]=W.z,eb+=9;j.bindBuffer(j.ARRAY_BUFFER,v.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,ib,D)}if(Ob){Sa=0;
-for(Ha=Rb.length;Sa<Ha;Sa++){E=ya=0;for(I=aa.length;E<I;E++)Qa=aa[E],M=kb[Qa],P=Rb[Sa].vertices[M.a],X=Rb[Sa].vertices[M.b],W=Rb[Sa].vertices[M.c],wa=fb[Sa],wa[ya]=P.x,wa[ya+1]=P.y,wa[ya+2]=P.z,wa[ya+3]=X.x,wa[ya+4]=X.y,wa[ya+5]=X.z,wa[ya+6]=W.x,wa[ya+7]=W.y,wa[ya+8]=W.z,G.morphNormals&&(O?(Ra=Fb[Sa].vertexNormals[Qa],ba=Ra.a,ca=Ra.b,ia=Ra.c):ia=ca=ba=Fb[Sa].faceNormals[Qa],xa=sb[Sa],xa[ya]=ba.x,xa[ya+1]=ba.y,xa[ya+2]=ba.z,xa[ya+3]=ca.x,xa[ya+4]=ca.y,xa[ya+5]=ca.z,xa[ya+6]=ia.x,xa[ya+7]=ia.y,xa[ya+
-8]=ia.z),ya+=9;j.bindBuffer(j.ARRAY_BUFFER,v.__webglMorphTargetsBuffers[Sa]);j.bufferData(j.ARRAY_BUFFER,fb[Sa],D);G.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglMorphNormalsBuffers[Sa]),j.bufferData(j.ARRAY_BUFFER,sb[Sa],D))}}if(Qb.length){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],la=Qb[M.a],oa=Qb[M.b],pa=Qb[M.c],Xa[qa]=la.x,Xa[qa+1]=la.y,Xa[qa+2]=la.z,Xa[qa+3]=la.w,Xa[qa+4]=oa.x,Xa[qa+5]=oa.y,Xa[qa+6]=oa.z,Xa[qa+7]=oa.w,Xa[qa+8]=pa.x,Xa[qa+9]=pa.y,Xa[qa+10]=pa.z,Xa[qa+11]=pa.w,ta=fc[M.a],ua=
-fc[M.b],va=fc[M.c],Wa[qa]=ta.x,Wa[qa+1]=ta.y,Wa[qa+2]=ta.z,Wa[qa+3]=ta.w,Wa[qa+4]=ua.x,Wa[qa+5]=ua.y,Wa[qa+6]=ua.z,Wa[qa+7]=ua.w,Wa[qa+8]=va.x,Wa[qa+9]=va.y,Wa[qa+10]=va.z,Wa[qa+11]=va.w,qa+=12;0<qa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglSkinIndicesBuffer),j.bufferData(j.ARRAY_BUFFER,Wa,D),j.bindBuffer(j.ARRAY_BUFFER,v.__webglSkinWeightsBuffer),j.bufferData(j.ARRAY_BUFFER,Xa,D))}if(Nb&&K){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],Q=M.vertexColors,da=M.color,3===Q.length&&K===THREE.VertexColors?(fa=
-Q[0],ga=Q[1],ka=Q[2]):ka=ga=fa=da,jb[Za]=fa.r,jb[Za+1]=fa.g,jb[Za+2]=fa.b,jb[Za+3]=ga.r,jb[Za+4]=ga.g,jb[Za+5]=ga.b,jb[Za+6]=ka.r,jb[Za+7]=ka.g,jb[Za+8]=ka.b,Za+=9;0<Za&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,jb,D))}if(Mb&&Ia.hasTangents){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],Z=M.vertexTangents,Ba=Z[0],ea=Z[1],Aa=Z[2],Va[Ta]=Ba.x,Va[Ta+1]=Ba.y,Va[Ta+2]=Ba.z,Va[Ta+3]=Ba.w,Va[Ta+4]=ea.x,Va[Ta+5]=ea.y,Va[Ta+6]=ea.z,Va[Ta+7]=ea.w,Va[Ta+8]=Aa.x,Va[Ta+9]=Aa.y,Va[Ta+
-10]=Aa.z,Va[Ta+11]=Aa.w,Ta+=12;j.bindBuffer(j.ARRAY_BUFFER,v.__webglTangentBuffer);j.bufferData(j.ARRAY_BUFFER,Va,D)}if(Db&&J){E=0;for(I=aa.length;E<I;E++)if(M=kb[aa[E]],R=M.vertexNormals,U=M.normal,3===R.length&&O)for(Ca=0;3>Ca;Ca++)Ga=R[Ca],Ma[hb]=Ga.x,Ma[hb+1]=Ga.y,Ma[hb+2]=Ga.z,hb+=3;else for(Ca=0;3>Ca;Ca++)Ma[hb]=U.x,Ma[hb+1]=U.y,Ma[hb+2]=U.z,hb+=3;j.bindBuffer(j.ARRAY_BUFFER,v.__webglNormalBuffer);j.bufferData(j.ARRAY_BUFFER,Ma,D)}if(yb&&Cb&&N){E=0;for(I=aa.length;E<I;E++)if(V=aa[E],$=Cb[V],
-void 0!==$)for(Ca=0;3>Ca;Ca++)Ea=$[Ca],bb[Oa]=Ea.x,bb[Oa+1]=Ea.y,Oa+=2;0<Oa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglUVBuffer),j.bufferData(j.ARRAY_BUFFER,bb,D))}if(yb&&Eb&&N){E=0;for(I=aa.length;E<I;E++)if(V=aa[E],Fa=Eb[V],void 0!==Fa)for(Ca=0;3>Ca;Ca++)La=Fa[Ca],cb[Pa]=La.x,cb[Pa+1]=La.y,Pa+=2;0<Pa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglUV2Buffer),j.bufferData(j.ARRAY_BUFFER,cb,D))}if(Bb){E=0;for(I=aa.length;E<I;E++)Pb[Ua]=db,Pb[Ua+1]=db+1,Pb[Ua+2]=db+2,Ua+=3,vb[tb]=db,vb[tb+1]=db+1,vb[tb+2]=db,vb[tb+
-3]=db+2,vb[tb+4]=db+1,vb[tb+5]=db+2,tb+=6,db+=3;j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,v.__webglFaceBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,Pb,D);j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,v.__webglLineBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,vb,D)}if(pb){Ca=0;for(Da=pb.length;Ca<Da;Ca++)if(H=pb[Ca],H.__original.needsUpdate){L=0;if(1===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],H.array[L]=H.value[M.a],H.array[L+1]=H.value[M.b],H.array[L+2]=H.value[M.c],
-L+=3}else{if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],H.array[L]=Na,H.array[L+1]=Na,H.array[L+2]=Na,L+=3}}else if(2===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=X.x,H.array[L+3]=X.y,H.array[L+4]=W.x,H.array[L+5]=W.y,L+=6}else{if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=P.x,H.array[L+1]=P.y,
-H.array[L+2]=X.x,H.array[L+3]=X.y,H.array[L+4]=W.x,H.array[L+5]=W.y,L+=6}}else if(3===H.size){var na;na="c"===H.type?["r","g","b"]:["x","y","z"];if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}else if("faces"===H.boundTo){E=
-0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}else if("faceVertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],P=Na[0],X=Na[1],W=Na[2],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],
-H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}}else if(4===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+11]=W.w,L+=12}else if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=
-P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+11]=W.w,L+=12}else if("faceVertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],P=Na[0],X=Na[1],W=Na[2],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+
-11]=W.w,L+=12}j.bindBuffer(j.ARRAY_BUFFER,H.buffer);j.bufferData(j.ARRAY_BUFFER,H.array,D)}}F&&(delete v.__inittedArrays,delete v.__colorArray,delete v.__normalArray,delete v.__tangentArray,delete v.__uvArray,delete v.__uv2Array,delete v.__faceArray,delete v.__vertexArray,delete v.__lineArray,delete v.__skinIndexArray,delete v.__skinWeightArray)}}l.verticesNeedUpdate=!1;l.morphTargetsNeedUpdate=!1;l.elementsNeedUpdate=!1;l.uvsNeedUpdate=!1;l.normalsNeedUpdate=!1;l.colorsNeedUpdate=!1;l.tangentsNeedUpdate=
-!1;l.buffersNeedUpdate=!1;t.attributes&&r(t)}else if(i instanceof THREE.Line){t=d(i,l);p=t.attributes&&n(t);if(l.verticesNeedUpdate||l.colorsNeedUpdate||l.lineDistancesNeedUpdate||p){var Ya=l,Sb=j.DYNAMIC_DRAW,Ib=void 0,Jb=void 0,Kb=void 0,Tb=void 0,ma=void 0,Ub=void 0,Gb=Ya.vertices,Hb=Ya.colors,kc=Ya.lineDistances,Zb=Gb.length,$b=Hb.length,ac=kc.length,Vb=Ya.__vertexArray,Wb=Ya.__colorArray,lc=Ya.__lineDistanceArray,bc=Ya.colorsNeedUpdate,cc=Ya.lineDistancesNeedUpdate,gc=Ya.__webglCustomAttributesList,
-Xb=void 0,mc=void 0,za=void 0,zb=void 0,Ja=void 0,ja=void 0;if(Ya.verticesNeedUpdate){for(Ib=0;Ib<Zb;Ib++)Tb=Gb[Ib],ma=3*Ib,Vb[ma]=Tb.x,Vb[ma+1]=Tb.y,Vb[ma+2]=Tb.z;j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,Vb,Sb)}if(bc){for(Jb=0;Jb<$b;Jb++)Ub=Hb[Jb],ma=3*Jb,Wb[ma]=Ub.r,Wb[ma+1]=Ub.g,Wb[ma+2]=Ub.b;j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglColorBuffer);j.bufferData(j.ARRAY_BUFFER,Wb,Sb)}if(cc){for(Kb=0;Kb<ac;Kb++)lc[Kb]=kc[Kb];j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglLineDistanceBuffer);
-j.bufferData(j.ARRAY_BUFFER,lc,Sb)}if(gc){Xb=0;for(mc=gc.length;Xb<mc;Xb++)if(ja=gc[Xb],ja.needsUpdate&&(void 0===ja.boundTo||"vertices"===ja.boundTo)){ma=0;zb=ja.value.length;if(1===ja.size)for(za=0;za<zb;za++)ja.array[za]=ja.value[za];else if(2===ja.size)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ma+=2;else if(3===ja.size)if("c"===ja.type)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.r,ja.array[ma+1]=Ja.g,ja.array[ma+2]=Ja.b,ma+=3;else for(za=0;za<zb;za++)Ja=
-ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ja.array[ma+2]=Ja.z,ma+=3;else if(4===ja.size)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ja.array[ma+2]=Ja.z,ja.array[ma+3]=Ja.w,ma+=4;j.bindBuffer(j.ARRAY_BUFFER,ja.buffer);j.bufferData(j.ARRAY_BUFFER,ja.array,Sb)}}}l.verticesNeedUpdate=!1;l.colorsNeedUpdate=!1;l.lineDistancesNeedUpdate=!1;t.attributes&&r(t)}else if(i instanceof THREE.ParticleSystem){t=d(i,l);p=t.attributes&&n(t);if(l.verticesNeedUpdate||l.colorsNeedUpdate||
-i.sortParticles||p){var lb=l,hc=j.DYNAMIC_DRAW,Lb=i,Ka=void 0,mb=void 0,nb=void 0,T=void 0,ob=void 0,ub=void 0,Yb=lb.vertices,ic=Yb.length,jc=lb.colors,nc=jc.length,wb=lb.__vertexArray,xb=lb.__colorArray,qb=lb.__sortArray,oc=lb.verticesNeedUpdate,pc=lb.colorsNeedUpdate,rb=lb.__webglCustomAttributesList,$a=void 0,Ab=void 0,Y=void 0,ab=void 0,ha=void 0,S=void 0;if(Lb.sortParticles){gb.copy(ra);gb.multiply(Lb.matrixWorld);for(Ka=0;Ka<ic;Ka++)nb=Yb[Ka],sa.copy(nb),sa.applyProjection(gb),qb[Ka]=[sa.z,
-Ka];qb.sort(k);for(Ka=0;Ka<ic;Ka++)nb=Yb[qb[Ka][1]],T=3*Ka,wb[T]=nb.x,wb[T+1]=nb.y,wb[T+2]=nb.z;for(mb=0;mb<nc;mb++)T=3*mb,ub=jc[qb[mb][1]],xb[T]=ub.r,xb[T+1]=ub.g,xb[T+2]=ub.b;if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],void 0===S.boundTo||"vertices"===S.boundTo)if(T=0,ab=S.value.length,1===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],S.array[Y]=S.value[ob];else if(2===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,T+=2;else if(3===S.size)if("c"===S.type)for(Y=
-0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.r,S.array[T+1]=ha.g,S.array[T+2]=ha.b,T+=3;else for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,T+=3;else if(4===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,S.array[T+3]=ha.w,T+=4}}else{if(oc)for(Ka=0;Ka<ic;Ka++)nb=Yb[Ka],T=3*Ka,wb[T]=nb.x,wb[T+1]=nb.y,wb[T+2]=nb.z;if(pc)for(mb=0;mb<nc;mb++)ub=jc[mb],T=3*mb,xb[T]=ub.r,xb[T+1]=ub.g,xb[T+2]=ub.b;
-if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],S.needsUpdate&&(void 0===S.boundTo||"vertices"===S.boundTo))if(ab=S.value.length,T=0,1===S.size)for(Y=0;Y<ab;Y++)S.array[Y]=S.value[Y];else if(2===S.size)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,T+=2;else if(3===S.size)if("c"===S.type)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.r,S.array[T+1]=ha.g,S.array[T+2]=ha.b,T+=3;else for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,T+=3;else if(4===
-S.size)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,S.array[T+3]=ha.w,T+=4}}if(oc||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,lb.__webglVertexBuffer),j.bufferData(j.ARRAY_BUFFER,wb,hc);if(pc||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,lb.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,xb,hc);if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],S.needsUpdate||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,S.buffer),j.bufferData(j.ARRAY_BUFFER,S.array,hc)}}l.verticesNeedUpdate=
-!1;l.colorsNeedUpdate=!1;t.attributes&&r(t)}}};this.initMaterial=function(a,b,c,d){var e,f,h,g;a.addEventListener("dispose",Fb);var i,k,l,m,p;a instanceof THREE.MeshDepthMaterial?p="depth":a instanceof THREE.MeshNormalMaterial?p="normal":a instanceof THREE.MeshBasicMaterial?p="basic":a instanceof THREE.MeshLambertMaterial?p="lambert":a instanceof THREE.MeshPhongMaterial?p="phong":a instanceof THREE.LineBasicMaterial?p="basic":a instanceof THREE.LineDashedMaterial?p="dashed":a instanceof THREE.ParticleSystemMaterial&&
-(p="particle_basic");if(p){var n=THREE.ShaderLib[p];a.uniforms=THREE.UniformsUtils.clone(n.uniforms);a.vertexShader=n.vertexShader;a.fragmentShader=n.fragmentShader}var q=e=0,r=0,t=n=0;for(f=b.length;t<f;t++)h=b[t],h.onlyShadow||(h instanceof THREE.DirectionalLight&&e++,h instanceof THREE.PointLight&&q++,h instanceof THREE.SpotLight&&r++,h instanceof THREE.HemisphereLight&&n++);f=q;h=r;g=n;r=n=0;for(q=b.length;r<q;r++)t=b[r],t.castShadow&&(t instanceof THREE.SpotLight&&n++,t instanceof THREE.DirectionalLight&&
-!t.shadowCascade&&n++);m=n;yb&&d&&d.useVertexTexture?l=1024:(b=j.getParameter(j.MAX_VERTEX_UNIFORM_VECTORS),b=Math.floor((b-20)/4),void 0!==d&&d instanceof THREE.SkinnedMesh&&(b=Math.min(d.bones.length,b),b<d.bones.length&&console.warn("WebGLRenderer: too many bones - "+d.bones.length+", this GPU supports just "+b+" (try OpenGL instead of ANGLE)")),l=b);a:{var r=a.fragmentShader,q=a.vertexShader,n=a.uniforms,b=a.attributes,t=a.defines,c={map:!!a.map,envMap:!!a.envMap,lightMap:!!a.lightMap,bumpMap:!!a.bumpMap,
-normalMap:!!a.normalMap,specularMap:!!a.specularMap,vertexColors:a.vertexColors,fog:c,useFog:a.fog,fogExp:c instanceof THREE.FogExp2,sizeAttenuation:a.sizeAttenuation,skinning:a.skinning,maxBones:l,useVertexTexture:yb&&d&&d.useVertexTexture,morphTargets:a.morphTargets,morphNormals:a.morphNormals,maxMorphTargets:this.maxMorphTargets,maxMorphNormals:this.maxMorphNormals,maxDirLights:e,maxPointLights:f,maxSpotLights:h,maxHemiLights:g,maxShadows:m,shadowMapEnabled:this.shadowMapEnabled&&d.receiveShadow,
-shadowMapType:this.shadowMapType,shadowMapDebug:this.shadowMapDebug,shadowMapCascade:this.shadowMapCascade,alphaTest:a.alphaTest,metal:a.metal,perPixel:a.perPixel,wrapAround:a.wrapAround,doubleSided:a.side===THREE.DoubleSide,flipSided:a.side===THREE.BackSide},d=a.index0AttributeName,s,u,w;e=[];p?e.push(p):(e.push(r),e.push(q));for(u in t)e.push(u),e.push(t[u]);for(s in c)e.push(s),e.push(c[s]);p=e.join();s=0;for(u=ca.length;s<u;s++)if(e=ca[s],e.code===p){e.usedTimes++;k=e.program;break a}s="SHADOWMAP_TYPE_BASIC";
-c.shadowMapType===THREE.PCFShadowMap?s="SHADOWMAP_TYPE_PCF":c.shadowMapType===THREE.PCFSoftShadowMap&&(s="SHADOWMAP_TYPE_PCF_SOFT");u=[];for(w in t)e=t[w],!1!==e&&(e="#define "+w+" "+e,u.push(e));e=u.join("\n");w=j.createProgram();u=["precision "+R+" float;","precision "+R+" int;",e,Bb?"#define VERTEX_TEXTURES":"",K.gammaInput?"#define GAMMA_INPUT":"",K.gammaOutput?"#define GAMMA_OUTPUT":"",K.physicallyBasedShading?"#define PHYSICALLY_BASED_SHADING":"","#define MAX_DIR_LIGHTS "+c.maxDirLights,"#define MAX_POINT_LIGHTS "+
-c.maxPointLights,"#define MAX_SPOT_LIGHTS "+c.maxSpotLights,"#define MAX_HEMI_LIGHTS "+c.maxHemiLights,"#define MAX_SHADOWS "+c.maxShadows,"#define MAX_BONES "+c.maxBones,c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.lightMap?"#define USE_LIGHTMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.vertexColors?"#define USE_COLOR":"",c.skinning?"#define USE_SKINNING":"",c.useVertexTexture?"#define BONE_TEXTURE":
-"",c.morphTargets?"#define USE_MORPHTARGETS":"",c.morphNormals?"#define USE_MORPHNORMALS":"",c.perPixel?"#define PHONG_PER_PIXEL":"",c.wrapAround?"#define WRAP_AROUND":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+s:"",c.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",c.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",c.sizeAttenuation?"#define USE_SIZEATTENUATION":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\nattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\nattribute vec3 morphTarget0;\nattribute vec3 morphTarget1;\nattribute vec3 morphTarget2;\nattribute vec3 morphTarget3;\n#ifdef USE_MORPHNORMALS\nattribute vec3 morphNormal0;\nattribute vec3 morphNormal1;\nattribute vec3 morphNormal2;\nattribute vec3 morphNormal3;\n#else\nattribute vec3 morphTarget4;\nattribute vec3 morphTarget5;\nattribute vec3 morphTarget6;\nattribute vec3 morphTarget7;\n#endif\n#endif\n#ifdef USE_SKINNING\nattribute vec4 skinIndex;\nattribute vec4 skinWeight;\n#endif\n"].join("\n");
-s=["precision "+R+" float;","precision "+R+" int;",c.bumpMap||c.normalMap?"#extension GL_OES_standard_derivatives : enable":"",e,"#define MAX_DIR_LIGHTS "+c.maxDirLights,"#define MAX_POINT_LIGHTS "+c.maxPointLights,"#define MAX_SPOT_LIGHTS "+c.maxSpotLights,"#define MAX_HEMI_LIGHTS "+c.maxHemiLights,"#define MAX_SHADOWS "+c.maxShadows,c.alphaTest?"#define ALPHATEST "+c.alphaTest:"",K.gammaInput?"#define GAMMA_INPUT":"",K.gammaOutput?"#define GAMMA_OUTPUT":"",K.physicallyBasedShading?"#define PHYSICALLY_BASED_SHADING":
-"",c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.lightMap?"#define USE_LIGHTMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.vertexColors?"#define USE_COLOR":"",c.metal?"#define METAL":"",c.perPixel?"#define PHONG_PER_PIXEL":"",c.wrapAround?"#define WRAP_AROUND":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":
-"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+s:"",c.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",c.shadowMapCascade?"#define SHADOWMAP_CASCADE":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n");u=C("vertex",u+q);s=C("fragment",s+r);j.attachShader(w,u);j.attachShader(w,s);d&&j.bindAttribLocation(w,0,d);j.linkProgram(w);j.getProgramParameter(w,j.LINK_STATUS)||(console.error("Could not initialise shader\nVALIDATE_STATUS: "+j.getProgramParameter(w,
-j.VALIDATE_STATUS)+", gl error ["+j.getError()+"]"),console.error("Program Info Log: "+j.getProgramInfoLog(w)));j.deleteShader(s);j.deleteShader(u);w.uniforms={};w.attributes={};var v;s="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences".split(" ");c.useVertexTexture?(s.push("boneTexture"),s.push("boneTextureWidth"),s.push("boneTextureHeight")):s.push("boneGlobalMatrices");for(v in n)s.push(v);v=s;s=0;for(u=v.length;s<u;s++)n=v[s],w.uniforms[n]=
-j.getUniformLocation(w,n);s="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(v=0;v<c.maxMorphTargets;v++)s.push("morphTarget"+v);for(v=0;v<c.maxMorphNormals;v++)s.push("morphNormal"+v);for(k in b)s.push(k);k=s;v=0;for(b=k.length;v<b;v++)s=k[v],w.attributes[s]=j.getAttribLocation(w,s);w.id=Fa++;ca.push({program:w,code:p,usedTimes:1});K.info.memory.programs=ca.length;k=w}a.program=k;v=a.program.attributes;if(a.morphTargets){a.numSupportedMorphTargets=0;b="morphTarget";
-for(k=0;k<this.maxMorphTargets;k++)w=b+k,0<=v[w]&&a.numSupportedMorphTargets++}if(a.morphNormals){a.numSupportedMorphNormals=0;b="morphNormal";for(k=0;k<this.maxMorphNormals;k++)w=b+k,0<=v[w]&&a.numSupportedMorphNormals++}a.uniformsList=[];for(i in a.uniforms)a.uniformsList.push([a.uniforms[i],i])};this.setFaceCulling=function(a,b){a===THREE.CullFaceNone?j.disable(j.CULL_FACE):(b===THREE.FrontFaceDirectionCW?j.frontFace(j.CW):j.frontFace(j.CCW),a===THREE.CullFaceBack?j.cullFace(j.BACK):a===THREE.CullFaceFront?
-j.cullFace(j.FRONT):j.cullFace(j.FRONT_AND_BACK),j.enable(j.CULL_FACE))};this.setMaterialFaces=function(a){var b=a.side===THREE.DoubleSide,a=a.side===THREE.BackSide;Z!==b&&(b?j.disable(j.CULL_FACE):j.enable(j.CULL_FACE),Z=b);U!==a&&(a?j.frontFace(j.CW):j.frontFace(j.CCW),U=a)};this.setDepthTest=function(a){Ga!==a&&(a?j.enable(j.DEPTH_TEST):j.disable(j.DEPTH_TEST),Ga=a)};this.setDepthWrite=function(a){fa!==a&&(j.depthMask(a),fa=a)};this.setBlending=function(a,b,c,d){a!==ka&&(a===THREE.NoBlending?j.disable(j.BLEND):
-a===THREE.AdditiveBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.SRC_ALPHA,j.ONE)):a===THREE.SubtractiveBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.ZERO,j.ONE_MINUS_SRC_COLOR)):a===THREE.MultiplyBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.ZERO,j.SRC_COLOR)):a===THREE.CustomBlending?j.enable(j.BLEND):(j.enable(j.BLEND),j.blendEquationSeparate(j.FUNC_ADD,j.FUNC_ADD),j.blendFuncSeparate(j.SRC_ALPHA,j.ONE_MINUS_SRC_ALPHA,j.ONE,
-j.ONE_MINUS_SRC_ALPHA)),ka=a);if(a===THREE.CustomBlending){if(b!==ta&&(j.blendEquation(v(b)),ta=b),c!==ia||d!==La)j.blendFunc(v(c),v(d)),ia=c,La=d}else La=ia=ta=null};this.setTexture=function(a,b){if(a.needsUpdate){a.__webglInit||(a.__webglInit=!0,a.addEventListener("dispose",Db),a.__webglTexture=j.createTexture(),K.info.memory.textures++);j.activeTexture(j.TEXTURE0+b);j.bindTexture(j.TEXTURE_2D,a.__webglTexture);j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL,a.flipY);j.pixelStorei(j.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
-a.premultiplyAlpha);j.pixelStorei(j.UNPACK_ALIGNMENT,a.unpackAlignment);var c=a.image,d=0===(c.width&c.width-1)&&0===(c.height&c.height-1),e=v(a.format),f=v(a.type);E(j.TEXTURE_2D,a,d);var h=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<h.length&&d){for(var g=0,i=h.length;g<i;g++)c=h[g],j.texImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,e,f,c.data);a.generateMipmaps=!1}else j.texImage2D(j.TEXTURE_2D,0,e,c.width,c.height,0,e,f,c.data);else if(a instanceof THREE.CompressedTexture){g=0;for(i=h.length;g<
-i;g++)c=h[g],a.format!==THREE.RGBAFormat?j.compressedTexImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,c.data):j.texImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,e,f,c.data)}else if(0<h.length&&d){g=0;for(i=h.length;g<i;g++)c=h[g],j.texImage2D(j.TEXTURE_2D,g,e,e,f,c);a.generateMipmaps=!1}else j.texImage2D(j.TEXTURE_2D,0,e,e,f,a.image);a.generateMipmaps&&d&&j.generateMipmap(j.TEXTURE_2D);a.needsUpdate=!1;if(a.onUpdate)a.onUpdate()}else j.activeTexture(j.TEXTURE0+b),j.bindTexture(j.TEXTURE_2D,a.__webglTexture)};
-this.setRenderTarget=function(a){var b=a instanceof THREE.WebGLRenderTargetCube;if(a&&!a.__webglFramebuffer){void 0===a.depthBuffer&&(a.depthBuffer=!0);void 0===a.stencilBuffer&&(a.stencilBuffer=!0);a.addEventListener("dispose",Eb);a.__webglTexture=j.createTexture();K.info.memory.textures++;var c=0===(a.width&a.width-1)&&0===(a.height&a.height-1),d=v(a.format),e=v(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];j.bindTexture(j.TEXTURE_CUBE_MAP,a.__webglTexture);E(j.TEXTURE_CUBE_MAP,
-a,c);for(var f=0;6>f;f++){a.__webglFramebuffer[f]=j.createFramebuffer();a.__webglRenderbuffer[f]=j.createRenderbuffer();j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var h=a,g=j.TEXTURE_CUBE_MAP_POSITIVE_X+f;j.bindFramebuffer(j.FRAMEBUFFER,a.__webglFramebuffer[f]);j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,g,h.__webglTexture,0);I(a.__webglRenderbuffer[f],a)}c&&j.generateMipmap(j.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=j.createFramebuffer(),a.__webglRenderbuffer=
-a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:j.createRenderbuffer(),j.bindTexture(j.TEXTURE_2D,a.__webglTexture),E(j.TEXTURE_2D,a,c),j.texImage2D(j.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=j.TEXTURE_2D,j.bindFramebuffer(j.FRAMEBUFFER,a.__webglFramebuffer),j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_ATTACHMENT,j.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&
-a.stencilBuffer&&j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_STENCIL_ATTACHMENT,j.RENDERBUFFER,a.__webglRenderbuffer):I(a.__webglRenderbuffer,a),c&&j.generateMipmap(j.TEXTURE_2D);b?j.bindTexture(j.TEXTURE_CUBE_MAP,null):j.bindTexture(j.TEXTURE_2D,null);j.bindRenderbuffer(j.RENDERBUFFER,null);j.bindFramebuffer(j.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=Ma,a=fb,d=bb,e=cb);b!==da&&(j.bindFramebuffer(j.FRAMEBUFFER,b),
-j.viewport(d,e,c,a),da=b);sb=c;pb=a};this.shadowMapPlugin=new THREE.ShadowMapPlugin;this.addPrePlugin(this.shadowMapPlugin);this.addPostPlugin(new THREE.SpritePlugin);this.addPostPlugin(new THREE.LensFlarePlugin)};THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
-THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=null};
-THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;a.shareDepthFrom=this.shareDepthFrom;
-return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);THREE.RenderableVertex=function(){this.positionWorld=new THREE.Vector3;this.positionScreen=new THREE.Vector4;this.visible=!0};THREE.RenderableVertex.prototype.copy=function(a){this.positionWorld.copy(a.positionWorld);this.positionScreen.copy(a.positionScreen)};THREE.RenderableFace3=function(){this.id=0;this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.v3=new THREE.RenderableVertex;this.centroidModel=new THREE.Vector3;this.normalModel=new THREE.Vector3;this.normalModelView=new THREE.Vector3;this.vertexNormalsLength=0;this.vertexNormalsModel=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];this.vertexNormalsModelView=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];this.material=this.color=null;this.uvs=[[]];this.z=
-0};THREE.RenderableObject=function(){this.id=0;this.object=null;this.z=0};THREE.RenderableSprite=function(){this.id=0;this.object=null;this.rotation=this.z=this.y=this.x=0;this.scale=new THREE.Vector2;this.material=null};THREE.RenderableLine=function(){this.id=0;this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.vertexColors=[new THREE.Color,new THREE.Color];this.material=null;this.z=0};THREE.GeometryUtils={merge:function(a,b,c){var d,e,f=a.vertices.length,h=b instanceof THREE.Mesh?b.geometry:b,g=a.vertices,i=h.vertices,k=a.faces,m=h.faces,a=a.faceVertexUvs[0],h=h.faceVertexUvs[0];void 0===c&&(c=0);b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),d=b.matrix,e=(new THREE.Matrix3).getNormalMatrix(d));for(var b=0,l=i.length;b<l;b++){var p=i[b].clone();d&&p.applyMatrix4(d);g.push(p)}b=0;for(l=m.length;b<l;b++){var p=m[b],s,t,n=p.vertexNormals,r=p.vertexColors;s=new THREE.Face3(p.a+
-f,p.b+f,p.c+f);s.normal.copy(p.normal);e&&s.normal.applyMatrix3(e).normalize();g=0;for(i=n.length;g<i;g++)t=n[g].clone(),e&&t.applyMatrix3(e).normalize(),s.vertexNormals.push(t);s.color.copy(p.color);g=0;for(i=r.length;g<i;g++)t=r[g],s.vertexColors.push(t.clone());s.materialIndex=p.materialIndex+c;s.centroid.copy(p.centroid);d&&s.centroid.applyMatrix4(d);k.push(s)}b=0;for(l=h.length;b<l;b++){c=h[b];d=[];g=0;for(i=c.length;g<i;g++)d.push(new THREE.Vector2(c[g].x,c[g].y));a.push(d)}},randomPointInTriangle:function(){var a=
-new THREE.Vector3;return function(b,c,d){var e=new THREE.Vector3,f=THREE.Math.random16(),h=THREE.Math.random16();1<f+h&&(f=1-f,h=1-h);var g=1-f-h;e.copy(b);e.multiplyScalar(f);a.copy(c);a.multiplyScalar(h);e.add(a);a.copy(d);a.multiplyScalar(g);e.add(a);return e}}(),randomPointInFace:function(a,b){return THREE.GeometryUtils.randomPointInTriangle(b.vertices[a.a],b.vertices[a.b],b.vertices[a.c])},randomPointsInGeometry:function(a,b){function c(a){function b(c,d){if(d<c)return c;var e=c+Math.floor((d-
-c)/2);return k[e]>a?b(c,e-1):k[e]<a?b(e+1,d):e}return b(0,k.length-1)}var d,e,f=a.faces,h=a.vertices,g=f.length,i=0,k=[],m,l,p;for(e=0;e<g;e++)d=f[e],m=h[d.a],l=h[d.b],p=h[d.c],d._area=THREE.GeometryUtils.triangleArea(m,l,p),i+=d._area,k[e]=i;d=[];for(e=0;e<b;e++)h=THREE.Math.random16()*i,h=c(h),d[e]=THREE.GeometryUtils.randomPointInFace(f[h],a,!0);return d},triangleArea:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){a.subVectors(d,c);b.subVectors(e,c);a.cross(b);return 0.5*
-a.length()}}(),center:function(a){a.computeBoundingBox();var b=a.boundingBox,c=new THREE.Vector3;c.addVectors(b.min,b.max);c.multiplyScalar(-0.5);a.applyMatrix((new THREE.Matrix4).makeTranslation(c.x,c.y,c.z));a.computeBoundingBox();return c},triangulateQuads:function(a){var b,c,d,e,f=[],h=[];b=0;for(c=a.faceVertexUvs.length;b<c;b++)h[b]=[];b=0;for(c=a.faces.length;b<c;b++){f.push(a.faces[b]);d=0;for(e=a.faceVertexUvs.length;d<e;d++)h[d].push(a.faceVertexUvs[d][b])}a.faces=f;a.faceVertexUvs=h;a.computeCentroids();
-a.computeFaceNormals();a.computeVertexNormals();a.hasTangents&&a.computeTangents()}};THREE.ImageUtils={crossOrigin:"anonymous",loadTexture:function(a,b,c){var d=new THREE.ImageLoader;d.crossOrigin=this.crossOrigin;var e=new THREE.Texture(void 0,b),b=d.load(a,function(){e.needsUpdate=!0;c&&c(e)});e.image=b;e.sourceFile=a;return e},loadCompressedTexture:function(a,b,c,d){var e=new THREE.CompressedTexture;e.mapping=b;var f=new XMLHttpRequest;f.onload=function(){var a=THREE.ImageUtils.parseDDS(f.response,!0);e.format=a.format;e.mipmaps=a.mipmaps;e.image.width=a.width;e.image.height=a.height;
-e.generateMipmaps=!1;e.needsUpdate=!0;c&&c(e)};f.onerror=d;f.open("GET",a,!0);f.responseType="arraybuffer";f.send(null);return e},loadTextureCube:function(a,b,c,d){var e=[];e.loadCount=0;var f=new THREE.Texture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;for(var b=0,h=a.length;b<h;++b){var g=new Image;e[b]=g;g.onload=function(){e.loadCount+=1;6===e.loadCount&&(f.needsUpdate=!0,c&&c(f))};g.onerror=d;g.crossOrigin=this.crossOrigin;g.src=a[b]}return f},loadCompressedTextureCube:function(a,b,c,d){var e=
-[];e.loadCount=0;var f=new THREE.CompressedTexture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;f.generateMipmaps=!1;b=function(a,b){return function(){var d=THREE.ImageUtils.parseDDS(a.response,!0);b.format=d.format;b.mipmaps=d.mipmaps;b.width=d.width;b.height=d.height;e.loadCount+=1;6===e.loadCount&&(f.format=d.format,f.needsUpdate=!0,c&&c(f))}};if(a instanceof Array)for(var h=0,g=a.length;h<g;++h){var i={};e[h]=i;var k=new XMLHttpRequest;k.onload=b(k,i);k.onerror=d;i=a[h];k.open("GET",i,!0);k.responseType=
-"arraybuffer";k.send(null)}else k=new XMLHttpRequest,k.onload=function(){var a=THREE.ImageUtils.parseDDS(k.response,!0);if(a.isCubemap){for(var b=a.mipmaps.length/a.mipmapCount,d=0;d<b;d++){e[d]={mipmaps:[]};for(var h=0;h<a.mipmapCount;h++)e[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+h]),e[d].format=a.format,e[d].width=a.width,e[d].height=a.height}f.format=a.format;f.needsUpdate=!0;c&&c(f)}},k.onerror=d,k.open("GET",a,!0),k.responseType="arraybuffer",k.send(null);return f},loadDDSTexture:function(a,
-b,c,d){var e=[];e.loadCount=0;var f=new THREE.CompressedTexture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;f.generateMipmaps=!1;var h=new XMLHttpRequest;h.onload=function(){var a=THREE.ImageUtils.parseDDS(h.response,!0);if(a.isCubemap)for(var b=a.mipmaps.length/a.mipmapCount,d=0;d<b;d++){e[d]={mipmaps:[]};for(var m=0;m<a.mipmapCount;m++)e[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+m]),e[d].format=a.format,e[d].width=a.width,e[d].height=a.height}else f.image.width=a.width,f.image.height=a.height,
-f.mipmaps=a.mipmaps;f.format=a.format;f.needsUpdate=!0;c&&c(f)};h.onerror=d;h.open("GET",a,!0);h.responseType="arraybuffer";h.send(null);return f},parseDDS:function(a,b){function c(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var d={mipmaps:[],width:0,height:0,format:null,mipmapCount:1},e=c("DXT1"),f=c("DXT3"),h=c("DXT5"),g=new Int32Array(a,0,31);if(542327876!==g[0])return console.error("ImageUtils.parseDDS(): Invalid magic number in DDS header"),d;if(!g[20]&
-4)return console.error("ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code"),d;var i=g[21],k=!1;switch(i){case e:e=8;d.format=THREE.RGB_S3TC_DXT1_Format;break;case f:e=16;d.format=THREE.RGBA_S3TC_DXT3_Format;break;case h:e=16;d.format=THREE.RGBA_S3TC_DXT5_Format;break;default:if(32==g[22]&&g[23]&16711680&&g[24]&65280&&g[25]&255&&g[26]&4278190080)k=!0,e=64,d.format=THREE.RGBAFormat;else return console.error("ImageUtils.parseDDS(): Unsupported FourCC code: ",String.fromCharCode(i&
-255,i>>8&255,i>>16&255,i>>24&255)),d}d.mipmapCount=1;g[2]&131072&&!1!==b&&(d.mipmapCount=Math.max(1,g[7]));d.isCubemap=g[28]&512?!0:!1;d.width=g[4];d.height=g[3];for(var g=g[1]+4,f=d.width,h=d.height,i=d.isCubemap?6:1,m=0;m<i;m++){for(var l=0;l<d.mipmapCount;l++){if(k){var p;p=f;for(var s=h,t=4*p*s,n=new Uint8Array(a,g,t),t=new Uint8Array(t),r=0,q=0,u=0;u<s;u++)for(var w=0;w<p;w++){var z=n[q];q++;var B=n[q];q++;var D=n[q];q++;var x=n[q];q++;t[r]=D;r++;t[r]=B;r++;t[r]=z;r++;t[r]=x;r++}p=t;s=p.length}else s=
-Math.max(4,f)/4*Math.max(4,h)/4*e,p=new Uint8Array(a,g,s);d.mipmaps.push({data:p,width:f,height:h});g+=s;f=Math.max(0.5*f,1);h=Math.max(0.5*h,1)}f=d.width;h=d.height}return d},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]},b=b|1,d=a.width,e=a.height,f=document.createElement("canvas");f.width=d;f.height=e;var h=f.getContext("2d");h.drawImage(a,0,0);for(var g=h.getImageData(0,0,d,e).data,i=h.createImageData(d,e),k=i.data,m=0;m<
-d;m++)for(var l=0;l<e;l++){var p=0>l-1?0:l-1,s=l+1>e-1?e-1:l+1,t=0>m-1?0:m-1,n=m+1>d-1?d-1:m+1,r=[],q=[0,0,g[4*(l*d+m)]/255*b];r.push([-1,0,g[4*(l*d+t)]/255*b]);r.push([-1,-1,g[4*(p*d+t)]/255*b]);r.push([0,-1,g[4*(p*d+m)]/255*b]);r.push([1,-1,g[4*(p*d+n)]/255*b]);r.push([1,0,g[4*(l*d+n)]/255*b]);r.push([1,1,g[4*(s*d+n)]/255*b]);r.push([0,1,g[4*(s*d+m)]/255*b]);r.push([-1,1,g[4*(s*d+t)]/255*b]);p=[];t=r.length;for(s=0;s<t;s++){var n=r[s],u=r[(s+1)%t],n=[n[0]-q[0],n[1]-q[1],n[2]-q[2]],u=[u[0]-q[0],
-u[1]-q[1],u[2]-q[2]];p.push(c([n[1]*u[2]-n[2]*u[1],n[2]*u[0]-n[0]*u[2],n[0]*u[1]-n[1]*u[0]]))}r=[0,0,0];for(s=0;s<p.length;s++)r[0]+=p[s][0],r[1]+=p[s][1],r[2]+=p[s][2];r[0]/=p.length;r[1]/=p.length;r[2]/=p.length;q=4*(l*d+m);k[q]=255*((r[0]+1)/2)|0;k[q+1]=255*((r[1]+1)/2)|0;k[q+2]=255*r[2]|0;k[q+3]=255}h.putImageData(i,0,0);return f},generateDataTexture:function(a,b,c){for(var d=a*b,e=new Uint8Array(3*d),f=Math.floor(255*c.r),h=Math.floor(255*c.g),c=Math.floor(255*c.b),g=0;g<d;g++)e[3*g]=f,e[3*g+
-1]=h,e[3*g+2]=c;a=new THREE.DataTexture(e,a,b,THREE.RGBFormat);a.needsUpdate=!0;return a}};THREE.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new THREE.Object3D,d=0,e=b.length;d<e;d++)c.add(new THREE.Mesh(a,b[d]));return c},detach:function(a,b,c){a.applyMatrix(b.matrixWorld);b.remove(a);c.add(a)},attach:function(a,b,c){var d=new THREE.Matrix4;d.getInverse(c.matrixWorld);a.applyMatrix(d);b.remove(a);c.add(a)}};THREE.FontUtils={faces:{},face:"helvetiker",weight:"normal",style:"normal",size:150,divisions:10,getFace:function(){return this.faces[this.face][this.weight][this.style]},loadFace:function(a){var b=a.familyName.toLowerCase();this.faces[b]=this.faces[b]||{};this.faces[b][a.cssFontWeight]=this.faces[b][a.cssFontWeight]||{};this.faces[b][a.cssFontWeight][a.cssFontStyle]=a;return this.faces[b][a.cssFontWeight][a.cssFontStyle]=a},drawText:function(a){for(var b=this.getFace(),c=this.size/b.resolution,d=
-0,e=String(a).split(""),f=e.length,h=[],a=0;a<f;a++){var g=new THREE.Path,g=this.extractGlyphPoints(e[a],b,c,d,g),d=d+g.offset;h.push(g.path)}return{paths:h,offset:d/2}},extractGlyphPoints:function(a,b,c,d,e){var f=[],h,g,i,k,m,l,p,s,t,n,r,q=b.glyphs[a]||b.glyphs["?"];if(q){if(q.o){b=q._cachedOutline||(q._cachedOutline=q.o.split(" "));k=b.length;for(a=0;a<k;)switch(i=b[a++],i){case "m":i=b[a++]*c+d;m=b[a++]*c;e.moveTo(i,m);break;case "l":i=b[a++]*c+d;m=b[a++]*c;e.lineTo(i,m);break;case "q":i=b[a++]*
-c+d;m=b[a++]*c;s=b[a++]*c+d;t=b[a++]*c;e.quadraticCurveTo(s,t,i,m);if(h=f[f.length-1]){l=h.x;p=h.y;h=1;for(g=this.divisions;h<=g;h++){var u=h/g;THREE.Shape.Utils.b2(u,l,s,i);THREE.Shape.Utils.b2(u,p,t,m)}}break;case "b":if(i=b[a++]*c+d,m=b[a++]*c,s=b[a++]*c+d,t=b[a++]*-c,n=b[a++]*c+d,r=b[a++]*-c,e.bezierCurveTo(i,m,s,t,n,r),h=f[f.length-1]){l=h.x;p=h.y;h=1;for(g=this.divisions;h<=g;h++)u=h/g,THREE.Shape.Utils.b3(u,l,s,n,i),THREE.Shape.Utils.b3(u,p,t,r,m)}}}return{offset:q.ha*c,path:e}}}};
-THREE.FontUtils.generateShapes=function(a,b){var b=b||{},c=void 0!==b.curveSegments?b.curveSegments:4,d=void 0!==b.font?b.font:"helvetiker",e=void 0!==b.weight?b.weight:"normal",f=void 0!==b.style?b.style:"normal";THREE.FontUtils.size=void 0!==b.size?b.size:100;THREE.FontUtils.divisions=c;THREE.FontUtils.face=d;THREE.FontUtils.weight=e;THREE.FontUtils.style=f;c=THREE.FontUtils.drawText(a).paths;d=[];e=0;for(f=c.length;e<f;e++)Array.prototype.push.apply(d,c[e].toShapes());return d};
-(function(a){var b=function(a){for(var b=a.length,e=0,f=b-1,h=0;h<b;f=h++)e+=a[f].x*a[h].y-a[h].x*a[f].y;return 0.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],h=[],g=[],i,k,m;if(0<b(a))for(k=0;k<e;k++)h[k]=k;else for(k=0;k<e;k++)h[k]=e-1-k;var l=2*e;for(k=e-1;2<e;){if(0>=l--){console.log("Warning, unable to triangulate polygon!");break}i=k;e<=i&&(i=0);k=i+1;e<=k&&(k=0);m=k+1;e<=m&&(m=0);var p;a:{var s=p=void 0,t=void 0,n=void 0,r=void 0,q=void 0,u=void 0,w=void 0,z=
-void 0,s=a[h[i]].x,t=a[h[i]].y,n=a[h[k]].x,r=a[h[k]].y,q=a[h[m]].x,u=a[h[m]].y;if(1E-10>(n-s)*(u-t)-(r-t)*(q-s))p=!1;else{var B=void 0,D=void 0,x=void 0,F=void 0,A=void 0,O=void 0,C=void 0,E=void 0,I=void 0,y=void 0,I=E=C=z=w=void 0,B=q-n,D=u-r,x=s-q,F=t-u,A=n-s,O=r-t;for(p=0;p<e;p++)if(!(p===i||p===k||p===m))if(w=a[h[p]].x,z=a[h[p]].y,C=w-s,E=z-t,I=w-n,y=z-r,w-=q,z-=u,I=B*y-D*I,C=A*E-O*C,E=x*z-F*w,-1E-10<=I&&-1E-10<=E&&-1E-10<=C){p=!1;break a}p=!0}}if(p){f.push([a[h[i]],a[h[k]],a[h[m]]]);g.push([h[i],
-h[k],h[m]]);i=k;for(m=k+1;m<e;i++,m++)h[i]=h[m];e--;l=2*e}}return d?g:f};a.Triangulate.area=b;return a})(THREE.FontUtils);self._typeface_js={faces:THREE.FontUtils.faces,loadFace:THREE.FontUtils.loadFace};THREE.typeface_js=self._typeface_js;THREE.Curve=function(){};THREE.Curve.prototype.getPoint=function(){console.log("Warning, getPoint() not implemented!");return null};THREE.Curve.prototype.getPointAt=function(a){a=this.getUtoTmapping(a);return this.getPoint(a)};THREE.Curve.prototype.getPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPoint(b/a));return c};THREE.Curve.prototype.getSpacedPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPointAt(b/a));return c};
+THREE.MorphAnimMesh.prototype.interpolateTargets=function(a,b,c){for(var d=this.morphTargetInfluences,e=0,f=d.length;e<f;e++)d[e]=0;-1<a&&(d[a]=1-c);-1<b&&(d[b]=c)};
+THREE.MorphAnimMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.MorphAnimMesh(this.geometry,this.material));a.duration=this.duration;a.mirroredLoop=this.mirroredLoop;a.time=this.time;a.lastKeyframe=this.lastKeyframe;a.currentKeyframe=this.currentKeyframe;a.direction=this.direction;a.directionBackwards=this.directionBackwards;THREE.Mesh.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.objects=[]};THREE.LOD.prototype=Object.create(THREE.Object3D.prototype);
+THREE.LOD.prototype.constructor=THREE.LOD;THREE.LOD.prototype.addLevel=function(a,b){void 0===b&&(b=0);b=Math.abs(b);for(var c=0;c<this.objects.length&&!(b<this.objects[c].distance);c++);this.objects.splice(c,0,{distance:b,object:a});this.add(a)};THREE.LOD.prototype.getObjectForDistance=function(a){for(var b=1,c=this.objects.length;b<c&&!(a<this.objects[b].distance);b++);return this.objects[b-1].object};
+THREE.LOD.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.origin.distanceTo(a);this.getObjectForDistance(d).raycast(b,c)}}();
+THREE.LOD.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){if(1<this.objects.length){a.setFromMatrixPosition(c.matrixWorld);b.setFromMatrixPosition(this.matrixWorld);c=a.distanceTo(b);this.objects[0].object.visible=!0;for(var d=1,e=this.objects.length;d<e;d++)if(c>=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;d<e;d++)this.objects[d].object.visible=!1}}}();
+THREE.LOD.prototype.clone=function(a){void 0===a&&(a=new THREE.LOD);THREE.Object3D.prototype.clone.call(this,a);for(var b=0,c=this.objects.length;b<c;b++){var d=this.objects[b].object.clone();d.visible=0===b;a.addLevel(d,this.objects[b].distance)}return a};
+THREE.Sprite=function(){var a=new Uint16Array([0,1,2,0,2,3]),b=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),c=new Float32Array([0,0,1,0,1,1,0,1]),d=new THREE.BufferGeometry;d.addAttribute("index",new THREE.BufferAttribute(a,1));d.addAttribute("position",new THREE.BufferAttribute(b,3));d.addAttribute("uv",new THREE.BufferAttribute(c,2));return function(a){THREE.Object3D.call(this);this.type="Sprite";this.geometry=d;this.material=void 0!==a?a:new THREE.SpriteMaterial}}();
+THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.constructor=THREE.Sprite;THREE.Sprite.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.distanceToPoint(a);d>this.scale.x||c.push({distance:d,point:this.position,face:null,object:this})}}();THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};
+THREE.Particle=THREE.Sprite;THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)};THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);THREE.LensFlare.prototype.constructor=THREE.LensFlare;
+THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})};
+THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;a<b;a++)c=this.lensFlares[a],c.x=this.positionScreen.x+d*c.distance,c.y=this.positionScreen.y+e*c.distance,c.wantedRotation=c.x*Math.PI*.25,c.rotation+=.25*(c.wantedRotation-c.rotation)};THREE.Scene=function(){THREE.Object3D.call(this);this.type="Scene";this.overrideMaterial=this.fog=null;this.autoUpdate=!0};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype);
+THREE.Scene.prototype.constructor=THREE.Scene;THREE.Scene.prototype.clone=function(a){void 0===a&&(a=new THREE.Scene);THREE.Object3D.prototype.clone.call(this,a);null!==this.fog&&(a.fog=this.fog.clone());null!==this.overrideMaterial&&(a.overrideMaterial=this.overrideMaterial.clone());a.autoUpdate=this.autoUpdate;a.matrixAutoUpdate=this.matrixAutoUpdate;return a};THREE.Fog=function(a,b,c){this.name="";this.color=new THREE.Color(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3};
+THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)};THREE.FogExp2=function(a,b){this.name="";this.color=new THREE.Color(a);this.density=void 0!==b?b:2.5E-4};THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)};THREE.ShaderChunk={};THREE.ShaderChunk.common="#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\nfloat square( in float a ) { return a*a; }\nvec2  square( in vec2 a )  { return vec2( a.x*a.x, a.y*a.y ); }\nvec3  square( in vec3 a )  { return vec3( a.x*a.x, a.y*a.y, a.z*a.z ); }\nvec4  square( in vec4 a )  { return vec4( a.x*a.x, a.y*a.y, a.z*a.z, a.w*a.w ); }\nfloat saturate( in float a ) { return clamp( a, 0.0, 1.0 ); }\nvec2  saturate( in vec2 a )  { return clamp( a, 0.0, 1.0 ); }\nvec3  saturate( in vec3 a )  { return clamp( a, 0.0, 1.0 ); }\nvec4  saturate( in vec4 a )  { return clamp( a, 0.0, 1.0 ); }\nfloat average( in float a ) { return a; }\nfloat average( in vec2 a )  { return ( a.x + a.y) * 0.5; }\nfloat average( in vec3 a )  { return ( a.x + a.y + a.z) / 3.0; }\nfloat average( in vec4 a )  { return ( a.x + a.y + a.z + a.w) * 0.25; }\nfloat whiteCompliment( in float a ) { return saturate( 1.0 - a ); }\nvec2  whiteCompliment( in vec2 a )  { return saturate( vec2(1.0) - a ); }\nvec3  whiteCompliment( in vec3 a )  { return saturate( vec3(1.0) - a ); }\nvec4  whiteCompliment( in vec4 a )  { return saturate( vec4(1.0) - a ); }\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n}\n// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\treturn normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal) {\n\tfloat distance = dot( planeNormal, point-pointOnPlane );\n\treturn point - distance * planeNormal;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn pointOnLine + lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) );\n}\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\tif ( decayExponent > 0.0 ) {\n\t  return pow( saturate( 1.0 - lightDistance / cutoffDistance ), decayExponent );\n\t}\n\treturn 1.0;\n}\n\nvec3 inputToLinear( in vec3 a ) {\n#ifdef GAMMA_INPUT\n\treturn pow( a, vec3( float( GAMMA_FACTOR ) ) );\n#else\n\treturn a;\n#endif\n}\nvec3 linearToOutput( in vec3 a ) {\n#ifdef GAMMA_OUTPUT\n\treturn pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n#else\n\treturn a;\n#endif\n}\n";
+THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n";THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\tvec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n\tfloat dotProduct = dot( transformedNormal, dirVector );\n\tvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t#endif\n\n\t#endif\n\n\t#ifdef WRAP_AROUND\n\n\t\tvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\tdirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tdirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n\t\t#endif\n\n\t#endif\n\n\tvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n\t#endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\t\tlVector = normalize( lVector );\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\tpointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tpointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\tvLightFront += pointLightColor[ i ] * pointLightWeighting * attenuation;\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += pointLightColor[ i ] * pointLightWeightingBack * attenuation;\n\n\t\t#endif\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\tfloat attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n\t\t\tlVector = normalize( lVector );\n\n\t\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\t\t\tvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\t\tvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\t\tspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n\t\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\t\tspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\tvLightFront += spotLightColor[ i ] * spotLightWeighting * attenuation * spotEffect;\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvLightBack += spotLightColor[ i ] * spotLightWeightingBack * attenuation * spotEffect;\n\n\t\t\t#endif\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\tfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n\t\tvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n\t\t#endif\n\n\t}\n\n#endif\n\nvLightFront += ambientLightColor;\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack += ambientLightColor;\n\n#endif\n";
+THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n\n#endif\n";THREE.ShaderChunk.default_vertex="#ifdef USE_SKINNING\n\n\tvec4 mvPosition = modelViewMatrix * skinned;\n\n#elif defined( USE_MORPHTARGETS )\n\n\tvec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#else\n\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n";
+THREE.ShaderChunk.map_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\n\t#ifdef USE_MORPHNORMALS\n\n\tvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n\t#else\n\n\tvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n\t#endif\n\n#endif\n";
+THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n\tuniform float logDepthBufFC;\n\n#endif";THREE.ShaderChunk.lightmap_pars_vertex="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\n#endif";THREE.ShaderChunk.lights_phong_fragment="#ifndef FLAT_SHADED\n\n\tvec3 normal = normalize( vNormal );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n\t#endif\n\n#else\n\n\tvec3 fdx = dFdx( vViewPosition );\n\tvec3 fdy = dFdy( vViewPosition );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef USE_NORMALMAP\n\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\t\tlVector = normalize( lVector );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\ttotalDiffuseLight += pointLightColor[ i ] * pointDiffuseWeight * attenuation;\n\n\t\t\t\t// specular\n\n\t\tvec3 pointHalfVector = normalize( lVector + viewPosition );\n\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\tfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\ttotalSpecularLight += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * attenuation * specularNormalization;\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n\t\tlVector = normalize( lVector );\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\t// diffuse\n\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n\t\t\t#else\n\n\t\t\t\tfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t\t#endif\n\n\t\t\ttotalDiffuseLight += spotLightColor[ i ] * spotDiffuseWeight * attenuation * spotEffect;\n\n\t\t\t// specular\n\n\t\t\tvec3 spotHalfVector = normalize( lVector + viewPosition );\n\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\tfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\ttotalSpecularLight += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * attenuation * specularNormalization * spotEffect;\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\t\tvec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, dirVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\ttotalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;\n\n\t\t// specular\n\n\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\tfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n\t\t/*\n\t\t// fresnel term from skin shader\n\t\tconst float F0 = 0.128;\n\n\t\tfloat base = 1.0 - dot( viewPosition, dirHalfVector );\n\t\tfloat exponential = pow( base, 5.0 );\n\n\t\tfloat fresnel = exponential + F0 * ( 1.0 - exponential );\n\t\t*/\n\n\t\t/*\n\t\t// fresnel term from fresnel shader\n\t\tconst float mFresnelBias = 0.08;\n\t\tconst float mFresnelScale = 0.3;\n\t\tconst float mFresnelPower = 5.0;\n\n\t\tfloat fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n\t\t*/\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t// \t\tdirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\ttotalSpecularLight += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\ttotalDiffuseLight += hemiColor;\n\n\t\t// specular (sky light)\n\n\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\tfloat hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n\t\t// specular (ground light)\n\n\t\tvec3 lVectorGround = -lVector;\n\n\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\tfloat hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\ttotalSpecularLight += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n\t}\n\n#endif\n\n#ifdef METAL\n\n\toutgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) * specular + totalSpecularLight + emissive;\n\n#else\n\n\toutgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) + totalSpecularLight + emissive;\n\n#endif\n";
+THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n\tuniform vec3 fogColor;\n\n\t#ifdef FOG_EXP2\n\n\t\tuniform float fogDensity;\n\n\t#else\n\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n\n#endif";THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n\tvec3 morphedNormal = vec3( 0.0 );\n\n\tmorphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tmorphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tmorphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tmorphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n\tmorphedNormal += normal;\n\n#endif";
+THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n\tuniform float reflectivity;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tuniform float refractionRatio;\n\n\t#else\n\n\t\tvarying vec3 vReflect;\n\n\t#endif\n\n#endif\n";THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
+THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\n\t// Per-Pixel Tangent Space Normal Mapping\n\t// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\n\t}\n\n#endif\n";
+THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n";THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\tuniform sampler2D lightMap;\n\n#endif";THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n\t}\n\n#endif";
+THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvWorldPosition = worldPosition.xyz;\n\n#endif";THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n\tvec4 texelColor = texture2D( map, vUv );\n\n\ttexelColor.xyz = inputToLinear( texelColor.xyz );\n\n\tdiffuseColor *= texelColor;\n\n#endif";THREE.ShaderChunk.lightmap_vertex="#ifdef USE_LIGHTMAP\n\n\tvUv2 = uv2;\n\n#endif";THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n\tdiffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n";
+THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif\n";THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n\tvColor.xyz = inputToLinear( color.xyz );\n\n#endif";THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n\t#ifdef USE_MORPHTARGETS\n\n\tvec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n\t#else\n\n\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned  = bindMatrixInverse * skinned;\n\n#endif\n";
+THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvarying vec3 vReflect;\n\n\tuniform float refractionRatio;\n\n#endif\n";THREE.ShaderChunk.linear_to_gamma_fragment="\n\toutgoingLight = linearToOutput( outgoingLight );\n";THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif";THREE.ShaderChunk.lights_lambert_pars_vertex="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n";
+THREE.ShaderChunk.map_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n\n#endif\n";THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n\t\t// Transforming Normal Vectors with the Inverse Transformation\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n\t\t#else\n\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t\t#endif\n\n\t#else\n\n\t\tvec3 reflectVec = vReflect;\n\n\t#endif\n\n\t#ifdef DOUBLE_SIDED\n\t\tfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\t#else\n\t\tfloat flipNormal = 1.0;\n\t#endif\n\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n\t\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\tvec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#endif\n\n\tenvColor.xyz = inputToLinear( envColor.xyz );\n\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n\t#endif\n\n#endif\n";
+THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n\tuniform sampler2D specularMap;\n\n#endif";THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n\t#ifndef USE_MORPHNORMALS\n\n\tuniform float morphTargetInfluences[ 8 ];\n\n\t#else\n\n\tuniform float morphTargetInfluences[ 4 ];\n\n\t#endif\n\n#endif";
+THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n\n#else\n\n\tspecularStrength = 1.0;\n\n#endif";THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n\t#else\n\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\n\t#endif\n\n\t#ifdef FOG_EXP2\n\n\t\tfloat fogFactor = exp2( - square( fogDensity ) * square( depth ) * LOG2 );\n\t\tfogFactor = whiteCompliment( fogFactor );\n\n\t#else\n\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\n\t#endif\n\t\n\toutgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif";
+THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\n\t// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n\t// http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n\t// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n\tvec2 dHdxy_fwd() {\n\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n\t\treturn vec2( dBx, dBy );\n\n\t}\n\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\t\t// normalized\n\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\n\t}\n\n#endif\n";
+THREE.ShaderChunk.defaultnormal_vertex="#ifdef USE_SKINNING\n\n\tvec3 objectNormal = skinnedNormal.xyz;\n\n#elif defined( USE_MORPHNORMALS )\n\n\tvec3 objectNormal = morphedNormal;\n\n#else\n\n\tvec3 objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n\tobjectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n";THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n\tvarying vec3 vNormal;\n\n#endif\n";
+THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";THREE.ShaderChunk.map_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
+THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n\toutgoingLight *= diffuseColor.xyz * texture2D( lightMap, vUv2 ).xyz;\n\n#endif";THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\tuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif";THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n\tdiffuseColor.rgb *= vColor;\n\n#endif";THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n\tvec3 morphed = vec3( 0.0 );\n\tmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\tmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\tmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\tmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n\t#ifndef USE_MORPHNORMALS\n\n\tmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\tmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\tmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\tmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n\t#endif\n\n\tmorphed += position;\n\n#endif";
+THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvec3 worldNormal = transformDirection( objectNormal, modelMatrix );\n\n\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n\t#ifdef ENVMAP_MODE_REFLECTION\n\n\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\n\t#else\n\n\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t#endif\n\n#endif\n";
+THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n\t#ifdef SHADOWMAP_DEBUG\n\n\t\tvec3 frustumColors[3];\n\t\tfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n\t\tfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n\t\tfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n\t#endif\n\n\t#ifdef SHADOWMAP_CASCADE\n\n\t\tint inFrustumCount = 0;\n\n\t#endif\n\n\tfloat fDepth;\n\tvec3 shadowColor = vec3( 1.0 );\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\t\t\t\t// if ( something && something ) breaks ATI OpenGL shader compiler\n\t\t\t\t// if ( all( something, something ) ) using this instead\n\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\n\t\t\t\t// don't shadow pixels outside of light frustum\n\t\t\t\t// use just first frustum (for cascades)\n\t\t\t\t// don't shadow pixels behind far plane of light frustum\n\n\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\tinFrustumCount += int( inFrustum );\n\t\t\tbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n\t\t#else\n\n\t\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n\t\t#endif\n\n\t\tbool frustumTest = all( frustumTestVec );\n\n\t\tif ( frustumTest ) {\n\n\t\t\tshadowCoord.z += shadowBias[ i ];\n\n\t\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t/*\n\t\t\t\t\t\t// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n\t\t\t\t\t\t// must enroll loop manually\n\n\t\t\t\tfor ( float y = -1.25; y <= 1.25; y += 1.25 )\n\t\t\t\t\tfor ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n\t\t\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n\t\t\t\t\t\t\t\t// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n\t\t\t\t\t\t\t\t//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n\t\t\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\t\t\tif ( fDepth < shadowCoord.z )\n\t\t\t\t\t\t\tshadow += 1.0;\n\n\t\t\t\t}\n\n\t\t\t\tshadow /= 9.0;\n\n\t\t*/\n\n\t\t\t\tconst float shadowDelta = 1.0 / 9.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.25 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.25 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.25 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.25 * yPixelOffset;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.0 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.0 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.0 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.0 * yPixelOffset;\n\n\t\t\t\tmat3 shadowKernel;\n\t\t\t\tmat3 depthKernel;\n\n\t\t\t\tdepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tdepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tdepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tdepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tdepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tdepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tdepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tdepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tdepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n\t\t\t\tvec3 shadowZ = vec3( shadowCoord.z );\n\t\t\t\tshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n\t\t\t\tshadowKernel[0] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n\t\t\t\tshadowKernel[1] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n\t\t\t\tshadowKernel[2] *= vec3(0.25);\n\n\t\t\t\tvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n\t\t\t\tshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n\t\t\t\tshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n\t\t\t\tvec4 shadowValues;\n\t\t\t\tshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n\t\t\t\tshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n\t\t\t\tshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n\t\t\t\tshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n\t\t\t\tshadow = dot( shadowValues, vec4( 1.0 ) );\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#else\n\n\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\tif ( fDepth < shadowCoord.z )\n\n\t\t// spot with multiple shadows is darker\n\n\t\t\t\t\tshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n\t\t// spot with multiple shadows has the same color as single shadow spot\n\n\t\t// \t\t\t\t\tshadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n\t\t\t#endif\n\n\t\t}\n\n\n\t\t#ifdef SHADOWMAP_DEBUG\n\n\t\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\t\tif ( inFrustum && inFrustumCount == 1 ) outgoingLight *= frustumColors[ i ];\n\n\t\t\t#else\n\n\t\t\t\tif ( inFrustum ) outgoingLight *= frustumColors[ i ];\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t}\n\n\t// NOTE: I am unsure if this is correct in linear space.  -bhouston, Dec 29, 2014\n\tshadowColor = inputToLinear( shadowColor );\n\n\toutgoingLight = outgoingLight * shadowColor;\n\n#endif\n";
+THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n\t#ifdef USE_SKINNING\n\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\n\t#elif defined( USE_MORPHTARGETS )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n\t#else\n\n\t\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n#endif\n";THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n\tuniform sampler2D shadowMap[ MAX_SHADOWS ];\n\tuniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n\tuniform float shadowDarkness[ MAX_SHADOWS ];\n\tuniform float shadowBias[ MAX_SHADOWS ];\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n\tfloat unpackDepth( const in vec4 rgba_depth ) {\n\n\t\tconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n\t\tfloat depth = dot( rgba_depth, bit_shift );\n\t\treturn depth;\n\n\t}\n\n#endif";
+THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\n\t#ifdef BONE_TEXTURE\n\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\n\t\t\ty = dy * ( y + 0.5 );\n\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\n\t\t\treturn bone;\n\n\t\t}\n\n\t#else\n\n\t\tuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tmat4 bone = boneGlobalMatrices[ int(i) ];\n\t\t\treturn bone;\n\n\t\t}\n\n\t#endif\n\n#endif\n";
+THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n\tuniform float logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\t#extension GL_EXT_frag_depth : enable\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n";
+THREE.UniformsUtils={merge:function(a){for(var b={},c=0;c<a.length;c++){var d=this.clone(a[c]),e;for(e in d)b[e]=d[e]}return b},clone:function(a){var b={},c;for(c in a){b[c]={};for(var d in a[c]){var e=a[c][d];b[c][d]=e instanceof THREE.Color||e instanceof THREE.Vector2||e instanceof THREE.Vector3||e instanceof THREE.Vector4||e instanceof THREE.Matrix4||e instanceof THREE.Texture?e.clone():e instanceof Array?e.slice():e}}return b}};
+THREE.UniformsLib={common:{diffuse:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},map:{type:"t",value:null},offsetRepeat:{type:"v4",value:new THREE.Vector4(0,0,1,1)},lightMap:{type:"t",value:null},specularMap:{type:"t",value:null},alphaMap:{type:"t",value:null},envMap:{type:"t",value:null},flipEnvMap:{type:"f",value:-1},reflectivity:{type:"f",value:1},refractionRatio:{type:"f",value:.98},morphTargetInfluences:{type:"f",value:0}},bump:{bumpMap:{type:"t",value:null},bumpScale:{type:"f",
+value:1}},normalmap:{normalMap:{type:"t",value:null},normalScale:{type:"v2",value:new THREE.Vector2(1,1)}},fog:{fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},lights:{ambientLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]},directionalLightColor:{type:"fv",value:[]},hemisphereLightDirection:{type:"fv",value:[]},hemisphereLightSkyColor:{type:"fv",value:[]},hemisphereLightGroundColor:{type:"fv",
+value:[]},pointLightColor:{type:"fv",value:[]},pointLightPosition:{type:"fv",value:[]},pointLightDistance:{type:"fv1",value:[]},pointLightDecay:{type:"fv1",value:[]},spotLightColor:{type:"fv",value:[]},spotLightPosition:{type:"fv",value:[]},spotLightDirection:{type:"fv",value:[]},spotLightDistance:{type:"fv1",value:[]},spotLightAngleCos:{type:"fv1",value:[]},spotLightExponent:{type:"fv1",value:[]},spotLightDecay:{type:"fv1",value:[]}},particle:{psColor:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",
+value:1},size:{type:"f",value:1},scale:{type:"f",value:1},map:{type:"t",value:null},offsetRepeat:{type:"v4",value:new THREE.Vector4(0,0,1,1)},fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},shadowmap:{shadowMap:{type:"tv",value:[]},shadowMapSize:{type:"v2v",value:[]},shadowBias:{type:"fv1",value:[]},shadowDarkness:{type:"fv1",value:[]},shadowMatrix:{type:"m4v",value:[]}}};
+THREE.ShaderLib={basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.shadowmap]),vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",
+THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.skinbase_vertex,"\t#ifdef USE_ENVMAP",THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"\t#endif",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.shadowmap_vertex,
+"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );",
+THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,"\toutgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n}"].join("\n")},
+lambert:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{emissive:{type:"c",value:new THREE.Color(0)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.common,THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_lambert_pars_vertex,
+THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,
+THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_lambert_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,
+THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,
+THREE.ShaderChunk.specularmap_fragment,"\t#ifdef DOUBLE_SIDED\n\t\tif ( gl_FrontFacing )\n\t\t\toutgoingLight += diffuseColor.rgb * vLightFront + emissive;\n\t\telse\n\t\t\toutgoingLight += diffuseColor.rgb * vLightBack + emissive;\n\t#else\n\t\toutgoingLight += diffuseColor.rgb * vLightFront + emissive;\n\t#endif",THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n}"].join("\n")},
+phong:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.bump,THREE.UniformsLib.normalmap,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{emissive:{type:"c",value:new THREE.Color(0)},specular:{type:"c",value:new THREE.Color(1118481)},shininess:{type:"f",value:30},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif",THREE.ShaderChunk.common,
+THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_phong_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,
+THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"\tvViewPosition = -mvPosition.xyz;",THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_phong_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;",
+THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.lights_phong_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.bumpmap_pars_fragment,THREE.ShaderChunk.normalmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,
+"void main() {\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,THREE.ShaderChunk.lights_phong_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,
+THREE.ShaderChunk.fog_fragment,"\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n}"].join("\n")},particle_basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.particle,THREE.UniformsLib.shadowmap]),vertexShader:["uniform float size;\nuniform float scale;",THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\tgl_Position = projectionMatrix * mvPosition;",
+THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 psColor;\nuniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_particle_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( psColor, opacity );",
+THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_particle_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.alphatest_fragment,"\toutgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.fog_fragment,"\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n}"].join("\n")},dashed:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,{scale:{type:"f",value:1},dashSize:{type:"f",value:1},totalSize:{type:"f",value:2}}]),
+vertexShader:["uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;",THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;",
+THREE.ShaderChunk.common,THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment,"\toutgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.fog_fragment,"\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n}"].join("\n")},
+depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;\nuniform float mFar;\nuniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,
+"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\tfloat color = 1.0 - smoothstep( mNear, mFar, depth );\n\tgl_FragColor = vec4( vec3( color ), opacity );\n}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,
+"void main() {\n\tvNormal = normalize( normalMatrix * normal );",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},
+tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,
+"void main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},equirect:{uniforms:{tEquirect:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\nvec3 direction = normalize( vWorldPosition );\nvec2 sampleUV;\nsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\nsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\ngl_FragColor = texture2D( tEquirect, sampleUV );",THREE.ShaderChunk.logdepthbuf_fragment,
+"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {\n\tconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\tconst vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n\tvec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );\n\tres -= res.xxyz * bit_mask;\n\treturn res;\n}\nvoid main() {",
+THREE.ShaderChunk.logdepthbuf_fragment,"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );\n\t#else\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n\t#endif\n}"].join("\n")}};
+THREE.WebGLRenderer=function(a){function b(a){var b=a.geometry;a=a.material;var c=b.vertices.length;if(a.attributes){void 0===b.__webglCustomAttributesList&&(b.__webglCustomAttributesList=[]);for(var d in a.attributes){var e=a.attributes[d];if(!e.__webglInitialized||e.createUniqueBuffers){e.__webglInitialized=!0;var f=1;"v2"===e.type?f=2:"v3"===e.type?f=3:"v4"===e.type?f=4:"c"===e.type&&(f=3);e.size=f;e.array=new Float32Array(c*f);e.buffer=m.createBuffer();e.buffer.belongsToAttribute=d;e.needsUpdate=
+!0}b.__webglCustomAttributesList.push(e)}}}function c(a,b){return a.material instanceof THREE.MeshFaceMaterial?a.material.materials[b.materialIndex]:a.material}function d(a,b,c,d){c=c.attributes;var e=b.attributes;b=b.attributesKeys;for(var f=0,g=b.length;f<g;f++){var h=b[f],k=e[h];if(0<=k){var n=c[h];void 0!==n?(h=n.itemSize,m.bindBuffer(m.ARRAY_BUFFER,n.buffer),W.enableAttribute(k),m.vertexAttribPointer(k,h,m.FLOAT,!1,0,d*h*4)):void 0!==a.defaultAttributeValues&&(2===a.defaultAttributeValues[h].length?
+m.vertexAttrib2fv(k,a.defaultAttributeValues[h]):3===a.defaultAttributeValues[h].length&&m.vertexAttrib3fv(k,a.defaultAttributeValues[h]))}}W.disableUnusedAttributes()}function e(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function f(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function g(a,
+b){return b[0]-a[0]}function h(a){if(!1!==a.visible){if(!(a instanceof THREE.Scene||a instanceof THREE.Group)){void 0===a.__webglInit&&(a.__webglInit=!0,a._modelViewMatrix=new THREE.Matrix4,a._normalMatrix=new THREE.Matrix3,a.addEventListener("removed",wb));var c=a.geometry;if(void 0!==c&&void 0===c.__webglInit)if(c.__webglInit=!0,c.addEventListener("dispose",jb),c instanceof THREE.BufferGeometry)B.info.memory.geometries++;else if(a instanceof THREE.Mesh)q(a,c);else if(a instanceof THREE.Line){if(void 0===
+c.__webglVertexBuffer){c.__webglVertexBuffer=m.createBuffer();c.__webglColorBuffer=m.createBuffer();c.__webglLineDistanceBuffer=m.createBuffer();B.info.memory.geometries++;var d=c.vertices.length;c.__vertexArray=new Float32Array(3*d);c.__colorArray=new Float32Array(3*d);c.__lineDistanceArray=new Float32Array(1*d);c.__webglLineCount=d;b(a);c.verticesNeedUpdate=!0;c.colorsNeedUpdate=!0;c.lineDistancesNeedUpdate=!0}}else a instanceof THREE.PointCloud&&void 0===c.__webglVertexBuffer&&(c.__webglVertexBuffer=
+m.createBuffer(),c.__webglColorBuffer=m.createBuffer(),B.info.memory.geometries++,d=c.vertices.length,c.__vertexArray=new Float32Array(3*d),c.__colorArray=new Float32Array(3*d),c.__webglParticleCount=d,b(a),c.verticesNeedUpdate=!0,c.colorsNeedUpdate=!0);if(void 0===a.__webglActive)if(a.__webglActive=!0,a instanceof THREE.Mesh)if(c instanceof THREE.BufferGeometry)n(ba,c,a);else{if(c instanceof THREE.Geometry)for(var c=Ua[c.id],d=0,e=c.length;d<e;d++)n(ba,c[d],a)}else a instanceof THREE.Line||a instanceof
+THREE.PointCloud?n(ba,c,a):(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)&&qa.push({id:null,object:a,opaque:null,transparent:null,z:0});if(a instanceof THREE.Light)ca.push(a);else if(a instanceof THREE.Sprite)Xa.push(a);else if(a instanceof THREE.LensFlare)Ya.push(a);else if((c=ba[a.id])&&(!1===a.frustumCulled||!0===cb.intersectsObject(a)))for(d=0,e=c.length;d<e;d++){var f=c[d],g=f,k=g.object,l=g.buffer,p=k.geometry,k=k.material;k instanceof THREE.MeshFaceMaterial?(k=k.materials[p instanceof
+THREE.BufferGeometry?0:l.materialIndex],g.material=k,k.transparent?Qa.push(g):Ka.push(g)):k&&(g.material=k,k.transparent?Qa.push(g):Ka.push(g));f.render=!0;!0===B.sortObjects&&(wa.setFromMatrixPosition(a.matrixWorld),wa.applyProjection(db),f.z=wa.z)}}d=0;for(e=a.children.length;d<e;d++)h(a.children[d])}}function k(a,b,c,d,e){for(var f,g=0,h=a.length;g<h;g++){f=a[g];var k=f.object,m=f.buffer;w(k,b);if(e)f=e;else{f=f.material;if(!f)continue;u(f)}B.setMaterialFaces(f);m instanceof THREE.BufferGeometry?
+B.renderBufferDirect(b,c,d,f,m,k):B.renderBuffer(b,c,d,f,m,k)}}function l(a,b,c,d,e,f){for(var g,h=0,k=a.length;h<k;h++){g=a[h];var m=g.object;if(m.visible){if(f)g=f;else{g=g[b];if(!g)continue;u(g)}B.renderImmediateObject(c,d,e,g,m)}}}function p(a){var b=a.object.material;b.transparent?(a.transparent=b,a.opaque=null):(a.opaque=b,a.transparent=null)}function q(a,b){var d=a.material,e=!1;if(void 0===Ua[b.id]||!0===b.groupsNeedUpdate){delete ba[a.id];for(var f=Ua,g=b.id,d=d instanceof THREE.MeshFaceMaterial,
+h=da.get("OES_element_index_uint")?4294967296:65535,k,e={},l=b.morphTargets.length,p=b.morphNormals.length,q,s={},t=[],r=0,w=b.faces.length;r<w;r++){k=b.faces[r];var u=d?k.materialIndex:0;u in e||(e[u]={hash:u,counter:0});k=e[u].hash+"_"+e[u].counter;k in s||(q={id:Qb++,faces3:[],materialIndex:u,vertices:0,numMorphTargets:l,numMorphNormals:p},s[k]=q,t.push(q));s[k].vertices+3>h&&(e[u].counter+=1,k=e[u].hash+"_"+e[u].counter,k in s||(q={id:Qb++,faces3:[],materialIndex:u,vertices:0,numMorphTargets:l,
+numMorphNormals:p},s[k]=q,t.push(q)));s[k].faces3.push(r);s[k].vertices+=3}f[g]=t;b.groupsNeedUpdate=!1}f=Ua[b.id];g=0;for(d=f.length;g<d;g++){h=f[g];if(void 0===h.__webglVertexBuffer){e=h;e.__webglVertexBuffer=m.createBuffer();e.__webglNormalBuffer=m.createBuffer();e.__webglTangentBuffer=m.createBuffer();e.__webglColorBuffer=m.createBuffer();e.__webglUVBuffer=m.createBuffer();e.__webglUV2Buffer=m.createBuffer();e.__webglSkinIndicesBuffer=m.createBuffer();e.__webglSkinWeightsBuffer=m.createBuffer();
+e.__webglFaceBuffer=m.createBuffer();e.__webglLineBuffer=m.createBuffer();if(p=e.numMorphTargets)for(e.__webglMorphTargetsBuffers=[],l=0;l<p;l++)e.__webglMorphTargetsBuffers.push(m.createBuffer());if(p=e.numMorphNormals)for(e.__webglMorphNormalsBuffers=[],l=0;l<p;l++)e.__webglMorphNormalsBuffers.push(m.createBuffer());B.info.memory.geometries++;e=h;r=a;w=r.geometry;p=e.faces3;l=3*p.length;s=1*p.length;t=3*p.length;p=c(r,e);e.__vertexArray=new Float32Array(3*l);e.__normalArray=new Float32Array(3*l);
+e.__colorArray=new Float32Array(3*l);e.__uvArray=new Float32Array(2*l);1<w.faceVertexUvs.length&&(e.__uv2Array=new Float32Array(2*l));w.hasTangents&&(e.__tangentArray=new Float32Array(4*l));r.geometry.skinWeights.length&&r.geometry.skinIndices.length&&(e.__skinIndexArray=new Float32Array(4*l),e.__skinWeightArray=new Float32Array(4*l));r=null!==da.get("OES_element_index_uint")&&21845<s?Uint32Array:Uint16Array;e.__typeArray=r;e.__faceArray=new r(3*s);e.__lineArray=new r(2*t);if(w=e.numMorphTargets)for(e.__morphTargetsArrays=
+[],r=0;r<w;r++)e.__morphTargetsArrays.push(new Float32Array(3*l));if(w=e.numMorphNormals)for(e.__morphNormalsArrays=[],r=0;r<w;r++)e.__morphNormalsArrays.push(new Float32Array(3*l));e.__webglFaceCount=3*s;e.__webglLineCount=2*t;if(p.attributes)for(s in void 0===e.__webglCustomAttributesList&&(e.__webglCustomAttributesList=[]),s=void 0,p.attributes){var t=p.attributes[s],r={},v;for(v in t)r[v]=t[v];if(!r.__webglInitialized||r.createUniqueBuffers)r.__webglInitialized=!0,w=1,"v2"===r.type?w=2:"v3"===
+r.type?w=3:"v4"===r.type?w=4:"c"===r.type&&(w=3),r.size=w,r.array=new Float32Array(l*w),r.buffer=m.createBuffer(),r.buffer.belongsToAttribute=s,t.needsUpdate=!0,r.__original=t;e.__webglCustomAttributesList.push(r)}e.__inittedArrays=!0;b.verticesNeedUpdate=!0;b.morphTargetsNeedUpdate=!0;b.elementsNeedUpdate=!0;b.uvsNeedUpdate=!0;b.normalsNeedUpdate=!0;b.tangentsNeedUpdate=!0;e=b.colorsNeedUpdate=!0}else e=!1;(e||void 0===a.__webglActive)&&n(ba,h,a)}a.__webglActive=!0}function n(a,b,c){var d=c.id;a[d]=
+a[d]||[];a[d].push({id:d,buffer:b,object:c,material:null,z:0})}function t(a){var b=a.geometry;if(b instanceof THREE.BufferGeometry)for(var d=b.attributes,e=b.attributesKeys,f=0,g=e.length;f<g;f++){var h=e[f],k=d[h],n="index"===h?m.ELEMENT_ARRAY_BUFFER:m.ARRAY_BUFFER;void 0===k.buffer?(k.buffer=m.createBuffer(),m.bindBuffer(n,k.buffer),m.bufferData(n,k.array,k instanceof THREE.DynamicBufferAttribute?m.DYNAMIC_DRAW:m.STATIC_DRAW),k.needsUpdate=!1):!0===k.needsUpdate&&(m.bindBuffer(n,k.buffer),void 0===
+k.updateRange||-1===k.updateRange.count?m.bufferSubData(n,0,k.array):0===k.updateRange.count?console.error("THREE.WebGLRenderer.updateObject: using updateRange for THREE.DynamicBufferAttribute and marked as needsUpdate but count is 0, ensure you are using set methods or updating manually."):(m.bufferSubData(n,k.updateRange.offset*k.array.BYTES_PER_ELEMENT,k.array.subarray(k.updateRange.offset,k.updateRange.offset+k.updateRange.count)),k.updateRange.count=0),k.needsUpdate=!1)}else if(a instanceof THREE.Mesh){!0===
+b.groupsNeedUpdate&&q(a,b);for(var l=Ua[b.id],f=0,p=l.length;f<p;f++){var t=l[f],w=c(a,t),u=w.attributes&&r(w);if(b.verticesNeedUpdate||b.morphTargetsNeedUpdate||b.elementsNeedUpdate||b.uvsNeedUpdate||b.normalsNeedUpdate||b.colorsNeedUpdate||b.tangentsNeedUpdate||u){var v=t,x=a,D=m.DYNAMIC_DRAW,A=!b.dynamic,E=w;if(v.__inittedArrays){var G=!1===E instanceof THREE.MeshPhongMaterial&&E.shading===THREE.FlatShading,y=void 0,z=void 0,F=void 0,B=void 0,I=void 0,H=void 0,M=void 0,R=void 0,P=void 0,U=void 0,
+O=void 0,J=void 0,L=void 0,N=void 0,Ka=void 0,V=void 0,W=void 0,Qa=void 0,Ya=void 0,Xa=void 0,da=void 0,ba=void 0,ja=void 0,Pa=void 0,ka=void 0,Q=void 0,ha=void 0,ia=void 0,ob=void 0,Y=void 0,ub=void 0,pa=void 0,ab=void 0,oa=void 0,ca=void 0,qa=void 0,Ca=void 0,ta=void 0,na=void 0,wa=void 0,La=0,Ma=0,kb=0,yb=0,zb=0,Ra=0,Aa=0,eb=0,Ha=0,la=0,ra=0,K=0,za=void 0,Sa=v.__vertexArray,Ab=v.__uvArray,lb=v.__uv2Array,Na=v.__normalArray,sa=v.__tangentArray,Da=v.__colorArray,Ea=v.__skinIndexArray,Fa=v.__skinWeightArray,
+Gb=v.__morphTargetsArrays,Bb=v.__morphNormalsArrays,mb=v.__webglCustomAttributesList,C=void 0,Va=v.__faceArray,Ta=v.__lineArray,ea=x.geometry,fb=ea.elementsNeedUpdate,vb=ea.uvsNeedUpdate,Mb=ea.normalsNeedUpdate,Ob=ea.tangentsNeedUpdate,ib=ea.colorsNeedUpdate,sb=ea.morphTargetsNeedUpdate,Cb=ea.vertices,$=v.faces3,xa=ea.faces,Hb=ea.faceVertexUvs[0],Oa=ea.faceVertexUvs[1],$a=ea.skinIndices,Ga=ea.skinWeights,nb=ea.morphTargets,bb=ea.morphNormals;if(ea.verticesNeedUpdate){y=0;for(z=$.length;y<z;y++)B=
+xa[$[y]],J=Cb[B.a],L=Cb[B.b],N=Cb[B.c],Sa[Ma]=J.x,Sa[Ma+1]=J.y,Sa[Ma+2]=J.z,Sa[Ma+3]=L.x,Sa[Ma+4]=L.y,Sa[Ma+5]=L.z,Sa[Ma+6]=N.x,Sa[Ma+7]=N.y,Sa[Ma+8]=N.z,Ma+=9;m.bindBuffer(m.ARRAY_BUFFER,v.__webglVertexBuffer);m.bufferData(m.ARRAY_BUFFER,Sa,D)}if(sb)for(ca=0,qa=nb.length;ca<qa;ca++){y=ra=0;for(z=$.length;y<z;y++)na=$[y],B=xa[na],J=nb[ca].vertices[B.a],L=nb[ca].vertices[B.b],N=nb[ca].vertices[B.c],Ca=Gb[ca],Ca[ra]=J.x,Ca[ra+1]=J.y,Ca[ra+2]=J.z,Ca[ra+3]=L.x,Ca[ra+4]=L.y,Ca[ra+5]=L.z,Ca[ra+6]=N.x,Ca[ra+
+7]=N.y,Ca[ra+8]=N.z,E.morphNormals&&(G?Xa=Ya=Qa=bb[ca].faceNormals[na]:(wa=bb[ca].vertexNormals[na],Qa=wa.a,Ya=wa.b,Xa=wa.c),ta=Bb[ca],ta[ra]=Qa.x,ta[ra+1]=Qa.y,ta[ra+2]=Qa.z,ta[ra+3]=Ya.x,ta[ra+4]=Ya.y,ta[ra+5]=Ya.z,ta[ra+6]=Xa.x,ta[ra+7]=Xa.y,ta[ra+8]=Xa.z),ra+=9;m.bindBuffer(m.ARRAY_BUFFER,v.__webglMorphTargetsBuffers[ca]);m.bufferData(m.ARRAY_BUFFER,Gb[ca],D);E.morphNormals&&(m.bindBuffer(m.ARRAY_BUFFER,v.__webglMorphNormalsBuffers[ca]),m.bufferData(m.ARRAY_BUFFER,Bb[ca],D))}if(Ga.length){y=0;
+for(z=$.length;y<z;y++)B=xa[$[y]],Pa=Ga[B.a],ka=Ga[B.b],Q=Ga[B.c],Fa[la]=Pa.x,Fa[la+1]=Pa.y,Fa[la+2]=Pa.z,Fa[la+3]=Pa.w,Fa[la+4]=ka.x,Fa[la+5]=ka.y,Fa[la+6]=ka.z,Fa[la+7]=ka.w,Fa[la+8]=Q.x,Fa[la+9]=Q.y,Fa[la+10]=Q.z,Fa[la+11]=Q.w,ha=$a[B.a],ia=$a[B.b],ob=$a[B.c],Ea[la]=ha.x,Ea[la+1]=ha.y,Ea[la+2]=ha.z,Ea[la+3]=ha.w,Ea[la+4]=ia.x,Ea[la+5]=ia.y,Ea[la+6]=ia.z,Ea[la+7]=ia.w,Ea[la+8]=ob.x,Ea[la+9]=ob.y,Ea[la+10]=ob.z,Ea[la+11]=ob.w,la+=12;0<la&&(m.bindBuffer(m.ARRAY_BUFFER,v.__webglSkinIndicesBuffer),
+m.bufferData(m.ARRAY_BUFFER,Ea,D),m.bindBuffer(m.ARRAY_BUFFER,v.__webglSkinWeightsBuffer),m.bufferData(m.ARRAY_BUFFER,Fa,D))}if(ib){y=0;for(z=$.length;y<z;y++)B=xa[$[y]],M=B.vertexColors,R=B.color,3===M.length&&E.vertexColors===THREE.VertexColors?(da=M[0],ba=M[1],ja=M[2]):ja=ba=da=R,Da[Ha]=da.r,Da[Ha+1]=da.g,Da[Ha+2]=da.b,Da[Ha+3]=ba.r,Da[Ha+4]=ba.g,Da[Ha+5]=ba.b,Da[Ha+6]=ja.r,Da[Ha+7]=ja.g,Da[Ha+8]=ja.b,Ha+=9;0<Ha&&(m.bindBuffer(m.ARRAY_BUFFER,v.__webglColorBuffer),m.bufferData(m.ARRAY_BUFFER,Da,
+D))}if(Ob&&ea.hasTangents){y=0;for(z=$.length;y<z;y++)B=xa[$[y]],P=B.vertexTangents,Ka=P[0],V=P[1],W=P[2],sa[Aa]=Ka.x,sa[Aa+1]=Ka.y,sa[Aa+2]=Ka.z,sa[Aa+3]=Ka.w,sa[Aa+4]=V.x,sa[Aa+5]=V.y,sa[Aa+6]=V.z,sa[Aa+7]=V.w,sa[Aa+8]=W.x,sa[Aa+9]=W.y,sa[Aa+10]=W.z,sa[Aa+11]=W.w,Aa+=12;m.bindBuffer(m.ARRAY_BUFFER,v.__webglTangentBuffer);m.bufferData(m.ARRAY_BUFFER,sa,D)}if(Mb){y=0;for(z=$.length;y<z;y++)if(B=xa[$[y]],I=B.vertexNormals,H=B.normal,3===I.length&&!1===G)for(Y=0;3>Y;Y++)pa=I[Y],Na[Ra]=pa.x,Na[Ra+1]=
+pa.y,Na[Ra+2]=pa.z,Ra+=3;else for(Y=0;3>Y;Y++)Na[Ra]=H.x,Na[Ra+1]=H.y,Na[Ra+2]=H.z,Ra+=3;m.bindBuffer(m.ARRAY_BUFFER,v.__webglNormalBuffer);m.bufferData(m.ARRAY_BUFFER,Na,D)}if(vb&&Hb){y=0;for(z=$.length;y<z;y++)if(F=$[y],U=Hb[F],void 0!==U)for(Y=0;3>Y;Y++)ab=U[Y],Ab[kb]=ab.x,Ab[kb+1]=ab.y,kb+=2;0<kb&&(m.bindBuffer(m.ARRAY_BUFFER,v.__webglUVBuffer),m.bufferData(m.ARRAY_BUFFER,Ab,D))}if(vb&&Oa){y=0;for(z=$.length;y<z;y++)if(F=$[y],O=Oa[F],void 0!==O)for(Y=0;3>Y;Y++)oa=O[Y],lb[yb]=oa.x,lb[yb+1]=oa.y,
+yb+=2;0<yb&&(m.bindBuffer(m.ARRAY_BUFFER,v.__webglUV2Buffer),m.bufferData(m.ARRAY_BUFFER,lb,D))}if(fb){y=0;for(z=$.length;y<z;y++)Va[zb]=La,Va[zb+1]=La+1,Va[zb+2]=La+2,zb+=3,Ta[eb]=La,Ta[eb+1]=La+1,Ta[eb+2]=La,Ta[eb+3]=La+2,Ta[eb+4]=La+1,Ta[eb+5]=La+2,eb+=6,La+=3;m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,v.__webglFaceBuffer);m.bufferData(m.ELEMENT_ARRAY_BUFFER,Va,D);m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,v.__webglLineBuffer);m.bufferData(m.ELEMENT_ARRAY_BUFFER,Ta,D)}if(mb)for(Y=0,ub=mb.length;Y<ub;Y++)if(C=
+mb[Y],C.__original.needsUpdate){K=0;if(1===C.size)if(void 0===C.boundTo||"vertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)B=xa[$[y]],C.array[K]=C.value[B.a],C.array[K+1]=C.value[B.b],C.array[K+2]=C.value[B.c],K+=3;else{if("faces"===C.boundTo)for(y=0,z=$.length;y<z;y++)za=C.value[$[y]],C.array[K]=za,C.array[K+1]=za,C.array[K+2]=za,K+=3}else if(2===C.size)if(void 0===C.boundTo||"vertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)B=xa[$[y]],J=C.value[B.a],L=C.value[B.b],N=C.value[B.c],C.array[K]=J.x,
+C.array[K+1]=J.y,C.array[K+2]=L.x,C.array[K+3]=L.y,C.array[K+4]=N.x,C.array[K+5]=N.y,K+=6;else{if("faces"===C.boundTo)for(y=0,z=$.length;y<z;y++)N=L=J=za=C.value[$[y]],C.array[K]=J.x,C.array[K+1]=J.y,C.array[K+2]=L.x,C.array[K+3]=L.y,C.array[K+4]=N.x,C.array[K+5]=N.y,K+=6}else if(3===C.size){var T;T="c"===C.type?["r","g","b"]:["x","y","z"];if(void 0===C.boundTo||"vertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)B=xa[$[y]],J=C.value[B.a],L=C.value[B.b],N=C.value[B.c],C.array[K]=J[T[0]],C.array[K+1]=
+J[T[1]],C.array[K+2]=J[T[2]],C.array[K+3]=L[T[0]],C.array[K+4]=L[T[1]],C.array[K+5]=L[T[2]],C.array[K+6]=N[T[0]],C.array[K+7]=N[T[1]],C.array[K+8]=N[T[2]],K+=9;else if("faces"===C.boundTo)for(y=0,z=$.length;y<z;y++)N=L=J=za=C.value[$[y]],C.array[K]=J[T[0]],C.array[K+1]=J[T[1]],C.array[K+2]=J[T[2]],C.array[K+3]=L[T[0]],C.array[K+4]=L[T[1]],C.array[K+5]=L[T[2]],C.array[K+6]=N[T[0]],C.array[K+7]=N[T[1]],C.array[K+8]=N[T[2]],K+=9;else if("faceVertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)za=C.value[$[y]],
+J=za[0],L=za[1],N=za[2],C.array[K]=J[T[0]],C.array[K+1]=J[T[1]],C.array[K+2]=J[T[2]],C.array[K+3]=L[T[0]],C.array[K+4]=L[T[1]],C.array[K+5]=L[T[2]],C.array[K+6]=N[T[0]],C.array[K+7]=N[T[1]],C.array[K+8]=N[T[2]],K+=9}else if(4===C.size)if(void 0===C.boundTo||"vertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)B=xa[$[y]],J=C.value[B.a],L=C.value[B.b],N=C.value[B.c],C.array[K]=J.x,C.array[K+1]=J.y,C.array[K+2]=J.z,C.array[K+3]=J.w,C.array[K+4]=L.x,C.array[K+5]=L.y,C.array[K+6]=L.z,C.array[K+7]=L.w,C.array[K+
+8]=N.x,C.array[K+9]=N.y,C.array[K+10]=N.z,C.array[K+11]=N.w,K+=12;else if("faces"===C.boundTo)for(y=0,z=$.length;y<z;y++)N=L=J=za=C.value[$[y]],C.array[K]=J.x,C.array[K+1]=J.y,C.array[K+2]=J.z,C.array[K+3]=J.w,C.array[K+4]=L.x,C.array[K+5]=L.y,C.array[K+6]=L.z,C.array[K+7]=L.w,C.array[K+8]=N.x,C.array[K+9]=N.y,C.array[K+10]=N.z,C.array[K+11]=N.w,K+=12;else if("faceVertices"===C.boundTo)for(y=0,z=$.length;y<z;y++)za=C.value[$[y]],J=za[0],L=za[1],N=za[2],C.array[K]=J.x,C.array[K+1]=J.y,C.array[K+2]=
+J.z,C.array[K+3]=J.w,C.array[K+4]=L.x,C.array[K+5]=L.y,C.array[K+6]=L.z,C.array[K+7]=L.w,C.array[K+8]=N.x,C.array[K+9]=N.y,C.array[K+10]=N.z,C.array[K+11]=N.w,K+=12;m.bindBuffer(m.ARRAY_BUFFER,C.buffer);m.bufferData(m.ARRAY_BUFFER,C.array,D)}A&&(delete v.__inittedArrays,delete v.__colorArray,delete v.__normalArray,delete v.__tangentArray,delete v.__uvArray,delete v.__uv2Array,delete v.__faceArray,delete v.__vertexArray,delete v.__lineArray,delete v.__skinIndexArray,delete v.__skinWeightArray)}}}b.verticesNeedUpdate=
+!1;b.morphTargetsNeedUpdate=!1;b.elementsNeedUpdate=!1;b.uvsNeedUpdate=!1;b.normalsNeedUpdate=!1;b.colorsNeedUpdate=!1;b.tangentsNeedUpdate=!1;w.attributes&&s(w)}else if(a instanceof THREE.Line){w=c(a,b);u=w.attributes&&r(w);if(b.verticesNeedUpdate||b.colorsNeedUpdate||b.lineDistancesNeedUpdate||u){var Db=m.DYNAMIC_DRAW,S,aa,Z,Ba,X,Eb,Rb=b.vertices,Ib=b.colors,gb=b.lineDistances,ya=Rb.length,pb=Ib.length,qb=gb.length,Wa=b.__vertexArray,tb=b.__colorArray,hb=b.__lineDistanceArray,$b=b.colorsNeedUpdate,
+Fb=b.lineDistancesNeedUpdate,Sb=b.__webglCustomAttributesList,Jb,cb,ua,Kb,Ia,fa;if(b.verticesNeedUpdate){for(S=0;S<ya;S++)Ba=Rb[S],X=3*S,Wa[X]=Ba.x,Wa[X+1]=Ba.y,Wa[X+2]=Ba.z;m.bindBuffer(m.ARRAY_BUFFER,b.__webglVertexBuffer);m.bufferData(m.ARRAY_BUFFER,Wa,Db)}if($b){for(aa=0;aa<pb;aa++)Eb=Ib[aa],X=3*aa,tb[X]=Eb.r,tb[X+1]=Eb.g,tb[X+2]=Eb.b;m.bindBuffer(m.ARRAY_BUFFER,b.__webglColorBuffer);m.bufferData(m.ARRAY_BUFFER,tb,Db)}if(Fb){for(Z=0;Z<qb;Z++)hb[Z]=gb[Z];m.bindBuffer(m.ARRAY_BUFFER,b.__webglLineDistanceBuffer);
+m.bufferData(m.ARRAY_BUFFER,hb,Db)}if(Sb)for(Jb=0,cb=Sb.length;Jb<cb;Jb++)if(fa=Sb[Jb],fa.needsUpdate&&(void 0===fa.boundTo||"vertices"===fa.boundTo)){X=0;Kb=fa.value.length;if(1===fa.size)for(ua=0;ua<Kb;ua++)fa.array[ua]=fa.value[ua];else if(2===fa.size)for(ua=0;ua<Kb;ua++)Ia=fa.value[ua],fa.array[X]=Ia.x,fa.array[X+1]=Ia.y,X+=2;else if(3===fa.size)if("c"===fa.type)for(ua=0;ua<Kb;ua++)Ia=fa.value[ua],fa.array[X]=Ia.r,fa.array[X+1]=Ia.g,fa.array[X+2]=Ia.b,X+=3;else for(ua=0;ua<Kb;ua++)Ia=fa.value[ua],
+fa.array[X]=Ia.x,fa.array[X+1]=Ia.y,fa.array[X+2]=Ia.z,X+=3;else if(4===fa.size)for(ua=0;ua<Kb;ua++)Ia=fa.value[ua],fa.array[X]=Ia.x,fa.array[X+1]=Ia.y,fa.array[X+2]=Ia.z,fa.array[X+3]=Ia.w,X+=4;m.bindBuffer(m.ARRAY_BUFFER,fa.buffer);m.bufferData(m.ARRAY_BUFFER,fa.array,Db);fa.needsUpdate=!1}}b.verticesNeedUpdate=!1;b.colorsNeedUpdate=!1;b.lineDistancesNeedUpdate=!1;w.attributes&&s(w)}else if(a instanceof THREE.PointCloud){w=c(a,b);u=w.attributes&&r(w);if(b.verticesNeedUpdate||b.colorsNeedUpdate||
+u){var db=m.DYNAMIC_DRAW,Tb,Ub,ac,ma,bc,Nb=b.vertices,Vb=Nb.length,Pb=b.colors,rb=Pb.length,cc=b.__vertexArray,dc=b.__colorArray,wb=b.colorsNeedUpdate,gc=b.__webglCustomAttributesList,ec,jb,va,Lb,Ja,ga;if(b.verticesNeedUpdate){for(Tb=0;Tb<Vb;Tb++)ac=Nb[Tb],ma=3*Tb,cc[ma]=ac.x,cc[ma+1]=ac.y,cc[ma+2]=ac.z;m.bindBuffer(m.ARRAY_BUFFER,b.__webglVertexBuffer);m.bufferData(m.ARRAY_BUFFER,cc,db)}if(wb){for(Ub=0;Ub<rb;Ub++)bc=Pb[Ub],ma=3*Ub,dc[ma]=bc.r,dc[ma+1]=bc.g,dc[ma+2]=bc.b;m.bindBuffer(m.ARRAY_BUFFER,
+b.__webglColorBuffer);m.bufferData(m.ARRAY_BUFFER,dc,db)}if(gc)for(ec=0,jb=gc.length;ec<jb;ec++){ga=gc[ec];if(ga.needsUpdate&&(void 0===ga.boundTo||"vertices"===ga.boundTo))if(Lb=ga.value.length,ma=0,1===ga.size)for(va=0;va<Lb;va++)ga.array[va]=ga.value[va];else if(2===ga.size)for(va=0;va<Lb;va++)Ja=ga.value[va],ga.array[ma]=Ja.x,ga.array[ma+1]=Ja.y,ma+=2;else if(3===ga.size)if("c"===ga.type)for(va=0;va<Lb;va++)Ja=ga.value[va],ga.array[ma]=Ja.r,ga.array[ma+1]=Ja.g,ga.array[ma+2]=Ja.b,ma+=3;else for(va=
+0;va<Lb;va++)Ja=ga.value[va],ga.array[ma]=Ja.x,ga.array[ma+1]=Ja.y,ga.array[ma+2]=Ja.z,ma+=3;else if(4===ga.size)for(va=0;va<Lb;va++)Ja=ga.value[va],ga.array[ma]=Ja.x,ga.array[ma+1]=Ja.y,ga.array[ma+2]=Ja.z,ga.array[ma+3]=Ja.w,ma+=4;m.bindBuffer(m.ARRAY_BUFFER,ga.buffer);m.bufferData(m.ARRAY_BUFFER,ga.array,db);ga.needsUpdate=!1}}b.verticesNeedUpdate=!1;b.colorsNeedUpdate=!1;w.attributes&&s(w)}}function r(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;return!1}function s(a){for(var b in a.attributes)a.attributes[b].needsUpdate=
+!1}function u(a){!0===a.transparent?W.setBlending(a.blending,a.blendEquation,a.blendSrc,a.blendDst,a.blendEquationAlpha,a.blendSrcAlpha,a.blendDstAlpha):W.setBlending(THREE.NoBlending);W.setDepthTest(a.depthTest);W.setDepthWrite(a.depthWrite);W.setColorWrite(a.colorWrite);W.setPolygonOffset(a.polygonOffset,a.polygonOffsetFactor,a.polygonOffsetUnits)}function v(a,b,c,d,e){var f,g,h,k;Mb=0;if(d.needsUpdate){d.program&&hc(d);d.addEventListener("dispose",ic);var n=pc[d.type];if(n){var l=THREE.ShaderLib[n];
+d.__webglShader={uniforms:THREE.UniformsUtils.clone(l.uniforms),vertexShader:l.vertexShader,fragmentShader:l.fragmentShader}}else d.__webglShader={uniforms:d.uniforms,vertexShader:d.vertexShader,fragmentShader:d.fragmentShader};for(var p=0,q=0,r=0,s=0,t=0,w=b.length;t<w;t++){var v=b[t];v.onlyShadow||!1===v.visible||(v instanceof THREE.DirectionalLight&&p++,v instanceof THREE.PointLight&&q++,v instanceof THREE.SpotLight&&r++,v instanceof THREE.HemisphereLight&&s++)}f=p;g=q;h=r;k=s;for(var u,z=0,G=
+0,F=b.length;G<F;G++){var J=b[G];J.castShadow&&(J instanceof THREE.SpotLight&&z++,J instanceof THREE.DirectionalLight&&!J.shadowCascade&&z++)}u=z;var H;if(Nb&&e&&e.skeleton&&e.skeleton.useVertexTexture)H=1024;else{var N=m.getParameter(m.MAX_VERTEX_UNIFORM_VECTORS),M=Math.floor((N-20)/4);void 0!==e&&e instanceof THREE.SkinnedMesh&&(M=Math.min(e.skeleton.bones.length,M),M<e.skeleton.bones.length&&THREE.warn("WebGLRenderer: too many bones - "+e.skeleton.bones.length+", this GPU supports just "+M+" (try OpenGL instead of ANGLE)"));
+H=M}var P={precision:L,supportsVertexTextures:Vb,map:!!d.map,envMap:!!d.envMap,envMapMode:d.envMap&&d.envMap.mapping,lightMap:!!d.lightMap,bumpMap:!!d.bumpMap,normalMap:!!d.normalMap,specularMap:!!d.specularMap,alphaMap:!!d.alphaMap,combine:d.combine,vertexColors:d.vertexColors,fog:c,useFog:d.fog,fogExp:c instanceof THREE.FogExp2,flatShading:d.shading===THREE.FlatShading,sizeAttenuation:d.sizeAttenuation,logarithmicDepthBuffer:ja,skinning:d.skinning,maxBones:H,useVertexTexture:Nb&&e&&e.skeleton&&
+e.skeleton.useVertexTexture,morphTargets:d.morphTargets,morphNormals:d.morphNormals,maxMorphTargets:B.maxMorphTargets,maxMorphNormals:B.maxMorphNormals,maxDirLights:f,maxPointLights:g,maxSpotLights:h,maxHemiLights:k,maxShadows:u,shadowMapEnabled:B.shadowMapEnabled&&e.receiveShadow&&0<u,shadowMapType:B.shadowMapType,shadowMapDebug:B.shadowMapDebug,shadowMapCascade:B.shadowMapCascade,alphaTest:d.alphaTest,metal:d.metal,wrapAround:d.wrapAround,doubleSided:d.side===THREE.DoubleSide,flipSided:d.side===
+THREE.BackSide},R=[];n?R.push(n):(R.push(d.fragmentShader),R.push(d.vertexShader));if(void 0!==d.defines)for(var O in d.defines)R.push(O),R.push(d.defines[O]);for(O in P)R.push(O),R.push(P[O]);for(var Ka=R.join(),V,W=0,Qa=Pa.length;W<Qa;W++){var Ya=Pa[W];if(Ya.code===Ka){V=Ya;V.usedTimes++;break}}void 0===V&&(V=new THREE.WebGLProgram(B,Ka,d,P),Pa.push(V),B.info.memory.programs=Pa.length);d.program=V;var Xa=V.attributes;if(d.morphTargets){d.numSupportedMorphTargets=0;for(var ca,da="morphTarget",ba=
+0;ba<B.maxMorphTargets;ba++)ca=da+ba,0<=Xa[ca]&&d.numSupportedMorphTargets++}if(d.morphNormals)for(d.numSupportedMorphNormals=0,da="morphNormal",ba=0;ba<B.maxMorphNormals;ba++)ca=da+ba,0<=Xa[ca]&&d.numSupportedMorphNormals++;d.uniformsList=[];for(var ha in d.__webglShader.uniforms){var ta=d.program.uniforms[ha];ta&&d.uniformsList.push([d.__webglShader.uniforms[ha],ta])}d.needsUpdate=!1}d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(B.maxMorphTargets));
+var ab=!1,oa=!1,qa=!1,Ua=d.program,ka=Ua.uniforms,Q=d.__webglShader.uniforms;Ua.id!==ob&&(m.useProgram(Ua.program),ob=Ua.id,qa=oa=ab=!0);d.id!==ub&&(-1===ub&&(qa=!0),ub=d.id,oa=!0);if(ab||a!==vb)m.uniformMatrix4fv(ka.projectionMatrix,!1,a.projectionMatrix.elements),ja&&m.uniform1f(ka.logDepthBufFC,2/(Math.log(a.far+1)/Math.LN2)),a!==vb&&(vb=a),(d instanceof THREE.ShaderMaterial||d instanceof THREE.MeshPhongMaterial||d.envMap)&&null!==ka.cameraPosition&&(wa.setFromMatrixPosition(a.matrixWorld),m.uniform3f(ka.cameraPosition,
+wa.x,wa.y,wa.z)),(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.MeshBasicMaterial||d instanceof THREE.ShaderMaterial||d.skinning)&&null!==ka.viewMatrix&&m.uniformMatrix4fv(ka.viewMatrix,!1,a.matrixWorldInverse.elements);if(d.skinning)if(e.bindMatrix&&null!==ka.bindMatrix&&m.uniformMatrix4fv(ka.bindMatrix,!1,e.bindMatrix.elements),e.bindMatrixInverse&&null!==ka.bindMatrixInverse&&m.uniformMatrix4fv(ka.bindMatrixInverse,!1,e.bindMatrixInverse.elements),
+Nb&&e.skeleton&&e.skeleton.useVertexTexture){if(null!==ka.boneTexture){var db=D();m.uniform1i(ka.boneTexture,db);B.setTexture(e.skeleton.boneTexture,db)}null!==ka.boneTextureWidth&&m.uniform1i(ka.boneTextureWidth,e.skeleton.boneTextureWidth);null!==ka.boneTextureHeight&&m.uniform1i(ka.boneTextureHeight,e.skeleton.boneTextureHeight)}else e.skeleton&&e.skeleton.boneMatrices&&null!==ka.boneGlobalMatrices&&m.uniformMatrix4fv(ka.boneGlobalMatrices,!1,e.skeleton.boneMatrices);if(oa){c&&d.fog&&(Q.fogColor.value=
+c.color,c instanceof THREE.Fog?(Q.fogNear.value=c.near,Q.fogFar.value=c.far):c instanceof THREE.FogExp2&&(Q.fogDensity.value=c.density));if(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d.lights){if(Ob){var qa=!0,ia,Za,Y,bb=0,cb=0,ib=0,xb,pb,qb,Ca,jb,na=jc,rb=na.directional.colors,La=na.directional.positions,Ma=na.point.colors,kb=na.point.positions,yb=na.point.distances,zb=na.point.decays,Ra=na.spot.colors,Aa=na.spot.positions,eb=na.spot.distances,Ha=na.spot.directions,
+la=na.spot.anglesCos,ra=na.spot.exponents,K=na.spot.decays,za=na.hemi.skyColors,Sa=na.hemi.groundColors,Ab=na.hemi.positions,lb=0,Na=0,sa=0,Da=0,Ea=0,Fa=0,Gb=0,Bb=0,mb=0,C=0,Va=0,Ta=0;ia=0;for(Za=b.length;ia<Za;ia++)Y=b[ia],Y.onlyShadow||(xb=Y.color,Ca=Y.intensity,jb=Y.distance,Y instanceof THREE.AmbientLight?Y.visible&&(bb+=xb.r,cb+=xb.g,ib+=xb.b):Y instanceof THREE.DirectionalLight?(Ea+=1,Y.visible&&(pa.setFromMatrixPosition(Y.matrixWorld),wa.setFromMatrixPosition(Y.target.matrixWorld),pa.sub(wa),
+pa.normalize(),mb=3*lb,La[mb]=pa.x,La[mb+1]=pa.y,La[mb+2]=pa.z,y(rb,mb,xb,Ca),lb+=1)):Y instanceof THREE.PointLight?(Fa+=1,Y.visible&&(C=3*Na,y(Ma,C,xb,Ca),wa.setFromMatrixPosition(Y.matrixWorld),kb[C]=wa.x,kb[C+1]=wa.y,kb[C+2]=wa.z,yb[Na]=jb,zb[Na]=0===Y.distance?0:Y.decay,Na+=1)):Y instanceof THREE.SpotLight?(Gb+=1,Y.visible&&(Va=3*sa,y(Ra,Va,xb,Ca),pa.setFromMatrixPosition(Y.matrixWorld),Aa[Va]=pa.x,Aa[Va+1]=pa.y,Aa[Va+2]=pa.z,eb[sa]=jb,wa.setFromMatrixPosition(Y.target.matrixWorld),pa.sub(wa),
+pa.normalize(),Ha[Va]=pa.x,Ha[Va+1]=pa.y,Ha[Va+2]=pa.z,la[sa]=Math.cos(Y.angle),ra[sa]=Y.exponent,K[sa]=0===Y.distance?0:Y.decay,sa+=1)):Y instanceof THREE.HemisphereLight&&(Bb+=1,Y.visible&&(pa.setFromMatrixPosition(Y.matrixWorld),pa.normalize(),Ta=3*Da,Ab[Ta]=pa.x,Ab[Ta+1]=pa.y,Ab[Ta+2]=pa.z,pb=Y.color,qb=Y.groundColor,y(za,Ta,pb,Ca),y(Sa,Ta,qb,Ca),Da+=1)));ia=3*lb;for(Za=Math.max(rb.length,3*Ea);ia<Za;ia++)rb[ia]=0;ia=3*Na;for(Za=Math.max(Ma.length,3*Fa);ia<Za;ia++)Ma[ia]=0;ia=3*sa;for(Za=Math.max(Ra.length,
+3*Gb);ia<Za;ia++)Ra[ia]=0;ia=3*Da;for(Za=Math.max(za.length,3*Bb);ia<Za;ia++)za[ia]=0;ia=3*Da;for(Za=Math.max(Sa.length,3*Bb);ia<Za;ia++)Sa[ia]=0;na.directional.length=lb;na.point.length=Na;na.spot.length=sa;na.hemi.length=Da;na.ambient[0]=bb;na.ambient[1]=cb;na.ambient[2]=ib;Ob=!1}if(qa){var ea=jc;Q.ambientLightColor.value=ea.ambient;Q.directionalLightColor.value=ea.directional.colors;Q.directionalLightDirection.value=ea.directional.positions;Q.pointLightColor.value=ea.point.colors;Q.pointLightPosition.value=
+ea.point.positions;Q.pointLightDistance.value=ea.point.distances;Q.pointLightDecay.value=ea.point.decays;Q.spotLightColor.value=ea.spot.colors;Q.spotLightPosition.value=ea.spot.positions;Q.spotLightDistance.value=ea.spot.distances;Q.spotLightDirection.value=ea.spot.directions;Q.spotLightAngleCos.value=ea.spot.anglesCos;Q.spotLightExponent.value=ea.spot.exponents;Q.spotLightDecay.value=ea.spot.decays;Q.hemisphereLightSkyColor.value=ea.hemi.skyColors;Q.hemisphereLightGroundColor.value=ea.hemi.groundColors;
+Q.hemisphereLightDirection.value=ea.hemi.positions;x(Q,!0)}else x(Q,!1)}if(d instanceof THREE.MeshBasicMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.MeshPhongMaterial){Q.opacity.value=d.opacity;Q.diffuse.value=d.color;Q.map.value=d.map;Q.lightMap.value=d.lightMap;Q.specularMap.value=d.specularMap;Q.alphaMap.value=d.alphaMap;d.bumpMap&&(Q.bumpMap.value=d.bumpMap,Q.bumpScale.value=d.bumpScale);d.normalMap&&(Q.normalMap.value=d.normalMap,Q.normalScale.value.copy(d.normalScale));
+var fb;d.map?fb=d.map:d.specularMap?fb=d.specularMap:d.normalMap?fb=d.normalMap:d.bumpMap?fb=d.bumpMap:d.alphaMap&&(fb=d.alphaMap);if(void 0!==fb){var wb=fb.offset,Qb=fb.repeat;Q.offsetRepeat.value.set(wb.x,wb.y,Qb.x,Qb.y)}Q.envMap.value=d.envMap;Q.flipEnvMap.value=d.envMap instanceof THREE.WebGLRenderTargetCube?1:-1;Q.reflectivity.value=d.reflectivity;Q.refractionRatio.value=d.refractionRatio}if(d instanceof THREE.LineBasicMaterial)Q.diffuse.value=d.color,Q.opacity.value=d.opacity;else if(d instanceof
+THREE.LineDashedMaterial)Q.diffuse.value=d.color,Q.opacity.value=d.opacity,Q.dashSize.value=d.dashSize,Q.totalSize.value=d.dashSize+d.gapSize,Q.scale.value=d.scale;else if(d instanceof THREE.PointCloudMaterial){if(Q.psColor.value=d.color,Q.opacity.value=d.opacity,Q.size.value=d.size,Q.scale.value=U.height/2,Q.map.value=d.map,null!==d.map){var Wb=d.map.offset,Xb=d.map.repeat;Q.offsetRepeat.value.set(Wb.x,Wb.y,Xb.x,Xb.y)}}else d instanceof THREE.MeshPhongMaterial?(Q.shininess.value=d.shininess,Q.emissive.value=
+d.emissive,Q.specular.value=d.specular,d.wrapAround&&Q.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshLambertMaterial?(Q.emissive.value=d.emissive,d.wrapAround&&Q.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshDepthMaterial?(Q.mNear.value=a.near,Q.mFar.value=a.far,Q.opacity.value=d.opacity):d instanceof THREE.MeshNormalMaterial&&(Q.opacity.value=d.opacity);if(e.receiveShadow&&!d._shadowPass&&Q.shadowMatrix)for(var sb=0,Cb=0,$=b.length;Cb<$;Cb++){var xa=b[Cb];xa.castShadow&&(xa instanceof
+THREE.SpotLight||xa instanceof THREE.DirectionalLight&&!xa.shadowCascade)&&(Q.shadowMap.value[sb]=xa.shadowMap,Q.shadowMapSize.value[sb]=xa.shadowMapSize,Q.shadowMatrix.value[sb]=xa.shadowMatrix,Q.shadowDarkness.value[sb]=xa.shadowDarkness,Q.shadowBias.value[sb]=xa.shadowBias,sb++)}for(var Hb=d.uniformsList,Oa,$a,Ga,nb=0,fc=Hb.length;nb<fc;nb++){var T=Hb[nb][0];if(!1!==T.needsUpdate){var Db=T.type,S=T.value,aa=Hb[nb][1];switch(Db){case "1i":m.uniform1i(aa,S);break;case "1f":m.uniform1f(aa,S);break;
+case "2f":m.uniform2f(aa,S[0],S[1]);break;case "3f":m.uniform3f(aa,S[0],S[1],S[2]);break;case "4f":m.uniform4f(aa,S[0],S[1],S[2],S[3]);break;case "1iv":m.uniform1iv(aa,S);break;case "3iv":m.uniform3iv(aa,S);break;case "1fv":m.uniform1fv(aa,S);break;case "2fv":m.uniform2fv(aa,S);break;case "3fv":m.uniform3fv(aa,S);break;case "4fv":m.uniform4fv(aa,S);break;case "Matrix3fv":m.uniformMatrix3fv(aa,!1,S);break;case "Matrix4fv":m.uniformMatrix4fv(aa,!1,S);break;case "i":m.uniform1i(aa,S);break;case "f":m.uniform1f(aa,
+S);break;case "v2":m.uniform2f(aa,S.x,S.y);break;case "v3":m.uniform3f(aa,S.x,S.y,S.z);break;case "v4":m.uniform4f(aa,S.x,S.y,S.z,S.w);break;case "c":m.uniform3f(aa,S.r,S.g,S.b);break;case "iv1":m.uniform1iv(aa,S);break;case "iv":m.uniform3iv(aa,S);break;case "fv1":m.uniform1fv(aa,S);break;case "fv":m.uniform3fv(aa,S);break;case "v2v":void 0===T._array&&(T._array=new Float32Array(2*S.length));for(var Z=0,Ba=S.length;Z<Ba;Z++)Ga=2*Z,T._array[Ga]=S[Z].x,T._array[Ga+1]=S[Z].y;m.uniform2fv(aa,T._array);
+break;case "v3v":void 0===T._array&&(T._array=new Float32Array(3*S.length));Z=0;for(Ba=S.length;Z<Ba;Z++)Ga=3*Z,T._array[Ga]=S[Z].x,T._array[Ga+1]=S[Z].y,T._array[Ga+2]=S[Z].z;m.uniform3fv(aa,T._array);break;case "v4v":void 0===T._array&&(T._array=new Float32Array(4*S.length));Z=0;for(Ba=S.length;Z<Ba;Z++)Ga=4*Z,T._array[Ga]=S[Z].x,T._array[Ga+1]=S[Z].y,T._array[Ga+2]=S[Z].z,T._array[Ga+3]=S[Z].w;m.uniform4fv(aa,T._array);break;case "m3":m.uniformMatrix3fv(aa,!1,S.elements);break;case "m3v":void 0===
+T._array&&(T._array=new Float32Array(9*S.length));Z=0;for(Ba=S.length;Z<Ba;Z++)S[Z].flattenToArrayOffset(T._array,9*Z);m.uniformMatrix3fv(aa,!1,T._array);break;case "m4":m.uniformMatrix4fv(aa,!1,S.elements);break;case "m4v":void 0===T._array&&(T._array=new Float32Array(16*S.length));Z=0;for(Ba=S.length;Z<Ba;Z++)S[Z].flattenToArrayOffset(T._array,16*Z);m.uniformMatrix4fv(aa,!1,T._array);break;case "t":Oa=S;$a=D();m.uniform1i(aa,$a);if(!Oa)continue;if(Oa instanceof THREE.CubeTexture||Oa.image instanceof
+Array&&6===Oa.image.length){var X=Oa,Eb=$a;if(6===X.image.length)if(X.needsUpdate){X.image.__webglTextureCube||(X.addEventListener("dispose",Pb),X.image.__webglTextureCube=m.createTexture(),B.info.memory.textures++);m.activeTexture(m.TEXTURE0+Eb);m.bindTexture(m.TEXTURE_CUBE_MAP,X.image.__webglTextureCube);m.pixelStorei(m.UNPACK_FLIP_Y_WEBGL,X.flipY);for(var Rb=X instanceof THREE.CompressedTexture,Ib=X.image[0]instanceof THREE.DataTexture,gb=[],ya=0;6>ya;ya++)gb[ya]=!B.autoScaleCubemaps||Rb||Ib?Ib?
+X.image[ya].image:X.image[ya]:E(X.image[ya],qc);var Yb=gb[0],Zb=THREE.Math.isPowerOfTwo(Yb.width)&&THREE.Math.isPowerOfTwo(Yb.height),Wa=I(X.format),tb=I(X.type);A(m.TEXTURE_CUBE_MAP,X,Zb);for(ya=0;6>ya;ya++)if(Rb)for(var hb,$b=gb[ya].mipmaps,Fb=0,Sb=$b.length;Fb<Sb;Fb++)hb=$b[Fb],X.format!==THREE.RGBAFormat&&X.format!==THREE.RGBFormat?-1<kc().indexOf(Wa)?m.compressedTexImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+ya,Fb,Wa,hb.width,hb.height,0,hb.data):THREE.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()"):
+m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+ya,Fb,Wa,hb.width,hb.height,0,Wa,tb,hb.data);else Ib?m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+ya,0,Wa,gb[ya].width,gb[ya].height,0,Wa,tb,gb[ya].data):m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+ya,0,Wa,Wa,tb,gb[ya]);X.generateMipmaps&&Zb&&m.generateMipmap(m.TEXTURE_CUBE_MAP);X.needsUpdate=!1;if(X.onUpdate)X.onUpdate()}else m.activeTexture(m.TEXTURE0+Eb),m.bindTexture(m.TEXTURE_CUBE_MAP,X.image.__webglTextureCube)}else if(Oa instanceof THREE.WebGLRenderTargetCube){var Jb=
+Oa;m.activeTexture(m.TEXTURE0+$a);m.bindTexture(m.TEXTURE_CUBE_MAP,Jb.__webglTexture)}else B.setTexture(Oa,$a);break;case "tv":void 0===T._array&&(T._array=[]);Z=0;for(Ba=T.value.length;Z<Ba;Z++)T._array[Z]=D();m.uniform1iv(aa,T._array);Z=0;for(Ba=T.value.length;Z<Ba;Z++)Oa=T.value[Z],$a=T._array[Z],Oa&&B.setTexture(Oa,$a);break;default:THREE.warn("THREE.WebGLRenderer: Unknown uniform type: "+Db)}}}}m.uniformMatrix4fv(ka.modelViewMatrix,!1,e._modelViewMatrix.elements);ka.normalMatrix&&m.uniformMatrix3fv(ka.normalMatrix,
+!1,e._normalMatrix.elements);null!==ka.modelMatrix&&m.uniformMatrix4fv(ka.modelMatrix,!1,e.matrixWorld.elements);return Ua}function x(a,b){a.ambientLightColor.needsUpdate=b;a.directionalLightColor.needsUpdate=b;a.directionalLightDirection.needsUpdate=b;a.pointLightColor.needsUpdate=b;a.pointLightPosition.needsUpdate=b;a.pointLightDistance.needsUpdate=b;a.pointLightDecay.needsUpdate=b;a.spotLightColor.needsUpdate=b;a.spotLightPosition.needsUpdate=b;a.spotLightDistance.needsUpdate=b;a.spotLightDirection.needsUpdate=
+b;a.spotLightAngleCos.needsUpdate=b;a.spotLightExponent.needsUpdate=b;a.spotLightDecay.needsUpdate=b;a.hemisphereLightSkyColor.needsUpdate=b;a.hemisphereLightGroundColor.needsUpdate=b;a.hemisphereLightDirection.needsUpdate=b}function D(){var a=Mb;a>=Wb&&THREE.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+Wb);Mb+=1;return a}function w(a,b){a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,a.matrixWorld);a._normalMatrix.getNormalMatrix(a._modelViewMatrix)}
+function y(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function A(a,b,c){c?(m.texParameteri(a,m.TEXTURE_WRAP_S,I(b.wrapS)),m.texParameteri(a,m.TEXTURE_WRAP_T,I(b.wrapT)),m.texParameteri(a,m.TEXTURE_MAG_FILTER,I(b.magFilter)),m.texParameteri(a,m.TEXTURE_MIN_FILTER,I(b.minFilter))):(m.texParameteri(a,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE),m.texParameteri(a,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE),b.wrapS===THREE.ClampToEdgeWrapping&&b.wrapT===THREE.ClampToEdgeWrapping||THREE.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( "+
+b.sourceFile+" )"),m.texParameteri(a,m.TEXTURE_MAG_FILTER,z(b.magFilter)),m.texParameteri(a,m.TEXTURE_MIN_FILTER,z(b.minFilter)),b.minFilter!==THREE.NearestFilter&&b.minFilter!==THREE.LinearFilter&&THREE.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( "+b.sourceFile+" )"));(c=da.get("EXT_texture_filter_anisotropic"))&&b.type!==THREE.FloatType&&b.type!==THREE.HalfFloatType&&(1<b.anisotropy||b.__currentAnisotropy)&&
+(m.texParameterf(a,c.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,B.getMaxAnisotropy())),b.__currentAnisotropy=b.anisotropy)}function E(a,b){if(a.width>b||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElement("canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,d.width,d.height);THREE.warn("THREE.WebGLRenderer: image is too big ("+a.width+"x"+a.height+"). Resized to "+d.width+"x"+d.height,a);return d}return a}
+function G(a,b){m.bindRenderbuffer(m.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(m.renderbufferStorage(m.RENDERBUFFER,m.DEPTH_COMPONENT16,b.width,b.height),m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_ATTACHMENT,m.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(m.renderbufferStorage(m.RENDERBUFFER,m.DEPTH_STENCIL,b.width,b.height),m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_STENCIL_ATTACHMENT,m.RENDERBUFFER,a)):m.renderbufferStorage(m.RENDERBUFFER,m.RGBA4,b.width,b.height)}function F(a){a instanceof
+THREE.WebGLRenderTargetCube?(m.bindTexture(m.TEXTURE_CUBE_MAP,a.__webglTexture),m.generateMipmap(m.TEXTURE_CUBE_MAP),m.bindTexture(m.TEXTURE_CUBE_MAP,null)):(m.bindTexture(m.TEXTURE_2D,a.__webglTexture),m.generateMipmap(m.TEXTURE_2D),m.bindTexture(m.TEXTURE_2D,null))}function z(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?m.NEAREST:m.LINEAR}function I(a){var b;if(a===THREE.RepeatWrapping)return m.REPEAT;if(a===THREE.ClampToEdgeWrapping)return m.CLAMP_TO_EDGE;
+if(a===THREE.MirroredRepeatWrapping)return m.MIRRORED_REPEAT;if(a===THREE.NearestFilter)return m.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return m.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return m.NEAREST_MIPMAP_LINEAR;if(a===THREE.LinearFilter)return m.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return m.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return m.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return m.UNSIGNED_BYTE;if(a===THREE.UnsignedShort4444Type)return m.UNSIGNED_SHORT_4_4_4_4;
+if(a===THREE.UnsignedShort5551Type)return m.UNSIGNED_SHORT_5_5_5_1;if(a===THREE.UnsignedShort565Type)return m.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return m.BYTE;if(a===THREE.ShortType)return m.SHORT;if(a===THREE.UnsignedShortType)return m.UNSIGNED_SHORT;if(a===THREE.IntType)return m.INT;if(a===THREE.UnsignedIntType)return m.UNSIGNED_INT;if(a===THREE.FloatType)return m.FLOAT;b=da.get("OES_texture_half_float");if(null!==b&&a===THREE.HalfFloatType)return b.HALF_FLOAT_OES;if(a===THREE.AlphaFormat)return m.ALPHA;
+if(a===THREE.RGBFormat)return m.RGB;if(a===THREE.RGBAFormat)return m.RGBA;if(a===THREE.LuminanceFormat)return m.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return m.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return m.FUNC_ADD;if(a===THREE.SubtractEquation)return m.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return m.FUNC_REVERSE_SUBTRACT;if(a===THREE.ZeroFactor)return m.ZERO;if(a===THREE.OneFactor)return m.ONE;if(a===THREE.SrcColorFactor)return m.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return m.ONE_MINUS_SRC_COLOR;
+if(a===THREE.SrcAlphaFactor)return m.SRC_ALPHA;if(a===THREE.OneMinusSrcAlphaFactor)return m.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return m.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return m.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return m.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return m.ONE_MINUS_DST_COLOR;if(a===THREE.SrcAlphaSaturateFactor)return m.SRC_ALPHA_SATURATE;b=da.get("WEBGL_compressed_texture_s3tc");if(null!==b){if(a===THREE.RGB_S3TC_DXT1_Format)return b.COMPRESSED_RGB_S3TC_DXT1_EXT;
+if(a===THREE.RGBA_S3TC_DXT1_Format)return b.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return b.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return b.COMPRESSED_RGBA_S3TC_DXT5_EXT}b=da.get("WEBGL_compressed_texture_pvrtc");if(null!==b){if(a===THREE.RGB_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(a===THREE.RGB_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(a===THREE.RGBA_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+if(a===THREE.RGBA_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}b=da.get("EXT_blend_minmax");if(null!==b){if(a===THREE.MinEquation)return b.MIN_EXT;if(a===THREE.MaxEquation)return b.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);a=a||{};var U=void 0!==a.canvas?a.canvas:document.createElement("canvas"),M=void 0!==a.context?a.context:null,H=1,L=void 0!==a.precision?a.precision:"highp",P=void 0!==a.alpha?a.alpha:!1,N=void 0!==a.depth?a.depth:!0,R=void 0!==a.stencil?
+a.stencil:!0,V=void 0!==a.antialias?a.antialias:!1,J=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,oa=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,ja=void 0!==a.logarithmicDepthBuffer?a.logarithmicDepthBuffer:!1,ha=new THREE.Color(0),O=0,ca=[],ba={},qa=[],Ka=[],Qa=[],Xa=[],Ya=[];this.domElement=U;this.context=null;this.sortObjects=this.autoClearStencil=this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.gammaFactor=2;this.shadowMapEnabled=this.gammaOutput=this.gammaInput=
+!1;this.shadowMapType=THREE.PCFShadowMap;this.shadowMapCullFace=THREE.CullFaceFront;this.shadowMapCascade=this.shadowMapDebug=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;this.info={memory:{programs:0,geometries:0,textures:0},render:{calls:0,vertices:0,faces:0,points:0}};var B=this,Pa=[],ob=null,ab=null,ub=-1,ta="",vb=null,Mb=0,ib=0,bb=0,pb=U.width,qb=U.height,Xb=0,fc=0,cb=new THREE.Frustum,db=new THREE.Matrix4,wa=new THREE.Vector3,pa=new THREE.Vector3,Ob=!0,jc={ambient:[0,
+0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},m;try{var Yb={alpha:P,depth:N,stencil:R,antialias:V,premultipliedAlpha:J,preserveDrawingBuffer:oa};m=M||U.getContext("webgl",Yb)||U.getContext("experimental-webgl",Yb);if(null===m){if(null!==U.getContext("webgl"))throw"Error creating WebGL context with your selected attributes.";
+throw"Error creating WebGL context.";}U.addEventListener("webglcontextlost",function(a){a.preventDefault();Zb();lc();ba={}},!1)}catch(rc){THREE.error("THREE.WebGLRenderer: "+rc)}var W=new THREE.WebGLState(m,I);void 0===m.getShaderPrecisionFormat&&(m.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}});var da=new THREE.WebGLExtensions(m);da.get("OES_texture_float");da.get("OES_texture_float_linear");da.get("OES_texture_half_float");da.get("OES_texture_half_float_linear");
+da.get("OES_standard_derivatives");ja&&da.get("EXT_frag_depth");var rb=function(a,b,c,d){!0===J&&(a*=d,b*=d,c*=d);m.clearColor(a,b,c,d)},lc=function(){m.clearColor(0,0,0,1);m.clearDepth(1);m.clearStencil(0);m.enable(m.DEPTH_TEST);m.depthFunc(m.LEQUAL);m.frontFace(m.CCW);m.cullFace(m.BACK);m.enable(m.CULL_FACE);m.enable(m.BLEND);m.blendEquation(m.FUNC_ADD);m.blendFunc(m.SRC_ALPHA,m.ONE_MINUS_SRC_ALPHA);m.viewport(ib,bb,pb,qb);rb(ha.r,ha.g,ha.b,O)},Zb=function(){vb=ob=null;ta="";ub=-1;Ob=!0;W.reset()};
+lc();this.context=m;this.state=W;var Wb=m.getParameter(m.MAX_TEXTURE_IMAGE_UNITS),sc=m.getParameter(m.MAX_VERTEX_TEXTURE_IMAGE_UNITS),tc=m.getParameter(m.MAX_TEXTURE_SIZE),qc=m.getParameter(m.MAX_CUBE_MAP_TEXTURE_SIZE),Vb=0<sc,Nb=Vb&&da.get("OES_texture_float"),uc=m.getShaderPrecisionFormat(m.VERTEX_SHADER,m.HIGH_FLOAT),vc=m.getShaderPrecisionFormat(m.VERTEX_SHADER,m.MEDIUM_FLOAT),wc=m.getShaderPrecisionFormat(m.FRAGMENT_SHADER,m.HIGH_FLOAT),xc=m.getShaderPrecisionFormat(m.FRAGMENT_SHADER,m.MEDIUM_FLOAT),
+kc=function(){var a;return function(){if(void 0!==a)return a;a=[];if(da.get("WEBGL_compressed_texture_pvrtc")||da.get("WEBGL_compressed_texture_s3tc"))for(var b=m.getParameter(m.COMPRESSED_TEXTURE_FORMATS),c=0;c<b.length;c++)a.push(b[c]);return a}}(),yc=0<uc.precision&&0<wc.precision,mc=0<vc.precision&&0<xc.precision;"highp"!==L||yc||(mc?(L="mediump",THREE.warn("THREE.WebGLRenderer: highp not supported, using mediump.")):(L="lowp",THREE.warn("THREE.WebGLRenderer: highp and mediump not supported, using lowp.")));
+"mediump"!==L||mc||(L="lowp",THREE.warn("THREE.WebGLRenderer: mediump not supported, using lowp."));var zc=new THREE.ShadowMapPlugin(this,ca,ba,qa),Ac=new THREE.SpritePlugin(this,Xa),Bc=new THREE.LensFlarePlugin(this,Ya);this.getContext=function(){return m};this.forceContextLoss=function(){da.get("WEBGL_lose_context").loseContext()};this.supportsVertexTextures=function(){return Vb};this.supportsFloatTextures=function(){return da.get("OES_texture_float")};this.supportsHalfFloatTextures=function(){return da.get("OES_texture_half_float")};
+this.supportsStandardDerivatives=function(){return da.get("OES_standard_derivatives")};this.supportsCompressedTextureS3TC=function(){return da.get("WEBGL_compressed_texture_s3tc")};this.supportsCompressedTexturePVRTC=function(){return da.get("WEBGL_compressed_texture_pvrtc")};this.supportsBlendMinMax=function(){return da.get("EXT_blend_minmax")};this.getMaxAnisotropy=function(){var a;return function(){if(void 0!==a)return a;var b=da.get("EXT_texture_filter_anisotropic");return a=null!==b?m.getParameter(b.MAX_TEXTURE_MAX_ANISOTROPY_EXT):
+0}}();this.getPrecision=function(){return L};this.getPixelRatio=function(){return H};this.setPixelRatio=function(a){H=a};this.setSize=function(a,b,c){U.width=a*H;U.height=b*H;!1!==c&&(U.style.width=a+"px",U.style.height=b+"px");this.setViewport(0,0,a,b)};this.setViewport=function(a,b,c,d){ib=a*H;bb=b*H;pb=c*H;qb=d*H;m.viewport(ib,bb,pb,qb)};this.setScissor=function(a,b,c,d){m.scissor(a*H,b*H,c*H,d*H)};this.enableScissorTest=function(a){a?m.enable(m.SCISSOR_TEST):m.disable(m.SCISSOR_TEST)};this.getClearColor=
+function(){return ha};this.setClearColor=function(a,b){ha.set(a);O=void 0!==b?b:1;rb(ha.r,ha.g,ha.b,O)};this.getClearAlpha=function(){return O};this.setClearAlpha=function(a){O=a;rb(ha.r,ha.g,ha.b,O)};this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=m.COLOR_BUFFER_BIT;if(void 0===b||b)d|=m.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=m.STENCIL_BUFFER_BIT;m.clear(d)};this.clearColor=function(){m.clear(m.COLOR_BUFFER_BIT)};this.clearDepth=function(){m.clear(m.DEPTH_BUFFER_BIT)};this.clearStencil=function(){m.clear(m.STENCIL_BUFFER_BIT)};
+this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.resetGLState=Zb;var wb=function(a){a.target.traverse(function(a){a.removeEventListener("remove",wb);if(a instanceof THREE.Mesh||a instanceof THREE.PointCloud||a instanceof THREE.Line)delete ba[a.id];else if(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)for(var b=qa,c=b.length-1;0<=c;c--)b[c].object===a&&b.splice(c,1);delete a.__webglInit;delete a._modelViewMatrix;delete a._normalMatrix;delete a.__webglActive})},
+jb=function(a){a=a.target;a.removeEventListener("dispose",jb);delete a.__webglInit;if(a instanceof THREE.BufferGeometry){for(var b in a.attributes){var c=a.attributes[b];void 0!==c.buffer&&(m.deleteBuffer(c.buffer),delete c.buffer)}B.info.memory.geometries--}else if(b=Ua[a.id],void 0!==b){for(var c=0,d=b.length;c<d;c++){var e=b[c];if(void 0!==e.numMorphTargets){for(var f=0,g=e.numMorphTargets;f<g;f++)m.deleteBuffer(e.__webglMorphTargetsBuffers[f]);delete e.__webglMorphTargetsBuffers}if(void 0!==e.numMorphNormals){f=
+0;for(g=e.numMorphNormals;f<g;f++)m.deleteBuffer(e.__webglMorphNormalsBuffers[f]);delete e.__webglMorphNormalsBuffers}nc(e)}delete Ua[a.id]}else nc(a);ta=""},Pb=function(a){a=a.target;a.removeEventListener("dispose",Pb);a.image&&a.image.__webglTextureCube?(m.deleteTexture(a.image.__webglTextureCube),delete a.image.__webglTextureCube):void 0!==a.__webglInit&&(m.deleteTexture(a.__webglTexture),delete a.__webglTexture,delete a.__webglInit);B.info.memory.textures--},oc=function(a){a=a.target;a.removeEventListener("dispose",
+oc);if(a&&void 0!==a.__webglTexture){m.deleteTexture(a.__webglTexture);delete a.__webglTexture;if(a instanceof THREE.WebGLRenderTargetCube)for(var b=0;6>b;b++)m.deleteFramebuffer(a.__webglFramebuffer[b]),m.deleteRenderbuffer(a.__webglRenderbuffer[b]);else m.deleteFramebuffer(a.__webglFramebuffer),m.deleteRenderbuffer(a.__webglRenderbuffer);delete a.__webglFramebuffer;delete a.__webglRenderbuffer}B.info.memory.textures--},ic=function(a){a=a.target;a.removeEventListener("dispose",ic);hc(a)},nc=function(a){for(var b=
+"__webglVertexBuffer __webglNormalBuffer __webglTangentBuffer __webglColorBuffer __webglUVBuffer __webglUV2Buffer __webglSkinIndicesBuffer __webglSkinWeightsBuffer __webglFaceBuffer __webglLineBuffer __webglLineDistanceBuffer".split(" "),c=0,d=b.length;c<d;c++){var e=b[c];void 0!==a[e]&&(m.deleteBuffer(a[e]),delete a[e])}if(void 0!==a.__webglCustomAttributesList){for(e in a.__webglCustomAttributesList)m.deleteBuffer(a.__webglCustomAttributesList[e].buffer);delete a.__webglCustomAttributesList}B.info.memory.geometries--},
+hc=function(a){var b=a.program.program;if(void 0!==b){a.program=void 0;var c,d,e=!1;a=0;for(c=Pa.length;a<c;a++)if(d=Pa[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=Pa.length;a<c;a++)d=Pa[a],d.program!==b&&e.push(d);Pa=e;m.deleteProgram(b);B.info.memory.programs--}}};this.renderBufferImmediate=function(a,b,c){W.initAttributes();a.hasPositions&&!a.__webglVertexBuffer&&(a.__webglVertexBuffer=m.createBuffer());a.hasNormals&&!a.__webglNormalBuffer&&(a.__webglNormalBuffer=
+m.createBuffer());a.hasUvs&&!a.__webglUvBuffer&&(a.__webglUvBuffer=m.createBuffer());a.hasColors&&!a.__webglColorBuffer&&(a.__webglColorBuffer=m.createBuffer());a.hasPositions&&(m.bindBuffer(m.ARRAY_BUFFER,a.__webglVertexBuffer),m.bufferData(m.ARRAY_BUFFER,a.positionArray,m.DYNAMIC_DRAW),W.enableAttribute(b.attributes.position),m.vertexAttribPointer(b.attributes.position,3,m.FLOAT,!1,0,0));if(a.hasNormals){m.bindBuffer(m.ARRAY_BUFFER,a.__webglNormalBuffer);if(!1===c instanceof THREE.MeshPhongMaterial&&
+c.shading===THREE.FlatShading){var d,e,f,g,h,k,n,l,p,q,r,s=3*a.count;for(r=0;r<s;r+=9)q=a.normalArray,d=q[r],e=q[r+1],f=q[r+2],g=q[r+3],k=q[r+4],l=q[r+5],h=q[r+6],n=q[r+7],p=q[r+8],d=(d+g+h)/3,e=(e+k+n)/3,f=(f+l+p)/3,q[r]=d,q[r+1]=e,q[r+2]=f,q[r+3]=d,q[r+4]=e,q[r+5]=f,q[r+6]=d,q[r+7]=e,q[r+8]=f}m.bufferData(m.ARRAY_BUFFER,a.normalArray,m.DYNAMIC_DRAW);W.enableAttribute(b.attributes.normal);m.vertexAttribPointer(b.attributes.normal,3,m.FLOAT,!1,0,0)}a.hasUvs&&c.map&&(m.bindBuffer(m.ARRAY_BUFFER,a.__webglUvBuffer),
+m.bufferData(m.ARRAY_BUFFER,a.uvArray,m.DYNAMIC_DRAW),W.enableAttribute(b.attributes.uv),m.vertexAttribPointer(b.attributes.uv,2,m.FLOAT,!1,0,0));a.hasColors&&c.vertexColors!==THREE.NoColors&&(m.bindBuffer(m.ARRAY_BUFFER,a.__webglColorBuffer),m.bufferData(m.ARRAY_BUFFER,a.colorArray,m.DYNAMIC_DRAW),W.enableAttribute(b.attributes.color),m.vertexAttribPointer(b.attributes.color,3,m.FLOAT,!1,0,0));W.disableUnusedAttributes();m.drawArrays(m.TRIANGLES,0,a.count);a.count=0};this.renderBufferDirect=function(a,
+b,c,e,f,g){if(!1!==e.visible)if(t(g),a=v(a,b,c,e,g),b=!1,c="direct_"+f.id+"_"+a.id+"_"+(e.wireframe?1:0),c!==ta&&(ta=c,b=!0),b&&W.initAttributes(),g instanceof THREE.Mesh){g=!0===e.wireframe?m.LINES:m.TRIANGLES;var h=f.attributes.index;if(h){var k,n;h.array instanceof Uint32Array&&da.get("OES_element_index_uint")?(k=m.UNSIGNED_INT,n=4):(k=m.UNSIGNED_SHORT,n=2);c=f.offsets;if(0===c.length)b&&(d(e,a,f,0),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,h.buffer)),m.drawElements(g,h.array.length,k,0),B.info.render.calls++,
+B.info.render.vertices+=h.array.length,B.info.render.faces+=h.array.length/3;else{b=!0;for(var l=0,p=c.length;l<p;l++){var q=c[l].index;b&&(d(e,a,f,q),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,h.buffer));m.drawElements(g,c[l].count,k,c[l].start*n);B.info.render.calls++;B.info.render.vertices+=c[l].count;B.info.render.faces+=c[l].count/3}}}else b&&d(e,a,f,0),e=f.attributes.position,m.drawArrays(g,0,e.array.length/e.itemSize),B.info.render.calls++,B.info.render.vertices+=e.array.length/e.itemSize,B.info.render.faces+=
+e.array.length/(3*e.itemSize)}else if(g instanceof THREE.PointCloud)if(g=m.POINTS,h=f.attributes.index)if(h.array instanceof Uint32Array&&da.get("OES_element_index_uint")?(k=m.UNSIGNED_INT,n=4):(k=m.UNSIGNED_SHORT,n=2),c=f.offsets,0===c.length)b&&(d(e,a,f,0),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,h.buffer)),m.drawElements(g,h.array.length,k,0),B.info.render.calls++,B.info.render.points+=h.array.length;else for(1<c.length&&(b=!0),l=0,p=c.length;l<p;l++)q=c[l].index,b&&(d(e,a,f,q),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,
+h.buffer)),m.drawElements(g,c[l].count,k,c[l].start*n),B.info.render.calls++,B.info.render.points+=c[l].count;else if(b&&d(e,a,f,0),e=f.attributes.position,c=f.offsets,0===c.length)m.drawArrays(g,0,e.array.length/3),B.info.render.calls++,B.info.render.points+=e.array.length/3;else for(l=0,p=c.length;l<p;l++)m.drawArrays(g,c[l].index,c[l].count),B.info.render.calls++,B.info.render.points+=c[l].count;else if(g instanceof THREE.Line)if(g=g.mode===THREE.LineStrip?m.LINE_STRIP:m.LINES,W.setLineWidth(e.linewidth*
+H),h=f.attributes.index)if(h.array instanceof Uint32Array?(k=m.UNSIGNED_INT,n=4):(k=m.UNSIGNED_SHORT,n=2),c=f.offsets,0===c.length)b&&(d(e,a,f,0),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,h.buffer)),m.drawElements(g,h.array.length,k,0),B.info.render.calls++,B.info.render.vertices+=h.array.length;else for(1<c.length&&(b=!0),l=0,p=c.length;l<p;l++)q=c[l].index,b&&(d(e,a,f,q),m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,h.buffer)),m.drawElements(g,c[l].count,k,c[l].start*n),B.info.render.calls++,B.info.render.vertices+=
+c[l].count;else if(b&&d(e,a,f,0),e=f.attributes.position,c=f.offsets,0===c.length)m.drawArrays(g,0,e.array.length/3),B.info.render.calls++,B.info.render.vertices+=e.array.length/3;else for(l=0,p=c.length;l<p;l++)m.drawArrays(g,c[l].index,c[l].count),B.info.render.calls++,B.info.render.vertices+=c[l].count};this.renderBuffer=function(a,b,c,d,e,f){if(!1!==d.visible){t(f);c=v(a,b,c,d,f);b=c.attributes;a=!1;c=e.id+"_"+c.id+"_"+(d.wireframe?1:0);c!==ta&&(ta=c,a=!0);a&&W.initAttributes();if(!d.morphTargets&&
+0<=b.position)a&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglVertexBuffer),W.enableAttribute(b.position),m.vertexAttribPointer(b.position,3,m.FLOAT,!1,0,0));else if(f.morphTargetBase){c=d.program.attributes;-1!==f.morphTargetBase&&0<=c.position?(m.bindBuffer(m.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[f.morphTargetBase]),W.enableAttribute(c.position),m.vertexAttribPointer(c.position,3,m.FLOAT,!1,0,0)):0<=c.position&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglVertexBuffer),W.enableAttribute(c.position),m.vertexAttribPointer(c.position,
+3,m.FLOAT,!1,0,0));if(f.morphTargetForcedOrder.length)for(var h=0,k=f.morphTargetForcedOrder,n=f.morphTargetInfluences,l;h<d.numSupportedMorphTargets&&h<k.length;)l=c["morphTarget"+h],0<=l&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[k[h]]),W.enableAttribute(l),m.vertexAttribPointer(l,3,m.FLOAT,!1,0,0)),l=c["morphNormal"+h],0<=l&&d.morphNormals&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[k[h]]),W.enableAttribute(l),m.vertexAttribPointer(l,3,m.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[h]=
+n[k[h]],h++;else{k=[];n=f.morphTargetInfluences;h=f.geometry.morphTargets;n.length>h.length&&(console.warn("THREE.WebGLRenderer: Influences array is bigger than morphTargets array."),n.length=h.length);h=0;for(l=n.length;h<l;h++)k.push([n[h],h]);k.length>d.numSupportedMorphTargets?(k.sort(g),k.length=d.numSupportedMorphTargets):k.length>d.numSupportedMorphNormals?k.sort(g):0===k.length&&k.push([0,0]);for(var h=0,p=d.numSupportedMorphTargets;h<p;h++)if(k[h]){var q=k[h][1];l=c["morphTarget"+h];0<=l&&
+(m.bindBuffer(m.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[q]),W.enableAttribute(l),m.vertexAttribPointer(l,3,m.FLOAT,!1,0,0));l=c["morphNormal"+h];0<=l&&d.morphNormals&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[q]),W.enableAttribute(l),m.vertexAttribPointer(l,3,m.FLOAT,!1,0,0));f.__webglMorphTargetInfluences[h]=n[q]}else f.__webglMorphTargetInfluences[h]=0}null!==d.program.uniforms.morphTargetInfluences&&m.uniform1fv(d.program.uniforms.morphTargetInfluences,f.__webglMorphTargetInfluences)}if(a){if(e.__webglCustomAttributesList)for(c=
+0,n=e.__webglCustomAttributesList.length;c<n;c++)k=e.__webglCustomAttributesList[c],0<=b[k.buffer.belongsToAttribute]&&(m.bindBuffer(m.ARRAY_BUFFER,k.buffer),W.enableAttribute(b[k.buffer.belongsToAttribute]),m.vertexAttribPointer(b[k.buffer.belongsToAttribute],k.size,m.FLOAT,!1,0,0));0<=b.color&&(0<f.geometry.colors.length||0<f.geometry.faces.length?(m.bindBuffer(m.ARRAY_BUFFER,e.__webglColorBuffer),W.enableAttribute(b.color),m.vertexAttribPointer(b.color,3,m.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&
+m.vertexAttrib3fv(b.color,d.defaultAttributeValues.color));0<=b.normal&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglNormalBuffer),W.enableAttribute(b.normal),m.vertexAttribPointer(b.normal,3,m.FLOAT,!1,0,0));0<=b.tangent&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglTangentBuffer),W.enableAttribute(b.tangent),m.vertexAttribPointer(b.tangent,4,m.FLOAT,!1,0,0));0<=b.uv&&(f.geometry.faceVertexUvs[0]?(m.bindBuffer(m.ARRAY_BUFFER,e.__webglUVBuffer),W.enableAttribute(b.uv),m.vertexAttribPointer(b.uv,2,m.FLOAT,!1,0,
+0)):void 0!==d.defaultAttributeValues&&m.vertexAttrib2fv(b.uv,d.defaultAttributeValues.uv));0<=b.uv2&&(f.geometry.faceVertexUvs[1]?(m.bindBuffer(m.ARRAY_BUFFER,e.__webglUV2Buffer),W.enableAttribute(b.uv2),m.vertexAttribPointer(b.uv2,2,m.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&m.vertexAttrib2fv(b.uv2,d.defaultAttributeValues.uv2));d.skinning&&0<=b.skinIndex&&0<=b.skinWeight&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglSkinIndicesBuffer),W.enableAttribute(b.skinIndex),m.vertexAttribPointer(b.skinIndex,
+4,m.FLOAT,!1,0,0),m.bindBuffer(m.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),W.enableAttribute(b.skinWeight),m.vertexAttribPointer(b.skinWeight,4,m.FLOAT,!1,0,0));0<=b.lineDistance&&(m.bindBuffer(m.ARRAY_BUFFER,e.__webglLineDistanceBuffer),W.enableAttribute(b.lineDistance),m.vertexAttribPointer(b.lineDistance,1,m.FLOAT,!1,0,0))}W.disableUnusedAttributes();f instanceof THREE.Mesh?(f=e.__typeArray===Uint32Array?m.UNSIGNED_INT:m.UNSIGNED_SHORT,d.wireframe?(W.setLineWidth(d.wireframeLinewidth*H),a&&m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,
+e.__webglLineBuffer),m.drawElements(m.LINES,e.__webglLineCount,f,0)):(a&&m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,e.__webglFaceBuffer),m.drawElements(m.TRIANGLES,e.__webglFaceCount,f,0)),B.info.render.calls++,B.info.render.vertices+=e.__webglFaceCount,B.info.render.faces+=e.__webglFaceCount/3):f instanceof THREE.Line?(f=f.mode===THREE.LineStrip?m.LINE_STRIP:m.LINES,W.setLineWidth(d.linewidth*H),m.drawArrays(f,0,e.__webglLineCount),B.info.render.calls++):f instanceof THREE.PointCloud&&(m.drawArrays(m.POINTS,
+0,e.__webglParticleCount),B.info.render.calls++,B.info.render.points+=e.__webglParticleCount)}};this.render=function(a,b,c,d){if(!1===b instanceof THREE.Camera)THREE.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");else{var g=a.fog;ta="";ub=-1;vb=null;Ob=!0;!0===a.autoUpdate&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();a.traverse(function(a){a instanceof THREE.SkinnedMesh&&a.skeleton.update()});b.matrixWorldInverse.getInverse(b.matrixWorld);db.multiplyMatrices(b.projectionMatrix,
+b.matrixWorldInverse);cb.setFromMatrix(db);ca.length=0;Ka.length=0;Qa.length=0;Xa.length=0;Ya.length=0;h(a);!0===B.sortObjects&&(Ka.sort(e),Qa.sort(f));zc.render(a,b);B.info.render.calls=0;B.info.render.vertices=0;B.info.render.faces=0;B.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil);d=0;for(var n=qa.length;d<n;d++){var m=qa[d],q=m.object;q.visible&&(w(q,b),p(m))}a.overrideMaterial?(d=a.overrideMaterial,u(d),
+k(Ka,b,ca,g,d),k(Qa,b,ca,g,d),l(qa,"",b,ca,g,d)):(W.setBlending(THREE.NoBlending),k(Ka,b,ca,g,null),l(qa,"opaque",b,ca,g,null),k(Qa,b,ca,g,null),l(qa,"transparent",b,ca,g,null));Ac.render(a,b);Bc.render(a,b,Xb,fc);c&&c.generateMipmaps&&c.minFilter!==THREE.NearestFilter&&c.minFilter!==THREE.LinearFilter&&F(c);W.setDepthTest(!0);W.setDepthWrite(!0);W.setColorWrite(!0)}};this.renderImmediateObject=function(a,b,c,d,e){var f=v(a,b,c,d,e);ta="";B.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,
+m,cb):e.render(function(a){B.renderBufferImmediate(a,f,d)})};var Ua={},Qb=0,pc={MeshDepthMaterial:"depth",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointCloudMaterial:"particle_basic"};this.setFaceCulling=function(a,b){a===THREE.CullFaceNone?m.disable(m.CULL_FACE):(b===THREE.FrontFaceDirectionCW?m.frontFace(m.CW):m.frontFace(m.CCW),a===THREE.CullFaceBack?m.cullFace(m.BACK):a===
+THREE.CullFaceFront?m.cullFace(m.FRONT):m.cullFace(m.FRONT_AND_BACK),m.enable(m.CULL_FACE))};this.setMaterialFaces=function(a){W.setDoubleSided(a.side===THREE.DoubleSide);W.setFlipSided(a.side===THREE.BackSide)};this.uploadTexture=function(a){void 0===a.__webglInit&&(a.__webglInit=!0,a.addEventListener("dispose",Pb),a.__webglTexture=m.createTexture(),B.info.memory.textures++);m.bindTexture(m.TEXTURE_2D,a.__webglTexture);m.pixelStorei(m.UNPACK_FLIP_Y_WEBGL,a.flipY);m.pixelStorei(m.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
+a.premultiplyAlpha);m.pixelStorei(m.UNPACK_ALIGNMENT,a.unpackAlignment);a.image=E(a.image,tc);var b=a.image,c=THREE.Math.isPowerOfTwo(b.width)&&THREE.Math.isPowerOfTwo(b.height),d=I(a.format),e=I(a.type);A(m.TEXTURE_2D,a,c);var f=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<f.length&&c){for(var g=0,h=f.length;g<h;g++)b=f[g],m.texImage2D(m.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);a.generateMipmaps=!1}else m.texImage2D(m.TEXTURE_2D,0,d,b.width,b.height,0,d,e,b.data);else if(a instanceof THREE.CompressedTexture)for(g=
+0,h=f.length;g<h;g++)b=f[g],a.format!==THREE.RGBAFormat&&a.format!==THREE.RGBFormat?-1<kc().indexOf(d)?m.compressedTexImage2D(m.TEXTURE_2D,g,d,b.width,b.height,0,b.data):THREE.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):m.texImage2D(m.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);else if(0<f.length&&c){g=0;for(h=f.length;g<h;g++)b=f[g],m.texImage2D(m.TEXTURE_2D,g,d,d,e,b);a.generateMipmaps=!1}else m.texImage2D(m.TEXTURE_2D,0,d,d,e,a.image);
+a.generateMipmaps&&c&&m.generateMipmap(m.TEXTURE_2D);a.needsUpdate=!1;if(a.onUpdate)a.onUpdate()};this.setTexture=function(a,b){m.activeTexture(m.TEXTURE0+b);a.needsUpdate?B.uploadTexture(a):m.bindTexture(m.TEXTURE_2D,a.__webglTexture)};this.setRenderTarget=function(a){var b=a instanceof THREE.WebGLRenderTargetCube;if(a&&void 0===a.__webglFramebuffer){void 0===a.depthBuffer&&(a.depthBuffer=!0);void 0===a.stencilBuffer&&(a.stencilBuffer=!0);a.addEventListener("dispose",oc);a.__webglTexture=m.createTexture();
+B.info.memory.textures++;var c=THREE.Math.isPowerOfTwo(a.width)&&THREE.Math.isPowerOfTwo(a.height),d=I(a.format),e=I(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];m.bindTexture(m.TEXTURE_CUBE_MAP,a.__webglTexture);A(m.TEXTURE_CUBE_MAP,a,c);for(var f=0;6>f;f++){a.__webglFramebuffer[f]=m.createFramebuffer();a.__webglRenderbuffer[f]=m.createRenderbuffer();m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var g=a,h=m.TEXTURE_CUBE_MAP_POSITIVE_X+f;m.bindFramebuffer(m.FRAMEBUFFER,
+a.__webglFramebuffer[f]);m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,h,g.__webglTexture,0);G(a.__webglRenderbuffer[f],a)}c&&m.generateMipmap(m.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=m.createFramebuffer(),a.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:m.createRenderbuffer(),m.bindTexture(m.TEXTURE_2D,a.__webglTexture),A(m.TEXTURE_2D,a,c),m.texImage2D(m.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=m.TEXTURE_2D,m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer),
+m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_STENCIL_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer):G(a.__webglRenderbuffer,a),c&&m.generateMipmap(m.TEXTURE_2D);b?m.bindTexture(m.TEXTURE_CUBE_MAP,null):m.bindTexture(m.TEXTURE_2D,null);m.bindRenderbuffer(m.RENDERBUFFER,
+null);m.bindFramebuffer(m.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=pb,a=qb,d=ib,e=bb);b!==ab&&(m.bindFramebuffer(m.FRAMEBUFFER,b),m.viewport(d,e,c,a),ab=b);Xb=c;fc=a};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(!(a instanceof THREE.WebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else if(a.__webglFramebuffer)if(a.format!==THREE.RGBAFormat)console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.");
+else{var g=!1;a.__webglFramebuffer!==ab&&(m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer),g=!0);m.checkFramebufferStatus(m.FRAMEBUFFER)===m.FRAMEBUFFER_COMPLETE?m.readPixels(b,c,d,e,m.RGBA,m.UNSIGNED_BYTE,f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.");g&&m.bindFramebuffer(m.FRAMEBUFFER,ab)}};this.initMaterial=function(){THREE.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};this.addPrePlugin=function(){THREE.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};
+this.addPostPlugin=function(){THREE.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};this.updateShadowMap=function(){THREE.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")}};
+THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
+THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=void 0!==c.shareDepthFrom?c.shareDepthFrom:null};
+THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){this.width=a;this.height=b},clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;
+a.shareDepthFrom=this.shareDepthFrom;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube.prototype.constructor=THREE.WebGLRenderTargetCube;
+THREE.WebGLExtensions=function(a){var b={};this.get=function(c){if(void 0!==b[c])return b[c];var d;switch(c){case "EXT_texture_filter_anisotropic":d=a.getExtension("EXT_texture_filter_anisotropic")||a.getExtension("MOZ_EXT_texture_filter_anisotropic")||a.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case "WEBGL_compressed_texture_s3tc":d=a.getExtension("WEBGL_compressed_texture_s3tc")||a.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");
+break;case "WEBGL_compressed_texture_pvrtc":d=a.getExtension("WEBGL_compressed_texture_pvrtc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:d=a.getExtension(c)}null===d&&THREE.warn("THREE.WebGLRenderer: "+c+" extension not supported.");return b[c]=d}};
+THREE.WebGLProgram=function(){var a=0;return function(b,c,d,e){var f=b.context,g=d.defines,h=d.__webglShader.uniforms,k=d.attributes,l=d.__webglShader.vertexShader,p=d.__webglShader.fragmentShader,q=d.index0AttributeName;void 0===q&&!0===e.morphTargets&&(q="position");var n="SHADOWMAP_TYPE_BASIC";e.shadowMapType===THREE.PCFShadowMap?n="SHADOWMAP_TYPE_PCF":e.shadowMapType===THREE.PCFSoftShadowMap&&(n="SHADOWMAP_TYPE_PCF_SOFT");var t="ENVMAP_TYPE_CUBE",r="ENVMAP_MODE_REFLECTION",s="ENVMAP_BLENDING_MULTIPLY";
+if(e.envMap){switch(d.envMap.mapping){case THREE.CubeReflectionMapping:case THREE.CubeRefractionMapping:t="ENVMAP_TYPE_CUBE";break;case THREE.EquirectangularReflectionMapping:case THREE.EquirectangularRefractionMapping:t="ENVMAP_TYPE_EQUIREC";break;case THREE.SphericalReflectionMapping:t="ENVMAP_TYPE_SPHERE"}switch(d.envMap.mapping){case THREE.CubeRefractionMapping:case THREE.EquirectangularRefractionMapping:r="ENVMAP_MODE_REFRACTION"}switch(d.combine){case THREE.MultiplyOperation:s="ENVMAP_BLENDING_MULTIPLY";
+break;case THREE.MixOperation:s="ENVMAP_BLENDING_MIX";break;case THREE.AddOperation:s="ENVMAP_BLENDING_ADD"}}var u=0<b.gammaFactor?b.gammaFactor:1,v,x;v=[];for(var D in g)x=g[D],!1!==x&&(x="#define "+D+" "+x,v.push(x));v=v.join("\n");g=f.createProgram();d instanceof THREE.RawShaderMaterial?b=d="":(d=["precision "+e.precision+" float;","precision "+e.precision+" int;",v,e.supportsVertexTextures?"#define VERTEX_TEXTURES":"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":
+"","#define GAMMA_FACTOR "+u,"#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,"#define MAX_BONES "+e.maxBones,e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+r:"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":
+"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.flatShading?"#define FLAT_SHADED":"",e.skinning?"#define USE_SKINNING":"",e.useVertexTexture?"#define BONE_TEXTURE":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals?"#define USE_MORPHNORMALS":"",e.wrapAround?"#define WRAP_AROUND":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+n:"",e.shadowMapDebug?
+"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\n\tattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\n\tattribute vec3 morphTarget0;\n\tattribute vec3 morphTarget1;\n\tattribute vec3 morphTarget2;\n\tattribute vec3 morphTarget3;\n\t#ifdef USE_MORPHNORMALS\n\t\tattribute vec3 morphNormal0;\n\t\tattribute vec3 morphNormal1;\n\t\tattribute vec3 morphNormal2;\n\t\tattribute vec3 morphNormal3;\n\t#else\n\t\tattribute vec3 morphTarget4;\n\t\tattribute vec3 morphTarget5;\n\t\tattribute vec3 morphTarget6;\n\t\tattribute vec3 morphTarget7;\n\t#endif\n#endif\n#ifdef USE_SKINNING\n\tattribute vec4 skinIndex;\n\tattribute vec4 skinWeight;\n#endif\n"].join("\n"),
+b=["precision "+e.precision+" float;","precision "+e.precision+" int;",e.bumpMap||e.normalMap||e.flatShading?"#extension GL_OES_standard_derivatives : enable":"",v,"#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,e.alphaTest?"#define ALPHATEST "+e.alphaTest:"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+
+u,e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+t:"",e.envMap?"#define "+r:"",e.envMap?"#define "+s:"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.flatShading?"#define FLAT_SHADED":"",e.metal?"#define METAL":
+"",e.wrapAround?"#define WRAP_AROUND":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+n:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n"));l=new THREE.WebGLShader(f,f.VERTEX_SHADER,d+l);p=new THREE.WebGLShader(f,f.FRAGMENT_SHADER,
+b+p);f.attachShader(g,l);f.attachShader(g,p);void 0!==q&&f.bindAttribLocation(g,0,q);f.linkProgram(g);q=f.getProgramInfoLog(g);!1===f.getProgramParameter(g,f.LINK_STATUS)&&THREE.error("THREE.WebGLProgram: shader error: "+f.getError(),"gl.VALIDATE_STATUS",f.getProgramParameter(g,f.VALIDATE_STATUS),"gl.getPRogramInfoLog",q);""!==q&&THREE.warn("THREE.WebGLProgram: gl.getProgramInfoLog()"+q);f.deleteShader(l);f.deleteShader(p);q="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences bindMatrix bindMatrixInverse".split(" ");
+e.useVertexTexture?(q.push("boneTexture"),q.push("boneTextureWidth"),q.push("boneTextureHeight")):q.push("boneGlobalMatrices");e.logarithmicDepthBuffer&&q.push("logDepthBufFC");for(var w in h)q.push(w);h=q;w={};q=0;for(b=h.length;q<b;q++)n=h[q],w[n]=f.getUniformLocation(g,n);this.uniforms=w;q="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(h=0;h<e.maxMorphTargets;h++)q.push("morphTarget"+h);for(h=0;h<e.maxMorphNormals;h++)q.push("morphNormal"+h);for(var y in k)q.push(y);
+e=q;k={};y=0;for(h=e.length;y<h;y++)w=e[y],k[w]=f.getAttribLocation(g,w);this.attributes=k;this.attributesKeys=Object.keys(this.attributes);this.id=a++;this.code=c;this.usedTimes=1;this.program=g;this.vertexShader=l;this.fragmentShader=p;return this}}();
+THREE.WebGLShader=function(){var a=function(a){a=a.split("\n");for(var c=0;c<a.length;c++)a[c]=c+1+": "+a[c];return a.join("\n")};return function(b,c,d){c=b.createShader(c);b.shaderSource(c,d);b.compileShader(c);!1===b.getShaderParameter(c,b.COMPILE_STATUS)&&THREE.error("THREE.WebGLShader: Shader couldn't compile.");""!==b.getShaderInfoLog(c)&&THREE.warn("THREE.WebGLShader: gl.getShaderInfoLog()",b.getShaderInfoLog(c),a(d));return c}}();
+THREE.WebGLState=function(a,b){var c=new Uint8Array(16),d=new Uint8Array(16),e=null,f=null,g=null,h=null,k=null,l=null,p=null,q=null,n=null,t=null,r=null,s=null,u=null,v=null,x=null,D=null;this.initAttributes=function(){for(var a=0,b=c.length;a<b;a++)c[a]=0};this.enableAttribute=function(b){c[b]=1;0===d[b]&&(a.enableVertexAttribArray(b),d[b]=1)};this.disableUnusedAttributes=function(){for(var b=0,e=d.length;b<e;b++)d[b]!==c[b]&&(a.disableVertexAttribArray(b),d[b]=0)};this.setBlending=function(c,d,
+n,q,r,s,t){c!==e&&(c===THREE.NoBlending?a.disable(a.BLEND):c===THREE.AdditiveBlending?(a.enable(a.BLEND),a.blendEquation(a.FUNC_ADD),a.blendFunc(a.SRC_ALPHA,a.ONE)):c===THREE.SubtractiveBlending?(a.enable(a.BLEND),a.blendEquation(a.FUNC_ADD),a.blendFunc(a.ZERO,a.ONE_MINUS_SRC_COLOR)):c===THREE.MultiplyBlending?(a.enable(a.BLEND),a.blendEquation(a.FUNC_ADD),a.blendFunc(a.ZERO,a.SRC_COLOR)):c===THREE.CustomBlending?a.enable(a.BLEND):(a.enable(a.BLEND),a.blendEquationSeparate(a.FUNC_ADD,a.FUNC_ADD),
+a.blendFuncSeparate(a.SRC_ALPHA,a.ONE_MINUS_SRC_ALPHA,a.ONE,a.ONE_MINUS_SRC_ALPHA)),e=c);if(c===THREE.CustomBlending){r=r||d;s=s||n;t=t||q;if(d!==f||r!==k)a.blendEquationSeparate(b(d),b(r)),f=d,k=r;if(n!==g||q!==h||s!==l||t!==p)a.blendFuncSeparate(b(n),b(q),b(s),b(t)),g=n,h=q,l=s,p=t}else p=l=k=h=g=f=null};this.setDepthTest=function(b){q!==b&&(b?a.enable(a.DEPTH_TEST):a.disable(a.DEPTH_TEST),q=b)};this.setDepthWrite=function(b){n!==b&&(a.depthMask(b),n=b)};this.setColorWrite=function(b){t!==b&&(a.colorMask(b,
+b,b,b),t=b)};this.setDoubleSided=function(b){r!==b&&(b?a.disable(a.CULL_FACE):a.enable(a.CULL_FACE),r=b)};this.setFlipSided=function(b){s!==b&&(b?a.frontFace(a.CW):a.frontFace(a.CCW),s=b)};this.setLineWidth=function(b){b!==u&&(a.lineWidth(b),u=b)};this.setPolygonOffset=function(b,c,d){v!==b&&(b?a.enable(a.POLYGON_OFFSET_FILL):a.disable(a.POLYGON_OFFSET_FILL),v=b);!b||x===c&&D===d||(a.polygonOffset(c,d),x=c,D=d)};this.reset=function(){for(var a=0;a<d.length;a++)d[a]=0;s=r=t=n=q=e=null}};
+THREE.LensFlarePlugin=function(a,b){var c,d,e,f,g,h,k,l,p,q,n=a.context,t,r,s,u,v,x;this.render=function(D,w,y,A){if(0!==b.length){D=new THREE.Vector3;var E=A/y,G=.5*y,F=.5*A,z=16/A,I=new THREE.Vector2(z*E,z),U=new THREE.Vector3(1,1,0),M=new THREE.Vector2(1,1);if(void 0===s){var z=new Float32Array([-1,-1,0,0,1,-1,1,0,1,1,1,1,-1,1,0,1]),H=new Uint16Array([0,1,2,0,2,3]);t=n.createBuffer();r=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,t);n.bufferData(n.ARRAY_BUFFER,z,n.STATIC_DRAW);n.bindBuffer(n.ELEMENT_ARRAY_BUFFER,
+r);n.bufferData(n.ELEMENT_ARRAY_BUFFER,H,n.STATIC_DRAW);v=n.createTexture();x=n.createTexture();n.bindTexture(n.TEXTURE_2D,v);n.texImage2D(n.TEXTURE_2D,0,n.RGB,16,16,0,n.RGB,n.UNSIGNED_BYTE,null);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.NEAREST);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.NEAREST);n.bindTexture(n.TEXTURE_2D,x);n.texImage2D(n.TEXTURE_2D,0,
+n.RGBA,16,16,0,n.RGBA,n.UNSIGNED_BYTE,null);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.NEAREST);n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.NEAREST);var z=(u=0<n.getParameter(n.MAX_VERTEX_TEXTURE_IMAGE_UNITS))?{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility =        visibility.r / 9.0;\nvVisibility *= 1.0 - visibility.g / 9.0;\nvVisibility *=       visibility.b / 9.0;\nvVisibility *= 1.0 - visibility.a / 9.0;\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
+fragmentShader:"uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"}:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
+fragmentShader:"precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"},
+H=n.createProgram(),L=n.createShader(n.FRAGMENT_SHADER),P=n.createShader(n.VERTEX_SHADER),N="precision "+a.getPrecision()+" float;\n";n.shaderSource(L,N+z.fragmentShader);n.shaderSource(P,N+z.vertexShader);n.compileShader(L);n.compileShader(P);n.attachShader(H,L);n.attachShader(H,P);n.linkProgram(H);s=H;p=n.getAttribLocation(s,"position");q=n.getAttribLocation(s,"uv");c=n.getUniformLocation(s,"renderType");d=n.getUniformLocation(s,"map");e=n.getUniformLocation(s,"occlusionMap");f=n.getUniformLocation(s,
+"opacity");g=n.getUniformLocation(s,"color");h=n.getUniformLocation(s,"scale");k=n.getUniformLocation(s,"rotation");l=n.getUniformLocation(s,"screenPosition")}n.useProgram(s);n.enableVertexAttribArray(p);n.enableVertexAttribArray(q);n.uniform1i(e,0);n.uniform1i(d,1);n.bindBuffer(n.ARRAY_BUFFER,t);n.vertexAttribPointer(p,2,n.FLOAT,!1,16,0);n.vertexAttribPointer(q,2,n.FLOAT,!1,16,8);n.bindBuffer(n.ELEMENT_ARRAY_BUFFER,r);n.disable(n.CULL_FACE);n.depthMask(!1);H=0;for(L=b.length;H<L;H++)if(z=16/A,I.set(z*
+E,z),P=b[H],D.set(P.matrixWorld.elements[12],P.matrixWorld.elements[13],P.matrixWorld.elements[14]),D.applyMatrix4(w.matrixWorldInverse),D.applyProjection(w.projectionMatrix),U.copy(D),M.x=U.x*G+G,M.y=U.y*F+F,u||0<M.x&&M.x<y&&0<M.y&&M.y<A){n.activeTexture(n.TEXTURE1);n.bindTexture(n.TEXTURE_2D,v);n.copyTexImage2D(n.TEXTURE_2D,0,n.RGB,M.x-8,M.y-8,16,16,0);n.uniform1i(c,0);n.uniform2f(h,I.x,I.y);n.uniform3f(l,U.x,U.y,U.z);n.disable(n.BLEND);n.enable(n.DEPTH_TEST);n.drawElements(n.TRIANGLES,6,n.UNSIGNED_SHORT,
+0);n.activeTexture(n.TEXTURE0);n.bindTexture(n.TEXTURE_2D,x);n.copyTexImage2D(n.TEXTURE_2D,0,n.RGBA,M.x-8,M.y-8,16,16,0);n.uniform1i(c,1);n.disable(n.DEPTH_TEST);n.activeTexture(n.TEXTURE1);n.bindTexture(n.TEXTURE_2D,v);n.drawElements(n.TRIANGLES,6,n.UNSIGNED_SHORT,0);P.positionScreen.copy(U);P.customUpdateCallback?P.customUpdateCallback(P):P.updateLensFlares();n.uniform1i(c,2);n.enable(n.BLEND);for(var N=0,R=P.lensFlares.length;N<R;N++){var V=P.lensFlares[N];.001<V.opacity&&.001<V.scale&&(U.x=V.x,
+U.y=V.y,U.z=V.z,z=V.size*V.scale/A,I.x=z*E,I.y=z,n.uniform3f(l,U.x,U.y,U.z),n.uniform2f(h,I.x,I.y),n.uniform1f(k,V.rotation),n.uniform1f(f,V.opacity),n.uniform3f(g,V.color.r,V.color.g,V.color.b),a.state.setBlending(V.blending,V.blendEquation,V.blendSrc,V.blendDst),a.setTexture(V.texture,1),n.drawElements(n.TRIANGLES,6,n.UNSIGNED_SHORT,0))}}n.enable(n.CULL_FACE);n.enable(n.DEPTH_TEST);n.depthMask(!0);a.resetGLState()}}};
+THREE.ShadowMapPlugin=function(a,b,c,d){function e(a,b,d){if(b.visible){var f=c[b.id];if(f&&b.castShadow&&(!1===b.frustumCulled||!0===p.intersectsObject(b)))for(var g=0,h=f.length;g<h;g++){var k=f[g];b._modelViewMatrix.multiplyMatrices(d.matrixWorldInverse,b.matrixWorld);s.push(k)}g=0;for(h=b.children.length;g<h;g++)e(a,b.children[g],d)}}var f=a.context,g,h,k,l,p=new THREE.Frustum,q=new THREE.Matrix4,n=new THREE.Vector3,t=new THREE.Vector3,r=new THREE.Vector3,s=[],u=THREE.ShaderLib.depthRGBA,v=THREE.UniformsUtils.clone(u.uniforms);
+g=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader});h=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0});k=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,skinning:!0});l=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0,skinning:!0});g._shadowPass=!0;h._shadowPass=!0;k._shadowPass=
+!0;l._shadowPass=!0;this.render=function(c,v){if(!1!==a.shadowMapEnabled){var u,y,A,E,G,F,z,I,U=[];E=0;f.clearColor(1,1,1,1);f.disable(f.BLEND);f.enable(f.CULL_FACE);f.frontFace(f.CCW);a.shadowMapCullFace===THREE.CullFaceFront?f.cullFace(f.FRONT):f.cullFace(f.BACK);a.state.setDepthTest(!0);u=0;for(y=b.length;u<y;u++)if(A=b[u],A.castShadow)if(A instanceof THREE.DirectionalLight&&A.shadowCascade)for(G=0;G<A.shadowCascadeCount;G++){var M;if(A.shadowCascadeArray[G])M=A.shadowCascadeArray[G];else{z=A;
+var H=G;M=new THREE.DirectionalLight;M.isVirtual=!0;M.onlyShadow=!0;M.castShadow=!0;M.shadowCameraNear=z.shadowCameraNear;M.shadowCameraFar=z.shadowCameraFar;M.shadowCameraLeft=z.shadowCameraLeft;M.shadowCameraRight=z.shadowCameraRight;M.shadowCameraBottom=z.shadowCameraBottom;M.shadowCameraTop=z.shadowCameraTop;M.shadowCameraVisible=z.shadowCameraVisible;M.shadowDarkness=z.shadowDarkness;M.shadowBias=z.shadowCascadeBias[H];M.shadowMapWidth=z.shadowCascadeWidth[H];M.shadowMapHeight=z.shadowCascadeHeight[H];
+M.pointsWorld=[];M.pointsFrustum=[];I=M.pointsWorld;F=M.pointsFrustum;for(var L=0;8>L;L++)I[L]=new THREE.Vector3,F[L]=new THREE.Vector3;I=z.shadowCascadeNearZ[H];z=z.shadowCascadeFarZ[H];F[0].set(-1,-1,I);F[1].set(1,-1,I);F[2].set(-1,1,I);F[3].set(1,1,I);F[4].set(-1,-1,z);F[5].set(1,-1,z);F[6].set(-1,1,z);F[7].set(1,1,z);M.originalCamera=v;F=new THREE.Gyroscope;F.position.copy(A.shadowCascadeOffset);F.add(M);F.add(M.target);v.add(F);A.shadowCascadeArray[G]=M}H=A;I=G;z=H.shadowCascadeArray[I];z.position.copy(H.position);
+z.target.position.copy(H.target.position);z.lookAt(z.target);z.shadowCameraVisible=H.shadowCameraVisible;z.shadowDarkness=H.shadowDarkness;z.shadowBias=H.shadowCascadeBias[I];F=H.shadowCascadeNearZ[I];H=H.shadowCascadeFarZ[I];z=z.pointsFrustum;z[0].z=F;z[1].z=F;z[2].z=F;z[3].z=F;z[4].z=H;z[5].z=H;z[6].z=H;z[7].z=H;U[E]=M;E++}else U[E]=A,E++;u=0;for(y=U.length;u<y;u++){A=U[u];A.shadowMap||(G=THREE.LinearFilter,a.shadowMapType===THREE.PCFSoftShadowMap&&(G=THREE.NearestFilter),A.shadowMap=new THREE.WebGLRenderTarget(A.shadowMapWidth,
+A.shadowMapHeight,{minFilter:G,magFilter:G,format:THREE.RGBAFormat}),A.shadowMapSize=new THREE.Vector2(A.shadowMapWidth,A.shadowMapHeight),A.shadowMatrix=new THREE.Matrix4);if(!A.shadowCamera){if(A instanceof THREE.SpotLight)A.shadowCamera=new THREE.PerspectiveCamera(A.shadowCameraFov,A.shadowMapWidth/A.shadowMapHeight,A.shadowCameraNear,A.shadowCameraFar);else if(A instanceof THREE.DirectionalLight)A.shadowCamera=new THREE.OrthographicCamera(A.shadowCameraLeft,A.shadowCameraRight,A.shadowCameraTop,
+A.shadowCameraBottom,A.shadowCameraNear,A.shadowCameraFar);else{THREE.error("THREE.ShadowMapPlugin: Unsupported light type for shadow",A);continue}c.add(A.shadowCamera);!0===c.autoUpdate&&c.updateMatrixWorld()}A.shadowCameraVisible&&!A.cameraHelper&&(A.cameraHelper=new THREE.CameraHelper(A.shadowCamera),c.add(A.cameraHelper));if(A.isVirtual&&M.originalCamera==v){G=v;E=A.shadowCamera;F=A.pointsFrustum;z=A.pointsWorld;n.set(Infinity,Infinity,Infinity);t.set(-Infinity,-Infinity,-Infinity);for(H=0;8>
+H;H++)I=z[H],I.copy(F[H]),I.unproject(G),I.applyMatrix4(E.matrixWorldInverse),I.x<n.x&&(n.x=I.x),I.x>t.x&&(t.x=I.x),I.y<n.y&&(n.y=I.y),I.y>t.y&&(t.y=I.y),I.z<n.z&&(n.z=I.z),I.z>t.z&&(t.z=I.z);E.left=n.x;E.right=t.x;E.top=t.y;E.bottom=n.y;E.updateProjectionMatrix()}E=A.shadowMap;F=A.shadowMatrix;G=A.shadowCamera;G.position.setFromMatrixPosition(A.matrixWorld);r.setFromMatrixPosition(A.target.matrixWorld);G.lookAt(r);G.updateMatrixWorld();G.matrixWorldInverse.getInverse(G.matrixWorld);A.cameraHelper&&
+(A.cameraHelper.visible=A.shadowCameraVisible);A.shadowCameraVisible&&A.cameraHelper.update();F.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1);F.multiply(G.projectionMatrix);F.multiply(G.matrixWorldInverse);q.multiplyMatrices(G.projectionMatrix,G.matrixWorldInverse);p.setFromMatrix(q);a.setRenderTarget(E);a.clear();s.length=0;e(c,c,G);A=0;for(E=s.length;A<E;A++)z=s[A],F=z.object,z=z.buffer,H=F.material instanceof THREE.MeshFaceMaterial?F.material.materials[0]:F.material,I=void 0!==F.geometry.morphTargets&&
+0<F.geometry.morphTargets.length&&H.morphTargets,L=F instanceof THREE.SkinnedMesh&&H.skinning,I=F.customDepthMaterial?F.customDepthMaterial:L?I?l:k:I?h:g,a.setMaterialFaces(H),z instanceof THREE.BufferGeometry?a.renderBufferDirect(G,b,null,I,z,F):a.renderBuffer(G,b,null,I,z,F);A=0;for(E=d.length;A<E;A++)z=d[A],F=z.object,F.visible&&F.castShadow&&(F._modelViewMatrix.multiplyMatrices(G.matrixWorldInverse,F.matrixWorld),a.renderImmediateObject(G,b,null,g,F))}u=a.getClearColor();y=a.getClearAlpha();f.clearColor(u.r,
+u.g,u.b,y);f.enable(f.BLEND);a.shadowMapCullFace===THREE.CullFaceFront&&f.cullFace(f.BACK);a.resetGLState()}}};
+THREE.SpritePlugin=function(a,b){var c,d,e,f,g,h,k,l,p,q,n,t,r,s,u,v,x;function D(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var w=a.context,y,A,E,G,F=new THREE.Vector3,z=new THREE.Quaternion,I=new THREE.Vector3;this.render=function(U,M){if(0!==b.length){if(void 0===E){var H=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),L=new Uint16Array([0,1,2,0,2,3]);y=w.createBuffer();A=w.createBuffer();w.bindBuffer(w.ARRAY_BUFFER,y);w.bufferData(w.ARRAY_BUFFER,H,w.STATIC_DRAW);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,
+A);w.bufferData(w.ELEMENT_ARRAY_BUFFER,L,w.STATIC_DRAW);var H=w.createProgram(),L=w.createShader(w.VERTEX_SHADER),P=w.createShader(w.FRAGMENT_SHADER);w.shaderSource(L,["precision "+a.getPrecision()+" float;","uniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = position * scale;\nvec2 rotatedPosition;\nrotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\nrotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\nvec4 finalPosition;\nfinalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition;\nfinalPosition = projectionMatrix * finalPosition;\ngl_Position = finalPosition;\n}"].join("\n"));
+w.shaderSource(P,["precision "+a.getPrecision()+" float;","uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n"));
+w.compileShader(L);w.compileShader(P);w.attachShader(H,L);w.attachShader(H,P);w.linkProgram(H);E=H;v=w.getAttribLocation(E,"position");x=w.getAttribLocation(E,"uv");c=w.getUniformLocation(E,"uvOffset");d=w.getUniformLocation(E,"uvScale");e=w.getUniformLocation(E,"rotation");f=w.getUniformLocation(E,"scale");g=w.getUniformLocation(E,"color");h=w.getUniformLocation(E,"map");k=w.getUniformLocation(E,"opacity");l=w.getUniformLocation(E,"modelViewMatrix");p=w.getUniformLocation(E,"projectionMatrix");q=
+w.getUniformLocation(E,"fogType");n=w.getUniformLocation(E,"fogDensity");t=w.getUniformLocation(E,"fogNear");r=w.getUniformLocation(E,"fogFar");s=w.getUniformLocation(E,"fogColor");u=w.getUniformLocation(E,"alphaTest");H=document.createElement("canvas");H.width=8;H.height=8;L=H.getContext("2d");L.fillStyle="white";L.fillRect(0,0,8,8);G=new THREE.Texture(H);G.needsUpdate=!0}w.useProgram(E);w.enableVertexAttribArray(v);w.enableVertexAttribArray(x);w.disable(w.CULL_FACE);w.enable(w.BLEND);w.bindBuffer(w.ARRAY_BUFFER,
+y);w.vertexAttribPointer(v,2,w.FLOAT,!1,16,0);w.vertexAttribPointer(x,2,w.FLOAT,!1,16,8);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,A);w.uniformMatrix4fv(p,!1,M.projectionMatrix.elements);w.activeTexture(w.TEXTURE0);w.uniform1i(h,0);L=H=0;(P=U.fog)?(w.uniform3f(s,P.color.r,P.color.g,P.color.b),P instanceof THREE.Fog?(w.uniform1f(t,P.near),w.uniform1f(r,P.far),w.uniform1i(q,1),L=H=1):P instanceof THREE.FogExp2&&(w.uniform1f(n,P.density),w.uniform1i(q,2),L=H=2)):(w.uniform1i(q,0),L=H=0);for(var P=0,N=b.length;P<
+N;P++){var R=b[P];R._modelViewMatrix.multiplyMatrices(M.matrixWorldInverse,R.matrixWorld);R.z=-R._modelViewMatrix.elements[14]}b.sort(D);for(var V=[],P=0,N=b.length;P<N;P++){var R=b[P],J=R.material;w.uniform1f(u,J.alphaTest);w.uniformMatrix4fv(l,!1,R._modelViewMatrix.elements);R.matrixWorld.decompose(F,z,I);V[0]=I.x;V[1]=I.y;R=0;U.fog&&J.fog&&(R=L);H!==R&&(w.uniform1i(q,R),H=R);null!==J.map?(w.uniform2f(c,J.map.offset.x,J.map.offset.y),w.uniform2f(d,J.map.repeat.x,J.map.repeat.y)):(w.uniform2f(c,
+0,0),w.uniform2f(d,1,1));w.uniform1f(k,J.opacity);w.uniform3f(g,J.color.r,J.color.g,J.color.b);w.uniform1f(e,J.rotation);w.uniform2fv(f,V);a.state.setBlending(J.blending,J.blendEquation,J.blendSrc,J.blendDst);a.state.setDepthTest(J.depthTest);a.state.setDepthWrite(J.depthWrite);J.map&&J.map.image&&J.map.image.width?a.setTexture(J.map,0):a.setTexture(G,0);w.drawElements(w.TRIANGLES,6,w.UNSIGNED_SHORT,0)}w.enable(w.CULL_FACE);a.resetGLState()}}};
+THREE.GeometryUtils={merge:function(a,b,c){THREE.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var d;b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),d=b.matrix,b=b.geometry);a.merge(b,d,c)},center:function(a){THREE.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.");return a.center()}};
+THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.Texture(void 0,b);e.load(a,function(a){f.image=a;f.needsUpdate=!0;c&&c(f)},void 0,function(a){d&&d(a)});f.sourceFile=a;return f},loadTextureCube:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.CubeTexture([],b);f.flipY=!1;var g=0;b=function(b){e.load(a[b],function(a){f.images[b]=a;g+=1;6===g&&(f.needsUpdate=!0,c&&
+c(f))},void 0,d)};for(var h=0,k=a.length;h<k;++h)b(h);return f},loadCompressedTexture:function(){THREE.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){THREE.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]};b|=1;var d=a.width,e=a.height,f=document.createElement("canvas");
+f.width=d;f.height=e;var g=f.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,0,d,e).data,k=g.createImageData(d,e),l=k.data,p=0;p<d;p++)for(var q=0;q<e;q++){var n=0>q-1?0:q-1,t=q+1>e-1?e-1:q+1,r=0>p-1?0:p-1,s=p+1>d-1?d-1:p+1,u=[],v=[0,0,h[4*(q*d+p)]/255*b];u.push([-1,0,h[4*(q*d+r)]/255*b]);u.push([-1,-1,h[4*(n*d+r)]/255*b]);u.push([0,-1,h[4*(n*d+p)]/255*b]);u.push([1,-1,h[4*(n*d+s)]/255*b]);u.push([1,0,h[4*(q*d+s)]/255*b]);u.push([1,1,h[4*(t*d+s)]/255*b]);u.push([0,1,h[4*(t*d+p)]/255*
+b]);u.push([-1,1,h[4*(t*d+r)]/255*b]);n=[];r=u.length;for(t=0;t<r;t++){var s=u[t],x=u[(t+1)%r],s=[s[0]-v[0],s[1]-v[1],s[2]-v[2]],x=[x[0]-v[0],x[1]-v[1],x[2]-v[2]];n.push(c([s[1]*x[2]-s[2]*x[1],s[2]*x[0]-s[0]*x[2],s[0]*x[1]-s[1]*x[0]]))}u=[0,0,0];for(t=0;t<n.length;t++)u[0]+=n[t][0],u[1]+=n[t][1],u[2]+=n[t][2];u[0]/=n.length;u[1]/=n.length;u[2]/=n.length;v=4*(q*d+p);l[v]=(u[0]+1)/2*255|0;l[v+1]=(u[1]+1)/2*255|0;l[v+2]=255*u[2]|0;l[v+3]=255}g.putImageData(k,0,0);return f},generateDataTexture:function(a,
+b,c){var d=a*b,e=new Uint8Array(3*d),f=Math.floor(255*c.r),g=Math.floor(255*c.g);c=Math.floor(255*c.b);for(var h=0;h<d;h++)e[3*h]=f,e[3*h+1]=g,e[3*h+2]=c;a=new THREE.DataTexture(e,a,b,THREE.RGBFormat);a.needsUpdate=!0;return a}};
+THREE.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new THREE.Object3D,d=0,e=b.length;d<e;d++)c.add(new THREE.Mesh(a,b[d]));return c},detach:function(a,b,c){a.applyMatrix(b.matrixWorld);b.remove(a);c.add(a)},attach:function(a,b,c){var d=new THREE.Matrix4;d.getInverse(c.matrixWorld);a.applyMatrix(d);b.remove(a);c.add(a)}};
+THREE.FontUtils={faces:{},face:"helvetiker",weight:"normal",style:"normal",size:150,divisions:10,getFace:function(){try{return this.faces[this.face][this.weight][this.style]}catch(a){throw"The font "+this.face+" with "+this.weight+" weight and "+this.style+" style is missing.";}},loadFace:function(a){var b=a.familyName.toLowerCase();this.faces[b]=this.faces[b]||{};this.faces[b][a.cssFontWeight]=this.faces[b][a.cssFontWeight]||{};this.faces[b][a.cssFontWeight][a.cssFontStyle]=a;return this.faces[b][a.cssFontWeight][a.cssFontStyle]=
+a},drawText:function(a){var b=this.getFace(),c=this.size/b.resolution,d=0,e=String(a).split(""),f=e.length,g=[];for(a=0;a<f;a++){var h=new THREE.Path,h=this.extractGlyphPoints(e[a],b,c,d,h),d=d+h.offset;g.push(h.path)}return{paths:g,offset:d/2}},extractGlyphPoints:function(a,b,c,d,e){var f=[],g,h,k,l,p,q,n,t,r,s,u,v=b.glyphs[a]||b.glyphs["?"];if(v){if(v.o)for(b=v._cachedOutline||(v._cachedOutline=v.o.split(" ")),l=b.length,a=0;a<l;)switch(k=b[a++],k){case "m":k=b[a++]*c+d;p=b[a++]*c;e.moveTo(k,p);
+break;case "l":k=b[a++]*c+d;p=b[a++]*c;e.lineTo(k,p);break;case "q":k=b[a++]*c+d;p=b[a++]*c;t=b[a++]*c+d;r=b[a++]*c;e.quadraticCurveTo(t,r,k,p);if(g=f[f.length-1])for(q=g.x,n=g.y,g=1,h=this.divisions;g<=h;g++){var x=g/h;THREE.Shape.Utils.b2(x,q,t,k);THREE.Shape.Utils.b2(x,n,r,p)}break;case "b":if(k=b[a++]*c+d,p=b[a++]*c,t=b[a++]*c+d,r=b[a++]*c,s=b[a++]*c+d,u=b[a++]*c,e.bezierCurveTo(t,r,s,u,k,p),g=f[f.length-1])for(q=g.x,n=g.y,g=1,h=this.divisions;g<=h;g++)x=g/h,THREE.Shape.Utils.b3(x,q,t,s,k),THREE.Shape.Utils.b3(x,
+n,r,u,p)}return{offset:v.ha*c,path:e}}}};
+THREE.FontUtils.generateShapes=function(a,b){b=b||{};var c=void 0!==b.curveSegments?b.curveSegments:4,d=void 0!==b.font?b.font:"helvetiker",e=void 0!==b.weight?b.weight:"normal",f=void 0!==b.style?b.style:"normal";THREE.FontUtils.size=void 0!==b.size?b.size:100;THREE.FontUtils.divisions=c;THREE.FontUtils.face=d;THREE.FontUtils.weight=e;THREE.FontUtils.style=f;c=THREE.FontUtils.drawText(a).paths;d=[];e=0;for(f=c.length;e<f;e++)Array.prototype.push.apply(d,c[e].toShapes());return d};
+(function(a){var b=function(a){for(var b=a.length,e=0,f=b-1,g=0;g<b;f=g++)e+=a[f].x*a[g].y-a[g].x*a[f].y;return.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],g=[],h=[],k,l,p;if(0<b(a))for(l=0;l<e;l++)g[l]=l;else for(l=0;l<e;l++)g[l]=e-1-l;var q=2*e;for(l=e-1;2<e;){if(0>=q--){THREE.warn("THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()");break}k=l;e<=k&&(k=0);l=k+1;e<=l&&(l=0);p=l+1;e<=p&&(p=0);var n;a:{var t=n=void 0,r=void 0,s=void 0,
+u=void 0,v=void 0,x=void 0,D=void 0,w=void 0,t=a[g[k]].x,r=a[g[k]].y,s=a[g[l]].x,u=a[g[l]].y,v=a[g[p]].x,x=a[g[p]].y;if(1E-10>(s-t)*(x-r)-(u-r)*(v-t))n=!1;else{var y=void 0,A=void 0,E=void 0,G=void 0,F=void 0,z=void 0,I=void 0,U=void 0,M=void 0,H=void 0,M=U=I=w=D=void 0,y=v-s,A=x-u,E=t-v,G=r-x,F=s-t,z=u-r;for(n=0;n<e;n++)if(D=a[g[n]].x,w=a[g[n]].y,!(D===t&&w===r||D===s&&w===u||D===v&&w===x)&&(I=D-t,U=w-r,M=D-s,H=w-u,D-=v,w-=x,M=y*H-A*M,I=F*U-z*I,U=E*w-G*D,-1E-10<=M&&-1E-10<=U&&-1E-10<=I)){n=!1;break a}n=
+!0}}if(n){f.push([a[g[k]],a[g[l]],a[g[p]]]);h.push([g[k],g[l],g[p]]);k=l;for(p=l+1;p<e;k++,p++)g[k]=g[p];e--;q=2*e}}return d?h:f};a.Triangulate.area=b;return a})(THREE.FontUtils);self._typeface_js={faces:THREE.FontUtils.faces,loadFace:THREE.FontUtils.loadFace};THREE.typeface_js=self._typeface_js;
+THREE.Audio=function(a){THREE.Object3D.call(this);this.type="Audio";this.context=a.context;this.source=this.context.createBufferSource();this.source.onended=this.onEnded.bind(this);this.gain=this.context.createGain();this.gain.connect(this.context.destination);this.panner=this.context.createPanner();this.panner.connect(this.gain);this.autoplay=!1;this.startTime=0;this.isPlaying=!1};THREE.Audio.prototype=Object.create(THREE.Object3D.prototype);THREE.Audio.prototype.constructor=THREE.Audio;
+THREE.Audio.prototype.load=function(a){var b=this,c=new XMLHttpRequest;c.open("GET",a,!0);c.responseType="arraybuffer";c.onload=function(a){b.context.decodeAudioData(this.response,function(a){b.source.buffer=a;b.autoplay&&b.play()})};c.send();return this};
+THREE.Audio.prototype.play=function(){if(!0===this.isPlaying)THREE.warn("THREE.Audio: Audio is already playing.");else{var a=this.context.createBufferSource();a.buffer=this.source.buffer;a.loop=this.source.loop;a.onended=this.source.onended;a.connect(this.panner);a.start(0,this.startTime);this.isPlaying=!0;this.source=a}};THREE.Audio.prototype.pause=function(){this.source.stop();this.startTime=this.context.currentTime};THREE.Audio.prototype.stop=function(){this.source.stop();this.startTime=0};
+THREE.Audio.prototype.onEnded=function(){this.isPlaying=!1};THREE.Audio.prototype.setLoop=function(a){this.source.loop=a};THREE.Audio.prototype.setRefDistance=function(a){this.panner.refDistance=a};THREE.Audio.prototype.setRolloffFactor=function(a){this.panner.rolloffFactor=a};THREE.Audio.prototype.setVolume=function(a){this.gain.gain.value=a};
+THREE.Audio.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3;return function(b){THREE.Object3D.prototype.updateMatrixWorld.call(this,b);a.setFromMatrixPosition(this.matrixWorld);this.panner.setPosition(a.x,a.y,a.z)}}();THREE.AudioListener=function(){THREE.Object3D.call(this);this.type="AudioListener";this.context=new (window.AudioContext||window.webkitAudioContext)};THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype);THREE.AudioListener.prototype.constructor=THREE.AudioListener;
+THREE.AudioListener.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g){THREE.Object3D.prototype.updateMatrixWorld.call(this,g);g=this.context.listener;var h=this.up;this.matrixWorld.decompose(a,b,c);d.set(0,0,-1).applyQuaternion(b);e.subVectors(a,f);g.setPosition(a.x,a.y,a.z);g.setOrientation(d.x,d.y,d.z,h.x,h.y,h.z);g.setVelocity(e.x,e.y,e.z);f.copy(a)}}();
+THREE.Curve=function(){};THREE.Curve.prototype.getPoint=function(a){THREE.warn("THREE.Curve: Warning, getPoint() not implemented!");return null};THREE.Curve.prototype.getPointAt=function(a){a=this.getUtoTmapping(a);return this.getPoint(a)};THREE.Curve.prototype.getPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPoint(b/a));return c};THREE.Curve.prototype.getSpacedPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPointAt(b/a));return c};
 THREE.Curve.prototype.getLength=function(){var a=this.getLengths();return a[a.length-1]};THREE.Curve.prototype.getLengths=function(a){a||(a=this.__arcLengthDivisions?this.__arcLengthDivisions:200);if(this.cacheArcLengths&&this.cacheArcLengths.length==a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;return this.cacheArcLengths=b};
-THREE.Curve.prototype.updateArcLengths=function(){this.needsUpdate=!0;this.getLengths()};THREE.Curve.prototype.getUtoTmapping=function(a,b){var c=this.getLengths(),d=0,e=c.length,f;f=b?b:a*c[e-1];for(var h=0,g=e-1,i;h<=g;)if(d=Math.floor(h+(g-h)/2),i=c[d]-f,0>i)h=d+1;else if(0<i)g=d-1;else{g=d;break}d=g;if(c[d]==f)return d/(e-1);h=c[d];return c=(d+(f-h)/(c[d+1]-h))/(e-1)};THREE.Curve.prototype.getTangent=function(a){var b=a-1E-4,a=a+1E-4;0>b&&(b=0);1<a&&(a=1);b=this.getPoint(b);return this.getPoint(a).clone().sub(b).normalize()};
+THREE.Curve.prototype.updateArcLengths=function(){this.needsUpdate=!0;this.getLengths()};THREE.Curve.prototype.getUtoTmapping=function(a,b){var c=this.getLengths(),d=0,e=c.length,f;f=b?b:a*c[e-1];for(var g=0,h=e-1,k;g<=h;)if(d=Math.floor(g+(h-g)/2),k=c[d]-f,0>k)g=d+1;else if(0<k)h=d-1;else{h=d;break}d=h;if(c[d]==f)return d/(e-1);g=c[d];return c=(d+(f-g)/(c[d+1]-g))/(e-1)};THREE.Curve.prototype.getTangent=function(a){var b=a-1E-4;a+=1E-4;0>b&&(b=0);1<a&&(a=1);b=this.getPoint(b);return this.getPoint(a).clone().sub(b).normalize()};
 THREE.Curve.prototype.getTangentAt=function(a){a=this.getUtoTmapping(a);return this.getTangent(a)};
-THREE.Curve.Utils={tangentQuadraticBezier:function(a,b,c,d){return 2*(1-a)*(c-b)+2*a*(d-c)},tangentCubicBezier:function(a,b,c,d,e){return-3*b*(1-a)*(1-a)+3*c*(1-a)*(1-a)-6*a*c*(1-a)+6*a*d*(1-a)-3*a*a*d+3*a*a*e},tangentSpline:function(a){return 6*a*a-6*a+(3*a*a-4*a+1)+(-6*a*a+6*a)+(3*a*a-2*a)},interpolate:function(a,b,c,d,e){var a=0.5*(c-a),d=0.5*(d-b),f=e*e;return(2*b-2*c+a+d)*e*f+(-3*b+3*c-2*a-d)*f+a*e+b}};
-THREE.Curve.create=function(a,b){a.prototype=Object.create(THREE.Curve.prototype);a.prototype.getPoint=b;return a};THREE.CurvePath=function(){this.curves=[];this.bends=[];this.autoClose=!1};THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype);THREE.CurvePath.prototype.add=function(a){this.curves.push(a)};THREE.CurvePath.prototype.checkConnection=function(){};THREE.CurvePath.prototype.closePath=function(){var a=this.curves[0].getPoint(0),b=this.curves[this.curves.length-1].getPoint(1);a.equals(b)||this.curves.push(new THREE.LineCurve(b,a))};
-THREE.CurvePath.prototype.getPoint=function(a){for(var b=a*this.getLength(),c=this.getCurveLengths(),a=0;a<c.length;){if(c[a]>=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]};
+THREE.Curve.Utils={tangentQuadraticBezier:function(a,b,c,d){return 2*(1-a)*(c-b)+2*a*(d-c)},tangentCubicBezier:function(a,b,c,d,e){return-3*b*(1-a)*(1-a)+3*c*(1-a)*(1-a)-6*a*c*(1-a)+6*a*d*(1-a)-3*a*a*d+3*a*a*e},tangentSpline:function(a,b,c,d,e){return 6*a*a-6*a+(3*a*a-4*a+1)+(-6*a*a+6*a)+(3*a*a-2*a)},interpolate:function(a,b,c,d,e){a=.5*(c-a);d=.5*(d-b);var f=e*e;return(2*b-2*c+a+d)*e*f+(-3*b+3*c-2*a-d)*f+a*e+b}};
+THREE.Curve.create=function(a,b){a.prototype=Object.create(THREE.Curve.prototype);a.prototype.constructor=a;a.prototype.getPoint=b;return a};THREE.CurvePath=function(){this.curves=[];this.bends=[];this.autoClose=!1};THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype);THREE.CurvePath.prototype.constructor=THREE.CurvePath;THREE.CurvePath.prototype.add=function(a){this.curves.push(a)};THREE.CurvePath.prototype.checkConnection=function(){};
+THREE.CurvePath.prototype.closePath=function(){var a=this.curves[0].getPoint(0),b=this.curves[this.curves.length-1].getPoint(1);a.equals(b)||this.curves.push(new THREE.LineCurve(b,a))};THREE.CurvePath.prototype.getPoint=function(a){var b=a*this.getLength(),c=this.getCurveLengths();for(a=0;a<c.length;){if(c[a]>=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]};
 THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length==this.curves.length)return this.cacheLengths;var a=[],b=0,c,d=this.curves.length;for(c=0;c<d;c++)b+=this.curves[c].getLength(),a.push(b);return this.cacheLengths=a};
-THREE.CurvePath.prototype.getBoundingBox=function(){var a=this.getPoints(),b,c,d,e,f,h;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var g,i,k,m,l=a[0]instanceof THREE.Vector3;m=l?new THREE.Vector3:new THREE.Vector2;i=0;for(k=a.length;i<k;i++)g=a[i],g.x>b?b=g.x:g.x<e&&(e=g.x),g.y>c?c=g.y:g.y<f&&(f=g.y),l&&(g.z>d?d=g.z:g.z<h&&(h=g.z)),m.add(g);a={minX:e,minY:f,maxX:b,maxY:c,centroid:m.divideScalar(k)};l&&(a.maxZ=d,a.minZ=h);return a};
+THREE.CurvePath.prototype.getBoundingBox=function(){var a=this.getPoints(),b,c,d,e,f,g;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var h,k,l,p,q=a[0]instanceof THREE.Vector3;p=q?new THREE.Vector3:new THREE.Vector2;k=0;for(l=a.length;k<l;k++)h=a[k],h.x>b?b=h.x:h.x<e&&(e=h.x),h.y>c?c=h.y:h.y<f&&(f=h.y),q&&(h.z>d?d=h.z:h.z<g&&(g=h.z)),p.add(h);a={minX:e,minY:f,maxX:b,maxY:c};q&&(a.maxZ=d,a.minZ=g);return a};
 THREE.CurvePath.prototype.createPointsGeometry=function(a){a=this.getPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createSpacedPointsGeometry=function(a){a=this.getSpacedPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createGeometry=function(a){for(var b=new THREE.Geometry,c=0;c<a.length;c++)b.vertices.push(new THREE.Vector3(a[c].x,a[c].y,a[c].z||0));return b};THREE.CurvePath.prototype.addWrapPath=function(a){this.bends.push(a)};
 THREE.CurvePath.prototype.getTransformedPoints=function(a,b){var c=this.getPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};THREE.CurvePath.prototype.getTransformedSpacedPoints=function(a,b){var c=this.getSpacedPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};
-THREE.CurvePath.prototype.getWrapPoints=function(a,b){var c=this.getBoundingBox(),d,e,f,h,g,i;d=0;for(e=a.length;d<e;d++)f=a[d],h=f.x,g=f.y,i=h/c.maxX,i=b.getUtoTmapping(i,h),h=b.getPoint(i),g=b.getNormalVector(i).multiplyScalar(g),f.x=h.x+g.x,f.y=h.y+g.y;return a};THREE.Gyroscope=function(){THREE.Object3D.call(this)};THREE.Gyroscope.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Gyroscope.prototype.updateMatrixWorld=function(a){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||a)this.parent?(this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorld.decompose(this.translationWorld,this.quaternionWorld,this.scaleWorld),this.matrix.decompose(this.translationObject,this.quaternionObject,this.scaleObject),this.matrixWorld.compose(this.translationWorld,this.quaternionObject,this.scaleWorld)):this.matrixWorld.copy(this.matrix),
-this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)};THREE.Gyroscope.prototype.translationWorld=new THREE.Vector3;THREE.Gyroscope.prototype.translationObject=new THREE.Vector3;THREE.Gyroscope.prototype.quaternionWorld=new THREE.Quaternion;THREE.Gyroscope.prototype.quaternionObject=new THREE.Quaternion;THREE.Gyroscope.prototype.scaleWorld=new THREE.Vector3;THREE.Gyroscope.prototype.scaleObject=new THREE.Vector3;THREE.Path=function(a){THREE.CurvePath.call(this);this.actions=[];a&&this.fromPoints(a)};THREE.Path.prototype=Object.create(THREE.CurvePath.prototype);THREE.PathActions={MOVE_TO:"moveTo",LINE_TO:"lineTo",QUADRATIC_CURVE_TO:"quadraticCurveTo",BEZIER_CURVE_TO:"bezierCurveTo",CSPLINE_THRU:"splineThru",ARC:"arc",ELLIPSE:"ellipse"};THREE.Path.prototype.fromPoints=function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;b<c;b++)this.lineTo(a[b].x,a[b].y)};
-THREE.Path.prototype.moveTo=function(a,b){var c=Array.prototype.slice.call(arguments);this.actions.push({action:THREE.PathActions.MOVE_TO,args:c})};THREE.Path.prototype.lineTo=function(a,b){var c=Array.prototype.slice.call(arguments),d=this.actions[this.actions.length-1].args,d=new THREE.LineCurve(new THREE.Vector2(d[d.length-2],d[d.length-1]),new THREE.Vector2(a,b));this.curves.push(d);this.actions.push({action:THREE.PathActions.LINE_TO,args:c})};
+THREE.CurvePath.prototype.getWrapPoints=function(a,b){var c=this.getBoundingBox(),d,e,f,g,h,k;d=0;for(e=a.length;d<e;d++)f=a[d],g=f.x,h=f.y,k=g/c.maxX,k=b.getUtoTmapping(k,g),g=b.getPoint(k),k=b.getTangent(k),k.set(-k.y,k.x).multiplyScalar(h),f.x=g.x+k.x,f.y=g.y+k.y;return a};THREE.Gyroscope=function(){THREE.Object3D.call(this)};THREE.Gyroscope.prototype=Object.create(THREE.Object3D.prototype);THREE.Gyroscope.prototype.constructor=THREE.Gyroscope;
+THREE.Gyroscope.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Quaternion,f=new THREE.Vector3;return function(g){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||g)this.parent?(this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorld.decompose(d,e,f),this.matrix.decompose(a,b,c),this.matrixWorld.compose(d,b,f)):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=
+!1,g=!0;for(var h=0,k=this.children.length;h<k;h++)this.children[h].updateMatrixWorld(g)}}();THREE.Path=function(a){THREE.CurvePath.call(this);this.actions=[];a&&this.fromPoints(a)};THREE.Path.prototype=Object.create(THREE.CurvePath.prototype);THREE.Path.prototype.constructor=THREE.Path;THREE.PathActions={MOVE_TO:"moveTo",LINE_TO:"lineTo",QUADRATIC_CURVE_TO:"quadraticCurveTo",BEZIER_CURVE_TO:"bezierCurveTo",CSPLINE_THRU:"splineThru",ARC:"arc",ELLIPSE:"ellipse"};
+THREE.Path.prototype.fromPoints=function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;b<c;b++)this.lineTo(a[b].x,a[b].y)};THREE.Path.prototype.moveTo=function(a,b){var c=Array.prototype.slice.call(arguments);this.actions.push({action:THREE.PathActions.MOVE_TO,args:c})};
+THREE.Path.prototype.lineTo=function(a,b){var c=Array.prototype.slice.call(arguments),d=this.actions[this.actions.length-1].args,d=new THREE.LineCurve(new THREE.Vector2(d[d.length-2],d[d.length-1]),new THREE.Vector2(a,b));this.curves.push(d);this.actions.push({action:THREE.PathActions.LINE_TO,args:c})};
 THREE.Path.prototype.quadraticCurveTo=function(a,b,c,d){var e=Array.prototype.slice.call(arguments),f=this.actions[this.actions.length-1].args,f=new THREE.QuadraticBezierCurve(new THREE.Vector2(f[f.length-2],f[f.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d));this.curves.push(f);this.actions.push({action:THREE.PathActions.QUADRATIC_CURVE_TO,args:e})};
-THREE.Path.prototype.bezierCurveTo=function(a,b,c,d,e,f){var h=Array.prototype.slice.call(arguments),g=this.actions[this.actions.length-1].args,g=new THREE.CubicBezierCurve(new THREE.Vector2(g[g.length-2],g[g.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d),new THREE.Vector2(e,f));this.curves.push(g);this.actions.push({action:THREE.PathActions.BEZIER_CURVE_TO,args:h})};
-THREE.Path.prototype.splineThru=function(a){var b=Array.prototype.slice.call(arguments),c=this.actions[this.actions.length-1].args,c=[new THREE.Vector2(c[c.length-2],c[c.length-1])];Array.prototype.push.apply(c,a);c=new THREE.SplineCurve(c);this.curves.push(c);this.actions.push({action:THREE.PathActions.CSPLINE_THRU,args:b})};THREE.Path.prototype.arc=function(a,b,c,d,e,f){var h=this.actions[this.actions.length-1].args;this.absarc(a+h[h.length-2],b+h[h.length-1],c,d,e,f)};
-THREE.Path.prototype.absarc=function(a,b,c,d,e,f){this.absellipse(a,b,c,c,d,e,f)};THREE.Path.prototype.ellipse=function(a,b,c,d,e,f,h){var g=this.actions[this.actions.length-1].args;this.absellipse(a+g[g.length-2],b+g[g.length-1],c,d,e,f,h)};THREE.Path.prototype.absellipse=function(a,b,c,d,e,f,h){var g=Array.prototype.slice.call(arguments),i=new THREE.EllipseCurve(a,b,c,d,e,f,h);this.curves.push(i);i=i.getPoint(1);g.push(i.x);g.push(i.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:g})};
-THREE.Path.prototype.getSpacedPoints=function(a){a||(a=40);for(var b=[],c=0;c<a;c++)b.push(this.getPoint(c/a));return b};
-THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return console.log("tata"),this.getSpacedPoints(a,b);var a=a||12,c=[],d,e,f,h,g,i,k,m,l,p,s,t,n;d=0;for(e=this.actions.length;d<e;d++)switch(f=this.actions[d],h=f.action,f=f.args,h){case THREE.PathActions.MOVE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.LINE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.QUADRATIC_CURVE_TO:g=f[2];i=f[3];l=f[0];p=f[1];0<c.length?(h=c[c.length-1],s=h.x,
-t=h.y):(h=this.actions[d-1].args,s=h[h.length-2],t=h[h.length-1]);for(f=1;f<=a;f++)n=f/a,h=THREE.Shape.Utils.b2(n,s,l,g),n=THREE.Shape.Utils.b2(n,t,p,i),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.BEZIER_CURVE_TO:g=f[4];i=f[5];l=f[0];p=f[1];k=f[2];m=f[3];0<c.length?(h=c[c.length-1],s=h.x,t=h.y):(h=this.actions[d-1].args,s=h[h.length-2],t=h[h.length-1]);for(f=1;f<=a;f++)n=f/a,h=THREE.Shape.Utils.b3(n,s,l,k,g),n=THREE.Shape.Utils.b3(n,t,p,m,i),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.CSPLINE_THRU:h=
-this.actions[d-1].args;n=[new THREE.Vector2(h[h.length-2],h[h.length-1])];h=a*f[0].length;n=n.concat(f[0]);n=new THREE.SplineCurve(n);for(f=1;f<=h;f++)c.push(n.getPointAt(f/h));break;case THREE.PathActions.ARC:g=f[0];i=f[1];p=f[2];k=f[3];h=f[4];l=!!f[5];s=h-k;t=2*a;for(f=1;f<=t;f++)n=f/t,l||(n=1-n),n=k+n*s,h=g+p*Math.cos(n),n=i+p*Math.sin(n),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.ELLIPSE:g=f[0];i=f[1];p=f[2];m=f[3];k=f[4];h=f[5];l=!!f[6];s=h-k;t=2*a;for(f=1;f<=t;f++)n=f/t,l||
-(n=1-n),n=k+n*s,h=g+p*Math.cos(n),n=i+m*Math.sin(n),c.push(new THREE.Vector2(h,n))}d=c[c.length-1];1E-10>Math.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c};
-THREE.Path.prototype.toShapes=function(a){var b,c,d,e,f=[],h=new THREE.Path;b=0;for(c=this.actions.length;b<c;b++)d=this.actions[b],e=d.args,d=d.action,d==THREE.PathActions.MOVE_TO&&0!=h.actions.length&&(f.push(h),h=new THREE.Path),h[d].apply(h,e);0!=h.actions.length&&f.push(h);if(0==f.length)return[];var g;e=[];if(1==f.length)return d=f[0],g=new THREE.Shape,g.actions=d.actions,g.curves=d.curves,e.push(g),e;b=!THREE.Shape.Utils.isClockWise(f[0].getPoints());if(a?!b:b){g=new THREE.Shape;b=0;for(c=
-f.length;b<c;b++)d=f[b],h=THREE.Shape.Utils.isClockWise(d.getPoints()),(h=a?!h:h)?(g.actions=d.actions,g.curves=d.curves,e.push(g),g=new THREE.Shape):g.holes.push(d)}else{g=void 0;b=0;for(c=f.length;b<c;b++)d=f[b],h=THREE.Shape.Utils.isClockWise(d.getPoints()),(h=a?!h:h)?(g&&e.push(g),g=new THREE.Shape,g.actions=d.actions,g.curves=d.curves):g.holes.push(d);e.push(g)}return e};THREE.Shape=function(){THREE.Path.apply(this,arguments);this.holes=[]};THREE.Shape.prototype=Object.create(THREE.Path.prototype);THREE.Shape.prototype.extrude=function(a){return new THREE.ExtrudeGeometry(this,a)};THREE.Shape.prototype.makeGeometry=function(a){return new THREE.ShapeGeometry(this,a)};THREE.Shape.prototype.getPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedPoints(a,this.bends);return d};
-THREE.Shape.prototype.getSpacedPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedSpacedPoints(a,this.bends);return d};THREE.Shape.prototype.extractAllPoints=function(a){return{shape:this.getTransformedPoints(a),holes:this.getPointsHoles(a)}};THREE.Shape.prototype.extractPoints=function(a){return this.useSpacedPoints?this.extractAllSpacedPoints(a):this.extractAllPoints(a)};
-THREE.Shape.prototype.extractAllSpacedPoints=function(a){return{shape:this.getTransformedSpacedPoints(a),holes:this.getSpacedPointsHoles(a)}};
-THREE.Shape.Utils={removeHoles:function(a,b){var c=a.concat(),d=c.concat(),e,f,h,g,i,k,m,l,p,s,t=[];for(i=0;i<b.length;i++){k=b[i];Array.prototype.push.apply(d,k);f=Number.POSITIVE_INFINITY;for(e=0;e<k.length;e++){p=k[e];s=[];for(l=0;l<c.length;l++)m=c[l],m=p.distanceToSquared(m),s.push(m),m<f&&(f=m,h=e,g=l)}e=0<=g-1?g-1:c.length-1;f=0<=h-1?h-1:k.length-1;var n=[k[h],c[g],c[e]];l=THREE.FontUtils.Triangulate.area(n);var r=[k[h],k[f],c[g]];p=THREE.FontUtils.Triangulate.area(r);s=g;m=h;g+=1;h+=-1;0>
-g&&(g+=c.length);g%=c.length;0>h&&(h+=k.length);h%=k.length;e=0<=g-1?g-1:c.length-1;f=0<=h-1?h-1:k.length-1;n=[k[h],c[g],c[e]];n=THREE.FontUtils.Triangulate.area(n);r=[k[h],k[f],c[g]];r=THREE.FontUtils.Triangulate.area(r);l+p>n+r&&(g=s,h=m,0>g&&(g+=c.length),g%=c.length,0>h&&(h+=k.length),h%=k.length,e=0<=g-1?g-1:c.length-1,f=0<=h-1?h-1:k.length-1);l=c.slice(0,g);p=c.slice(g);s=k.slice(h);m=k.slice(0,h);f=[k[h],k[f],c[g]];t.push([k[h],c[g],c[e]]);t.push(f);c=l.concat(s).concat(m).concat(p)}return{shape:c,
-isolatedPts:t,allpoints:d}},triangulateShape:function(a,b){var c=THREE.Shape.Utils.removeHoles(a,b),d=c.allpoints,e=c.isolatedPts,c=THREE.FontUtils.Triangulate(c.shape,!1),f,h,g,i,k={};f=0;for(h=d.length;f<h;f++)i=d[f].x+":"+d[f].y,void 0!==k[i]&&console.log("Duplicate point",i),k[i]=f;f=0;for(h=c.length;f<h;f++){g=c[f];for(d=0;3>d;d++)i=g[d].x+":"+g[d].y,i=k[i],void 0!==i&&(g[d]=i)}f=0;for(h=e.length;f<h;f++){g=e[f];for(d=0;3>d;d++)i=g[d].x+":"+g[d].y,i=k[i],void 0!==i&&(g[d]=i)}return c.concat(e)},
-isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,b)+this.b3p1(a,c)+this.b3p2(a,d)+
-this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};THREE.LineCurve.prototype.getTangent=function(){return this.v2.clone().sub(this.v1).normalize()};THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return new THREE.Vector2(b,a)};
-THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};
-THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.getPoint=function(a){var b=new THREE.Vector2,c=[],d=this.points,e;e=(d.length-1)*a;a=Math.floor(e);e-=a;c[0]=0==a?a:a-1;c[1]=a;c[2]=a>d.length-2?d.length-1:a+1;c[3]=a>d.length-3?d.length-1:a+2;b.x=THREE.Curve.Utils.interpolate(d[c[0]].x,d[c[1]].x,d[c[2]].x,d[c[3]].x,e);b.y=THREE.Curve.Utils.interpolate(d[c[0]].y,d[c[1]].y,d[c[2]].y,d[c[3]].y,e);return b};THREE.EllipseCurve=function(a,b,c,d,e,f,h){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=h};THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype);
-THREE.EllipseCurve.prototype.getPoint=function(a){var b;b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);b=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;a=this.aX+this.xRadius*Math.cos(b);b=this.aY+this.yRadius*Math.sin(b);return new THREE.Vector2(a,b)};THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype);THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b,c;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);c=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);a=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return new THREE.Vector3(b,c,a)});THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b,c;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);c=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);a=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return new THREE.Vector3(b,c,a)});THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e,a=(d.length-1)*a;e=Math.floor(a);a-=e;c[0]=0==e?e:e-1;c[1]=e;c[2]=e>d.length-2?d.length-1:e+1;c[3]=e>d.length-3?d.length-1:e+2;e=d[c[0]];var f=d[c[1]],h=d[c[2]],c=d[c[3]];b.x=THREE.Curve.Utils.interpolate(e.x,f.x,h.x,c.x,a);b.y=THREE.Curve.Utils.interpolate(e.y,f.y,h.y,c.y,a);b.z=THREE.Curve.Utils.interpolate(e.z,f.z,h.z,c.z,a);return b});THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e;e=(d.length-0)*a;a=Math.floor(e);e-=a;a+=0<a?0:(Math.floor(Math.abs(a)/d.length)+1)*d.length;c[0]=(a-1)%d.length;c[1]=a%d.length;c[2]=(a+1)%d.length;c[3]=(a+2)%d.length;b.x=THREE.Curve.Utils.interpolate(d[c[0]].x,d[c[1]].x,d[c[2]].x,d[c[3]].x,e);b.y=THREE.Curve.Utils.interpolate(d[c[0]].y,d[c[1]].y,d[c[2]].y,d[c[3]].y,e);b.z=THREE.Curve.Utils.interpolate(d[c[0]].z,
-d[c[1]].z,d[c[2]].z,d[c[3]].z,e);return b});THREE.AnimationHandler=function(){var a=[],b={},c={update:function(b){for(var c=0;c<a.length;c++)a[c].update(b)},addToUpdate:function(b){-1===a.indexOf(b)&&a.push(b)},removeFromUpdate:function(b){b=a.indexOf(b);-1!==b&&a.splice(b,1)},add:function(a){void 0!==b[a.name]&&console.log("THREE.AnimationHandler.add: Warning! "+a.name+" already exists in library. Overwriting.");b[a.name]=a;if(!0!==a.initialized){for(var c=0;c<a.hierarchy.length;c++){for(var d=0;d<a.hierarchy[c].keys.length;d++)if(0>a.hierarchy[c].keys[d].time&&
-(a.hierarchy[c].keys[d].time=0),void 0!==a.hierarchy[c].keys[d].rot&&!(a.hierarchy[c].keys[d].rot instanceof THREE.Quaternion)){var g=a.hierarchy[c].keys[d].rot;a.hierarchy[c].keys[d].rot=new THREE.Quaternion(g[0],g[1],g[2],g[3])}if(a.hierarchy[c].keys.length&&void 0!==a.hierarchy[c].keys[0].morphTargets){g={};for(d=0;d<a.hierarchy[c].keys.length;d++)for(var i=0;i<a.hierarchy[c].keys[d].morphTargets.length;i++){var k=a.hierarchy[c].keys[d].morphTargets[i];g[k]=-1}a.hierarchy[c].usedMorphTargets=g;
-for(d=0;d<a.hierarchy[c].keys.length;d++){var m={};for(k in g){for(i=0;i<a.hierarchy[c].keys[d].morphTargets.length;i++)if(a.hierarchy[c].keys[d].morphTargets[i]===k){m[k]=a.hierarchy[c].keys[d].morphTargetsInfluences[i];break}i===a.hierarchy[c].keys[d].morphTargets.length&&(m[k]=0)}a.hierarchy[c].keys[d].morphTargetsInfluences=m}}for(d=1;d<a.hierarchy[c].keys.length;d++)a.hierarchy[c].keys[d].time===a.hierarchy[c].keys[d-1].time&&(a.hierarchy[c].keys.splice(d,1),d--);for(d=0;d<a.hierarchy[c].keys.length;d++)a.hierarchy[c].keys[d].index=
-d}d=parseInt(a.length*a.fps,10);a.JIT={};a.JIT.hierarchy=[];for(c=0;c<a.hierarchy.length;c++)a.JIT.hierarchy.push(Array(d));a.initialized=!0}},get:function(a){if("string"===typeof a){if(b[a])return b[a];console.log("THREE.AnimationHandler.get: Couldn't find animation "+a);return null}},parse:function(a){var b=[];if(a instanceof THREE.SkinnedMesh)for(var c=0;c<a.bones.length;c++)b.push(a.bones[c]);else d(a,b);return b}},d=function(a,b){b.push(a);for(var c=0;c<a.children.length;c++)d(a.children[c],
-b)};c.LINEAR=0;c.CATMULLROM=1;c.CATMULLROM_FORWARD=2;return c}();THREE.Animation=function(a,b,c){this.root=a;this.data=THREE.AnimationHandler.get(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=1;this.isPlaying=!1;this.loop=this.isPaused=!0;this.interpolationType=void 0!==c?c:THREE.AnimationHandler.LINEAR;this.points=[];this.target=new THREE.Vector3};
-THREE.Animation.prototype.play=function(a,b){if(!1===this.isPlaying){this.isPlaying=!0;this.loop=void 0!==a?a:!0;this.currentTime=void 0!==b?b:0;var c,d=this.hierarchy.length,e;for(c=0;c<d;c++){e=this.hierarchy[c];e.matrixAutoUpdate=!0;void 0===e.animationCache&&(e.animationCache={},e.animationCache.prevKey={pos:0,rot:0,scl:0},e.animationCache.nextKey={pos:0,rot:0,scl:0},e.animationCache.originalMatrix=e instanceof THREE.Bone?e.skinMatrix:e.matrix);var f=e.animationCache.prevKey;e=e.animationCache.nextKey;
-f.pos=this.data.hierarchy[c].keys[0];f.rot=this.data.hierarchy[c].keys[0];f.scl=this.data.hierarchy[c].keys[0];e.pos=this.getNextKeyWith("pos",c,1);e.rot=this.getNextKeyWith("rot",c,1);e.scl=this.getNextKeyWith("scl",c,1)}this.update(0)}this.isPaused=!1;THREE.AnimationHandler.addToUpdate(this)};THREE.Animation.prototype.pause=function(){!0===this.isPaused?THREE.AnimationHandler.addToUpdate(this):THREE.AnimationHandler.removeFromUpdate(this);this.isPaused=!this.isPaused};
-THREE.Animation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.removeFromUpdate(this)};
-THREE.Animation.prototype.update=function(a){if(!1!==this.isPlaying){var b=["pos","rot","scl"],c,d,e,f,h,g,i,k,m;m=this.currentTime+=a*this.timeScale;k=this.currentTime%=this.data.length;parseInt(Math.min(k*this.data.fps,this.data.length*this.data.fps),10);for(var l=0,p=this.hierarchy.length;l<p;l++){a=this.hierarchy[l];i=a.animationCache;for(var s=0;3>s;s++){c=b[s];h=i.prevKey[c];g=i.nextKey[c];if(g.time<=m){if(k<m)if(this.loop){h=this.data.hierarchy[l].keys[0];for(g=this.getNextKeyWith(c,l,1);g.time<
-k;)h=g,g=this.getNextKeyWith(c,l,g.index+1)}else{this.stop();return}else{do h=g,g=this.getNextKeyWith(c,l,g.index+1);while(g.time<k)}i.prevKey[c]=h;i.nextKey[c]=g}a.matrixAutoUpdate=!0;a.matrixWorldNeedsUpdate=!0;d=(k-h.time)/(g.time-h.time);e=h[c];f=g[c];if(0>d||1<d)console.log("THREE.Animation.update: Warning! Scale out of bounds:"+d+" on bone "+l),d=0>d?0:1;if("pos"===c)if(c=a.position,this.interpolationType===THREE.AnimationHandler.LINEAR)c.x=e[0]+(f[0]-e[0])*d,c.y=e[1]+(f[1]-e[1])*d,c.z=e[2]+
-(f[2]-e[2])*d;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)this.points[0]=this.getPrevKeyWith("pos",l,h.index-1).pos,this.points[1]=e,this.points[2]=f,this.points[3]=this.getNextKeyWith("pos",l,g.index+1).pos,d=0.33*d+0.33,e=this.interpolateCatmullRom(this.points,d),c.x=e[0],c.y=e[1],c.z=e[2],this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD&&(d=this.interpolateCatmullRom(this.points,1.01*d),
-this.target.set(d[0],d[1],d[2]),this.target.sub(c),this.target.y=0,this.target.normalize(),d=Math.atan2(this.target.x,this.target.z),a.rotation.set(0,d,0))}else"rot"===c?THREE.Quaternion.slerp(e,f,a.quaternion,d):"scl"===c&&(c=a.scale,c.x=e[0]+(f[0]-e[0])*d,c.y=e[1]+(f[1]-e[1])*d,c.z=e[2]+(f[2]-e[2])*d)}}}};
-THREE.Animation.prototype.interpolateCatmullRom=function(a,b){var c=[],d=[],e,f,h,g,i,k;e=(a.length-1)*b;f=Math.floor(e);e-=f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>a.length-2?f:f+1;c[3]=f>a.length-3?f:f+2;f=a[c[0]];g=a[c[1]];i=a[c[2]];k=a[c[3]];c=e*e;h=e*c;d[0]=this.interpolate(f[0],g[0],i[0],k[0],e,c,h);d[1]=this.interpolate(f[1],g[1],i[1],k[1],e,c,h);d[2]=this.interpolate(f[2],g[2],i[2],k[2],e,c,h);return d};
-THREE.Animation.prototype.interpolate=function(a,b,c,d,e,f,h){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)*h+(-3*(b-c)-2*a-d)*f+a*e+b};THREE.Animation.prototype.getNextKeyWith=function(a,b,c){for(var d=this.data.hierarchy[b].keys,c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?c<d.length-1?c:d.length-1:c%d.length;c<d.length;c++)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[0]};
-THREE.Animation.prototype.getPrevKeyWith=function(a,b,c){for(var d=this.data.hierarchy[b].keys,c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?0<c?c:0:0<=c?c:c+d.length;0<=c;c--)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[d.length-1]};THREE.KeyFrameAnimation=function(a,b,c){this.root=a;this.data=THREE.AnimationHandler.get(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=0.001;this.isPlaying=!1;this.loop=this.isPaused=!0;this.JITCompile=void 0!==c?c:!0;a=0;for(b=this.hierarchy.length;a<b;a++){var c=this.data.hierarchy[a].sids,d=this.hierarchy[a];if(this.data.hierarchy[a].keys.length&&c){for(var e=0;e<c.length;e++){var f=c[e],h=this.getNextKeyWith(f,a,0);h&&h.apply(f)}d.matrixAutoUpdate=!1;this.data.hierarchy[a].node.updateMatrix();
+THREE.Path.prototype.bezierCurveTo=function(a,b,c,d,e,f){var g=Array.prototype.slice.call(arguments),h=this.actions[this.actions.length-1].args,h=new THREE.CubicBezierCurve(new THREE.Vector2(h[h.length-2],h[h.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d),new THREE.Vector2(e,f));this.curves.push(h);this.actions.push({action:THREE.PathActions.BEZIER_CURVE_TO,args:g})};
+THREE.Path.prototype.splineThru=function(a){var b=Array.prototype.slice.call(arguments),c=this.actions[this.actions.length-1].args,c=[new THREE.Vector2(c[c.length-2],c[c.length-1])];Array.prototype.push.apply(c,a);c=new THREE.SplineCurve(c);this.curves.push(c);this.actions.push({action:THREE.PathActions.CSPLINE_THRU,args:b})};THREE.Path.prototype.arc=function(a,b,c,d,e,f){var g=this.actions[this.actions.length-1].args;this.absarc(a+g[g.length-2],b+g[g.length-1],c,d,e,f)};
+THREE.Path.prototype.absarc=function(a,b,c,d,e,f){this.absellipse(a,b,c,c,d,e,f)};THREE.Path.prototype.ellipse=function(a,b,c,d,e,f,g){var h=this.actions[this.actions.length-1].args;this.absellipse(a+h[h.length-2],b+h[h.length-1],c,d,e,f,g)};THREE.Path.prototype.absellipse=function(a,b,c,d,e,f,g){var h=Array.prototype.slice.call(arguments),k=new THREE.EllipseCurve(a,b,c,d,e,f,g);this.curves.push(k);k=k.getPoint(1);h.push(k.x);h.push(k.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:h})};
+THREE.Path.prototype.getSpacedPoints=function(a,b){a||(a=40);for(var c=[],d=0;d<a;d++)c.push(this.getPoint(d/a));return c};
+THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return console.log("tata"),this.getSpacedPoints(a,b);a=a||12;var c=[],d,e,f,g,h,k,l,p,q,n,t,r,s;d=0;for(e=this.actions.length;d<e;d++)switch(f=this.actions[d],g=f.action,f=f.args,g){case THREE.PathActions.MOVE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.LINE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.QUADRATIC_CURVE_TO:h=f[2];k=f[3];q=f[0];n=f[1];0<c.length?(g=c[c.length-1],t=g.x,
+r=g.y):(g=this.actions[d-1].args,t=g[g.length-2],r=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b2(s,t,q,h),s=THREE.Shape.Utils.b2(s,r,n,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.BEZIER_CURVE_TO:h=f[4];k=f[5];q=f[0];n=f[1];l=f[2];p=f[3];0<c.length?(g=c[c.length-1],t=g.x,r=g.y):(g=this.actions[d-1].args,t=g[g.length-2],r=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b3(s,t,q,l,h),s=THREE.Shape.Utils.b3(s,r,n,p,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.CSPLINE_THRU:g=
+this.actions[d-1].args;s=[new THREE.Vector2(g[g.length-2],g[g.length-1])];g=a*f[0].length;s=s.concat(f[0]);s=new THREE.SplineCurve(s);for(f=1;f<=g;f++)c.push(s.getPointAt(f/g));break;case THREE.PathActions.ARC:h=f[0];k=f[1];n=f[2];l=f[3];g=f[4];q=!!f[5];t=g-l;r=2*a;for(f=1;f<=r;f++)s=f/r,q||(s=1-s),s=l+s*t,g=h+n*Math.cos(s),s=k+n*Math.sin(s),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.ELLIPSE:for(h=f[0],k=f[1],n=f[2],p=f[3],l=f[4],g=f[5],q=!!f[6],t=g-l,r=2*a,f=1;f<=r;f++)s=f/r,q||
+(s=1-s),s=l+s*t,g=h+n*Math.cos(s),s=k+p*Math.sin(s),c.push(new THREE.Vector2(g,s))}d=c[c.length-1];1E-10>Math.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c};
+THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;c<d;c++){var e=a[c],f=new THREE.Shape;f.actions=e.actions;f.curves=e.curves;b.push(f)}return b}function d(a,b){for(var c=b.length,d=!1,e=c-1,f=0;f<c;e=f++){var g=b[e],h=b[f],k=h.x-g.x,n=h.y-g.y;if(1E-10<Math.abs(n)){if(0>n&&(g=b[f],k=-k,h=b[e],n=-n),!(a.y<g.y||a.y>h.y))if(a.y==g.y){if(a.x==g.x)return!0}else{e=n*(a.x-g.x)-k*(a.y-g.y);if(0==e)return!0;0>e||(d=!d)}}else if(a.y==g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=
+h.x))return!0}return d}var e=function(a){var b,c,d,e,f=[],g=new THREE.Path;b=0;for(c=a.length;b<c;b++)d=a[b],e=d.args,d=d.action,d==THREE.PathActions.MOVE_TO&&0!=g.actions.length&&(f.push(g),g=new THREE.Path),g[d].apply(g,e);0!=g.actions.length&&f.push(g);return f}(this.actions);if(0==e.length)return[];if(!0===b)return c(e);var f,g,h,k=[];if(1==e.length)return g=e[0],h=new THREE.Shape,h.actions=g.actions,h.curves=g.curves,k.push(h),k;var l=!THREE.Shape.Utils.isClockWise(e[0].getPoints()),l=a?!l:l;
+h=[];var p=[],q=[],n=0,t;p[n]=void 0;q[n]=[];var r,s;r=0;for(s=e.length;r<s;r++)g=e[r],t=g.getPoints(),f=THREE.Shape.Utils.isClockWise(t),(f=a?!f:f)?(!l&&p[n]&&n++,p[n]={s:new THREE.Shape,p:t},p[n].s.actions=g.actions,p[n].s.curves=g.curves,l&&n++,q[n]=[]):q[n].push({h:g,p:t[0]});if(!p[0])return c(e);if(1<p.length){r=!1;s=[];g=0;for(e=p.length;g<e;g++)h[g]=[];g=0;for(e=p.length;g<e;g++)for(f=q[g],l=0;l<f.length;l++){n=f[l];t=!0;for(var u=0;u<p.length;u++)d(n.p,p[u].p)&&(g!=u&&s.push({froms:g,tos:u,
+hole:l}),t?(t=!1,h[u].push(n)):r=!0);t&&h[g].push(n)}0<s.length&&(r||(q=h))}r=0;for(s=p.length;r<s;r++)for(h=p[r].s,k.push(h),g=q[r],e=0,f=g.length;e<f;e++)h.holes.push(g[e].h);return k};THREE.Shape=function(){THREE.Path.apply(this,arguments);this.holes=[]};THREE.Shape.prototype=Object.create(THREE.Path.prototype);THREE.Shape.prototype.constructor=THREE.Shape;THREE.Shape.prototype.extrude=function(a){return new THREE.ExtrudeGeometry(this,a)};
+THREE.Shape.prototype.makeGeometry=function(a){return new THREE.ShapeGeometry(this,a)};THREE.Shape.prototype.getPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedPoints(a,this.bends);return d};THREE.Shape.prototype.getSpacedPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedSpacedPoints(a,this.bends);return d};
+THREE.Shape.prototype.extractAllPoints=function(a){return{shape:this.getTransformedPoints(a),holes:this.getPointsHoles(a)}};THREE.Shape.prototype.extractPoints=function(a){return this.useSpacedPoints?this.extractAllSpacedPoints(a):this.extractAllPoints(a)};THREE.Shape.prototype.extractAllSpacedPoints=function(a){return{shape:this.getTransformedSpacedPoints(a),holes:this.getSpacedPointsHoles(a)}};
+THREE.Shape.Utils={triangulateShape:function(a,b){function c(a,b,c){return a.x!=b.x?a.x<b.x?a.x<=c.x&&c.x<=b.x:b.x<=c.x&&c.x<=a.x:a.y<b.y?a.y<=c.y&&c.y<=b.y:b.y<=c.y&&c.y<=a.y}function d(a,b,d,e,f){var g=b.x-a.x,h=b.y-a.y,k=e.x-d.x,l=e.y-d.y,p=a.x-d.x,q=a.y-d.y,E=h*k-g*l,G=h*p-g*q;if(1E-10<Math.abs(E)){if(0<E){if(0>G||G>E)return[];k=l*p-k*q;if(0>k||k>E)return[]}else{if(0<G||G<E)return[];k=l*p-k*q;if(0<k||k<E)return[]}if(0==k)return!f||0!=G&&G!=E?[a]:[];if(k==E)return!f||0!=G&&G!=E?[b]:[];if(0==G)return[d];
+if(G==E)return[e];f=k/E;return[{x:a.x+f*g,y:a.y+f*h}]}if(0!=G||l*p!=k*q)return[];h=0==g&&0==h;k=0==k&&0==l;if(h&&k)return a.x!=d.x||a.y!=d.y?[]:[a];if(h)return c(d,e,a)?[a]:[];if(k)return c(a,b,d)?[d]:[];0!=g?(a.x<b.x?(g=a,k=a.x,h=b,a=b.x):(g=b,k=b.x,h=a,a=a.x),d.x<e.x?(b=d,E=d.x,l=e,d=e.x):(b=e,E=e.x,l=d,d=d.x)):(a.y<b.y?(g=a,k=a.y,h=b,a=b.y):(g=b,k=b.y,h=a,a=a.y),d.y<e.y?(b=d,E=d.y,l=e,d=e.y):(b=e,E=e.y,l=d,d=d.y));return k<=E?a<E?[]:a==E?f?[]:[b]:a<=d?[b,h]:[b,l]:k>d?[]:k==d?f?[]:[g]:a<=d?[g,h]:
+[g,l]}function e(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return 1E-10<Math.abs(a)?(b=g*c-d*b,0<a?0<=e&&0<=b:0<=e||0<=b):0<e}var f,g,h,k,l,p={};h=a.concat();f=0;for(g=b.length;f<g;f++)Array.prototype.push.apply(h,b[f]);f=0;for(g=h.length;f<g;f++)l=h[f].x+":"+h[f].y,void 0!==p[l]&&THREE.warn("THREE.Shape: Duplicate point",l),p[l]=f;f=function(a,b){function c(a,b){var d=h.length-1,f=a-1;0>f&&(f=d);var g=a+1;g>d&&(g=0);d=e(h[a],h[f],h[g],k[b]);
+if(!d)return!1;d=k.length-1;f=b-1;0>f&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;c<h.length;c++)if(e=c+1,e%=h.length,e=d(a,b,h[c],h[e],!0),0<e.length)return!0;return!1}function g(a,c){var e,f,h,k;for(e=0;e<l.length;e++)for(f=b[l[e]],h=0;h<f.length;h++)if(k=h+1,k%=f.length,k=d(a,c,f[h],f[k],!0),0<k.length)return!0;return!1}var h=a.concat(),k,l=[],p,q,A,E,G,F=[],z,I,U,M=0;for(p=b.length;M<p;M++)l.push(M);z=0;for(var H=2*l.length;0<l.length;){H--;if(0>
+H){console.log("Infinite Loop! Holes left:"+l.length+", Probably Hole outside Shape!");break}for(q=z;q<h.length;q++){A=h[q];p=-1;for(M=0;M<l.length;M++)if(E=l[M],G=A.x+":"+A.y+":"+E,void 0===F[G]){k=b[E];for(I=0;I<k.length;I++)if(E=k[I],c(q,I)&&!f(A,E)&&!g(A,E)){p=I;l.splice(M,1);z=h.slice(0,q+1);E=h.slice(q);I=k.slice(p);U=k.slice(0,p+1);h=z.concat(I).concat(U).concat(E);z=q;break}if(0<=p)break;F[G]=!0}if(0<=p)break}}return h}(a,b);var q=THREE.FontUtils.Triangulate(f,!1);f=0;for(g=q.length;f<g;f++)for(k=
+q[f],h=0;3>h;h++)l=k[h].x+":"+k[h].y,l=p[l],void 0!==l&&(k[h]=l);return q.concat()},isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*
+a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,b)+this.b3p1(a,c)+this.b3p2(a,d)+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.constructor=THREE.LineCurve;THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};
+THREE.LineCurve.prototype.getTangent=function(a){return this.v2.clone().sub(this.v1).normalize()};THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.constructor=THREE.QuadraticBezierCurve;
+THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b=new THREE.Vector2;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return b};THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b=new THREE.Vector2;b.x=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);return b.normalize()};
+THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.CubicBezierCurve.prototype.constructor=THREE.CubicBezierCurve;THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};
+THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.constructor=THREE.SplineCurve;
+THREE.SplineCurve.prototype.getPoint=function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector2;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);return c};THREE.EllipseCurve=function(a,b,c,d,e,f,g){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=g};
+THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype);THREE.EllipseCurve.prototype.constructor=THREE.EllipseCurve;THREE.EllipseCurve.prototype.getPoint=function(a){var b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);a=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;b=new THREE.Vector2;b.x=this.aX+this.xRadius*Math.cos(a);b.y=this.aY+this.yRadius*Math.sin(a);return b};
+THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype);THREE.ArcCurve.prototype.constructor=THREE.ArcCurve;THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});
+THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);b.z=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return b});
+THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);b.y=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b.z=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return b});
+THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
+THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-0;var c=Math.floor(a);a-=c;var c=c+(0<c?0:(Math.floor(Math.abs(c)/b.length)+1)*b.length),d=b[(c-1)%b.length],e=b[c%b.length],f=b[(c+1)%b.length],b=b[(c+2)%b.length],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
+THREE.AnimationHandler={LINEAR:0,CATMULLROM:1,CATMULLROM_FORWARD:2,add:function(){THREE.warn("THREE.AnimationHandler.add() has been deprecated.")},get:function(){THREE.warn("THREE.AnimationHandler.get() has been deprecated.")},remove:function(){THREE.warn("THREE.AnimationHandler.remove() has been deprecated.")},animations:[],init:function(a){if(!0===a.initialized)return a;for(var b=0;b<a.hierarchy.length;b++){for(var c=0;c<a.hierarchy[b].keys.length;c++)if(0>a.hierarchy[b].keys[c].time&&(a.hierarchy[b].keys[c].time=
+0),void 0!==a.hierarchy[b].keys[c].rot&&!(a.hierarchy[b].keys[c].rot instanceof THREE.Quaternion)){var d=a.hierarchy[b].keys[c].rot;a.hierarchy[b].keys[c].rot=(new THREE.Quaternion).fromArray(d)}if(a.hierarchy[b].keys.length&&void 0!==a.hierarchy[b].keys[0].morphTargets){d={};for(c=0;c<a.hierarchy[b].keys.length;c++)for(var e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++){var f=a.hierarchy[b].keys[c].morphTargets[e];d[f]=-1}a.hierarchy[b].usedMorphTargets=d;for(c=0;c<a.hierarchy[b].keys.length;c++){var g=
+{};for(f in d){for(e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++)if(a.hierarchy[b].keys[c].morphTargets[e]===f){g[f]=a.hierarchy[b].keys[c].morphTargetsInfluences[e];break}e===a.hierarchy[b].keys[c].morphTargets.length&&(g[f]=0)}a.hierarchy[b].keys[c].morphTargetsInfluences=g}}for(c=1;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].time===a.hierarchy[b].keys[c-1].time&&(a.hierarchy[b].keys.splice(c,1),c--);for(c=0;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].index=c}a.initialized=
+!0;return a},parse:function(a){var b=function(a,c){c.push(a);for(var d=0;d<a.children.length;d++)b(a.children[d],c)},c=[];if(a instanceof THREE.SkinnedMesh)for(var d=0;d<a.skeleton.bones.length;d++)c.push(a.skeleton.bones[d]);else b(a,c);return c},play:function(a){-1===this.animations.indexOf(a)&&this.animations.push(a)},stop:function(a){a=this.animations.indexOf(a);-1!==a&&this.animations.splice(a,1)},update:function(a){for(var b=0;b<this.animations.length;b++)this.animations[b].resetBlendWeights();
+for(b=0;b<this.animations.length;b++)this.animations[b].update(a)}};THREE.Animation=function(a,b){this.root=a;this.data=THREE.AnimationHandler.init(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=1;this.isPlaying=!1;this.loop=!0;this.weight=0;this.interpolationType=THREE.AnimationHandler.LINEAR};
+THREE.Animation.prototype={constructor:THREE.Animation,keyTypes:["pos","rot","scl"],play:function(a,b){this.currentTime=void 0!==a?a:0;this.weight=void 0!==b?b:1;this.isPlaying=!0;this.reset();THREE.AnimationHandler.play(this)},stop:function(){this.isPlaying=!1;THREE.AnimationHandler.stop(this)},reset:function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a];void 0===c.animationCache&&(c.animationCache={animations:{},blending:{positionWeight:0,quaternionWeight:0,scaleWeight:0}});
+var d=this.data.name,e=c.animationCache.animations,f=e[d];void 0===f&&(f={prevKey:{pos:0,rot:0,scl:0},nextKey:{pos:0,rot:0,scl:0},originalMatrix:c.matrix},e[d]=f);for(c=0;3>c;c++){for(var d=this.keyTypes[c],e=this.data.hierarchy[a].keys[0],g=this.getNextKeyWith(d,a,1);g.time<this.currentTime&&g.index>e.index;)e=g,g=this.getNextKeyWith(d,a,g.index+1);f.prevKey[d]=e;f.nextKey[d]=g}}},resetBlendWeights:function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a].animationCache;void 0!==
+c&&(c=c.blending,c.positionWeight=0,c.quaternionWeight=0,c.scaleWeight=0)}},update:function(){var a=[],b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Quaternion,e=function(a,b){var c=[],d=[],e,q,n,t,r,s;e=(a.length-1)*b;q=Math.floor(e);e-=q;c[0]=0===q?q:q-1;c[1]=q;c[2]=q>a.length-2?q:q+1;c[3]=q>a.length-3?q:q+2;q=a[c[0]];t=a[c[1]];r=a[c[2]];s=a[c[3]];c=e*e;n=e*c;d[0]=f(q[0],t[0],r[0],s[0],e,c,n);d[1]=f(q[1],t[1],r[1],s[1],e,c,n);d[2]=f(q[2],t[2],r[2],s[2],e,c,n);return d},f=function(a,b,c,d,
+e,f,n){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*n+(-3*(b-c)-2*a-d)*f+a*e+b};return function(f){if(!1!==this.isPlaying&&(this.currentTime+=f*this.timeScale,0!==this.weight)){f=this.data.length;if(this.currentTime>f||0>this.currentTime)this.loop?(this.currentTime%=f,0>this.currentTime&&(this.currentTime+=f),this.reset()):this.stop();f=0;for(var h=this.hierarchy.length;f<h;f++)for(var k=this.hierarchy[f],l=k.animationCache.animations[this.data.name],p=k.animationCache.blending,q=0;3>q;q++){var n=this.keyTypes[q],
+t=l.prevKey[n],r=l.nextKey[n];if(0<this.timeScale&&r.time<=this.currentTime||0>this.timeScale&&t.time>=this.currentTime){t=this.data.hierarchy[f].keys[0];for(r=this.getNextKeyWith(n,f,1);r.time<this.currentTime&&r.index>t.index;)t=r,r=this.getNextKeyWith(n,f,r.index+1);l.prevKey[n]=t;l.nextKey[n]=r}var s=(this.currentTime-t.time)/(r.time-t.time),u=t[n],v=r[n];0>s&&(s=0);1<s&&(s=1);if("pos"===n)if(this.interpolationType===THREE.AnimationHandler.LINEAR)c.x=u[0]+(v[0]-u[0])*s,c.y=u[1]+(v[1]-u[1])*s,
+c.z=u[2]+(v[2]-u[2])*s,t=this.weight/(this.weight+p.positionWeight),k.position.lerp(c,t),p.positionWeight+=this.weight;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)a[0]=this.getPrevKeyWith("pos",f,t.index-1).pos,a[1]=u,a[2]=v,a[3]=this.getNextKeyWith("pos",f,r.index+1).pos,s=.33*s+.33,r=e(a,s),t=this.weight/(this.weight+p.positionWeight),p.positionWeight+=this.weight,n=k.position,n.x+=(r[0]-n.x)*t,n.y+=(r[1]-
+n.y)*t,n.z+=(r[2]-n.z)*t,this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD&&(s=e(a,1.01*s),b.set(s[0],s[1],s[2]),b.sub(n),b.y=0,b.normalize(),s=Math.atan2(b.x,b.z),k.rotation.set(0,s,0))}else"rot"===n?(THREE.Quaternion.slerp(u,v,d,s),0===p.quaternionWeight?(k.quaternion.copy(d),p.quaternionWeight=this.weight):(t=this.weight/(this.weight+p.quaternionWeight),THREE.Quaternion.slerp(k.quaternion,d,k.quaternion,t),p.quaternionWeight+=this.weight)):"scl"===n&&(c.x=u[0]+(v[0]-u[0])*s,c.y=
+u[1]+(v[1]-u[1])*s,c.z=u[2]+(v[2]-u[2])*s,t=this.weight/(this.weight+p.scaleWeight),k.scale.lerp(c,t),p.scaleWeight+=this.weight)}return!0}}}(),getNextKeyWith:function(a,b,c){var d=this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?c<d.length-1?c:d.length-1:c%d.length;c<d.length;c++)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[0]},getPrevKeyWith:function(a,b,c){var d=
+this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?0<c?c:0:0<=c?c:c+d.length;0<=c;c--)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[d.length-1]}};
+THREE.KeyFrameAnimation=function(a){this.root=a.node;this.data=THREE.AnimationHandler.init(a);this.hierarchy=THREE.AnimationHandler.parse(this.root);this.currentTime=0;this.timeScale=.001;this.isPlaying=!1;this.loop=this.isPaused=!0;a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.data.hierarchy[a].sids,d=this.hierarchy[a];if(this.data.hierarchy[a].keys.length&&c){for(var e=0;e<c.length;e++){var f=c[e],g=this.getNextKeyWith(f,a,0);g&&g.apply(f)}d.matrixAutoUpdate=!1;this.data.hierarchy[a].node.updateMatrix();
 d.matrixWorldNeedsUpdate=!0}}};
-THREE.KeyFrameAnimation.prototype.play=function(a,b){if(!this.isPlaying){this.isPlaying=!0;this.loop=void 0!==a?a:!0;this.currentTime=void 0!==b?b:0;this.startTimeMs=b;this.startTime=1E7;this.endTime=-this.startTime;var c,d=this.hierarchy.length,e,f;for(c=0;c<d;c++)e=this.hierarchy[c],f=this.data.hierarchy[c],void 0===f.animationCache&&(f.animationCache={},f.animationCache.prevKey=null,f.animationCache.nextKey=null,f.animationCache.originalMatrix=e instanceof THREE.Bone?e.skinMatrix:e.matrix),e=this.data.hierarchy[c].keys,
-e.length&&(f.animationCache.prevKey=e[0],f.animationCache.nextKey=e[1],this.startTime=Math.min(e[0].time,this.startTime),this.endTime=Math.max(e[e.length-1].time,this.endTime));this.update(0)}this.isPaused=!1;THREE.AnimationHandler.addToUpdate(this)};THREE.KeyFrameAnimation.prototype.pause=function(){this.isPaused?THREE.AnimationHandler.addToUpdate(this):THREE.AnimationHandler.removeFromUpdate(this);this.isPaused=!this.isPaused};
-THREE.KeyFrameAnimation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.removeFromUpdate(this);for(var a=0;a<this.data.hierarchy.length;a++){var b=this.hierarchy[a],c=this.data.hierarchy[a];if(void 0!==c.animationCache){var d=c.animationCache.originalMatrix;b instanceof THREE.Bone?(d.copy(b.skinMatrix),b.skinMatrix=d):(d.copy(b.matrix),b.matrix=d);delete c.animationCache}}};
-THREE.KeyFrameAnimation.prototype.update=function(a){if(this.isPlaying){var b,c,d,e,f=this.data.JIT.hierarchy,h,g,i;g=this.currentTime+=a*this.timeScale;h=this.currentTime%=this.data.length;h<this.startTimeMs&&(h=this.currentTime=this.startTimeMs+h);e=parseInt(Math.min(h*this.data.fps,this.data.length*this.data.fps),10);if((i=h<g)&&!this.loop){for(var a=0,k=this.hierarchy.length;a<k;a++){var m=this.data.hierarchy[a].keys,f=this.data.hierarchy[a].sids;d=m.length-1;e=this.hierarchy[a];if(m.length){for(m=
-0;m<f.length;m++)h=f[m],(g=this.getPrevKeyWith(h,a,d))&&g.apply(h);this.data.hierarchy[a].node.updateMatrix();e.matrixWorldNeedsUpdate=!0}}this.stop()}else if(!(h<this.startTime)){a=0;for(k=this.hierarchy.length;a<k;a++){d=this.hierarchy[a];b=this.data.hierarchy[a];var m=b.keys,l=b.animationCache;if(this.JITCompile&&void 0!==f[a][e])d instanceof THREE.Bone?(d.skinMatrix=f[a][e],d.matrixWorldNeedsUpdate=!1):(d.matrix=f[a][e],d.matrixWorldNeedsUpdate=!0);else if(m.length){this.JITCompile&&l&&(d instanceof
-THREE.Bone?d.skinMatrix=l.originalMatrix:d.matrix=l.originalMatrix);b=l.prevKey;c=l.nextKey;if(b&&c){if(c.time<=g){if(i&&this.loop){b=m[0];for(c=m[1];c.time<h;)b=c,c=m[b.index+1]}else if(!i)for(var p=m.length-1;c.time<h&&c.index!==p;)b=c,c=m[b.index+1];l.prevKey=b;l.nextKey=c}c.time>=h?b.interpolate(c,h):b.interpolate(c,c.time)}this.data.hierarchy[a].node.updateMatrix();d.matrixWorldNeedsUpdate=!0}}if(this.JITCompile&&void 0===f[0][e]){this.hierarchy[0].updateMatrixWorld(!0);for(a=0;a<this.hierarchy.length;a++)f[a][e]=
-this.hierarchy[a]instanceof THREE.Bone?this.hierarchy[a].skinMatrix.clone():this.hierarchy[a].matrix.clone()}}}};THREE.KeyFrameAnimation.prototype.getNextKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c<b.length;c++)if(b[c].hasTarget(a))return b[c];return b[0]};THREE.KeyFrameAnimation.prototype.getPrevKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c=0<=c?c:c+b.length;0<=c;c--)if(b[c].hasTarget(a))return b[c];return b[b.length-1]};THREE.CubeCamera=function(a,b,c){THREE.Object3D.call(this);var d=new THREE.PerspectiveCamera(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new THREE.Vector3(1,0,0));this.add(d);var e=new THREE.PerspectiveCamera(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new THREE.Vector3(-1,0,0));this.add(e);var f=new THREE.PerspectiveCamera(90,1,a,b);f.up.set(0,0,1);f.lookAt(new THREE.Vector3(0,1,0));this.add(f);var h=new THREE.PerspectiveCamera(90,1,a,b);h.up.set(0,0,-1);h.lookAt(new THREE.Vector3(0,-1,0));this.add(h);var g=new THREE.PerspectiveCamera(90,
-1,a,b);g.up.set(0,-1,0);g.lookAt(new THREE.Vector3(0,0,1));this.add(g);var i=new THREE.PerspectiveCamera(90,1,a,b);i.up.set(0,-1,0);i.lookAt(new THREE.Vector3(0,0,-1));this.add(i);this.renderTarget=new THREE.WebGLRenderTargetCube(c,c,{format:THREE.RGBFormat,magFilter:THREE.LinearFilter,minFilter:THREE.LinearFilter});this.updateCubeMap=function(a,b){var c=this.renderTarget,p=c.generateMipmaps;c.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=1;a.render(b,e,c);c.activeCubeFace=
-2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,h,c);c.activeCubeFace=4;a.render(b,g,c);c.generateMipmaps=p;c.activeCubeFace=5;a.render(b,i,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.CombinedCamera=function(a,b,c,d,e,f,h){THREE.Camera.call(this);this.fov=c;this.left=-a/2;this.right=a/2;this.top=b/2;this.bottom=-b/2;this.cameraO=new THREE.OrthographicCamera(a/-2,a/2,b/2,b/-2,f,h);this.cameraP=new THREE.PerspectiveCamera(c,a/b,d,e);this.zoom=1;this.toPerspective()};THREE.CombinedCamera.prototype=Object.create(THREE.Camera.prototype);
-THREE.CombinedCamera.prototype.toPerspective=function(){this.near=this.cameraP.near;this.far=this.cameraP.far;this.cameraP.fov=this.fov/this.zoom;this.cameraP.updateProjectionMatrix();this.projectionMatrix=this.cameraP.projectionMatrix;this.inPerspectiveMode=!0;this.inOrthographicMode=!1};
-THREE.CombinedCamera.prototype.toOrthographic=function(){var a=this.cameraP.aspect,b=(this.cameraP.near+this.cameraP.far)/2,b=Math.tan(this.fov/2)*b,a=2*b*a/2,b=b/this.zoom,a=a/this.zoom;this.cameraO.left=-a;this.cameraO.right=a;this.cameraO.top=b;this.cameraO.bottom=-b;this.cameraO.updateProjectionMatrix();this.near=this.cameraO.near;this.far=this.cameraO.far;this.projectionMatrix=this.cameraO.projectionMatrix;this.inPerspectiveMode=!1;this.inOrthographicMode=!0};
-THREE.CombinedCamera.prototype.setSize=function(a,b){this.cameraP.aspect=a/b;this.left=-a/2;this.right=a/2;this.top=b/2;this.bottom=-b/2};THREE.CombinedCamera.prototype.setFov=function(a){this.fov=a;this.inPerspectiveMode?this.toPerspective():this.toOrthographic()};THREE.CombinedCamera.prototype.updateProjectionMatrix=function(){this.inPerspectiveMode?this.toPerspective():(this.toPerspective(),this.toOrthographic())};
-THREE.CombinedCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);var c=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.setFov(c);return c};THREE.CombinedCamera.prototype.setZoom=function(a){this.zoom=a;this.inPerspectiveMode?this.toPerspective():this.toOrthographic()};THREE.CombinedCamera.prototype.toFrontView=function(){this.rotation.x=0;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};
-THREE.CombinedCamera.prototype.toBackView=function(){this.rotation.x=0;this.rotation.y=Math.PI;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toLeftView=function(){this.rotation.x=0;this.rotation.y=-Math.PI/2;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toRightView=function(){this.rotation.x=0;this.rotation.y=Math.PI/2;this.rotation.z=0;this.rotationAutoUpdate=!1};
-THREE.CombinedCamera.prototype.toTopView=function(){this.rotation.x=-Math.PI/2;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toBottomView=function(){this.rotation.x=Math.PI/2;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.radius=a=a||50;this.segments=b=void 0!==b?Math.max(3,b):8;this.thetaStart=c=void 0!==c?c:0;this.thetaLength=d=void 0!==d?d:2*Math.PI;var e,f=[];e=new THREE.Vector3;var h=new THREE.Vector2(0.5,0.5);this.vertices.push(e);f.push(h);for(e=0;e<=b;e++){var g=new THREE.Vector3,i=c+e/b*d;g.x=a*Math.cos(i);g.y=a*Math.sin(i);this.vertices.push(g);f.push(new THREE.Vector2((g.x/a+1)/2,(g.y/a+1)/2))}c=new THREE.Vector3(0,0,1);for(e=1;e<=b;e++)this.faces.push(new THREE.Face3(e,
-e+1,0,[c,c,c])),this.faceVertexUvs[0].push([f[e],f[e+1],h]);this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,a)};THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CubeGeometry=function(a,b,c,d,e,f){function h(a,b,c,d,e,f,h,n){var r,q=g.widthSegments,u=g.heightSegments,w=e/2,z=f/2,B=g.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)r="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)r="y",u=g.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)r="x",q=g.depthSegments;var D=q+1,x=u+1,F=e/q,A=f/u,O=new THREE.Vector3;O[r]=0<h?1:-1;for(e=0;e<x;e++)for(f=0;f<D;f++){var C=new THREE.Vector3;C[a]=(f*F-w)*c;C[b]=(e*A-z)*d;C[r]=h;g.vertices.push(C)}for(e=
-0;e<u;e++)for(f=0;f<q;f++)z=f+D*e,a=f+D*(e+1),b=f+1+D*(e+1),c=f+1+D*e,d=new THREE.Vector2(f/q,1-e/u),h=new THREE.Vector2(f/q,1-(e+1)/u),r=new THREE.Vector2((f+1)/q,1-(e+1)/u),w=new THREE.Vector2((f+1)/q,1-e/u),z=new THREE.Face3(z+B,a+B,c+B),z.normal.copy(O),z.vertexNormals.push(O.clone(),O.clone(),O.clone()),z.materialIndex=n,g.faces.push(z),g.faceVertexUvs[0].push([d,h,w]),z=new THREE.Face3(a+B,b+B,c+B),z.normal.copy(O),z.vertexNormals.push(O.clone(),O.clone(),O.clone()),z.materialIndex=n,g.faces.push(z),
-g.faceVertexUvs[0].push([h.clone(),r,w.clone()])}THREE.Geometry.call(this);var g=this;this.width=a;this.height=b;this.depth=c;this.widthSegments=d||1;this.heightSegments=e||1;this.depthSegments=f||1;a=this.width/2;b=this.height/2;c=this.depth/2;h("z","y",-1,-1,this.depth,this.height,a,0);h("z","y",1,-1,this.depth,this.height,-a,1);h("x","z",1,1,this.width,this.depth,b,2);h("x","z",1,-1,this.width,this.depth,-b,3);h("x","y",1,-1,this.width,this.height,c,4);h("x","y",-1,-1,this.width,this.height,-c,
-5);this.computeCentroids();this.mergeVertices()};THREE.CubeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CylinderGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.radiusTop=a=void 0!==a?a:20;this.radiusBottom=b=void 0!==b?b:20;this.height=c=void 0!==c?c:100;this.radialSegments=d=d||8;this.heightSegments=e=e||1;this.openEnded=f=void 0!==f?f:!1;var h=c/2,g,i,k=[],m=[];for(i=0;i<=e;i++){var l=[],p=[],s=i/e,t=s*(b-a)+a;for(g=0;g<=d;g++){var n=g/d,r=new THREE.Vector3;r.x=t*Math.sin(2*n*Math.PI);r.y=-s*c+h;r.z=t*Math.cos(2*n*Math.PI);this.vertices.push(r);l.push(this.vertices.length-1);p.push(new THREE.Vector2(n,
-1-s))}k.push(l);m.push(p)}c=(b-a)/c;for(g=0;g<d;g++){0!==a?(l=this.vertices[k[0][g]].clone(),p=this.vertices[k[0][g+1]].clone()):(l=this.vertices[k[1][g]].clone(),p=this.vertices[k[1][g+1]].clone());l.setY(Math.sqrt(l.x*l.x+l.z*l.z)*c).normalize();p.setY(Math.sqrt(p.x*p.x+p.z*p.z)*c).normalize();for(i=0;i<e;i++){var s=k[i][g],t=k[i+1][g],n=k[i+1][g+1],r=k[i][g+1],q=l.clone(),u=l.clone(),w=p.clone(),z=p.clone(),B=m[i][g].clone(),D=m[i+1][g].clone(),x=m[i+1][g+1].clone(),F=m[i][g+1].clone();this.faces.push(new THREE.Face3(s,
-t,r,[q,u,z]));this.faceVertexUvs[0].push([B,D,F]);this.faces.push(new THREE.Face3(t,n,r,[u,w,z]));this.faceVertexUvs[0].push([D,x,F])}}if(!1===f&&0<a){this.vertices.push(new THREE.Vector3(0,h,0));for(g=0;g<d;g++)s=k[0][g],t=k[0][g+1],n=this.vertices.length-1,q=new THREE.Vector3(0,1,0),u=new THREE.Vector3(0,1,0),w=new THREE.Vector3(0,1,0),B=m[0][g].clone(),D=m[0][g+1].clone(),x=new THREE.Vector2(D.u,0),this.faces.push(new THREE.Face3(s,t,n,[q,u,w])),this.faceVertexUvs[0].push([B,D,x])}if(!1===f&&0<
-b){this.vertices.push(new THREE.Vector3(0,-h,0));for(g=0;g<d;g++)s=k[i][g+1],t=k[i][g],n=this.vertices.length-1,q=new THREE.Vector3(0,-1,0),u=new THREE.Vector3(0,-1,0),w=new THREE.Vector3(0,-1,0),B=m[i][g+1].clone(),D=m[i][g].clone(),x=new THREE.Vector2(D.u,1),this.faces.push(new THREE.Face3(s,t,n,[q,u,w])),this.faceVertexUvs[0].push([B,D,x])}this.computeCentroids();this.computeFaceNormals()};THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),a=a instanceof Array?a:[a],this.shapebb=a[a.length-1].getBoundingBox(),this.addShapeList(a,b),this.computeCentroids(),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d<c;d++)this.addShape(a[d],b)};
-THREE.ExtrudeGeometry.prototype.addShape=function(a,b){function c(a,b,c){b||console.log("die");return b.clone().multiplyScalar(c).add(a)}function d(a,b,c){var d=THREE.ExtrudeGeometry.__v1,e=THREE.ExtrudeGeometry.__v2,f=THREE.ExtrudeGeometry.__v3,g=THREE.ExtrudeGeometry.__v4,h=THREE.ExtrudeGeometry.__v5,i=THREE.ExtrudeGeometry.__v6;d.set(a.x-b.x,a.y-b.y);e.set(a.x-c.x,a.y-c.y);d=d.normalize();e=e.normalize();f.set(-d.y,d.x);g.set(e.y,-e.x);h.copy(a).add(f);i.copy(a).add(g);if(h.equals(i))return g.clone();
-h.copy(b).add(f);i.copy(c).add(g);f=d.dot(g);g=i.sub(h).dot(g);0===f&&(console.log("Either infinite or no solutions!"),0===g?console.log("Its finite solutions."):console.log("Too bad, no solutions."));g/=f;return 0>g?(b=Math.atan2(b.y-a.y,b.x-a.x),a=Math.atan2(c.y-a.y,c.x-a.x),b>a&&(a+=2*Math.PI),c=(b+a)/2,a=-Math.cos(c),c=-Math.sin(c),new THREE.Vector2(a,c)):d.multiplyScalar(g).add(h).sub(a).clone()}function e(c,d){var e,f;for(N=c.length;0<=--N;){e=N;f=N-1;0>f&&(f=c.length-1);for(var g=0,h=s+2*m,
-g=0;g<h;g++){var i=ba*g,k=ba*(g+1),l=d+e+i,i=d+f+i,p=d+f+k,k=d+e+k,n=c,q=g,r=h,t=e,v=f,l=l+E,i=i+E,p=p+E,k=k+E;C.faces.push(new THREE.Face3(l,i,k,null,null,u));C.faces.push(new THREE.Face3(i,p,k,null,null,u));l=w.generateSideWallUV(C,a,n,b,l,i,p,k,q,r,t,v);C.faceVertexUvs[0].push([l[0],l[1],l[3]]);C.faceVertexUvs[0].push([l[1],l[2],l[3]])}}}function f(a,b,c){C.vertices.push(new THREE.Vector3(a,b,c))}function h(c,d,e,f){c+=E;d+=E;e+=E;C.faces.push(new THREE.Face3(c,d,e,null,null,q));c=f?w.generateBottomUV(C,
-a,b,c,d,e):w.generateTopUV(C,a,b,c,d,e);C.faceVertexUvs[0].push(c)}var g=void 0!==b.amount?b.amount:100,i=void 0!==b.bevelThickness?b.bevelThickness:6,k=void 0!==b.bevelSize?b.bevelSize:i-2,m=void 0!==b.bevelSegments?b.bevelSegments:3,l=void 0!==b.bevelEnabled?b.bevelEnabled:!0,p=void 0!==b.curveSegments?b.curveSegments:12,s=void 0!==b.steps?b.steps:1,t=b.extrudePath,n,r=!1,q=b.material,u=b.extrudeMaterial,w=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,z,B,D,x;t&&(n=
-t.getSpacedPoints(s),r=!0,l=!1,z=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(t,s,!1),B=new THREE.Vector3,D=new THREE.Vector3,x=new THREE.Vector3);l||(k=i=m=0);var F,A,O,C=this,E=this.vertices.length,p=a.extractPoints(p),I=p.shape,p=p.holes;if(t=!THREE.Shape.Utils.isClockWise(I)){I=I.reverse();A=0;for(O=p.length;A<O;A++)F=p[A],THREE.Shape.Utils.isClockWise(F)&&(p[A]=F.reverse());t=!1}var y=THREE.Shape.Utils.triangulateShape(I,p),t=I;A=0;for(O=p.length;A<O;A++)F=p[A],I=I.concat(F);
-var v,G,R,J,ba=I.length,oa=y.length,pa=[],N=0,M=t.length;v=M-1;for(G=N+1;N<M;N++,v++,G++)v===M&&(v=0),G===M&&(G=0),pa[N]=d(t[N],t[v],t[G]);var Q=[],K,ca=pa.concat();A=0;for(O=p.length;A<O;A++){F=p[A];K=[];N=0;M=F.length;v=M-1;for(G=N+1;N<M;N++,v++,G++)v===M&&(v=0),G===M&&(G=0),K[N]=d(F[N],F[v],F[G]);Q.push(K);ca=ca.concat(K)}for(v=0;v<m;v++){F=v/m;R=i*(1-F);G=k*Math.sin(F*Math.PI/2);N=0;for(M=t.length;N<M;N++)J=c(t[N],pa[N],G),f(J.x,J.y,-R);A=0;for(O=p.length;A<O;A++){F=p[A];K=Q[A];N=0;for(M=F.length;N<
-M;N++)J=c(F[N],K[N],G),f(J.x,J.y,-R)}}G=k;for(N=0;N<ba;N++)J=l?c(I[N],ca[N],G):I[N],r?(D.copy(z.normals[0]).multiplyScalar(J.x),B.copy(z.binormals[0]).multiplyScalar(J.y),x.copy(n[0]).add(D).add(B),f(x.x,x.y,x.z)):f(J.x,J.y,0);for(F=1;F<=s;F++)for(N=0;N<ba;N++)J=l?c(I[N],ca[N],G):I[N],r?(D.copy(z.normals[F]).multiplyScalar(J.x),B.copy(z.binormals[F]).multiplyScalar(J.y),x.copy(n[F]).add(D).add(B),f(x.x,x.y,x.z)):f(J.x,J.y,g/s*F);for(v=m-1;0<=v;v--){F=v/m;R=i*(1-F);G=k*Math.sin(F*Math.PI/2);N=0;for(M=
-t.length;N<M;N++)J=c(t[N],pa[N],G),f(J.x,J.y,g+R);A=0;for(O=p.length;A<O;A++){F=p[A];K=Q[A];N=0;for(M=F.length;N<M;N++)J=c(F[N],K[N],G),r?f(J.x,J.y+n[s-1].y,n[s-1].x+R):f(J.x,J.y,g+R)}}if(l){i=0*ba;for(N=0;N<oa;N++)g=y[N],h(g[2]+i,g[1]+i,g[0]+i,!0);i=ba*(s+2*m);for(N=0;N<oa;N++)g=y[N],h(g[0]+i,g[1]+i,g[2]+i,!1)}else{for(N=0;N<oa;N++)g=y[N],h(g[2],g[1],g[0],!0);for(N=0;N<oa;N++)g=y[N],h(g[0]+ba*s,g[1]+ba*s,g[2]+ba*s,!1)}g=0;e(t,g);g+=t.length;A=0;for(O=p.length;A<O;A++)F=p[A],e(F,g),g+=F.length};
-THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(a,b,c,d,e,f){b=a.vertices[e].x;e=a.vertices[e].y;c=a.vertices[f].x;f=a.vertices[f].y;return[new THREE.Vector2(a.vertices[d].x,a.vertices[d].y),new THREE.Vector2(b,e),new THREE.Vector2(c,f)]},generateBottomUV:function(a,b,c,d,e,f){return this.generateTopUV(a,b,c,d,e,f)},generateSideWallUV:function(a,b,c,d,e,f,h,g){var b=a.vertices[e].x,c=a.vertices[e].y,e=a.vertices[e].z,d=a.vertices[f].x,i=a.vertices[f].y,f=a.vertices[f].z,k=a.vertices[h].x,
-m=a.vertices[h].y,h=a.vertices[h].z,l=a.vertices[g].x,p=a.vertices[g].y,a=a.vertices[g].z;return 0.01>Math.abs(c-i)?[new THREE.Vector2(b,1-e),new THREE.Vector2(d,1-f),new THREE.Vector2(k,1-h),new THREE.Vector2(l,1-a)]:[new THREE.Vector2(c,1-e),new THREE.Vector2(i,1-f),new THREE.Vector2(m,1-h),new THREE.Vector2(p,1-a)]}};THREE.ExtrudeGeometry.__v1=new THREE.Vector2;THREE.ExtrudeGeometry.__v2=new THREE.Vector2;THREE.ExtrudeGeometry.__v3=new THREE.Vector2;THREE.ExtrudeGeometry.__v4=new THREE.Vector2;
-THREE.ExtrudeGeometry.__v5=new THREE.Vector2;THREE.ExtrudeGeometry.__v6=new THREE.Vector2;THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);!1===a instanceof Array&&(a=[a]);this.shapebb=a[a.length-1].getBoundingBox();this.addShapeList(a,b);this.computeCentroids();this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c<d;c++)this.addShape(a[c],b);return this};
-THREE.ShapeGeometry.prototype.addShape=function(a,b){void 0===b&&(b={});var c=b.material,d=void 0===b.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:b.UVGenerator,e,f,h,g=this.vertices.length;e=a.extractPoints(void 0!==b.curveSegments?b.curveSegments:12);var i=e.shape,k=e.holes;if(!THREE.Shape.Utils.isClockWise(i)){i=i.reverse();e=0;for(f=k.length;e<f;e++)h=k[e],THREE.Shape.Utils.isClockWise(h)&&(k[e]=h.reverse())}var m=THREE.Shape.Utils.triangulateShape(i,k);e=0;for(f=k.length;e<f;e++)h=k[e],
-i=i.concat(h);k=i.length;f=m.length;for(e=0;e<k;e++)h=i[e],this.vertices.push(new THREE.Vector3(h.x,h.y,0));for(e=0;e<f;e++)k=m[e],i=k[0]+g,h=k[1]+g,k=k[2]+g,this.faces.push(new THREE.Face3(i,h,k,null,null,c)),this.faceVertexUvs[0].push(d.generateBottomUV(this,a,b,i,h,k))};THREE.LatheGeometry=function(a,b,c,d){THREE.Geometry.call(this);for(var b=b||12,c=c||0,d=d||2*Math.PI,e=1/(a.length-1),f=1/b,h=0,g=b;h<=g;h++)for(var i=c+h*f*d,k=Math.cos(i),m=Math.sin(i),i=0,l=a.length;i<l;i++){var p=a[i],s=new THREE.Vector3;s.x=k*p.x-m*p.y;s.y=m*p.x+k*p.y;s.z=p.z;this.vertices.push(s)}c=a.length;h=0;for(g=b;h<g;h++){i=0;for(l=a.length-1;i<l;i++){var b=m=i+c*h,d=m+c,k=m+1+c,m=m+1,p=h*f,s=i*e,t=p+f,n=s+e;this.faces.push(new THREE.Face3(b,d,m));this.faceVertexUvs[0].push([new THREE.Vector2(p,
-s),new THREE.Vector2(t,s),new THREE.Vector2(p,n)]);this.faces.push(new THREE.Face3(d,k,m));this.faceVertexUvs[0].push([new THREE.Vector2(t,s),new THREE.Vector2(t,n),new THREE.Vector2(p,n)])}}this.mergeVertices();this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.PlaneGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.width=a;this.height=b;this.widthSegments=c||1;this.heightSegments=d||1;for(var e=a/2,f=b/2,c=this.widthSegments,d=this.heightSegments,h=c+1,g=d+1,i=this.width/c,k=this.height/d,m=new THREE.Vector3(0,0,1),a=0;a<g;a++)for(b=0;b<h;b++)this.vertices.push(new THREE.Vector3(b*i-e,-(a*k-f),0));for(a=0;a<d;a++)for(b=0;b<c;b++){var l=b+h*a,e=b+h*(a+1),f=b+1+h*(a+1),g=b+1+h*a,i=new THREE.Vector2(b/c,1-a/d),k=new THREE.Vector2(b/c,1-(a+1)/
-d),p=new THREE.Vector2((b+1)/c,1-(a+1)/d),s=new THREE.Vector2((b+1)/c,1-a/d),l=new THREE.Face3(l,e,g);l.normal.copy(m);l.vertexNormals.push(m.clone(),m.clone(),m.clone());this.faces.push(l);this.faceVertexUvs[0].push([i,k,s]);l=new THREE.Face3(e,f,g);l.normal.copy(m);l.vertexNormals.push(m.clone(),m.clone(),m.clone());this.faces.push(l);this.faceVertexUvs[0].push([k.clone(),p,s.clone()])}this.computeCentroids()};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.RingGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);for(var a=a||0,b=b||50,e=void 0!==e?e:0,f=void 0!==f?f:2*Math.PI,c=void 0!==c?Math.max(3,c):8,d=void 0!==d?Math.max(3,d):8,h=[],g=a,i=(b-a)/d,a=0;a<=d;a++){for(b=0;b<=c;b++){var k=new THREE.Vector3,m=e+b/c*f;k.x=g*Math.cos(m);k.y=g*Math.sin(m);this.vertices.push(k);h.push(new THREE.Vector2((k.x/g+1)/2,-(k.y/g+1)/2+1))}g+=i}e=new THREE.Vector3(0,0,1);for(a=0;a<d;a++){f=a*c;for(b=0;b<=c;b++){var m=b+f,i=m+a,k=m+c+a,l=m+c+1+a;this.faces.push(new THREE.Face3(i,
-k,l,[e,e,e]));this.faceVertexUvs[0].push([h[i],h[k],h[l]]);i=m+a;k=m+c+1+a;l=m+1+a;this.faces.push(new THREE.Face3(i,k,l,[e,e,e]));this.faceVertexUvs[0].push([h[i],h[k],h[l]])}}this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,g)};THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.SphereGeometry=function(a,b,c,d,e,f,h){THREE.Geometry.call(this);this.radius=a=a||50;this.widthSegments=b=Math.max(3,Math.floor(b)||8);this.heightSegments=c=Math.max(2,Math.floor(c)||6);this.phiStart=d=void 0!==d?d:0;this.phiLength=e=void 0!==e?e:2*Math.PI;this.thetaStart=f=void 0!==f?f:0;this.thetaLength=h=void 0!==h?h:Math.PI;var g,i,k=[],m=[];for(i=0;i<=c;i++){var l=[],p=[];for(g=0;g<=b;g++){var s=g/b,t=i/c,n=new THREE.Vector3;n.x=-a*Math.cos(d+s*e)*Math.sin(f+t*h);n.y=a*Math.cos(f+t*h);
-n.z=a*Math.sin(d+s*e)*Math.sin(f+t*h);this.vertices.push(n);l.push(this.vertices.length-1);p.push(new THREE.Vector2(s,1-t))}k.push(l);m.push(p)}for(i=0;i<this.heightSegments;i++)for(g=0;g<this.widthSegments;g++){var b=k[i][g+1],c=k[i][g],d=k[i+1][g],e=k[i+1][g+1],f=this.vertices[b].clone().normalize(),h=this.vertices[c].clone().normalize(),l=this.vertices[d].clone().normalize(),p=this.vertices[e].clone().normalize(),s=m[i][g+1].clone(),t=m[i][g].clone(),n=m[i+1][g].clone(),r=m[i+1][g+1].clone();Math.abs(this.vertices[b].y)===
-this.radius?(this.faces.push(new THREE.Face3(b,d,e,[f,l,p])),this.faceVertexUvs[0].push([s,n,r])):Math.abs(this.vertices[d].y)===this.radius?(this.faces.push(new THREE.Face3(b,c,d,[f,h,l])),this.faceVertexUvs[0].push([s,t,n])):(this.faces.push(new THREE.Face3(b,c,e,[f,h,p])),this.faceVertexUvs[0].push([s,t,r]),this.faces.push(new THREE.Face3(c,d,e,[h,l,p])),this.faceVertexUvs[0].push([t.clone(),n,r.clone()]))}this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,
-a)};THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TextGeometry=function(a,b){var b=b||{},c=THREE.FontUtils.generateShapes(a,b);b.amount=void 0!==b.height?b.height:50;void 0===b.bevelThickness&&(b.bevelThickness=10);void 0===b.bevelSize&&(b.bevelSize=8);void 0===b.bevelEnabled&&(b.bevelEnabled=!1);THREE.ExtrudeGeometry.call(this,c,b)};THREE.TextGeometry.prototype=Object.create(THREE.ExtrudeGeometry.prototype);THREE.TorusGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.radius=a||100;this.tube=b||40;this.radialSegments=c||8;this.tubularSegments=d||6;this.arc=e||2*Math.PI;e=new THREE.Vector3;a=[];b=[];for(c=0;c<=this.radialSegments;c++)for(d=0;d<=this.tubularSegments;d++){var f=d/this.tubularSegments*this.arc,h=2*c/this.radialSegments*Math.PI;e.x=this.radius*Math.cos(f);e.y=this.radius*Math.sin(f);var g=new THREE.Vector3;g.x=(this.radius+this.tube*Math.cos(h))*Math.cos(f);g.y=(this.radius+this.tube*
-Math.cos(h))*Math.sin(f);g.z=this.tube*Math.sin(h);this.vertices.push(g);a.push(new THREE.Vector2(d/this.tubularSegments,c/this.radialSegments));b.push(g.clone().sub(e).normalize())}for(c=1;c<=this.radialSegments;c++)for(d=1;d<=this.tubularSegments;d++){var e=(this.tubularSegments+1)*c+d-1,f=(this.tubularSegments+1)*(c-1)+d-1,h=(this.tubularSegments+1)*(c-1)+d,g=(this.tubularSegments+1)*c+d,i=new THREE.Face3(e,f,g,[b[e],b[f],b[g]]);i.normal.add(b[e]);i.normal.add(b[f]);i.normal.add(b[g]);i.normal.normalize();
-this.faces.push(i);this.faceVertexUvs[0].push([a[e].clone(),a[f].clone(),a[g].clone()]);i=new THREE.Face3(f,h,g,[b[f],b[h],b[g]]);i.normal.add(b[f]);i.normal.add(b[h]);i.normal.add(b[g]);i.normal.normalize();this.faces.push(i);this.faceVertexUvs[0].push([a[f].clone(),a[h].clone(),a[g].clone()])}this.computeCentroids()};THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TorusKnotGeometry=function(a,b,c,d,e,f,h){function g(a,b,c,d,e){var f=Math.cos(a),g=Math.sin(a),a=b/c*a,b=Math.cos(a),f=0.5*(d*(2+b))*f,g=0.5*d*(2+b)*g,d=0.5*e*d*Math.sin(a);return new THREE.Vector3(f,g,d)}THREE.Geometry.call(this);this.radius=a||100;this.tube=b||40;this.radialSegments=c||64;this.tubularSegments=d||8;this.p=e||2;this.q=f||3;this.heightScale=h||1;this.grid=Array(this.radialSegments);c=new THREE.Vector3;d=new THREE.Vector3;e=new THREE.Vector3;for(a=0;a<this.radialSegments;++a){this.grid[a]=
-Array(this.tubularSegments);b=2*(a/this.radialSegments)*this.p*Math.PI;f=g(b,this.q,this.p,this.radius,this.heightScale);b=g(b+0.01,this.q,this.p,this.radius,this.heightScale);c.subVectors(b,f);d.addVectors(b,f);e.crossVectors(c,d);d.crossVectors(e,c);e.normalize();d.normalize();for(b=0;b<this.tubularSegments;++b){var i=2*(b/this.tubularSegments)*Math.PI,h=-this.tube*Math.cos(i),i=this.tube*Math.sin(i),k=new THREE.Vector3;k.x=f.x+h*d.x+i*e.x;k.y=f.y+h*d.y+i*e.y;k.z=f.z+h*d.z+i*e.z;this.grid[a][b]=
-this.vertices.push(k)-1}}for(a=0;a<this.radialSegments;++a)for(b=0;b<this.tubularSegments;++b){var e=(a+1)%this.radialSegments,f=(b+1)%this.tubularSegments,c=this.grid[a][b],d=this.grid[e][b],e=this.grid[e][f],f=this.grid[a][f],h=new THREE.Vector2(a/this.radialSegments,b/this.tubularSegments),i=new THREE.Vector2((a+1)/this.radialSegments,b/this.tubularSegments),k=new THREE.Vector2((a+1)/this.radialSegments,(b+1)/this.tubularSegments),m=new THREE.Vector2(a/this.radialSegments,(b+1)/this.tubularSegments);
-this.faces.push(new THREE.Face3(c,d,f));this.faceVertexUvs[0].push([h,i,m]);this.faces.push(new THREE.Face3(d,e,f));this.faceVertexUvs[0].push([i.clone(),k,m.clone()])}this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TubeGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.path=a;this.segments=b||64;this.radius=c||1;this.radialSegments=d||8;this.closed=e||!1;this.grid=[];var f,h,d=this.segments+1,g,i,k,e=new THREE.Vector3,m,l,b=new THREE.TubeGeometry.FrenetFrames(this.path,this.segments,this.closed);m=b.normals;l=b.binormals;this.tangents=b.tangents;this.normals=m;this.binormals=l;for(b=0;b<d;b++){this.grid[b]=[];c=b/(d-1);k=a.getPointAt(c);f=m[b];h=l[b];for(c=0;c<this.radialSegments;c++)g=2*(c/this.radialSegments)*
-Math.PI,i=-this.radius*Math.cos(g),g=this.radius*Math.sin(g),e.copy(k),e.x+=i*f.x+g*h.x,e.y+=i*f.y+g*h.y,e.z+=i*f.z+g*h.z,this.grid[b][c]=this.vertices.push(new THREE.Vector3(e.x,e.y,e.z))-1}for(b=0;b<this.segments;b++)for(c=0;c<this.radialSegments;c++)e=this.closed?(b+1)%this.segments:b+1,m=(c+1)%this.radialSegments,a=this.grid[b][c],d=this.grid[e][c],e=this.grid[e][m],m=this.grid[b][m],l=new THREE.Vector2(b/this.segments,c/this.radialSegments),f=new THREE.Vector2((b+1)/this.segments,c/this.radialSegments),
-h=new THREE.Vector2((b+1)/this.segments,(c+1)/this.radialSegments),i=new THREE.Vector2(b/this.segments,(c+1)/this.radialSegments),this.faces.push(new THREE.Face3(a,d,m)),this.faceVertexUvs[0].push([l,f,i]),this.faces.push(new THREE.Face3(d,e,m)),this.faceVertexUvs[0].push([f.clone(),h,i.clone()]);this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TubeGeometry.FrenetFrames=function(a,b,c){new THREE.Vector3;var d=new THREE.Vector3;new THREE.Vector3;var e=[],f=[],h=[],g=new THREE.Vector3,i=new THREE.Matrix4,b=b+1,k,m,l;this.tangents=e;this.normals=f;this.binormals=h;for(k=0;k<b;k++)m=k/(b-1),e[k]=a.getTangentAt(m),e[k].normalize();f[0]=new THREE.Vector3;h[0]=new THREE.Vector3;a=Number.MAX_VALUE;k=Math.abs(e[0].x);m=Math.abs(e[0].y);l=Math.abs(e[0].z);k<=a&&(a=k,d.set(1,0,0));m<=a&&(a=m,d.set(0,1,0));l<=a&&d.set(0,0,1);g.crossVectors(e[0],
-d).normalize();f[0].crossVectors(e[0],g);h[0].crossVectors(e[0],f[0]);for(k=1;k<b;k++)f[k]=f[k-1].clone(),h[k]=h[k-1].clone(),g.crossVectors(e[k-1],e[k]),1E-4<g.length()&&(g.normalize(),d=Math.acos(THREE.Math.clamp(e[k-1].dot(e[k]),-1,1)),f[k].applyMatrix4(i.makeRotationAxis(g,d))),h[k].crossVectors(e[k],f[k]);if(c){d=Math.acos(THREE.Math.clamp(f[0].dot(f[b-1]),-1,1));d/=b-1;0<e[0].dot(g.crossVectors(f[0],f[b-1]))&&(d=-d);for(k=1;k<b;k++)f[k].applyMatrix4(i.makeRotationAxis(e[k],d*k)),h[k].crossVectors(e[k],
-f[k])}};THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=g.vertices.push(b)-1;var c=Math.atan2(a.z,-a.x)/2/Math.PI+0.5,a=Math.atan2(-a.y,Math.sqrt(a.x*a.x+a.z*a.z))/Math.PI+0.5;b.uv=new THREE.Vector2(c,1-a);return b}function f(a,b,c){var d=new THREE.Face3(a.index,b.index,c.index,[a.clone(),b.clone(),c.clone()]);d.centroid.add(a).add(b).add(c).divideScalar(3);g.faces.push(d);d=Math.atan2(d.centroid.z,-d.centroid.x);g.faceVertexUvs[0].push([h(a.uv,a,d),h(b.uv,b,d),
-h(c.uv,c,d)])}function h(a,b,c){0>c&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+0.5,a.y));return a.clone()}THREE.Geometry.call(this);for(var c=c||1,d=d||0,g=this,i=0,k=a.length;i<k;i++)e(new THREE.Vector3(a[i][0],a[i][1],a[i][2]));for(var m=this.vertices,a=[],i=0,k=b.length;i<k;i++){var l=m[b[i][0]],p=m[b[i][1]],s=m[b[i][2]];a[i]=new THREE.Face3(l.index,p.index,s.index,[l.clone(),p.clone(),s.clone()])}i=0;for(k=a.length;i<k;i++){p=a[i];m=d;b=Math.pow(2,
-m);Math.pow(4,m);for(var m=e(g.vertices[p.a]),l=e(g.vertices[p.b]),t=e(g.vertices[p.c]),p=[],s=0;s<=b;s++){p[s]=[];for(var n=e(m.clone().lerp(t,s/b)),r=e(l.clone().lerp(t,s/b)),q=b-s,u=0;u<=q;u++)p[s][u]=0==u&&s==b?n:e(n.clone().lerp(r,u/q))}for(s=0;s<b;s++)for(u=0;u<2*(b-s)-1;u++)m=Math.floor(u/2),0==u%2?f(p[s][m+1],p[s+1][m],p[s][m]):f(p[s][m+1],p[s+1][m+1],p[s+1][m])}i=0;for(k=this.faceVertexUvs[0].length;i<k;i++)d=this.faceVertexUvs[0][i],a=d[0].x,b=d[1].x,m=d[2].x,l=Math.max(a,Math.max(b,m)),
-p=Math.min(a,Math.min(b,m)),0.9<l&&0.1>p&&(0.2>a&&(d[0].x+=1),0.2>b&&(d[1].x+=1),0.2>m&&(d[2].x+=1));i=0;for(k=this.vertices.length;i<k;i++)this.vertices[i].multiplyScalar(c);this.mergeVertices();this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.IcosahedronGeometry=function(a,b){this.radius=a;this.detail=b;var c=(1+Math.sqrt(5))/2;THREE.PolyhedronGeometry.call(this,[[-1,c,0],[1,c,0],[-1,-c,0],[1,-c,0],[0,-1,c],[0,1,c],[0,-1,-c],[0,1,-c],[c,0,-1],[c,0,1],[-c,0,-1],[-c,0,1]],[[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]],a,b)};THREE.IcosahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.OctahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]],[[0,2,4],[0,4,3],[0,3,5],[0,5,2],[1,2,5],[1,5,3],[1,3,4],[1,4,2]],a,b)};THREE.OctahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TetrahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[[1,1,1],[-1,-1,1],[-1,1,-1],[1,-1,-1]],[[2,1,0],[0,3,2],[1,3,0],[2,3,1]],a,b)};THREE.TetrahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ParametricGeometry=function(a,b,c){THREE.Geometry.call(this);var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],h,g,i,k,m=b+1;for(h=0;h<=c;h++){k=h/c;for(g=0;g<=b;g++)i=g/b,i=a(i,k),d.push(i)}var l,p,s,t;for(h=0;h<c;h++)for(g=0;g<b;g++)a=h*m+g,d=h*m+g+1,k=(h+1)*m+g+1,i=(h+1)*m+g,l=new THREE.Vector2(g/b,h/c),p=new THREE.Vector2((g+1)/b,h/c),s=new THREE.Vector2((g+1)/b,(h+1)/c),t=new THREE.Vector2(g/b,(h+1)/c),e.push(new THREE.Face3(a,d,i)),f.push([l,p,t]),e.push(new THREE.Face3(d,k,i)),
-f.push([p.clone(),s,t.clone()]);this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.AxisHelper=function(a){var a=a||1,b=new THREE.Geometry;b.vertices.push(new THREE.Vector3,new THREE.Vector3(a,0,0),new THREE.Vector3,new THREE.Vector3(0,a,0),new THREE.Vector3,new THREE.Vector3(0,0,a));b.colors.push(new THREE.Color(16711680),new THREE.Color(16755200),new THREE.Color(65280),new THREE.Color(11206400),new THREE.Color(255),new THREE.Color(43775));a=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.Line.call(this,b,a,THREE.LinePieces)};
-THREE.AxisHelper.prototype=Object.create(THREE.Line.prototype);THREE.ArrowHelper=function(a,b,c,d){THREE.Object3D.call(this);void 0===d&&(d=16776960);void 0===c&&(c=1);this.position=b;b=new THREE.Geometry;b.vertices.push(new THREE.Vector3(0,0,0));b.vertices.push(new THREE.Vector3(0,1,0));this.line=new THREE.Line(b,new THREE.LineBasicMaterial({color:d}));this.line.matrixAutoUpdate=!1;this.add(this.line);b=new THREE.CylinderGeometry(0,0.05,0.25,5,1);b.applyMatrix((new THREE.Matrix4).makeTranslation(0,0.875,0));this.cone=new THREE.Mesh(b,new THREE.MeshBasicMaterial({color:d}));
-this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(a);this.setLength(c)};THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.setDirection=function(){var a=new THREE.Vector3,b;return function(c){0.99999<c.y?this.quaternion.set(0,0,0,1):-0.99999>c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();THREE.ArrowHelper.prototype.setLength=function(a){this.scale.set(a,a,a)};
-THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.setHex(a);this.cone.material.color.setHex(a)};THREE.BoxHelper=function(a){var b=[new THREE.Vector3(1,1,1),new THREE.Vector3(-1,1,1),new THREE.Vector3(-1,-1,1),new THREE.Vector3(1,-1,1),new THREE.Vector3(1,1,-1),new THREE.Vector3(-1,1,-1),new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,-1,-1)];this.vertices=b;var c=new THREE.Geometry;c.vertices.push(b[0],b[1],b[1],b[2],b[2],b[3],b[3],b[0],b[4],b[5],b[5],b[6],b[6],b[7],b[7],b[4],b[0],b[4],b[1],b[5],b[2],b[6],b[3],b[7]);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);
-void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.vertices;d[0].set(b.x,b.y,b.z);d[1].set(c.x,b.y,b.z);d[2].set(c.x,c.y,b.z);d[3].set(b.x,c.y,b.z);d[4].set(b.x,b.y,c.z);d[5].set(c.x,b.y,c.z);d[6].set(c.x,c.y,c.z);d[7].set(b.x,c.y,c.z);this.geometry.computeBoundingSphere();this.geometry.verticesNeedUpdate=!0;this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};THREE.BoundingBoxHelper=function(a,b){var c=b||8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.CubeGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
-b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
-THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.CameraHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Camera,c=new THREE.Projector;return function(){function d(d,h,g,i){a.set(h,g,i);c.unprojectVector(a,b);d=e.pointMap[d];if(void 0!==d){h=0;for(g=d.length;h<g;h++)e.geometry.vertices[d[h]].copy(a)}}var e=this;b.projectionMatrix.copy(this.camera.projectionMatrix);d("c",0,0,-1);d("t",0,0,1);d("n1",-1,-1,-1);d("n2",1,-1,-1);d("n3",-1,1,-1);d("n4",1,1,-1);d("f1",-1,-1,1);d("f2",1,-1,1);d("f3",-1,1,1);d("f4",1,1,1);d("u1",
-0.7,1.1,-1);d("u2",-0.7,1.1,-1);d("u3",0,2,-1);d("cf1",-1,0,1);d("cf2",1,0,1);d("cf3",0,-1,1);d("cf4",0,1,1);d("cn1",-1,0,-1);d("cn2",1,0,-1);d("cn3",0,-1,-1);d("cn4",0,1,-1);this.geometry.verticesNeedUpdate=!0}}();THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;var c=new THREE.PlaneGeometry(b,b),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.lightPlane=new THREE.Mesh(c,d);this.add(this.lightPlane);c=new THREE.Geometry;c.vertices.push(new THREE.Vector3);c.vertices.push(new THREE.Vector3);c.computeLineDistances();
-d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine=new THREE.Line(c,d);this.add(this.targetLine);this.update()};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose();this.lightPlane.material.dispose();this.targetLine.geometry.dispose();this.targetLine.material.dispose()};
-THREE.DirectionalLightHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){a.getPositionFromMatrix(this.light.matrixWorld).negate();this.lightPlane.lookAt(a);this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine.geometry.vertices[1].copy(a);this.targetLine.geometry.verticesNeedUpdate=!0;this.targetLine.material.color.copy(this.lightPlane.material.color)}}();THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var a=c||16776960,d=d||1,b=new THREE.Geometry,c=0,e=this.object.geometry.faces.length;c<e;c++)b.vertices.push(new THREE.Vector3),b.vertices.push(new THREE.Vector3);THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:a,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.FaceNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.FaceNormalsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var b=this.geometry.vertices,c=this.object.geometry.faces,d=this.object.matrixWorld,e=0,f=c.length;e<f;e++){var h=c[e];a.copy(h.normal).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);var g=2*e;b[g].copy(h.centroid).applyMatrix4(d);b[g+1].addVectors(b[g],a)}this.geometry.verticesNeedUpdate=
-!0;return this}}();THREE.GridHelper=function(a,b){var c=new THREE.Geometry,d=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924);this.color2=new THREE.Color(8947848);for(var e=-a;e<=a;e+=b){c.vertices.push(new THREE.Vector3(-a,0,e),new THREE.Vector3(a,0,e),new THREE.Vector3(e,0,-a),new THREE.Vector3(e,0,a));var f=0===e?this.color1:this.color2;c.colors.push(f,f,f,f)}THREE.Line.call(this,c,d,THREE.LinePieces)};THREE.GridHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.GridHelper.prototype.setColors=function(a,b){this.color1.set(a);this.color2.set(b);this.geometry.colorsNeedUpdate=!0};THREE.HemisphereLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.colors=[new THREE.Color,new THREE.Color];var c=new THREE.SphereGeometry(b,4,2);c.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));for(var d=0;8>d;d++)c.faces[d].color=this.colors[4>d?0:1];d=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(c,d);this.add(this.lightSphere);
-this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()};
-THREE.HemisphereLightHelper.prototype.update=function(){var a=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.lightSphere.lookAt(a.getPositionFromMatrix(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}();THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrixWorld=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose();this.material.dispose()};
-THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)};THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;a=new THREE.CylinderGeometry(0,1,1,8,1,!0);a.applyMatrix((new THREE.Matrix4).makeTranslation(0,-0.5,0));a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));var b=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});this.cone=new THREE.Mesh(a,b);this.add(this.cone);this.update()};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
-THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.getPositionFromMatrix(this.light.matrixWorld);b.getPositionFromMatrix(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}();THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var b=c||16711680,d=d||1,c=new THREE.Geometry,a=a.geometry.faces,e=0,f=a.length;e<f;e++)for(var h=0,g=a[e].vertexNormals.length;h<g;h++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.VertexNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexNormalsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){var b=["a","b","c","d"];this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var c=this.geometry.vertices,d=this.object.geometry.vertices,e=this.object.geometry.faces,f=this.object.matrixWorld,h=0,g=0,i=e.length;g<i;g++)for(var k=e[g],m=0,l=k.vertexNormals.length;m<l;m++){var p=k.vertexNormals[m];c[h].copy(d[k[b[m]]]).applyMatrix4(f);a.copy(p).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);
-a.add(c[h]);h+=1;c[h].copy(a);h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();THREE.VertexTangentsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var b=c||255,d=d||1,c=new THREE.Geometry,a=a.geometry.faces,e=0,f=a.length;e<f;e++)for(var h=0,g=a[e].vertexTangents.length;h<g;h++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.update()};THREE.VertexTangentsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexTangentsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){var b=["a","b","c","d"];this.object.updateMatrixWorld(!0);for(var c=this.geometry.vertices,d=this.object.geometry.vertices,e=this.object.geometry.faces,f=this.object.matrixWorld,h=0,g=0,i=e.length;g<i;g++)for(var k=e[g],m=0,l=k.vertexTangents.length;m<l;m++){var p=k.vertexTangents[m];c[h].copy(d[k[b[m]]]).applyMatrix4(f);a.copy(p).transformDirection(f).multiplyScalar(this.size);a.add(c[h]);h+=1;c[h].copy(a);
-h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();THREE.WireframeHelper=function(a){for(var b=[0,0],c={},d=function(a,b){return a-b},e=["a","b","c","d"],f=new THREE.Geometry,h=a.geometry.vertices,g=a.geometry.faces,i=0,k=g.length;i<k;i++)for(var m=g[i],l=0;3>l;l++){b[0]=m[e[l]];b[1]=m[e[(l+1)%3]];b.sort(d);var p=b.toString();void 0===c[p]&&(f.vertices.push(h[b[0]]),f.vertices.push(h[b[1]]),c[p]=!0)}THREE.Line.call(this,f,new THREE.LineBasicMaterial({color:16777215}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};
-THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)};THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);
-THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})};
-THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;a<b;a++)c=this.lensFlares[a],c.x=this.positionScreen.x+d*c.distance,c.y=this.positionScreen.y+e*c.distance,c.wantedRotation=0.25*c.x*Math.PI,c.rotation+=0.25*(c.wantedRotation-c.rotation)};THREE.MorphBlendMesh=function(a,b){THREE.Mesh.call(this,a,b);this.animationsMap={};this.animationsList=[];var c=this.geometry.morphTargets.length;this.createAnimation("__default",0,c-1,c/1);this.setAnimationWeight("__default",1)};THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype);
+THREE.KeyFrameAnimation.prototype={constructor:THREE.KeyFrameAnimation,play:function(a){this.currentTime=void 0!==a?a:0;if(!1===this.isPlaying){this.isPlaying=!0;var b=this.hierarchy.length,c,d;for(a=0;a<b;a++)c=this.hierarchy[a],d=this.data.hierarchy[a],void 0===d.animationCache&&(d.animationCache={},d.animationCache.prevKey=null,d.animationCache.nextKey=null,d.animationCache.originalMatrix=c.matrix),c=this.data.hierarchy[a].keys,c.length&&(d.animationCache.prevKey=c[0],d.animationCache.nextKey=
+c[1],this.startTime=Math.min(c[0].time,this.startTime),this.endTime=Math.max(c[c.length-1].time,this.endTime));this.update(0)}this.isPaused=!1;THREE.AnimationHandler.play(this)},stop:function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.stop(this);for(var a=0;a<this.data.hierarchy.length;a++){var b=this.hierarchy[a],c=this.data.hierarchy[a];if(void 0!==c.animationCache){var d=c.animationCache.originalMatrix;d.copy(b.matrix);b.matrix=d;delete c.animationCache}}},update:function(a){if(!1!==
+this.isPlaying){this.currentTime+=a*this.timeScale;a=this.data.length;!0===this.loop&&this.currentTime>a&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a],d=this.data.hierarchy[a],e=d.keys,d=d.animationCache;if(e.length){var f=d.prevKey,g=d.nextKey;if(g.time<=this.currentTime){for(;g.time<this.currentTime&&g.index>f.index;)f=g,g=e[f.index+1];d.prevKey=f;d.nextKey=g}g.time>=this.currentTime?f.interpolate(g,this.currentTime):
+f.interpolate(g,g.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}},getNextKeyWith:function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c<b.length;c++)if(b[c].hasTarget(a))return b[c];return b[0]},getPrevKeyWith:function(a,b,c){b=this.data.hierarchy[b].keys;for(c=0<=c?c:c+b.length;0<=c;c--)if(b[c].hasTarget(a))return b[c];return b[b.length-1]}};
+THREE.MorphAnimation=function(a){this.mesh=a;this.frames=a.morphTargetInfluences.length;this.currentTime=0;this.duration=1E3;this.loop=!0;this.currentFrame=this.lastFrame=0;this.isPlaying=!1};
+THREE.MorphAnimation.prototype={constructor:THREE.MorphAnimation,play:function(){this.isPlaying=!0},pause:function(){this.isPlaying=!1},update:function(a){if(!1!==this.isPlaying){this.currentTime+=a;!0===this.loop&&this.currentTime>this.duration&&(this.currentTime%=this.duration);this.currentTime=Math.min(this.currentTime,this.duration);a=this.duration/this.frames;var b=Math.floor(this.currentTime/a),c=this.mesh.morphTargetInfluences;b!=this.currentFrame&&(c[this.lastFrame]=0,c[this.currentFrame]=
+1,c[b]=0,this.lastFrame=this.currentFrame,this.currentFrame=b);c[b]=this.currentTime%a/a;c[this.lastFrame]=1-c[b]}}};
+THREE.BoxGeometry=function(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,s){var u,v=h.widthSegments,x=h.heightSegments,D=e/2,w=f/2,y=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)u="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)u="y",x=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)u="x",v=h.depthSegments;var A=v+1,E=x+1,G=e/v,F=f/x,z=new THREE.Vector3;z[u]=0<g?1:-1;for(e=0;e<E;e++)for(f=0;f<A;f++){var I=new THREE.Vector3;I[a]=(f*G-D)*c;I[b]=(e*F-w)*d;I[u]=g;h.vertices.push(I)}for(e=
+0;e<x;e++)for(f=0;f<v;f++)w=f+A*e,a=f+A*(e+1),b=f+1+A*(e+1),c=f+1+A*e,d=new THREE.Vector2(f/v,1-e/x),g=new THREE.Vector2(f/v,1-(e+1)/x),u=new THREE.Vector2((f+1)/v,1-(e+1)/x),D=new THREE.Vector2((f+1)/v,1-e/x),w=new THREE.Face3(w+y,a+y,c+y),w.normal.copy(z),w.vertexNormals.push(z.clone(),z.clone(),z.clone()),w.materialIndex=s,h.faces.push(w),h.faceVertexUvs[0].push([d,g,D]),w=new THREE.Face3(a+y,b+y,c+y),w.normal.copy(z),w.vertexNormals.push(z.clone(),z.clone(),z.clone()),w.materialIndex=s,h.faces.push(w),
+h.faceVertexUvs[0].push([g.clone(),u,D.clone()])}THREE.Geometry.call(this);this.type="BoxGeometry";this.parameters={width:a,height:b,depth:c,widthSegments:d,heightSegments:e,depthSegments:f};this.widthSegments=d||1;this.heightSegments=e||1;this.depthSegments=f||1;var h=this;d=a/2;e=b/2;f=c/2;g("z","y",-1,-1,c,b,d,0);g("z","y",1,-1,c,b,-d,1);g("x","z",1,1,a,c,e,2);g("x","z",1,-1,a,c,-e,3);g("x","y",1,-1,a,b,f,4);g("x","y",-1,-1,a,b,-f,5);this.mergeVertices()};THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype);
+THREE.BoxGeometry.prototype.constructor=THREE.BoxGeometry;
+THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="CircleGeometry";this.parameters={radius:a,segments:b,thetaStart:c,thetaLength:d};a=a||50;b=void 0!==b?Math.max(3,b):8;c=void 0!==c?c:0;d=void 0!==d?d:2*Math.PI;var e,f=[];e=new THREE.Vector3;var g=new THREE.Vector2(.5,.5);this.vertices.push(e);f.push(g);for(e=0;e<=b;e++){var h=new THREE.Vector3,k=c+e/b*d;h.x=a*Math.cos(k);h.y=a*Math.sin(k);this.vertices.push(h);f.push(new THREE.Vector2((h.x/a+1)/2,(h.y/a+1)/2))}c=new THREE.Vector3(0,
+0,1);for(e=1;e<=b;e++)this.faces.push(new THREE.Face3(e,e+1,0,[c.clone(),c.clone(),c.clone()])),this.faceVertexUvs[0].push([f[e].clone(),f[e+1].clone(),g.clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,a)};THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CircleGeometry.prototype.constructor=THREE.CircleGeometry;
+THREE.CubeGeometry=function(a,b,c,d,e,f){THREE.warn("THREE.CubeGeometry has been renamed to THREE.BoxGeometry.");return new THREE.BoxGeometry(a,b,c,d,e,f)};
+THREE.CylinderGeometry=function(a,b,c,d,e,f,g,h){THREE.Geometry.call(this);this.type="CylinderGeometry";this.parameters={radiusTop:a,radiusBottom:b,height:c,radialSegments:d,heightSegments:e,openEnded:f,thetaStart:g,thetaLength:h};a=void 0!==a?a:20;b=void 0!==b?b:20;c=void 0!==c?c:100;d=d||8;e=e||1;f=void 0!==f?f:!1;g=void 0!==g?g:0;h=void 0!==h?h:2*Math.PI;var k=c/2,l,p,q=[],n=[];for(p=0;p<=e;p++){var t=[],r=[],s=p/e,u=s*(b-a)+a;for(l=0;l<=d;l++){var v=l/d,x=new THREE.Vector3;x.x=u*Math.sin(v*h+
+g);x.y=-s*c+k;x.z=u*Math.cos(v*h+g);this.vertices.push(x);t.push(this.vertices.length-1);r.push(new THREE.Vector2(v,1-s))}q.push(t);n.push(r)}c=(b-a)/c;for(l=0;l<d;l++)for(0!==a?(g=this.vertices[q[0][l]].clone(),h=this.vertices[q[0][l+1]].clone()):(g=this.vertices[q[1][l]].clone(),h=this.vertices[q[1][l+1]].clone()),g.setY(Math.sqrt(g.x*g.x+g.z*g.z)*c).normalize(),h.setY(Math.sqrt(h.x*h.x+h.z*h.z)*c).normalize(),p=0;p<e;p++){var t=q[p][l],r=q[p+1][l],s=q[p+1][l+1],u=q[p][l+1],v=g.clone(),x=g.clone(),
+D=h.clone(),w=h.clone(),y=n[p][l].clone(),A=n[p+1][l].clone(),E=n[p+1][l+1].clone(),G=n[p][l+1].clone();this.faces.push(new THREE.Face3(t,r,u,[v,x,w]));this.faceVertexUvs[0].push([y,A,G]);this.faces.push(new THREE.Face3(r,s,u,[x.clone(),D,w.clone()]));this.faceVertexUvs[0].push([A.clone(),E,G.clone()])}if(!1===f&&0<a)for(this.vertices.push(new THREE.Vector3(0,k,0)),l=0;l<d;l++)t=q[0][l],r=q[0][l+1],s=this.vertices.length-1,v=new THREE.Vector3(0,1,0),x=new THREE.Vector3(0,1,0),D=new THREE.Vector3(0,
+1,0),y=n[0][l].clone(),A=n[0][l+1].clone(),E=new THREE.Vector2(A.x,0),this.faces.push(new THREE.Face3(t,r,s,[v,x,D])),this.faceVertexUvs[0].push([y,A,E]);if(!1===f&&0<b)for(this.vertices.push(new THREE.Vector3(0,-k,0)),l=0;l<d;l++)t=q[e][l+1],r=q[e][l],s=this.vertices.length-1,v=new THREE.Vector3(0,-1,0),x=new THREE.Vector3(0,-1,0),D=new THREE.Vector3(0,-1,0),y=n[e][l+1].clone(),A=n[e][l].clone(),E=new THREE.Vector2(A.x,1),this.faces.push(new THREE.Face3(t,r,s,[v,x,D])),this.faceVertexUvs[0].push([y,
+A,E]);this.computeFaceNormals()};THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CylinderGeometry.prototype.constructor=THREE.CylinderGeometry;THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),this.type="ExtrudeGeometry",a=a instanceof Array?a:[a],this.addShapeList(a,b),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.constructor=THREE.ExtrudeGeometry;
+THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d<c;d++)this.addShape(a[d],b)};
+THREE.ExtrudeGeometry.prototype.addShape=function(a,b){function c(a,b,c){b||THREE.error("THREE.ExtrudeGeometry: vec does not exist");return b.clone().multiplyScalar(c).add(a)}function d(a,b,c){var d=1,d=a.x-b.x,e=a.y-b.y,f=c.x-a.x,g=c.y-a.y,h=d*d+e*e;if(1E-10<Math.abs(d*g-e*f)){var k=Math.sqrt(h),l=Math.sqrt(f*f+g*g),h=b.x-e/k;b=b.y+d/k;f=((c.x-g/l-h)*g-(c.y+f/l-b)*f)/(d*g-e*f);c=h+d*f-a.x;a=b+e*f-a.y;d=c*c+a*a;if(2>=d)return new THREE.Vector2(c,a);d=Math.sqrt(d/2)}else a=!1,1E-10<d?1E-10<f&&(a=!0):
+-1E-10>d?-1E-10>f&&(a=!0):Math.sign(e)==Math.sign(g)&&(a=!0),a?(c=-e,a=d,d=Math.sqrt(h)):(c=d,a=e,d=Math.sqrt(h/2));return new THREE.Vector2(c/d,a/d)}function e(a,b){var c,d;for(O=a.length;0<=--O;){c=O;d=O-1;0>d&&(d=a.length-1);for(var e=0,f=t+2*p,e=0;e<f;e++){var g=oa*e,h=oa*(e+1),k=b+c+g,g=b+d+g,l=b+d+h,h=b+c+h,k=k+U,g=g+U,l=l+U,h=h+U;I.faces.push(new THREE.Face3(k,g,h,null,null,x));I.faces.push(new THREE.Face3(g,l,h,null,null,x));k=D.generateSideWallUV(I,k,g,l,h);I.faceVertexUvs[0].push([k[0],
+k[1],k[3]]);I.faceVertexUvs[0].push([k[1],k[2],k[3]])}}}function f(a,b,c){I.vertices.push(new THREE.Vector3(a,b,c))}function g(a,b,c){a+=U;b+=U;c+=U;I.faces.push(new THREE.Face3(a,b,c,null,null,v));a=D.generateTopUV(I,a,b,c);I.faceVertexUvs[0].push(a)}var h=void 0!==b.amount?b.amount:100,k=void 0!==b.bevelThickness?b.bevelThickness:6,l=void 0!==b.bevelSize?b.bevelSize:k-2,p=void 0!==b.bevelSegments?b.bevelSegments:3,q=void 0!==b.bevelEnabled?b.bevelEnabled:!0,n=void 0!==b.curveSegments?b.curveSegments:
+12,t=void 0!==b.steps?b.steps:1,r=b.extrudePath,s,u=!1,v=b.material,x=b.extrudeMaterial,D=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,w,y,A,E;r&&(s=r.getSpacedPoints(t),u=!0,q=!1,w=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(r,t,!1),y=new THREE.Vector3,A=new THREE.Vector3,E=new THREE.Vector3);q||(l=k=p=0);var G,F,z,I=this,U=this.vertices.length,r=a.extractPoints(n),n=r.shape,M=r.holes;if(r=!THREE.Shape.Utils.isClockWise(n)){n=n.reverse();F=0;for(z=
+M.length;F<z;F++)G=M[F],THREE.Shape.Utils.isClockWise(G)&&(M[F]=G.reverse());r=!1}var H=THREE.Shape.Utils.triangulateShape(n,M),L=n;F=0;for(z=M.length;F<z;F++)G=M[F],n=n.concat(G);var P,N,R,V,J,oa=n.length,ja,ha=H.length,r=[],O=0;R=L.length;P=R-1;for(N=O+1;O<R;O++,P++,N++)P===R&&(P=0),N===R&&(N=0),r[O]=d(L[O],L[P],L[N]);var ca=[],ba,qa=r.concat();F=0;for(z=M.length;F<z;F++){G=M[F];ba=[];O=0;R=G.length;P=R-1;for(N=O+1;O<R;O++,P++,N++)P===R&&(P=0),N===R&&(N=0),ba[O]=d(G[O],G[P],G[N]);ca.push(ba);qa=
+qa.concat(ba)}for(P=0;P<p;P++){R=P/p;V=k*(1-R);N=l*Math.sin(R*Math.PI/2);O=0;for(R=L.length;O<R;O++)J=c(L[O],r[O],N),f(J.x,J.y,-V);F=0;for(z=M.length;F<z;F++)for(G=M[F],ba=ca[F],O=0,R=G.length;O<R;O++)J=c(G[O],ba[O],N),f(J.x,J.y,-V)}N=l;for(O=0;O<oa;O++)J=q?c(n[O],qa[O],N):n[O],u?(A.copy(w.normals[0]).multiplyScalar(J.x),y.copy(w.binormals[0]).multiplyScalar(J.y),E.copy(s[0]).add(A).add(y),f(E.x,E.y,E.z)):f(J.x,J.y,0);for(R=1;R<=t;R++)for(O=0;O<oa;O++)J=q?c(n[O],qa[O],N):n[O],u?(A.copy(w.normals[R]).multiplyScalar(J.x),
+y.copy(w.binormals[R]).multiplyScalar(J.y),E.copy(s[R]).add(A).add(y),f(E.x,E.y,E.z)):f(J.x,J.y,h/t*R);for(P=p-1;0<=P;P--){R=P/p;V=k*(1-R);N=l*Math.sin(R*Math.PI/2);O=0;for(R=L.length;O<R;O++)J=c(L[O],r[O],N),f(J.x,J.y,h+V);F=0;for(z=M.length;F<z;F++)for(G=M[F],ba=ca[F],O=0,R=G.length;O<R;O++)J=c(G[O],ba[O],N),u?f(J.x,J.y+s[t-1].y,s[t-1].x+V):f(J.x,J.y,h+V)}(function(){if(q){var a;a=0*oa;for(O=0;O<ha;O++)ja=H[O],g(ja[2]+a,ja[1]+a,ja[0]+a);a=t+2*p;a*=oa;for(O=0;O<ha;O++)ja=H[O],g(ja[0]+a,ja[1]+a,ja[2]+
+a)}else{for(O=0;O<ha;O++)ja=H[O],g(ja[2],ja[1],ja[0]);for(O=0;O<ha;O++)ja=H[O],g(ja[0]+oa*t,ja[1]+oa*t,ja[2]+oa*t)}})();(function(){var a=0;e(L,a);a+=L.length;F=0;for(z=M.length;F<z;F++)G=M[F],e(G,a),a+=G.length})()};
+THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(a,b,c,d){a=a.vertices;b=a[b];c=a[c];d=a[d];return[new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y),new THREE.Vector2(d.x,d.y)]},generateSideWallUV:function(a,b,c,d,e){a=a.vertices;b=a[b];c=a[c];d=a[d];e=a[e];return.01>Math.abs(b.y-c.y)?[new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z),new THREE.Vector2(e.x,1-e.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),new THREE.Vector2(e.y,1-e.z)]}};THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);this.type="ShapeGeometry";!1===a instanceof Array&&(a=[a]);this.addShapeList(a,b);this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.constructor=THREE.ShapeGeometry;THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c<d;c++)this.addShape(a[c],b);return this};
+THREE.ShapeGeometry.prototype.addShape=function(a,b){void 0===b&&(b={});var c=b.material,d=void 0===b.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:b.UVGenerator,e,f,g,h=this.vertices.length;e=a.extractPoints(void 0!==b.curveSegments?b.curveSegments:12);var k=e.shape,l=e.holes;if(!THREE.Shape.Utils.isClockWise(k))for(k=k.reverse(),e=0,f=l.length;e<f;e++)g=l[e],THREE.Shape.Utils.isClockWise(g)&&(l[e]=g.reverse());var p=THREE.Shape.Utils.triangulateShape(k,l);e=0;for(f=l.length;e<f;e++)g=l[e],
+k=k.concat(g);l=k.length;f=p.length;for(e=0;e<l;e++)g=k[e],this.vertices.push(new THREE.Vector3(g.x,g.y,0));for(e=0;e<f;e++)l=p[e],k=l[0]+h,g=l[1]+h,l=l[2]+h,this.faces.push(new THREE.Face3(k,g,l,null,null,c)),this.faceVertexUvs[0].push(d.generateTopUV(this,k,g,l))};
+THREE.LatheGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="LatheGeometry";this.parameters={points:a,segments:b,phiStart:c,phiLength:d};b=b||12;c=c||0;d=d||2*Math.PI;for(var e=1/(a.length-1),f=1/b,g=0,h=b;g<=h;g++)for(var k=c+g*f*d,l=Math.cos(k),p=Math.sin(k),k=0,q=a.length;k<q;k++){var n=a[k],t=new THREE.Vector3;t.x=l*n.x-p*n.y;t.y=p*n.x+l*n.y;t.z=n.z;this.vertices.push(t)}c=a.length;g=0;for(h=b;g<h;g++)for(k=0,q=a.length-1;k<q;k++){b=p=k+c*g;d=p+c;var l=p+1+c,p=p+1,n=g*f,t=k*e,r=
+n+f,s=t+e;this.faces.push(new THREE.Face3(b,d,p));this.faceVertexUvs[0].push([new THREE.Vector2(n,t),new THREE.Vector2(r,t),new THREE.Vector2(n,s)]);this.faces.push(new THREE.Face3(d,l,p));this.faceVertexUvs[0].push([new THREE.Vector2(r,t),new THREE.Vector2(r,s),new THREE.Vector2(n,s)])}this.mergeVertices();this.computeFaceNormals();this.computeVertexNormals()};THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.LatheGeometry.prototype.constructor=THREE.LatheGeometry;
+THREE.PlaneGeometry=function(a,b,c,d){console.info("THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.");THREE.Geometry.call(this);this.type="PlaneGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};this.fromBufferGeometry(new THREE.PlaneBufferGeometry(a,b,c,d))};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.PlaneGeometry.prototype.constructor=THREE.PlaneGeometry;
+THREE.PlaneBufferGeometry=function(a,b,c,d){THREE.BufferGeometry.call(this);this.type="PlaneBufferGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};var e=a/2,f=b/2;c=c||1;d=d||1;var g=c+1,h=d+1,k=a/c,l=b/d;b=new Float32Array(g*h*3);a=new Float32Array(g*h*3);for(var p=new Float32Array(g*h*2),q=0,n=0,t=0;t<h;t++)for(var r=t*l-f,s=0;s<g;s++)b[q]=s*k-e,b[q+1]=-r,a[q+2]=1,p[n]=s/c,p[n+1]=1-t/d,q+=3,n+=2;q=0;e=new (65535<b.length/3?Uint32Array:Uint16Array)(c*d*6);for(t=0;t<d;t++)for(s=
+0;s<c;s++)f=s+g*(t+1),h=s+1+g*(t+1),k=s+1+g*t,e[q]=s+g*t,e[q+1]=f,e[q+2]=k,e[q+3]=f,e[q+4]=h,e[q+5]=k,q+=6;this.addAttribute("index",new THREE.BufferAttribute(e,1));this.addAttribute("position",new THREE.BufferAttribute(b,3));this.addAttribute("normal",new THREE.BufferAttribute(a,3));this.addAttribute("uv",new THREE.BufferAttribute(p,2))};THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype);THREE.PlaneBufferGeometry.prototype.constructor=THREE.PlaneBufferGeometry;
+THREE.RingGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="RingGeometry";this.parameters={innerRadius:a,outerRadius:b,thetaSegments:c,phiSegments:d,thetaStart:e,thetaLength:f};a=a||0;b=b||50;e=void 0!==e?e:0;f=void 0!==f?f:2*Math.PI;c=void 0!==c?Math.max(3,c):8;d=void 0!==d?Math.max(1,d):8;var g,h=[],k=a,l=(b-a)/d;for(a=0;a<d+1;a++){for(g=0;g<c+1;g++){var p=new THREE.Vector3,q=e+g/c*f;p.x=k*Math.cos(q);p.y=k*Math.sin(q);this.vertices.push(p);h.push(new THREE.Vector2((p.x/b+1)/2,
+(p.y/b+1)/2))}k+=l}b=new THREE.Vector3(0,0,1);for(a=0;a<d;a++)for(e=a*(c+1),g=0;g<c;g++)f=q=g+e,l=q+c+1,p=q+c+2,this.faces.push(new THREE.Face3(f,l,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[l].clone(),h[p].clone()]),f=q,l=q+c+2,p=q+1,this.faces.push(new THREE.Face3(f,l,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[l].clone(),h[p].clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,k)};
+THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.RingGeometry.prototype.constructor=THREE.RingGeometry;
+THREE.SphereGeometry=function(a,b,c,d,e,f,g){THREE.Geometry.call(this);this.type="SphereGeometry";this.parameters={radius:a,widthSegments:b,heightSegments:c,phiStart:d,phiLength:e,thetaStart:f,thetaLength:g};a=a||50;b=Math.max(3,Math.floor(b)||8);c=Math.max(2,Math.floor(c)||6);d=void 0!==d?d:0;e=void 0!==e?e:2*Math.PI;f=void 0!==f?f:0;g=void 0!==g?g:Math.PI;var h,k,l=[],p=[];for(k=0;k<=c;k++){var q=[],n=[];for(h=0;h<=b;h++){var t=h/b,r=k/c,s=new THREE.Vector3;s.x=-a*Math.cos(d+t*e)*Math.sin(f+r*g);
+s.y=a*Math.cos(f+r*g);s.z=a*Math.sin(d+t*e)*Math.sin(f+r*g);this.vertices.push(s);q.push(this.vertices.length-1);n.push(new THREE.Vector2(t,1-r))}l.push(q);p.push(n)}for(k=0;k<c;k++)for(h=0;h<b;h++){d=l[k][h+1];e=l[k][h];f=l[k+1][h];g=l[k+1][h+1];var q=this.vertices[d].clone().normalize(),n=this.vertices[e].clone().normalize(),t=this.vertices[f].clone().normalize(),r=this.vertices[g].clone().normalize(),s=p[k][h+1].clone(),u=p[k][h].clone(),v=p[k+1][h].clone(),x=p[k+1][h+1].clone();Math.abs(this.vertices[d].y)===
+a?(s.x=(s.x+u.x)/2,this.faces.push(new THREE.Face3(d,f,g,[q,t,r])),this.faceVertexUvs[0].push([s,v,x])):Math.abs(this.vertices[f].y)===a?(v.x=(v.x+x.x)/2,this.faces.push(new THREE.Face3(d,e,f,[q,n,t])),this.faceVertexUvs[0].push([s,u,v])):(this.faces.push(new THREE.Face3(d,e,g,[q,n,r])),this.faceVertexUvs[0].push([s,u,x]),this.faces.push(new THREE.Face3(e,f,g,[n.clone(),t,r.clone()])),this.faceVertexUvs[0].push([u.clone(),v,x.clone()]))}this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,
+a)};THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.SphereGeometry.prototype.constructor=THREE.SphereGeometry;THREE.TextGeometry=function(a,b){b=b||{};var c=THREE.FontUtils.generateShapes(a,b);b.amount=void 0!==b.height?b.height:50;void 0===b.bevelThickness&&(b.bevelThickness=10);void 0===b.bevelSize&&(b.bevelSize=8);void 0===b.bevelEnabled&&(b.bevelEnabled=!1);THREE.ExtrudeGeometry.call(this,c,b);this.type="TextGeometry"};THREE.TextGeometry.prototype=Object.create(THREE.ExtrudeGeometry.prototype);
+THREE.TextGeometry.prototype.constructor=THREE.TextGeometry;
+THREE.TorusGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.type="TorusGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,arc:e};a=a||100;b=b||40;c=c||8;d=d||6;e=e||2*Math.PI;for(var f=new THREE.Vector3,g=[],h=[],k=0;k<=c;k++)for(var l=0;l<=d;l++){var p=l/d*e,q=k/c*Math.PI*2;f.x=a*Math.cos(p);f.y=a*Math.sin(p);var n=new THREE.Vector3;n.x=(a+b*Math.cos(q))*Math.cos(p);n.y=(a+b*Math.cos(q))*Math.sin(p);n.z=b*Math.sin(q);this.vertices.push(n);g.push(new THREE.Vector2(l/
+d,k/c));h.push(n.clone().sub(f).normalize())}for(k=1;k<=c;k++)for(l=1;l<=d;l++)a=(d+1)*k+l-1,b=(d+1)*(k-1)+l-1,e=(d+1)*(k-1)+l,f=(d+1)*k+l,p=new THREE.Face3(a,b,f,[h[a].clone(),h[b].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[a].clone(),g[b].clone(),g[f].clone()]),p=new THREE.Face3(b,e,f,[h[b].clone(),h[e].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[b].clone(),g[e].clone(),g[f].clone()]);this.computeFaceNormals()};
+THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TorusGeometry.prototype.constructor=THREE.TorusGeometry;
+THREE.TorusKnotGeometry=function(a,b,c,d,e,f,g){function h(a,b,c,d,e){var f=Math.cos(a),g=Math.sin(a);a*=b/c;b=Math.cos(a);f*=d*(2+b)*.5;g=d*(2+b)*g*.5;d=e*d*Math.sin(a)*.5;return new THREE.Vector3(f,g,d)}THREE.Geometry.call(this);this.type="TorusKnotGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,p:e,q:f,heightScale:g};a=a||100;b=b||40;c=c||64;d=d||8;e=e||2;f=f||3;g=g||1;for(var k=Array(c),l=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3,n=0;n<c;++n){k[n]=
+Array(d);var t=n/c*2*e*Math.PI,r=h(t,f,e,a,g),t=h(t+.01,f,e,a,g);l.subVectors(t,r);p.addVectors(t,r);q.crossVectors(l,p);p.crossVectors(q,l);q.normalize();p.normalize();for(t=0;t<d;++t){var s=t/d*2*Math.PI,u=-b*Math.cos(s),s=b*Math.sin(s),v=new THREE.Vector3;v.x=r.x+u*p.x+s*q.x;v.y=r.y+u*p.y+s*q.y;v.z=r.z+u*p.z+s*q.z;k[n][t]=this.vertices.push(v)-1}}for(n=0;n<c;++n)for(t=0;t<d;++t)e=(n+1)%c,f=(t+1)%d,a=k[n][t],b=k[e][t],e=k[e][f],f=k[n][f],g=new THREE.Vector2(n/c,t/d),l=new THREE.Vector2((n+1)/c,
+t/d),p=new THREE.Vector2((n+1)/c,(t+1)/d),q=new THREE.Vector2(n/c,(t+1)/d),this.faces.push(new THREE.Face3(a,b,f)),this.faceVertexUvs[0].push([g,l,q]),this.faces.push(new THREE.Face3(b,e,f)),this.faceVertexUvs[0].push([l.clone(),p,q.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TorusKnotGeometry.prototype.constructor=THREE.TorusKnotGeometry;
+THREE.TubeGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="TubeGeometry";this.parameters={path:a,segments:b,radius:c,radialSegments:d,closed:e};b=b||64;c=c||1;d=d||8;e=e||!1;f=f||THREE.TubeGeometry.NoTaper;var g=[],h,k,l=b+1,p,q,n,t,r,s=new THREE.Vector3,u,v,x;u=new THREE.TubeGeometry.FrenetFrames(a,b,e);v=u.normals;x=u.binormals;this.tangents=u.tangents;this.normals=v;this.binormals=x;for(u=0;u<l;u++)for(g[u]=[],p=u/(l-1),r=a.getPointAt(p),h=v[u],k=x[u],n=c*f(p),p=0;p<d;p++)q=
+p/d*2*Math.PI,t=-n*Math.cos(q),q=n*Math.sin(q),s.copy(r),s.x+=t*h.x+q*k.x,s.y+=t*h.y+q*k.y,s.z+=t*h.z+q*k.z,g[u][p]=this.vertices.push(new THREE.Vector3(s.x,s.y,s.z))-1;for(u=0;u<b;u++)for(p=0;p<d;p++)f=e?(u+1)%b:u+1,l=(p+1)%d,a=g[u][p],c=g[f][p],f=g[f][l],l=g[u][l],s=new THREE.Vector2(u/b,p/d),v=new THREE.Vector2((u+1)/b,p/d),x=new THREE.Vector2((u+1)/b,(p+1)/d),h=new THREE.Vector2(u/b,(p+1)/d),this.faces.push(new THREE.Face3(a,c,l)),this.faceVertexUvs[0].push([s,v,h]),this.faces.push(new THREE.Face3(c,
+f,l)),this.faceVertexUvs[0].push([v.clone(),x,h.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TubeGeometry.prototype.constructor=THREE.TubeGeometry;THREE.TubeGeometry.NoTaper=function(a){return 1};THREE.TubeGeometry.SinusoidalTaper=function(a){return Math.sin(Math.PI*a)};
+THREE.TubeGeometry.FrenetFrames=function(a,b,c){var d=new THREE.Vector3,e=[],f=[],g=[],h=new THREE.Vector3,k=new THREE.Matrix4;b+=1;var l,p,q;this.tangents=e;this.normals=f;this.binormals=g;for(l=0;l<b;l++)p=l/(b-1),e[l]=a.getTangentAt(p),e[l].normalize();f[0]=new THREE.Vector3;g[0]=new THREE.Vector3;a=Number.MAX_VALUE;l=Math.abs(e[0].x);p=Math.abs(e[0].y);q=Math.abs(e[0].z);l<=a&&(a=l,d.set(1,0,0));p<=a&&(a=p,d.set(0,1,0));q<=a&&d.set(0,0,1);h.crossVectors(e[0],d).normalize();f[0].crossVectors(e[0],
+h);g[0].crossVectors(e[0],f[0]);for(l=1;l<b;l++)f[l]=f[l-1].clone(),g[l]=g[l-1].clone(),h.crossVectors(e[l-1],e[l]),1E-4<h.length()&&(h.normalize(),d=Math.acos(THREE.Math.clamp(e[l-1].dot(e[l]),-1,1)),f[l].applyMatrix4(k.makeRotationAxis(h,d))),g[l].crossVectors(e[l],f[l]);if(c)for(d=Math.acos(THREE.Math.clamp(f[0].dot(f[b-1]),-1,1)),d/=b-1,0<e[0].dot(h.crossVectors(f[0],f[b-1]))&&(d=-d),l=1;l<b;l++)f[l].applyMatrix4(k.makeRotationAxis(e[l],d*l)),g[l].crossVectors(e[l],f[l])};
+THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=k.vertices.push(b)-1;var c=Math.atan2(a.z,-a.x)/2/Math.PI+.5;a=Math.atan2(-a.y,Math.sqrt(a.x*a.x+a.z*a.z))/Math.PI+.5;b.uv=new THREE.Vector2(c,1-a);return b}function f(a,b,c){var d=new THREE.Face3(a.index,b.index,c.index,[a.clone(),b.clone(),c.clone()]);k.faces.push(d);u.copy(a).add(b).add(c).divideScalar(3);d=Math.atan2(u.z,-u.x);k.faceVertexUvs[0].push([h(a.uv,a,d),h(b.uv,b,d),h(c.uv,c,d)])}function g(a,
+b){for(var c=Math.pow(2,b),d=e(k.vertices[a.a]),g=e(k.vertices[a.b]),h=e(k.vertices[a.c]),l=[],n=0;n<=c;n++){l[n]=[];for(var p=e(d.clone().lerp(h,n/c)),q=e(g.clone().lerp(h,n/c)),s=c-n,r=0;r<=s;r++)l[n][r]=0==r&&n==c?p:e(p.clone().lerp(q,r/s))}for(n=0;n<c;n++)for(r=0;r<2*(c-n)-1;r++)d=Math.floor(r/2),0==r%2?f(l[n][d+1],l[n+1][d],l[n][d]):f(l[n][d+1],l[n+1][d+1],l[n+1][d])}function h(a,b,c){0>c&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+.5,a.y));return a.clone()}
+THREE.Geometry.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var k=this,l=0,p=a.length;l<p;l+=3)e(new THREE.Vector3(a[l],a[l+1],a[l+2]));a=this.vertices;for(var q=[],n=l=0,p=b.length;l<p;l+=3,n++){var t=a[b[l]],r=a[b[l+1]],s=a[b[l+2]];q[n]=new THREE.Face3(t.index,r.index,s.index,[t.clone(),r.clone(),s.clone()])}for(var u=new THREE.Vector3,l=0,p=q.length;l<p;l++)g(q[l],d);l=0;for(p=this.faceVertexUvs[0].length;l<p;l++)b=this.faceVertexUvs[0][l],
+d=b[0].x,a=b[1].x,q=b[2].x,n=Math.max(d,Math.max(a,q)),t=Math.min(d,Math.min(a,q)),.9<n&&.1>t&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>q&&(b[2].x+=1));l=0;for(p=this.vertices.length;l<p;l++)this.vertices[l].multiplyScalar(c);this.mergeVertices();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.PolyhedronGeometry.prototype.constructor=THREE.PolyhedronGeometry;
+THREE.DodecahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};var c=(1+Math.sqrt(5))/2,d=1/c;THREE.PolyhedronGeometry.call(this,[-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,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0,-c,0,-d,c,0,-d,-c,0,d,c,0,d],[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],a,b)};THREE.DodecahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.DodecahedronGeometry.prototype.constructor=THREE.DodecahedronGeometry;
+THREE.IcosahedronGeometry=function(a,b){var c=(1+Math.sqrt(5))/2;THREE.PolyhedronGeometry.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[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],a,b);this.type="IcosahedronGeometry";this.parameters={radius:a,detail:b}};THREE.IcosahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
+THREE.IcosahedronGeometry.prototype.constructor=THREE.IcosahedronGeometry;THREE.OctahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};THREE.PolyhedronGeometry.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b}};THREE.OctahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.OctahedronGeometry.prototype.constructor=THREE.OctahedronGeometry;
+THREE.TetrahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b}};THREE.TetrahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TetrahedronGeometry.prototype.constructor=THREE.TetrahedronGeometry;
+THREE.ParametricGeometry=function(a,b,c){THREE.Geometry.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],g,h,k,l,p=b+1;for(g=0;g<=c;g++)for(l=g/c,h=0;h<=b;h++)k=h/b,k=a(k,l),d.push(k);var q,n,t,r;for(g=0;g<c;g++)for(h=0;h<b;h++)a=g*p+h,d=g*p+h+1,l=(g+1)*p+h+1,k=(g+1)*p+h,q=new THREE.Vector2(h/b,g/c),n=new THREE.Vector2((h+1)/b,g/c),t=new THREE.Vector2((h+1)/b,(g+1)/c),r=new THREE.Vector2(h/b,(g+1)/c),e.push(new THREE.Face3(a,
+d,k)),f.push([q,n,r]),e.push(new THREE.Face3(d,l,k)),f.push([n.clone(),t,r.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ParametricGeometry.prototype.constructor=THREE.ParametricGeometry;
+THREE.AxisHelper=function(a){a=a||1;var b=new Float32Array([0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a]),c=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]);a=new THREE.BufferGeometry;a.addAttribute("position",new THREE.BufferAttribute(b,3));a.addAttribute("color",new THREE.BufferAttribute(c,3));b=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.Line.call(this,a,b,THREE.LinePieces)};THREE.AxisHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.AxisHelper.prototype.constructor=THREE.AxisHelper;
+THREE.ArrowHelper=function(){var a=new THREE.Geometry;a.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var b=new THREE.CylinderGeometry(0,.5,1,5,1);b.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));return function(c,d,e,f,g,h){THREE.Object3D.call(this);void 0===f&&(f=16776960);void 0===e&&(e=1);void 0===g&&(g=.2*e);void 0===h&&(h=.2*g);this.position.copy(d);this.line=new THREE.Line(a,new THREE.LineBasicMaterial({color:f}));this.line.matrixAutoUpdate=!1;this.add(this.line);
+this.cone=new THREE.Mesh(b,new THREE.MeshBasicMaterial({color:f}));this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(c);this.setLength(e,g,h)}}();THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.constructor=THREE.ArrowHelper;
+THREE.ArrowHelper.prototype.setDirection=function(){var a=new THREE.Vector3,b;return function(c){.99999<c.y?this.quaternion.set(0,0,0,1):-.99999>c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();THREE.ArrowHelper.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,a-b,1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};
+THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.set(a);this.cone.material.color.set(a)};THREE.BoxHelper=function(a){var b=new THREE.BufferGeometry;b.addAttribute("position",new THREE.BufferAttribute(new Float32Array(72),3));THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);THREE.BoxHelper.prototype.constructor=THREE.BoxHelper;
+THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.geometry.attributes.position.array;d[0]=b.x;d[1]=b.y;d[2]=b.z;d[3]=c.x;d[4]=b.y;d[5]=b.z;d[6]=c.x;d[7]=b.y;d[8]=b.z;d[9]=c.x;d[10]=c.y;d[11]=b.z;d[12]=c.x;d[13]=c.y;d[14]=b.z;d[15]=b.x;d[16]=c.y;d[17]=b.z;d[18]=b.x;d[19]=c.y;d[20]=b.z;d[21]=b.x;d[22]=b.y;d[23]=b.z;d[24]=b.x;d[25]=b.y;d[26]=c.z;d[27]=c.x;d[28]=b.y;d[29]=c.z;d[30]=c.x;d[31]=b.y;
+d[32]=c.z;d[33]=c.x;d[34]=c.y;d[35]=c.z;d[36]=c.x;d[37]=c.y;d[38]=c.z;d[39]=b.x;d[40]=c.y;d[41]=c.z;d[42]=b.x;d[43]=c.y;d[44]=c.z;d[45]=b.x;d[46]=b.y;d[47]=c.z;d[48]=b.x;d[49]=b.y;d[50]=b.z;d[51]=b.x;d[52]=b.y;d[53]=c.z;d[54]=c.x;d[55]=b.y;d[56]=b.z;d[57]=c.x;d[58]=b.y;d[59]=c.z;d[60]=c.x;d[61]=c.y;d[62]=b.z;d[63]=c.x;d[64]=c.y;d[65]=c.z;d[66]=b.x;d[67]=c.y;d[68]=b.z;d[69]=b.x;d[70]=c.y;d[71]=c.z;this.geometry.attributes.position.needsUpdate=!0;this.geometry.computeBoundingSphere();this.matrix=a.matrixWorld;
+this.matrixAutoUpdate=!1};THREE.BoundingBoxHelper=function(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.constructor=THREE.BoundingBoxHelper;THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};
+THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
+b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
+THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);THREE.CameraHelper.prototype.constructor=THREE.CameraHelper;
+THREE.CameraHelper.prototype.update=function(){var a,b,c=new THREE.Vector3,d=new THREE.Camera,e=function(e,g,h,k){c.set(g,h,k).unproject(d);e=b[e];if(void 0!==e)for(g=0,h=e.length;g<h;g++)a.vertices[e[g]].copy(c)};return function(){a=this.geometry;b=this.pointMap;d.projectionMatrix.copy(this.camera.projectionMatrix);e("c",0,0,-1);e("t",0,0,1);e("n1",-1,-1,-1);e("n2",1,-1,-1);e("n3",-1,1,-1);e("n4",1,1,-1);e("f1",-1,-1,1);e("f2",1,-1,1);e("f3",-1,1,1);e("f4",1,1,1);e("u1",.7,1.1,-1);e("u2",-.7,1.1,
+-1);e("u3",0,2,-1);e("cf1",-1,0,1);e("cf2",1,0,1);e("cf3",0,-1,1);e("cf4",0,1,1);e("cn1",-1,0,-1);e("cn2",1,0,-1);e("cn3",0,-1,-1);e("cn4",0,1,-1);a.verticesNeedUpdate=!0}}();
+THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;b=b||1;var c=new THREE.Geometry;c.vertices.push(new THREE.Vector3(-b,b,0),new THREE.Vector3(b,b,0),new THREE.Vector3(b,-b,0),new THREE.Vector3(-b,-b,0),new THREE.Vector3(-b,b,0));var d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.lightPlane=new THREE.Line(c,d);this.add(this.lightPlane);
+c=new THREE.Geometry;c.vertices.push(new THREE.Vector3,new THREE.Vector3);d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine=new THREE.Line(c,d);this.add(this.targetLine);this.update()};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.DirectionalLightHelper.prototype.constructor=THREE.DirectionalLightHelper;
+THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose();this.lightPlane.material.dispose();this.targetLine.geometry.dispose();this.targetLine.material.dispose()};
+THREE.DirectionalLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(){a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);c.subVectors(b,a);this.lightPlane.lookAt(c);this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine.geometry.vertices[1].copy(c);this.targetLine.geometry.verticesNeedUpdate=!0;this.targetLine.material.color.copy(this.lightPlane.material.color)}}();
+THREE.EdgesHelper=function(a,b,c){b=void 0!==b?b:16777215;c=Math.cos(THREE.Math.degToRad(void 0!==c?c:1));var d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry,k;a.geometry instanceof THREE.BufferGeometry?(k=new THREE.Geometry,k.fromBufferGeometry(a.geometry)):k=a.geometry.clone();k.mergeVertices();k.computeFaceNormals();var l=k.vertices;k=k.faces;for(var p=0,q=0,n=k.length;q<n;q++)for(var t=k[q],r=0;3>r;r++){d[0]=t[g[r]];d[1]=t[g[(r+1)%3]];d.sort(f);var s=d.toString();
+void 0===e[s]?(e[s]={vert1:d[0],vert2:d[1],face1:q,face2:void 0},p++):e[s].face2=q}d=new Float32Array(6*p);f=0;for(s in e)if(g=e[s],void 0===g.face2||k[g.face1].normal.dot(k[g.face2].normal)<=c)p=l[g.vert1],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z,p=l[g.vert2],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3));THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:b}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};
+THREE.EdgesHelper.prototype=Object.create(THREE.Line.prototype);THREE.EdgesHelper.prototype.constructor=THREE.EdgesHelper;
+THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=new THREE.Geometry;c=0;for(var e=this.object.geometry.faces.length;c<e;c++)b.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:a,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.FaceNormalsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.FaceNormalsHelper.prototype.constructor=THREE.FaceNormalsHelper;
+THREE.FaceNormalsHelper.prototype.update=function(){var a=this.geometry.vertices,b=this.object,c=b.geometry.vertices,d=b.geometry.faces,e=b.matrixWorld;b.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(e);for(var f=b=0,g=d.length;b<g;b++,f+=2){var h=d[b];a[f].copy(c[h.a]).add(c[h.b]).add(c[h.c]).divideScalar(3).applyMatrix4(e);a[f+1].copy(h.normal).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size).add(a[f])}this.geometry.verticesNeedUpdate=!0;return this};
+THREE.GridHelper=function(a,b){var c=new THREE.Geometry,d=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924);this.color2=new THREE.Color(8947848);for(var e=-a;e<=a;e+=b){c.vertices.push(new THREE.Vector3(-a,0,e),new THREE.Vector3(a,0,e),new THREE.Vector3(e,0,-a),new THREE.Vector3(e,0,a));var f=0===e?this.color1:this.color2;c.colors.push(f,f,f,f)}THREE.Line.call(this,c,d,THREE.LinePieces)};THREE.GridHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.GridHelper.prototype.constructor=THREE.GridHelper;THREE.GridHelper.prototype.setColors=function(a,b){this.color1.set(a);this.color2.set(b);this.geometry.colorsNeedUpdate=!0};
+THREE.HemisphereLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.colors=[new THREE.Color,new THREE.Color];var c=new THREE.SphereGeometry(b,4,2);c.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));for(var d=0;8>d;d++)c.faces[d].color=this.colors[4>d?0:1];d=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(c,d);this.add(this.lightSphere);
+this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.constructor=THREE.HemisphereLightHelper;THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()};
+THREE.HemisphereLightHelper.prototype.update=function(){var a=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.lightSphere.lookAt(a.setFromMatrixPosition(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}();
+THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrix=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.PointLightHelper.prototype.constructor=THREE.PointLightHelper;
+THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose();this.material.dispose()};THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)};
+THREE.SkeletonHelper=function(a){this.bones=this.getBoneList(a);for(var b=new THREE.Geometry,c=0;c<this.bones.length;c++)this.bones[c].parent instanceof THREE.Bone&&(b.vertices.push(new THREE.Vector3),b.vertices.push(new THREE.Vector3),b.colors.push(new THREE.Color(0,0,1)),b.colors.push(new THREE.Color(0,1,0)));c=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors,depthTest:!1,depthWrite:!1,transparent:!0});THREE.Line.call(this,b,c,THREE.LinePieces);this.root=a;this.matrix=a.matrixWorld;
+this.matrixAutoUpdate=!1;this.update()};THREE.SkeletonHelper.prototype=Object.create(THREE.Line.prototype);THREE.SkeletonHelper.prototype.constructor=THREE.SkeletonHelper;THREE.SkeletonHelper.prototype.getBoneList=function(a){var b=[];a instanceof THREE.Bone&&b.push(a);for(var c=0;c<a.children.length;c++)b.push.apply(b,this.getBoneList(a.children[c]));return b};
+THREE.SkeletonHelper.prototype.update=function(){for(var a=this.geometry,b=(new THREE.Matrix4).getInverse(this.root.matrixWorld),c=new THREE.Matrix4,d=0,e=0;e<this.bones.length;e++){var f=this.bones[e];f.parent instanceof THREE.Bone&&(c.multiplyMatrices(b,f.matrixWorld),a.vertices[d].setFromMatrixPosition(c),c.multiplyMatrices(b,f.parent.matrixWorld),a.vertices[d+1].setFromMatrixPosition(c),d+=2)}a.verticesNeedUpdate=!0;a.computeBoundingSphere()};
+THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;a=new THREE.CylinderGeometry(0,1,1,8,1,!0);a.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));var b=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});this.cone=new THREE.Mesh(a,b);this.add(this.cone);this.update()};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.SpotLightHelper.prototype.constructor=THREE.SpotLightHelper;THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};
+THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}();
+THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:16711680;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexNormals.length;g<h;g++)c.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.VertexNormalsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.VertexNormalsHelper.prototype.constructor=THREE.VertexNormalsHelper;
+THREE.VertexNormalsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,l=f.length;k<l;k++)for(var p=f[k],q=0,n=p.vertexNormals.length;q<n;q++){var t=p.vertexNormals[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(t).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);
+b.add(d[h]);h+=1;d[h].copy(b);h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
+THREE.VertexTangentsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:255;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexTangents.length;g<h;g++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.update()};THREE.VertexTangentsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.VertexTangentsHelper.prototype.constructor=THREE.VertexTangentsHelper;
+THREE.VertexTangentsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,l=f.length;k<l;k++)for(var p=f[k],q=0,n=p.vertexTangents.length;q<n;q++){var t=p.vertexTangents[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(t).transformDirection(g).multiplyScalar(this.size);b.add(d[h]);h+=1;d[h].copy(b);
+h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
+THREE.WireframeHelper=function(a,b){var c=void 0!==b?b:16777215,d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry;if(a.geometry instanceof THREE.Geometry){for(var k=a.geometry.vertices,l=a.geometry.faces,p=0,q=new Uint32Array(6*l.length),n=0,t=l.length;n<t;n++)for(var r=l[n],s=0;3>s;s++){d[0]=r[g[s]];d[1]=r[g[(s+1)%3]];d.sort(f);var u=d.toString();void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++)}d=new Float32Array(6*p);n=0;for(t=p;n<t;n++)for(s=0;2>s;s++)p=
+k[q[2*n+s]],g=6*n+3*s,d[g+0]=p.x,d[g+1]=p.y,d[g+2]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3))}else if(a.geometry instanceof THREE.BufferGeometry){if(void 0!==a.geometry.attributes.index){k=a.geometry.attributes.position.array;t=a.geometry.attributes.index.array;l=a.geometry.drawcalls;p=0;0===l.length&&(l=[{count:t.length,index:0,start:0}]);for(var q=new Uint32Array(2*t.length),r=0,v=l.length;r<v;++r)for(var s=l[r].start,u=l[r].count,g=l[r].index,n=s,x=s+u;n<x;n+=3)for(s=0;3>s;s++)d[0]=
+g+t[n+s],d[1]=g+t[n+(s+1)%3],d.sort(f),u=d.toString(),void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++);d=new Float32Array(6*p);n=0;for(t=p;n<t;n++)for(s=0;2>s;s++)g=6*n+3*s,p=3*q[2*n+s],d[g+0]=k[p],d[g+1]=k[p+1],d[g+2]=k[p+2]}else for(k=a.geometry.attributes.position.array,p=k.length/3,q=p/3,d=new Float32Array(6*p),n=0,t=q;n<t;n++)for(s=0;3>s;s++)g=18*n+6*s,q=9*n+3*s,d[g+0]=k[q],d[g+1]=k[q+1],d[g+2]=k[q+2],p=9*n+(s+1)%3*3,d[g+3]=k[p],d[g+4]=k[p+1],d[g+5]=k[p+2];h.addAttribute("position",new THREE.BufferAttribute(d,
+3))}THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.WireframeHelper.prototype.constructor=THREE.WireframeHelper;THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(a){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);THREE.ImmediateRenderObject.prototype.constructor=THREE.ImmediateRenderObject;
+THREE.MorphBlendMesh=function(a,b){THREE.Mesh.call(this,a,b);this.animationsMap={};this.animationsList=[];var c=this.geometry.morphTargets.length;this.createAnimation("__default",0,c-1,c/1);this.setAnimationWeight("__default",1)};THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.MorphBlendMesh.prototype.constructor=THREE.MorphBlendMesh;
 THREE.MorphBlendMesh.prototype.createAnimation=function(a,b,c,d){b={startFrame:b,endFrame:c,length:c-b+1,fps:d,duration:(c-b)/d,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[a]=b;this.animationsList.push(b)};
-THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(a){for(var b=/([a-z]+)(\d+)/,c,d={},e=this.geometry,f=0,h=e.morphTargets.length;f<h;f++){var g=e.morphTargets[f].name.match(b);if(g&&1<g.length){var i=g[1];d[i]||(d[i]={start:Infinity,end:-Infinity});g=d[i];f<g.start&&(g.start=f);f>g.end&&(g.end=f);c||(c=i)}}for(i in d)g=d[i],this.createAnimation(i,g.start,g.end,a);this.firstAnimation=c};
+THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(a){for(var b=/([a-z]+)_?(\d+)/,c,d={},e=this.geometry,f=0,g=e.morphTargets.length;f<g;f++){var h=e.morphTargets[f].name.match(b);if(h&&1<h.length){var k=h[1];d[k]||(d[k]={start:Infinity,end:-Infinity});h=d[k];f<h.start&&(h.start=f);f>h.end&&(h.end=f);c||(c=k)}}for(k in d)h=d[k],this.createAnimation(k,h.start,h.end,a);this.firstAnimation=c};
 THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(a){if(a=this.animationsMap[a])a.direction=1,a.directionBackwards=!1};THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(a){if(a=this.animationsMap[a])a.direction=-1,a.directionBackwards=!0};THREE.MorphBlendMesh.prototype.setAnimationFPS=function(a,b){var c=this.animationsMap[a];c&&(c.fps=b,c.duration=(c.end-c.start)/c.fps)};
 THREE.MorphBlendMesh.prototype.setAnimationDuration=function(a,b){var c=this.animationsMap[a];c&&(c.duration=b,c.fps=(c.end-c.start)/c.duration)};THREE.MorphBlendMesh.prototype.setAnimationWeight=function(a,b){var c=this.animationsMap[a];c&&(c.weight=b)};THREE.MorphBlendMesh.prototype.setAnimationTime=function(a,b){var c=this.animationsMap[a];c&&(c.time=b)};THREE.MorphBlendMesh.prototype.getAnimationTime=function(a){var b=0;if(a=this.animationsMap[a])b=a.time;return b};
-THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):console.warn("animation["+a+"] undefined")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1};
-THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;b<c;b++){var d=this.animationsList[b];if(d.active){var e=d.duration/d.length;d.time+=d.direction*a;if(d.mirroredLoop){if(d.time>d.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),h=d.weight;
-f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*h,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*h;this.morphTargetInfluences[d.lastFrame]=(1-e)*h}}};THREE.LensFlarePlugin=function(){function a(a,c){var d=b.createProgram(),e=b.createShader(b.FRAGMENT_SHADER),f=b.createShader(b.VERTEX_SHADER),g="precision "+c+" float;\n";b.shaderSource(e,g+a.fragmentShader);b.shaderSource(f,g+a.vertexShader);b.compileShader(e);b.compileShader(f);b.attachShader(d,e);b.attachShader(d,f);b.linkProgram(d);return d}var b,c,d,e,f,h,g,i,k,m,l,p,s;this.init=function(t){b=t.context;c=t;d=t.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);t=0;e[t++]=-1;e[t++]=-1;
-e[t++]=0;e[t++]=0;e[t++]=1;e[t++]=-1;e[t++]=1;e[t++]=0;e[t++]=1;e[t++]=1;e[t++]=1;e[t++]=1;e[t++]=-1;e[t++]=1;e[t++]=0;e[t++]=1;t=0;f[t++]=0;f[t++]=1;f[t++]=2;f[t++]=0;f[t++]=2;f[t++]=3;h=b.createBuffer();g=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,h);b.bufferData(b.ARRAY_BUFFER,e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.bufferData(b.ELEMENT_ARRAY_BUFFER,f,b.STATIC_DRAW);i=b.createTexture();k=b.createTexture();b.bindTexture(b.TEXTURE_2D,i);b.texImage2D(b.TEXTURE_2D,0,b.RGB,16,16,
-0,b.RGB,b.UNSIGNED_BYTE,null);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);b.bindTexture(b.TEXTURE_2D,k);b.texImage2D(b.TEXTURE_2D,0,b.RGBA,16,16,0,b.RGBA,b.UNSIGNED_BYTE,null);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);
-b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);0>=b.getParameter(b.MAX_VERTEX_TEXTURE_IMAGE_UNITS)?(m=!1,l=a(THREE.ShaderFlares.lensFlare,d)):(m=!0,l=a(THREE.ShaderFlares.lensFlareVertexTexture,d));p={};s={};p.vertex=b.getAttribLocation(l,"position");p.uv=b.getAttribLocation(l,"uv");s.renderType=b.getUniformLocation(l,"renderType");s.map=b.getUniformLocation(l,"map");s.occlusionMap=b.getUniformLocation(l,"occlusionMap");s.opacity=
-b.getUniformLocation(l,"opacity");s.color=b.getUniformLocation(l,"color");s.scale=b.getUniformLocation(l,"scale");s.rotation=b.getUniformLocation(l,"rotation");s.screenPosition=b.getUniformLocation(l,"screenPosition")};this.render=function(a,d,e,f){var a=a.__webglFlares,u=a.length;if(u){var w=new THREE.Vector3,z=f/e,B=0.5*e,D=0.5*f,x=16/f,F=new THREE.Vector2(x*z,x),A=new THREE.Vector3(1,1,0),O=new THREE.Vector2(1,1),C=s,x=p;b.useProgram(l);b.enableVertexAttribArray(p.vertex);b.enableVertexAttribArray(p.uv);
-b.uniform1i(C.occlusionMap,0);b.uniform1i(C.map,1);b.bindBuffer(b.ARRAY_BUFFER,h);b.vertexAttribPointer(x.vertex,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(x.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.disable(b.CULL_FACE);b.depthMask(!1);var E,I,y,v,G;for(E=0;E<u;E++)if(x=16/f,F.set(x*z,x),v=a[E],w.set(v.matrixWorld.elements[12],v.matrixWorld.elements[13],v.matrixWorld.elements[14]),w.applyMatrix4(d.matrixWorldInverse),w.applyProjection(d.projectionMatrix),A.copy(w),O.x=A.x*B+B,
-O.y=A.y*D+D,m||0<O.x&&O.x<e&&0<O.y&&O.y<f){b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,i);b.copyTexImage2D(b.TEXTURE_2D,0,b.RGB,O.x-8,O.y-8,16,16,0);b.uniform1i(C.renderType,0);b.uniform2f(C.scale,F.x,F.y);b.uniform3f(C.screenPosition,A.x,A.y,A.z);b.disable(b.BLEND);b.enable(b.DEPTH_TEST);b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0);b.activeTexture(b.TEXTURE0);b.bindTexture(b.TEXTURE_2D,k);b.copyTexImage2D(b.TEXTURE_2D,0,b.RGBA,O.x-8,O.y-8,16,16,0);b.uniform1i(C.renderType,1);b.disable(b.DEPTH_TEST);
-b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,i);b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0);v.positionScreen.copy(A);v.customUpdateCallback?v.customUpdateCallback(v):v.updateLensFlares();b.uniform1i(C.renderType,2);b.enable(b.BLEND);I=0;for(y=v.lensFlares.length;I<y;I++)G=v.lensFlares[I],0.001<G.opacity&&0.001<G.scale&&(A.x=G.x,A.y=G.y,A.z=G.z,x=G.size*G.scale/f,F.x=x*z,F.y=x,b.uniform3f(C.screenPosition,A.x,A.y,A.z),b.uniform2f(C.scale,F.x,F.y),b.uniform1f(C.rotation,G.rotation),b.uniform1f(C.opacity,
-G.opacity),b.uniform3f(C.color,G.color.r,G.color.g,G.color.b),c.setBlending(G.blending,G.blendEquation,G.blendSrc,G.blendDst),c.setTexture(G.texture,1),b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0))}b.enable(b.CULL_FACE);b.enable(b.DEPTH_TEST);b.depthMask(!0)}}};THREE.ShadowMapPlugin=function(){var a,b,c,d,e,f,h=new THREE.Frustum,g=new THREE.Matrix4,i=new THREE.Vector3,k=new THREE.Vector3,m=new THREE.Vector3;this.init=function(g){a=g.context;b=g;var g=THREE.ShaderLib.depthRGBA,h=THREE.UniformsUtils.clone(g.uniforms);c=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h});d=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0});e=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,
-vertexShader:g.vertexShader,uniforms:h,skinning:!0});f=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0,skinning:!0});c._shadowPass=!0;d._shadowPass=!0;e._shadowPass=!0;f._shadowPass=!0};this.render=function(a,c){b.shadowMapEnabled&&b.shadowMapAutoUpdate&&this.update(a,c)};this.update=function(l,p){var s,t,n,r,q,u,w,z,B,D=[];r=0;a.clearColor(1,1,1,1);a.disable(a.BLEND);a.enable(a.CULL_FACE);a.frontFace(a.CCW);b.shadowMapCullFace===THREE.CullFaceFront?
-a.cullFace(a.FRONT):a.cullFace(a.BACK);b.setDepthTest(!0);s=0;for(t=l.__lights.length;s<t;s++)if(n=l.__lights[s],n.castShadow)if(n instanceof THREE.DirectionalLight&&n.shadowCascade)for(q=0;q<n.shadowCascadeCount;q++){var x;if(n.shadowCascadeArray[q])x=n.shadowCascadeArray[q];else{B=n;w=q;x=new THREE.DirectionalLight;x.isVirtual=!0;x.onlyShadow=!0;x.castShadow=!0;x.shadowCameraNear=B.shadowCameraNear;x.shadowCameraFar=B.shadowCameraFar;x.shadowCameraLeft=B.shadowCameraLeft;x.shadowCameraRight=B.shadowCameraRight;
-x.shadowCameraBottom=B.shadowCameraBottom;x.shadowCameraTop=B.shadowCameraTop;x.shadowCameraVisible=B.shadowCameraVisible;x.shadowDarkness=B.shadowDarkness;x.shadowBias=B.shadowCascadeBias[w];x.shadowMapWidth=B.shadowCascadeWidth[w];x.shadowMapHeight=B.shadowCascadeHeight[w];x.pointsWorld=[];x.pointsFrustum=[];z=x.pointsWorld;u=x.pointsFrustum;for(var F=0;8>F;F++)z[F]=new THREE.Vector3,u[F]=new THREE.Vector3;z=B.shadowCascadeNearZ[w];B=B.shadowCascadeFarZ[w];u[0].set(-1,-1,z);u[1].set(1,-1,z);u[2].set(-1,
-1,z);u[3].set(1,1,z);u[4].set(-1,-1,B);u[5].set(1,-1,B);u[6].set(-1,1,B);u[7].set(1,1,B);x.originalCamera=p;u=new THREE.Gyroscope;u.position=n.shadowCascadeOffset;u.add(x);u.add(x.target);p.add(u);n.shadowCascadeArray[q]=x;console.log("Created virtualLight",x)}w=n;z=q;B=w.shadowCascadeArray[z];B.position.copy(w.position);B.target.position.copy(w.target.position);B.lookAt(B.target);B.shadowCameraVisible=w.shadowCameraVisible;B.shadowDarkness=w.shadowDarkness;B.shadowBias=w.shadowCascadeBias[z];u=w.shadowCascadeNearZ[z];
-w=w.shadowCascadeFarZ[z];B=B.pointsFrustum;B[0].z=u;B[1].z=u;B[2].z=u;B[3].z=u;B[4].z=w;B[5].z=w;B[6].z=w;B[7].z=w;D[r]=x;r++}else D[r]=n,r++;s=0;for(t=D.length;s<t;s++){n=D[s];n.shadowMap||(q=THREE.LinearFilter,b.shadowMapType===THREE.PCFSoftShadowMap&&(q=THREE.NearestFilter),n.shadowMap=new THREE.WebGLRenderTarget(n.shadowMapWidth,n.shadowMapHeight,{minFilter:q,magFilter:q,format:THREE.RGBAFormat}),n.shadowMapSize=new THREE.Vector2(n.shadowMapWidth,n.shadowMapHeight),n.shadowMatrix=new THREE.Matrix4);
-if(!n.shadowCamera){if(n instanceof THREE.SpotLight)n.shadowCamera=new THREE.PerspectiveCamera(n.shadowCameraFov,n.shadowMapWidth/n.shadowMapHeight,n.shadowCameraNear,n.shadowCameraFar);else if(n instanceof THREE.DirectionalLight)n.shadowCamera=new THREE.OrthographicCamera(n.shadowCameraLeft,n.shadowCameraRight,n.shadowCameraTop,n.shadowCameraBottom,n.shadowCameraNear,n.shadowCameraFar);else{console.error("Unsupported light type for shadow");continue}l.add(n.shadowCamera);!0===l.autoUpdate&&l.updateMatrixWorld()}n.shadowCameraVisible&&
-!n.cameraHelper&&(n.cameraHelper=new THREE.CameraHelper(n.shadowCamera),n.shadowCamera.add(n.cameraHelper));if(n.isVirtual&&x.originalCamera==p){q=p;r=n.shadowCamera;u=n.pointsFrustum;B=n.pointsWorld;i.set(Infinity,Infinity,Infinity);k.set(-Infinity,-Infinity,-Infinity);for(w=0;8>w;w++)z=B[w],z.copy(u[w]),THREE.ShadowMapPlugin.__projector.unprojectVector(z,q),z.applyMatrix4(r.matrixWorldInverse),z.x<i.x&&(i.x=z.x),z.x>k.x&&(k.x=z.x),z.y<i.y&&(i.y=z.y),z.y>k.y&&(k.y=z.y),z.z<i.z&&(i.z=z.z),z.z>k.z&&
-(k.z=z.z);r.left=i.x;r.right=k.x;r.top=k.y;r.bottom=i.y;r.updateProjectionMatrix()}r=n.shadowMap;u=n.shadowMatrix;q=n.shadowCamera;q.position.getPositionFromMatrix(n.matrixWorld);m.getPositionFromMatrix(n.target.matrixWorld);q.lookAt(m);q.updateMatrixWorld();q.matrixWorldInverse.getInverse(q.matrixWorld);n.cameraHelper&&(n.cameraHelper.visible=n.shadowCameraVisible);n.shadowCameraVisible&&n.cameraHelper.update();u.set(0.5,0,0,0.5,0,0.5,0,0.5,0,0,0.5,0.5,0,0,0,1);u.multiply(q.projectionMatrix);u.multiply(q.matrixWorldInverse);
-g.multiplyMatrices(q.projectionMatrix,q.matrixWorldInverse);h.setFromMatrix(g);b.setRenderTarget(r);b.clear();B=l.__webglObjects;n=0;for(r=B.length;n<r;n++)if(w=B[n],u=w.object,w.render=!1,u.visible&&u.castShadow&&(!(u instanceof THREE.Mesh||u instanceof THREE.ParticleSystem)||!u.frustumCulled||h.intersectsObject(u)))u._modelViewMatrix.multiplyMatrices(q.matrixWorldInverse,u.matrixWorld),w.render=!0;n=0;for(r=B.length;n<r;n++)w=B[n],w.render&&(u=w.object,w=w.buffer,F=u.material instanceof THREE.MeshFaceMaterial?
-u.material.materials[0]:u.material,z=0<u.geometry.morphTargets.length&&F.morphTargets,F=u instanceof THREE.SkinnedMesh&&F.skinning,z=u.customDepthMaterial?u.customDepthMaterial:F?z?f:e:z?d:c,w instanceof THREE.BufferGeometry?b.renderBufferDirect(q,l.__lights,null,z,w,u):b.renderBuffer(q,l.__lights,null,z,w,u));B=l.__webglObjectsImmediate;n=0;for(r=B.length;n<r;n++)w=B[n],u=w.object,u.visible&&u.castShadow&&(u._modelViewMatrix.multiplyMatrices(q.matrixWorldInverse,u.matrixWorld),b.renderImmediateObject(q,
-l.__lights,null,c,u))}s=b.getClearColor();t=b.getClearAlpha();a.clearColor(s.r,s.g,s.b,t);a.enable(a.BLEND);b.shadowMapCullFace===THREE.CullFaceFront&&a.cullFace(a.BACK)}};THREE.ShadowMapPlugin.__projector=new THREE.Projector;THREE.SpritePlugin=function(){function a(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var b,c,d,e,f,h,g,i,k,m;this.init=function(a){b=a.context;c=a;d=a.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);a=0;e[a++]=-0.5;e[a++]=-0.5;e[a++]=0;e[a++]=0;e[a++]=0.5;e[a++]=-0.5;e[a++]=1;e[a++]=0;e[a++]=0.5;e[a++]=0.5;e[a++]=1;e[a++]=1;e[a++]=-0.5;e[a++]=0.5;e[a++]=0;e[a++]=1;a=0;f[a++]=0;f[a++]=1;f[a++]=2;f[a++]=0;f[a++]=2;f[a++]=3;h=b.createBuffer();g=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,h);
-b.bufferData(b.ARRAY_BUFFER,e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.bufferData(b.ELEMENT_ARRAY_BUFFER,f,b.STATIC_DRAW);var a=THREE.ShaderSprite.sprite,p=b.createProgram(),s=b.createShader(b.FRAGMENT_SHADER),t=b.createShader(b.VERTEX_SHADER),n="precision "+d+" float;\n";b.shaderSource(s,n+a.fragmentShader);b.shaderSource(t,n+a.vertexShader);b.compileShader(s);b.compileShader(t);b.attachShader(p,s);b.attachShader(p,t);b.linkProgram(p);i=p;k={};m={};k.position=b.getAttribLocation(i,
-"position");k.uv=b.getAttribLocation(i,"uv");m.uvOffset=b.getUniformLocation(i,"uvOffset");m.uvScale=b.getUniformLocation(i,"uvScale");m.rotation=b.getUniformLocation(i,"rotation");m.scale=b.getUniformLocation(i,"scale");m.alignment=b.getUniformLocation(i,"alignment");m.halfViewport=b.getUniformLocation(i,"halfViewport");m.color=b.getUniformLocation(i,"color");m.map=b.getUniformLocation(i,"map");m.opacity=b.getUniformLocation(i,"opacity");m.useScreenCoordinates=b.getUniformLocation(i,"useScreenCoordinates");
-m.sizeAttenuation=b.getUniformLocation(i,"sizeAttenuation");m.screenPosition=b.getUniformLocation(i,"screenPosition");m.modelViewMatrix=b.getUniformLocation(i,"modelViewMatrix");m.projectionMatrix=b.getUniformLocation(i,"projectionMatrix");m.fogType=b.getUniformLocation(i,"fogType");m.fogDensity=b.getUniformLocation(i,"fogDensity");m.fogNear=b.getUniformLocation(i,"fogNear");m.fogFar=b.getUniformLocation(i,"fogFar");m.fogColor=b.getUniformLocation(i,"fogColor");m.alphaTest=b.getUniformLocation(i,
-"alphaTest")};this.render=function(d,e,f,t){var n=d.__webglSprites,r=n.length;if(r){var q=k,u=m,f=0.5*f,t=0.5*t;b.useProgram(i);b.enableVertexAttribArray(q.position);b.enableVertexAttribArray(q.uv);b.disable(b.CULL_FACE);b.enable(b.BLEND);b.bindBuffer(b.ARRAY_BUFFER,h);b.vertexAttribPointer(q.position,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(q.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.uniformMatrix4fv(u.projectionMatrix,!1,e.projectionMatrix.elements);b.activeTexture(b.TEXTURE0);
-b.uniform1i(u.map,0);var w=q=0,z=d.fog;z?(b.uniform3f(u.fogColor,z.color.r,z.color.g,z.color.b),z instanceof THREE.Fog?(b.uniform1f(u.fogNear,z.near),b.uniform1f(u.fogFar,z.far),b.uniform1i(u.fogType,1),w=q=1):z instanceof THREE.FogExp2&&(b.uniform1f(u.fogDensity,z.density),b.uniform1i(u.fogType,2),w=q=2)):(b.uniform1i(u.fogType,0),w=q=0);for(var B,D,x=[],z=0;z<r;z++)B=n[z],D=B.material,B.visible&&0!==D.opacity&&(D.useScreenCoordinates?B.z=-B.position.z:(B._modelViewMatrix.multiplyMatrices(e.matrixWorldInverse,
-B.matrixWorld),B.z=-B._modelViewMatrix.elements[14]));n.sort(a);for(z=0;z<r;z++)B=n[z],D=B.material,B.visible&&0!==D.opacity&&(D.map&&D.map.image&&D.map.image.width)&&(b.uniform1f(u.alphaTest,D.alphaTest),!0===D.useScreenCoordinates?(b.uniform1i(u.useScreenCoordinates,1),b.uniform3f(u.screenPosition,(B.position.x*c.devicePixelRatio-f)/f,(t-B.position.y*c.devicePixelRatio)/t,Math.max(0,Math.min(1,B.position.z))),x[0]=c.devicePixelRatio*B.scale.x,x[1]=c.devicePixelRatio*B.scale.y):(b.uniform1i(u.useScreenCoordinates,
-0),b.uniform1i(u.sizeAttenuation,D.sizeAttenuation?1:0),b.uniformMatrix4fv(u.modelViewMatrix,!1,B._modelViewMatrix.elements),x[0]=B.scale.x,x[1]=B.scale.y),e=d.fog&&D.fog?w:0,q!==e&&(b.uniform1i(u.fogType,e),q=e),b.uniform2f(u.uvScale,D.uvScale.x,D.uvScale.y),b.uniform2f(u.uvOffset,D.uvOffset.x,D.uvOffset.y),b.uniform2f(u.alignment,D.alignment.x,D.alignment.y),b.uniform1f(u.opacity,D.opacity),b.uniform3f(u.color,D.color.r,D.color.g,D.color.b),b.uniform1f(u.rotation,B.rotation),b.uniform2fv(u.scale,
-x),b.uniform2f(u.halfViewport,f,t),c.setBlending(D.blending,D.blendEquation,D.blendSrc,D.blendDst),c.setDepthTest(D.depthTest),c.setDepthWrite(D.depthWrite),c.setTexture(D.map,0),b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0));b.enable(b.CULL_FACE)}}};THREE.DepthPassPlugin=function(){this.enabled=!1;this.renderTarget=null;var a,b,c,d,e,f,h=new THREE.Frustum,g=new THREE.Matrix4;this.init=function(g){a=g.context;b=g;var g=THREE.ShaderLib.depthRGBA,h=THREE.UniformsUtils.clone(g.uniforms);c=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h});d=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0});e=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,
-vertexShader:g.vertexShader,uniforms:h,skinning:!0});f=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0,skinning:!0});c._shadowPass=!0;d._shadowPass=!0;e._shadowPass=!0;f._shadowPass=!0};this.render=function(a,b){this.enabled&&this.update(a,b)};this.update=function(i,k){var m,l,p,s,t,n;a.clearColor(1,1,1,1);a.disable(a.BLEND);b.setDepthTest(!0);!0===i.autoUpdate&&i.updateMatrixWorld();k.matrixWorldInverse.getInverse(k.matrixWorld);g.multiplyMatrices(k.projectionMatrix,
-k.matrixWorldInverse);h.setFromMatrix(g);b.setRenderTarget(this.renderTarget);b.clear();n=i.__webglObjects;m=0;for(l=n.length;m<l;m++)if(p=n[m],t=p.object,p.render=!1,t.visible&&(!(t instanceof THREE.Mesh||t instanceof THREE.ParticleSystem)||!t.frustumCulled||h.intersectsObject(t)))t._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,t.matrixWorld),p.render=!0;var r;m=0;for(l=n.length;m<l;m++)if(p=n[m],p.render&&(t=p.object,p=p.buffer,!(t instanceof THREE.ParticleSystem)||t.customDepthMaterial))(r=
-t.material instanceof THREE.MeshFaceMaterial?t.material.materials[0]:t.material)&&b.setMaterialFaces(t.material),s=0<t.geometry.morphTargets.length&&r.morphTargets,r=t instanceof THREE.SkinnedMesh&&r.skinning,s=t.customDepthMaterial?t.customDepthMaterial:r?s?f:e:s?d:c,p instanceof THREE.BufferGeometry?b.renderBufferDirect(k,i.__lights,null,s,p,t):b.renderBuffer(k,i.__lights,null,s,p,t);n=i.__webglObjectsImmediate;m=0;for(l=n.length;m<l;m++)p=n[m],t=p.object,t.visible&&(t._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,
-t.matrixWorld),b.renderImmediateObject(k,i.__lights,null,c,t));m=b.getClearColor();l=b.getClearAlpha();a.clearColor(m.r,m.g,m.b,l);a.enable(a.BLEND)}};THREE.ShaderFlares={lensFlareVertexTexture:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility =        visibility.r / 9.0;\nvVisibility *= 1.0 - visibility.g / 9.0;\nvVisibility *=       visibility.b / 9.0;\nvVisibility *= 1.0 - visibility.a / 9.0;\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"},lensFlare:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"}};THREE.ShaderSprite={sprite:{vertexShader:"uniform int useScreenCoordinates;\nuniform int sizeAttenuation;\nuniform vec3 screenPosition;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 alignment;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nuniform vec2 halfViewport;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = ( position + alignment ) * scale;\nvec2 rotatedPosition;\nrotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\nrotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\nvec4 finalPosition;\nif( useScreenCoordinates != 0 ) {\nfinalPosition = vec4( screenPosition.xy + ( rotatedPosition / halfViewport ), screenPosition.z, 1.0 );\n} else {\nfinalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );\nfinalPosition = projectionMatrix * finalPosition;\n}\ngl_Position = finalPosition;\n}",
-fragmentShader:"uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"}};
+THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):THREE.warn("THREE.MorphBlendMesh: animation["+a+"] undefined in .playAnimation()")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1};
+THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;b<c;b++){var d=this.animationsList[b];if(d.active){var e=d.duration/d.length;d.time+=d.direction*a;if(d.mirroredLoop){if(d.time>d.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),g=d.weight;
+f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*g,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*g;this.morphTargetInfluences[d.lastFrame]=(1-e)*g}}};
diff --git a/gz3d/src/gz.js b/gz3d/src/gz.js
index 9aa8ac8..e8dd0ae 100644
--- a/gz3d/src/gz.js
+++ b/gz3d/src/gz.js
@@ -1,3 +1,3 @@
 var GZ3D = GZ3D || {
   REVISION : '1'
-};
+};
\ No newline at end of file
diff --git a/gz3d/src/gziface.js b/gz3d/src/gziface.js
index ccb90e0..3186fbb 100644
--- a/gz3d/src/gziface.js
+++ b/gz3d/src/gziface.js
@@ -1037,7 +1037,7 @@ GZ3D.GZIface.prototype.applyMaterial = function(obj, mat)
       var ambient = mat.ambient;
       if (ambient)
       {
-        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+        obj.material.emissive.setRGB(ambient[0], ambient[1], ambient[2]);
       }
       var diffuse = mat.diffuse;
       if (diffuse)
@@ -1372,7 +1372,7 @@ GZ3D.GZIface.prototype.parseMaterial = function(material)
       var ambient = mat['ambient'];
       if (ambient)
       {
-        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+        obj.material.emissive.setRGB(ambient[0], ambient[1], ambient[2]);
       }
       var diffuse = mat['diffuse'];
       if (diffuse)
diff --git a/gz3d/src/gzmanipulator.js b/gz3d/src/gzmanipulator.js
index e347212..84e9cad 100644
--- a/gz3d/src/gzmanipulator.js
+++ b/gz3d/src/gzmanipulator.js
@@ -46,7 +46,6 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
   var changeEvent = {type: 'change'};
 
   var ray = new THREE.Raycaster();
-  var projector = new THREE.Projector();
   var pointerVector = new THREE.Vector3();
 
   var point = new THREE.Vector3();
@@ -1142,7 +1141,7 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
     var y = (pointer.clientY - rect.top) / rect.height;
     pointerVector.set((x) * 2 - 1, - (y) * 2 + 1, 0.5);
 
-    projector.unprojectVector(pointerVector, scope.camera);
+    pointerVector.unproject(scope.camera);
     ray.set(camPosition, pointerVector.sub(camPosition).normalize());
 
     // checks all intersections between the ray and the objects,
@@ -1175,7 +1174,8 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
   function bakeTransformations(object)
   {
     var tempGeometry = new THREE.Geometry();
-    THREE.GeometryUtils.merge(tempGeometry, object);
+    object.updateMatrix();
+    tempGeometry.merge(object.geometry, object.matrix);
     object.geometry = tempGeometry;
     object.position.set(0, 0, 0);
     object.rotation.set(0, 0, 0);
diff --git a/gz3d/src/gzradialmenu.js b/gz3d/src/gzradialmenu.js
index 0fca39b..16c1cc5 100644
--- a/gz3d/src/gzradialmenu.js
+++ b/gz3d/src/gzradialmenu.js
@@ -353,9 +353,9 @@ GZ3D.RadialMenu.prototype.addItem = function(type, iconTexture)
   // Icon
   iconTexture = THREE.ImageUtils.loadTexture( iconTexture );
 
-  var iconMaterial = new THREE.SpriteMaterial( { useScreenCoordinates: true,
-      alignment: THREE.SpriteAlignment.center } );
-  iconMaterial.map = iconTexture;
+  var iconMaterial = new THREE.SpriteMaterial( {
+    map: iconTexture
+  } );
 
   var icon = new THREE.Sprite( iconMaterial );
   icon.scale.set( this.bgSize*this.iconProportion,
@@ -365,8 +365,6 @@ GZ3D.RadialMenu.prototype.addItem = function(type, iconTexture)
   // Background
   var bgMaterial = new THREE.SpriteMaterial( {
       map: this.bgShape,
-      useScreenCoordinates: true,
-      alignment: THREE.SpriteAlignment.center,
       color: this.plainColor } );
 
   var bg = new THREE.Sprite( bgMaterial );
@@ -375,8 +373,6 @@ GZ3D.RadialMenu.prototype.addItem = function(type, iconTexture)
   // Highlight
   var highlightMaterial = new THREE.SpriteMaterial({
       map: this.bgShape,
-      useScreenCoordinates: true,
-      alignment: THREE.SpriteAlignment.center,
       color: this.highlightColor});
 
   var highlight = new THREE.Sprite(highlightMaterial);
diff --git a/gz3d/src/gzscene.js b/gz3d/src/gzscene.js
index d69c9d1..8e39fb4 100644
--- a/gz3d/src/gzscene.js
+++ b/gz3d/src/gzscene.js
@@ -1,3 +1,5 @@
+
+
 /**
  * The scene is where everything is placed, from objects, to lights and cameras.
  * @constructor
@@ -97,7 +99,9 @@ GZ3D.Scene.prototype.init = function()
   this.timeDown = null;
 
   this.controls = new THREE.OrbitControls(this.camera);
-  this.scene.add(this.controls.targetIndicator);
+  if (this.controls.targetIndicator !== undefined) {
+    this.scene.add(this.controls.targetIndicator);
+  }
 
   this.emitter = new EventEmitter2({ verbose: true });
 
@@ -235,7 +239,7 @@ GZ3D.Scene.prototype.init = function()
 
   material = new THREE.MeshLambertMaterial();
   material.color = new THREE.Color(0xffff00);
-  material.ambient = material.color;
+  material.emissive = material.color;
 
   geometry = new THREE.CylinderGeometry(0.02, 0.02, 0.25, 36, 1, false);
 
@@ -570,13 +574,12 @@ GZ3D.Scene.prototype.onKeyDown = function(event)
  */
 GZ3D.Scene.prototype.getRayCastModel = function(pos, intersect)
 {
-  var projector = new THREE.Projector();
   var vector = new THREE.Vector3(
       ((pos.x - this.renderer.domElement.offsetLeft)
       / window.innerWidth) * 2 - 1,
       -((pos.y - this.renderer.domElement.offsetTop)
       / window.innerHeight) * 2 + 1, 1);
-  projector.unprojectVector(vector, this.camera);
+  vector.unproject(this.camera);
   var ray = new THREE.Raycaster( this.camera.position,
       vector.sub(this.camera.position).normalize() );
 
@@ -856,7 +859,7 @@ GZ3D.Scene.prototype.createCylinder = function(radius, length)
  */
 GZ3D.Scene.prototype.createBox = function(width, height, depth)
 {
-  var geometry = new THREE.CubeGeometry(width, height, depth, 1, 1, 1);
+  var geometry = new THREE.BoxGeometry(width, height, depth, 1, 1, 1);
 
   // Fix UVs so textures are mapped in a way that is consistent to gazebo
   // Some face uvs need to be rotated clockwise, while others anticlockwise
@@ -1282,7 +1285,7 @@ GZ3D.Scene.prototype.createRoads = function(points, width, texture)
  /* var ambient = mat['ambient'];
   if (ambient)
   {
-    material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+    material.emissive.setRGB(ambient[0], ambient[1], ambient[2]);
   }
   var diffuse = mat['diffuse'];
   if (diffuse)
@@ -1706,7 +1709,7 @@ GZ3D.Scene.prototype.setMaterial = function(obj, material)
       var ambient = material.ambient;
       if (ambient)
       {
-        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+        obj.material.emissive.setRGB(ambient[0], ambient[1], ambient[2]);
       }
       var diffuse = material.diffuse;
       if (diffuse)
diff --git a/gz3d/src/gzspawnmodel.js b/gz3d/src/gzspawnmodel.js
index 9675c22..75df2c6 100644
--- a/gz3d/src/gzspawnmodel.js
+++ b/gz3d/src/gzspawnmodel.js
@@ -18,7 +18,6 @@ GZ3D.SpawnModel = function(scene, domElement)
 GZ3D.SpawnModel.prototype.init = function()
 {
   this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
-  this.projector = new THREE.Projector();
   this.ray = new THREE.Ray();
   this.obj = null;
   this.active = false;
@@ -243,7 +242,7 @@ GZ3D.SpawnModel.prototype.moveSpawnedModel = function(positionX, positionY)
 {
   var vector = new THREE.Vector3( (positionX / window.innerWidth) * 2 - 1,
         -(positionY / window.innerHeight) * 2 + 1, 0.5);
-  this.projector.unprojectVector(vector, this.scene.camera);
+  vector.unproject(this.scene.camera);
   this.ray.set(this.scene.camera.position,
       vector.sub(this.scene.camera.position).normalize());
   var point = this.ray.intersectPlane(this.plane);
-- 
GitLab