From 8061f657b827178ebdefb312ee06df99dc0c3473 Mon Sep 17 00:00:00 2001
From: DUBREUIL Eliot <eliot.dubreuil1@etu.univ-lorraine.fr>
Date: Thu, 6 Feb 2025 15:31:01 +0000
Subject: [PATCH] Choix de l'image + la page web

---
 DUBREUIL/img.jpg                            |   Bin 12343 -> 116316 bytes
 DUBREUIL/index.html                         |    39 +-
 DUBREUIL/lib/Coordinates.js                 |   152 +
 DUBREUIL/lib/Detector.js                    |    40 +
 DUBREUIL/lib/OrbitAndPanControls.js         |   532 +
 DUBREUIL/lib/OrbitAndPanControls.new.js     |   530 +
 DUBREUIL/lib/TrackballControls.js           |   537 +
 DUBREUIL/lib/dat.gui.min.js                 |    94 +
 DUBREUIL/lib/jquery-1.8.3.min.js            |     2 +
 DUBREUIL/lib/sprintf.js                     |   134 +
 DUBREUIL/lib/stats.min.js                   |     6 +
 DUBREUIL/lib/three.js                       | 35996 ++++++++++++++++++
 DUBREUIL/lib/three.min.js                   |   706 +
 DUBREUIL/lib/tween.min.js                   |    13 +
 DUBREUIL/lib/uclass_BeveledBlockGeometry.js |   264 +
 DUBREUIL/lib/uclass_TeacupGeometry.js       |   529 +
 DUBREUIL/lib/uclass_TeapotGeometry.js       |   737 +
 DUBREUIL/lib/uclass_TeaspoonGeometry.js     |   527 +
 DUBREUIL/lib/uclass_shaders.js              |   286 +
 DUBREUIL/peinture.js                        |   348 +
 20 files changed, 41471 insertions(+), 1 deletion(-)
 create mode 100644 DUBREUIL/lib/Coordinates.js
 create mode 100644 DUBREUIL/lib/Detector.js
 create mode 100644 DUBREUIL/lib/OrbitAndPanControls.js
 create mode 100644 DUBREUIL/lib/OrbitAndPanControls.new.js
 create mode 100644 DUBREUIL/lib/TrackballControls.js
 create mode 100644 DUBREUIL/lib/dat.gui.min.js
 create mode 100644 DUBREUIL/lib/jquery-1.8.3.min.js
 create mode 100644 DUBREUIL/lib/sprintf.js
 create mode 100644 DUBREUIL/lib/stats.min.js
 create mode 100644 DUBREUIL/lib/three.js
 create mode 100644 DUBREUIL/lib/three.min.js
 create mode 100644 DUBREUIL/lib/tween.min.js
 create mode 100644 DUBREUIL/lib/uclass_BeveledBlockGeometry.js
 create mode 100644 DUBREUIL/lib/uclass_TeacupGeometry.js
 create mode 100644 DUBREUIL/lib/uclass_TeapotGeometry.js
 create mode 100644 DUBREUIL/lib/uclass_TeaspoonGeometry.js
 create mode 100644 DUBREUIL/lib/uclass_shaders.js
 create mode 100644 DUBREUIL/peinture.js

diff --git a/DUBREUIL/img.jpg b/DUBREUIL/img.jpg
index d642e136257de4fb317a18f7c422126216665b16..93d66fffc9558b52cff2c0693fbc0ec939e51dcc 100644
GIT binary patch
literal 116316
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
JM9Tla2>=F+m^}ah

literal 12343
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

diff --git a/DUBREUIL/index.html b/DUBREUIL/index.html
index 76e27dc..e958133 100644
--- a/DUBREUIL/index.html
+++ b/DUBREUIL/index.html
@@ -1 +1,38 @@
-Le travail n'a pas encore commencé!!!
\ No newline at end of file
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="UTF-8">
+    <title> Peinture </title>
+    <style>
+        body { margin: 0; flex: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; }
+        canvas { width: 50%; height: 100% }
+        .canvaEtImage{ flex-direction: row; display: flex; justify-content: center; align-items: center; gap: 20px; }
+        a { color: black; }
+    </style>
+</head>
+<body>
+    
+    <!-- API importe du site de Three.js -->
+    <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
+    <script type="importmap">
+        {
+            "imports": {
+            "three": "https://threejs.org/build/three.module.js",
+            "three/addons/": "https://threejs.org/examples/jsm/"
+            }
+        }
+    </script>
+    <!-- JQuery pour afficher les erreurs -->
+    <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
+    <!-- Un titre centre -->
+    <h1> <a href="https://fr.wikipedia.org/wiki/Le_Dessert_de_gaufrettes">Le Dessert de gaufrettes de Lubin Baugin</a> </h1>
+    <div class="canvaEtImage">   
+        <div id="webGL"></div>
+        <!-- Mon script avec un chemin relatif -->
+        <script type="module" src="./img.jpg"></script>
+        <!-- Image originale -->
+        <img src="./783px-Lubin_Baugin_001.jpg" alt="Original">
+    </div>
+    
+</body>
+</html>
\ No newline at end of file
diff --git a/DUBREUIL/lib/Coordinates.js b/DUBREUIL/lib/Coordinates.js
new file mode 100644
index 0000000..f9f6087
--- /dev/null
+++ b/DUBREUIL/lib/Coordinates.js
@@ -0,0 +1,152 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/*global THREE, scene*/
+import * as THREE from 'three';
+
+
+export let Coordinates = {
+	drawGrid:function(params) {
+		params = params || {};
+		var size = params.size !== undefined ? params.size:100;
+		var scale = params.scale !== undefined ? params.scale:0.1;
+		var orientation = params.orientation !== undefined ? params.orientation:"x";
+		var grid = new THREE.Mesh(
+			new THREE.PlaneGeometry(size, size, size * scale, size * scale),
+			new THREE.MeshBasicMaterial({ color: 0x555555, wireframe: true }) 
+			);
+		// Yes, these are poorly labeled! It would be a mess to fix.
+		// What's really going on here:
+		// "x" means "rotate 90 degrees around x", etc.
+		// So "x" really means "show a grid with a normal of Y"
+		//    "y" means "show a grid with a normal of X"
+		//    "z" means (logically enough) "show a grid with a normal of Z"
+		if (orientation === "x") {
+			grid.rotation.x = - Math.PI / 2;
+		} else if (orientation === "y") {
+			grid.rotation.y = - Math.PI / 2;
+		} else if (orientation === "z") {
+			grid.rotation.z = - Math.PI / 2;
+		}
+
+		window.scene.add(grid);
+	},
+	drawGround:function(params) {
+		params = params || {};
+		var size = params.size !== undefined ? params.size:100;
+		var color = params.color !== undefined ? params.color:0xFFFFFF;
+		var ground = new THREE.Mesh(
+			new THREE.PlaneGeometry(size, size),
+			// When we use a ground plane we use directional lights, so illuminating
+			// just the corners is sufficient.
+			// Use MeshPhongMaterial if you want to capture per-pixel lighting:
+			// new THREE.MeshPhongMaterial({ color: color, specular: 0x000000,
+			new THREE.MeshLambertMaterial({ color: color,
+				// polygonOffset moves the plane back from the eye a bit, so that the lines on top of
+				// the grid do not have z-fighting with the grid:
+				// Factor == 1 moves it back relative to the slope (more on-edge means move back farther)
+				// Units == 4 is a fixed amount to move back, and 4 is usually a good value
+				polygonOffset: true, polygonOffsetFactor: 1.0, polygonOffsetUnits: 4.0
+			}));
+		ground.rotation.x = - Math.PI / 2;
+		window.scene.add(ground);
+	},
+	drawAxes:function(params) {
+		// x = red, y = green, z = blue  (RGB = xyz)
+		params = params || {};
+		var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
+		var axisLength = params.axisLength !== undefined ? params.axisLength:11;
+		var axisTess = params.axisTess !== undefined ? params.axisTess:48;
+		var axisOrientation = params.axisOrientation !== undefined ? params.axisOrientation:"x";
+
+		var axisMaterial = new THREE.MeshLambertMaterial({ color: 0x000000, side: THREE.DoubleSide });
+		var axis = new THREE.Mesh(
+			new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true), 
+			axisMaterial
+			);
+		if (axisOrientation === "x") {
+			axis.rotation.z = - Math.PI / 2;
+			axis.position.x = axisLength/2-1;
+		} else if (axisOrientation === "y") {
+				axis.position.y = axisLength/2-1;
+		}
+		
+		window.scene.add( axis );
+		
+		var arrow = new THREE.Mesh(
+			new THREE.CylinderGeometry(0, 4*axisRadius, 8*axisRadius, axisTess, 1, true), 
+			axisMaterial
+			);
+		if (axisOrientation === "x") {
+			arrow.rotation.z = - Math.PI / 2;
+			arrow.position.x = axisLength - 1 + axisRadius*4/2;
+		} else if (axisOrientation === "y") {
+			arrow.position.y = axisLength - 1 + axisRadius*4/2;
+		}
+
+		window.scene.add( arrow );
+
+	},
+	drawAllAxes:function(params) {
+		params = params || {};
+		var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
+		var axisLength = params.axisLength !== undefined ? params.axisLength:11;
+		var axisTess = params.axisTess !== undefined ? params.axisTess:48;
+
+		var axisXMaterial = new THREE.MeshLambertMaterial({ color: 0xFF0000 });
+		var axisYMaterial = new THREE.MeshLambertMaterial({ color: 0x00FF00 });
+		var axisZMaterial = new THREE.MeshLambertMaterial({ color: 0x0000FF });
+		axisXMaterial.side = THREE.DoubleSide;
+		axisYMaterial.side = THREE.DoubleSide;
+		axisZMaterial.side = THREE.DoubleSide;
+		var axisX = new THREE.Mesh(
+			new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true), 
+			axisXMaterial
+			);
+		var axisY = new THREE.Mesh(
+			new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true), 
+			axisYMaterial
+			);
+		var axisZ = new THREE.Mesh(
+			new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true), 
+			axisZMaterial
+			);
+		axisX.rotation.z = - Math.PI / 2;
+		axisX.position.x = axisLength/2-1;
+
+		axisY.position.y = axisLength/2-1;
+		
+		axisZ.rotation.y = - Math.PI / 2;
+		axisZ.rotation.z = - Math.PI / 2;
+		axisZ.position.z = axisLength/2-1;
+
+		window.scene.add( axisX );
+		window.scene.add( axisY );
+		window.scene.add( axisZ );
+
+		var arrowX = new THREE.Mesh(
+			new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true), 
+			axisXMaterial
+			);
+		var arrowY = new THREE.Mesh(
+			new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true), 
+			axisYMaterial
+			);
+		var arrowZ = new THREE.Mesh(
+			new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true), 
+			axisZMaterial
+			);
+		arrowX.rotation.z = - Math.PI / 2;
+		arrowX.position.x = axisLength - 1 + axisRadius*4/2;
+
+		arrowY.position.y = axisLength - 1 + axisRadius*4/2;
+
+		arrowZ.rotation.z = - Math.PI / 2;
+		arrowZ.rotation.y = - Math.PI / 2;
+		arrowZ.position.z = axisLength - 1 + axisRadius*4/2;
+
+		window.scene.add( arrowX );
+		window.scene.add( arrowY );
+		window.scene.add( arrowZ );
+
+	}
+
+};
\ No newline at end of file
diff --git a/DUBREUIL/lib/Detector.js b/DUBREUIL/lib/Detector.js
new file mode 100644
index 0000000..9f59fcc
--- /dev/null
+++ b/DUBREUIL/lib/Detector.js
@@ -0,0 +1,40 @@
+// TODO: This should be replaced with the checking code from http://get.webgl.org
+// they have better supprot messages for different browsers
+var Detector={
+	canvas:!!window.CanvasRenderingContext2D,
+	webgl:(function(){
+		try{
+			return!!window.WebGLRenderingContext&&!!document.createElement('canvas').getContext('experimental-webgl');
+		}
+		catch(e){
+			return false;
+		}
+	})(),
+	workers:!!window.Worker,
+	fileapi:window.File&&window.FileReader&&window.FileList&&window.Blob,
+	getWebGLErrorMessage:function(){
+		var element=document.createElement('div');
+		element.id='webgl-error-message';
+		element.style.fontFamily='monospace';element.style.fontSize='13px';
+		element.style.fontWeight='normal';
+		element.style.textAlign='center';
+		element.style.background='#fff';
+		element.style.color='#000';
+		element.style.padding='1.5em';
+		element.style.width='400px';
+		element.style.margin='5em auto 0';
+		if(!this.webgl){
+			element.innerHTML=window.WebGLRenderingContext?['Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">WebGL</a>.<br />','Find out how to get it <a href="http://get.webgl.org/" style="color:#000">here</a>.'].join('\n'):['Your browser does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">WebGL</a>.<br/>','Find out how to get it <a href="http://get.webgl.org/" style="color:#000">here</a>.'].join('\n');
+		}
+	return element;
+	},
+	addGetWebGLMessage:function(parameters){
+		var parent,id,element;
+		parameters=parameters||{};
+		parent=parameters.parent!==undefined?parameters.parent:document.body;
+		id=parameters.id!==undefined?parameters.id:'unsupported';
+		element=Detector.getWebGLErrorMessage();
+		element.id=id;
+		document.body.insertBefore(element, document.body.childNodes[0]);
+	}
+};
\ No newline at end of file
diff --git a/DUBREUIL/lib/OrbitAndPanControls.js b/DUBREUIL/lib/OrbitAndPanControls.js
new file mode 100644
index 0000000..5c7ed23
--- /dev/null
+++ b/DUBREUIL/lib/OrbitAndPanControls.js
@@ -0,0 +1,532 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author qiao / https://github.com/qiao
+ * @author mrdoob / http://mrdoob.com
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author erich666 / http://erichaines.com
+ */
+/*global THREE, console */
+
+THREE.OrbitAndPanControls = function ( object, domElement ) {
+
+	THREE.EventDispatcher.call( this );
+
+	this.enabled = true;
+
+	this.object = object;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+
+	this.target = new THREE.Vector3();
+	// center is old, deprecated; use "target" instead
+	this.center = this.target;
+
+	// This option actually enables dollying in and out
+	this.noZoom = false;
+	this.zoomSpeed = 1.0;
+
+	this.noRotate = false;
+	this.rotateSpeed = 1.0;
+
+	this.noPan = false;
+
+	this.autoRotate = false;
+	this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+	this.minPolarAngle = 0; // radians
+	this.maxPolarAngle = Math.PI; // radians
+
+	this.minDistance = 0;
+	this.maxDistance = Infinity;
+
+	this.noKeys = false;
+	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 : 4, TOUCH_PAN : 5 };
+	var state = STATE.NONE;
+
+	// 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 ) {
+
+		if ( scope.object.fov !== undefined )
+		{
+			// perspective
+			var position = scope.object.position;
+			var offset = position.clone().sub( scope.target );
+			var targetDistance = offset.length();
+
+			// 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 / scope.domElement.height );
+			scope.panUp( 2 * delta.y * targetDistance / scope.domElement.height );
+		}
+		else if ( scope.object.top !== undefined )
+		{
+			// orthographic
+			scope.panLeft( delta.x * (scope.object.right - scope.object.left) / scope.domElement.width );
+			scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / scope.domElement.height );
+		}
+		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 z-axis around y-axis
+
+		var theta = Math.atan2( offset.x, offset.z );
+
+		// angle from y-axis
+
+		var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
+
+		if ( this.autoRotate ) {
+
+			this.rotateLeft( getAutoRotationAngle() );
+
+		}
+
+		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.y = radius * Math.cos( phi );
+		offset.z = radius * Math.sin( phi ) * Math.cos( theta );
+
+		position.copy( this.target ).add( offset );
+
+		this.object.lookAt( this.target );
+
+		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 );
+
+		}
+
+	};
+
+
+	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.button === 0 ) {
+			if ( scope.noRotate === true ) { return; }
+
+			state = STATE.ROTATE;
+
+			rotateStart.set( event.clientX, event.clientY );
+
+		} else if ( event.button === 1 ) {
+			if ( scope.noZoom === true ) { return; }
+
+			state = STATE.DOLLY;
+
+			dollyStart.set( event.clientX, event.clientY );
+
+		} else if ( event.button === 2 ) {
+			if ( scope.noPan === true ) { return; }
+
+			state = STATE.PAN;
+
+			panStart.set( event.clientX, event.clientY );
+
+		}
+
+		document.addEventListener( 'mousemove', onMouseMove, false );
+		document.addEventListener( 'mouseup', onMouseUp, false );
+
+	}
+
+	function onMouseMove( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		event.preventDefault();
+
+		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 / scope.domElement.width * scope.rotateSpeed );
+			// rotating up and down along whole screen attempts to go 360, but limited to 180
+			scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * 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 );
+
+		}
+
+	}
+
+	function onMouseUp( /* event */ ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		document.removeEventListener( 'mousemove', onMouseMove, false );
+		document.removeEventListener( 'mouseup', onMouseUp, false );
+
+		state = STATE.NONE;
+
+	}
+
+	function onMouseWheel( event ) {
+		// this is needed when the program is inside an iframe
+		// to prevent scrolling the whole page
+		event.preventDefault();
+		if ( scope.enabled === false ) { return; }
+		if ( scope.noZoom === true ) { return; }
+
+		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; }
+
+		// pan a pixel - I guess for precise positioning?
+		switch ( event.keyCode ) {
+
+			case scope.keys.UP:
+				scope.pan( new THREE.Vector2( 0, 1 ) );
+				break;
+			case scope.keys.BOTTOM:
+				scope.pan( new THREE.Vector2( 0, -1 ) );
+				break;
+			case scope.keys.LEFT:
+				scope.pan( new THREE.Vector2( 1, 0 ) );
+				break;
+			case scope.keys.RIGHT:
+				scope.pan( new THREE.Vector2( -1, 0 ) );
+				break;
+		}
+
+	}
+	
+	function touchstart( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		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
+				if ( scope.noZoom === true ) { return; }
+
+				state = STATE.TOUCH_DOLLY;
+
+				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 );
+				break;
+
+			case 3: // three-fingered touch: pan
+				if ( scope.noPan === true ) { return; }
+
+				state = STATE.TOUCH_PAN;
+
+				panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				state = STATE.NONE;
+
+		}
+	}
+
+	function touchmove( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		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 / scope.domElement.width * scope.rotateSpeed );
+				// rotating up and down along whole screen attempts to go 360, but limited to 180
+				scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
+
+				rotateStart.copy( rotateEnd );
+				break;
+
+			case 2: // two-fingered touch: dolly
+				if ( scope.noZoom === true ) { return; }
+				if ( state !== STATE.TOUCH_DOLLY ) { return; }
+
+				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 );
+
+				if ( dollyDelta.y > 0 ) {
+
+					scope.dollyOut();
+
+				} else {
+
+					scope.dollyIn();
+
+				}
+
+				dollyStart.copy( dollyEnd );
+				break;
+
+			case 3: // three-fingered touch: pan
+				if ( scope.noPan === true ) { return; }
+				if ( state !== STATE.TOUCH_PAN ) { return; }
+
+				panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				panDelta.subVectors( panEnd, panStart );
+				
+				scope.pan( panDelta );
+
+				panStart.copy( panEnd );
+				break;
+
+			default:
+				state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchend( /* event */ ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		state = STATE.NONE;
+	}
+
+	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 );
+
+};
diff --git a/DUBREUIL/lib/OrbitAndPanControls.new.js b/DUBREUIL/lib/OrbitAndPanControls.new.js
new file mode 100644
index 0000000..bb8c6b2
--- /dev/null
+++ b/DUBREUIL/lib/OrbitAndPanControls.new.js
@@ -0,0 +1,530 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author qiao / https://github.com/qiao
+ * @author mrdoob / http://mrdoob.com
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author erich666 / http://erichaines.com
+ */
+/*global THREE, console */
+
+THREE.OrbitAndPanControls = function ( object, domElement ) {
+
+	this.object = object;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+
+	this.target = new THREE.Vector3();
+	// center is old, deprecated; use "target" instead
+	this.center = this.target;
+
+	// This option actually enables dollying in and out
+	this.noZoom = false;
+	this.zoomSpeed = 1.0;
+
+	this.noRotate = false;
+	this.rotateSpeed = 1.0;
+
+	this.noPan = false;
+
+	this.autoRotate = false;
+	this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+	this.minPolarAngle = 0; // radians
+	this.maxPolarAngle = Math.PI; // radians
+
+	this.minDistance = 0;
+	this.maxDistance = Infinity;
+
+	this.noKeys = false;
+	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 : 4, TOUCH_PAN : 5 };
+	var state = STATE.NONE;
+
+	// 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 ) {
+
+		if ( scope.object.fov !== undefined )
+		{
+			// perspective
+			var position = scope.object.position;
+			var offset = position.clone().sub( scope.target );
+			var targetDistance = offset.length();
+
+			// 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 / scope.domElement.height );
+			scope.panUp( 2 * delta.y * targetDistance / scope.domElement.height );
+		}
+		else if ( scope.object.top !== undefined )
+		{
+			// orthographic
+			scope.panLeft( delta.x * (scope.object.right - scope.object.left) / scope.domElement.width );
+			scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / scope.domElement.height );
+		}
+		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 z-axis around y-axis
+
+		var theta = Math.atan2( offset.x, offset.z );
+
+		// angle from y-axis
+
+		var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
+
+		if ( this.autoRotate ) {
+
+			this.rotateLeft( getAutoRotationAngle() );
+
+		}
+
+		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.y = radius * Math.cos( phi );
+		offset.z = radius * Math.sin( phi ) * Math.cos( theta );
+
+		position.copy( this.target ).add( offset );
+
+		this.object.lookAt( this.target );
+
+		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 );
+
+		}
+
+	};
+
+
+	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.button === 0 ) {
+			if ( scope.noRotate === true ) { return; }
+
+			state = STATE.ROTATE;
+
+			rotateStart.set( event.clientX, event.clientY );
+
+		} else if ( event.button === 1 ) {
+			if ( scope.noZoom === true ) { return; }
+
+			state = STATE.DOLLY;
+
+			dollyStart.set( event.clientX, event.clientY );
+
+		} else if ( event.button === 2 ) {
+			if ( scope.noPan === true ) { return; }
+
+			state = STATE.PAN;
+
+			panStart.set( event.clientX, event.clientY );
+
+		}
+
+		document.addEventListener( 'mousemove', onMouseMove, false );
+		document.addEventListener( 'mouseup', onMouseUp, false );
+
+	}
+
+	function onMouseMove( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		event.preventDefault();
+
+		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 / scope.domElement.width * scope.rotateSpeed );
+			// rotating up and down along whole screen attempts to go 360, but limited to 180
+			scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * 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 );
+
+		}
+
+	}
+
+	function onMouseUp( /* event */ ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		document.removeEventListener( 'mousemove', onMouseMove, false );
+		document.removeEventListener( 'mouseup', onMouseUp, false );
+
+		state = STATE.NONE;
+
+	}
+
+	function onMouseWheel( event ) {
+		// this is needed when the program is inside an iframe
+		// to prevent scrolling the whole page
+		event.preventDefault();
+		if ( scope.enabled === false ) { return; }
+		if ( scope.noZoom === true ) { return; }
+
+		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; }
+
+		// pan a pixel - I guess for precise positioning?
+		switch ( event.keyCode ) {
+
+			case scope.keys.UP:
+				scope.pan( new THREE.Vector2( 0, 1 ) );
+				break;
+			case scope.keys.BOTTOM:
+				scope.pan( new THREE.Vector2( 0, -1 ) );
+				break;
+			case scope.keys.LEFT:
+				scope.pan( new THREE.Vector2( 1, 0 ) );
+				break;
+			case scope.keys.RIGHT:
+				scope.pan( new THREE.Vector2( -1, 0 ) );
+				break;
+		}
+
+	}
+	
+	function touchstart( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		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
+				if ( scope.noZoom === true ) { return; }
+
+				state = STATE.TOUCH_DOLLY;
+
+				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 );
+				break;
+
+			case 3: // three-fingered touch: pan
+				if ( scope.noPan === true ) { return; }
+
+				state = STATE.TOUCH_PAN;
+
+				panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				state = STATE.NONE;
+
+		}
+	}
+
+	function touchmove( event ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		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 / scope.domElement.width * scope.rotateSpeed );
+				// rotating up and down along whole screen attempts to go 360, but limited to 180
+				scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
+
+				rotateStart.copy( rotateEnd );
+				break;
+
+			case 2: // two-fingered touch: dolly
+				if ( scope.noZoom === true ) { return; }
+				if ( state !== STATE.TOUCH_DOLLY ) { return; }
+
+				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 );
+
+				if ( dollyDelta.y > 0 ) {
+
+					scope.dollyOut();
+
+				} else {
+
+					scope.dollyIn();
+
+				}
+
+				dollyStart.copy( dollyEnd );
+				break;
+
+			case 3: // three-fingered touch: pan
+				if ( scope.noPan === true ) { return; }
+				if ( state !== STATE.TOUCH_PAN ) { return; }
+
+				panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				panDelta.subVectors( panEnd, panStart );
+				
+				scope.pan( panDelta );
+
+				panStart.copy( panEnd );
+				break;
+
+			default:
+				state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchend( /* event */ ) {
+
+		if ( scope.enabled === false ) { return; }
+
+		state = STATE.NONE;
+	}
+
+	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.OrbitAndPanControls.prototype = Object.create( THREE.EventDispatcher.prototype );
diff --git a/DUBREUIL/lib/TrackballControls.js b/DUBREUIL/lib/TrackballControls.js
new file mode 100644
index 0000000..0935ce0
--- /dev/null
+++ b/DUBREUIL/lib/TrackballControls.js
@@ -0,0 +1,537 @@
+/**
+ * @author Eberhard Graether / http://egraether.com/
+ */
+
+THREE.TrackballControls = function ( object, domElement ) {
+
+	THREE.EventDispatcher.call( this );
+
+	var _this = this;
+	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
+
+	this.object = object;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+
+	this.screen = { width: 0, height: 0, offsetLeft: 0, offsetTop: 0 };
+	this.radius = ( this.screen.width + this.screen.height ) / 4;
+
+	this.rotateSpeed = 1.0;
+	this.zoomSpeed = 1.2;
+	this.panSpeed = 0.3;
+
+	this.noRotate = false;
+	this.noZoom = false;
+	this.noPan = false;
+
+	this.staticMoving = false;
+	this.dynamicDampingFactor = 0.2;
+
+	this.minDistance = 0;
+	this.maxDistance = Infinity;
+
+	this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
+
+	// internals
+
+	this.target = new THREE.Vector3();
+
+	var lastPosition = new THREE.Vector3();
+
+	var _state = STATE.NONE,
+	_prevState = STATE.NONE,
+
+	_eye = new THREE.Vector3(),
+
+	_rotateStart = new THREE.Vector3(),
+	_rotateEnd = new THREE.Vector3(),
+
+	_zoomStart = new THREE.Vector2(),
+	_zoomEnd = new THREE.Vector2(),
+
+	_touchZoomDistanceStart = 0,
+	_touchZoomDistanceEnd = 0,
+
+	_panStart = new THREE.Vector2(),
+	_panEnd = new THREE.Vector2();
+
+	// for reset
+
+	this.target0 = this.target.clone();
+	this.position0 = this.object.position.clone();
+	this.up0 = this.object.up.clone();
+
+	// events
+
+	var changeEvent = { type: 'change' };
+
+
+	// methods
+
+	this.handleResize = function () {
+
+		this.screen.width = window.innerWidth;
+		this.screen.height = window.innerHeight;
+
+		this.screen.offsetLeft = 0;
+		this.screen.offsetTop = 0;
+
+		this.radius = ( this.screen.width + this.screen.height ) / 4;
+
+	};
+
+	this.handleEvent = function ( event ) {
+
+		if ( typeof this[ event.type ] == 'function' ) {
+
+			this[ event.type ]( event );
+
+		}
+
+	};
+
+	this.getMouseOnScreen = function ( clientX, clientY ) {
+
+		return new THREE.Vector2(
+			( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,
+			( clientY - _this.screen.offsetTop ) / _this.radius * 0.5
+		);
+
+	};
+
+	this.getMouseProjectionOnBall = function ( clientX, clientY ) {
+
+		var mouseOnBall = new THREE.Vector3(
+			( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,
+			( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,
+			0.0
+		);
+
+		var length = mouseOnBall.length();
+
+		if ( length > 1.0 ) {
+
+			mouseOnBall.normalize();
+
+		} else {
+
+			mouseOnBall.z = Math.sqrt( 1.0 - length * length );
+
+		}
+
+		_eye.copy( _this.object.position ).sub( _this.target );
+
+		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 ) );
+
+		return projection;
+
+	};
+
+	this.rotateCamera = function () {
+
+		var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
+
+		if ( angle ) {
+
+			var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize(),
+				quaternion = new THREE.Quaternion();
+
+			angle *= _this.rotateSpeed;
+
+			quaternion.setFromAxisAngle( axis, -angle );
+
+			_eye.applyQuaternion( quaternion );
+			_this.object.up.applyQuaternion( quaternion );
+
+			_rotateEnd.applyQuaternion( quaternion );
+
+			if ( _this.staticMoving ) {
+
+				_rotateStart.copy( _rotateEnd );
+
+			} else {
+
+				quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
+				_rotateStart.applyQuaternion( quaternion );
+
+			}
+
+		}
+
+	};
+
+	this.zoomCamera = function () {
+
+		if ( _state === STATE.TOUCH_ZOOM ) {
+
+			var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+			_touchZoomDistanceStart = _touchZoomDistanceEnd;
+			_eye.multiplyScalar( factor );
+
+		} else {
+
+			var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
+
+			if ( factor !== 1.0 && factor > 0.0 ) {
+
+				_eye.multiplyScalar( factor );
+
+				if ( _this.staticMoving ) {
+
+					_zoomStart.copy( _zoomEnd );
+
+				} else {
+
+					_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.panCamera = function () {
+
+		var mouseChange = _panEnd.clone().sub( _panStart );
+
+		if ( mouseChange.lengthSq() ) {
+
+			mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
+
+			var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
+			pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
+
+			_this.object.position.add( pan );
+			_this.target.add( pan );
+
+			if ( _this.staticMoving ) {
+
+				_panStart = _panEnd;
+
+			} else {
+
+				_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
+
+			}
+
+		}
+
+	};
+
+	this.checkDistances = function () {
+
+		if ( !_this.noZoom || !_this.noPan ) {
+
+			if ( _this.object.position.lengthSq() > _this.maxDistance * _this.maxDistance ) {
+
+				_this.object.position.setLength( _this.maxDistance );
+
+			}
+
+			if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
+
+				_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
+
+			}
+
+		}
+
+	};
+
+	this.update = function () {
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		if ( !_this.noRotate ) {
+
+			_this.rotateCamera();
+
+		}
+
+		if ( !_this.noZoom ) {
+
+			_this.zoomCamera();
+
+		}
+
+		if ( !_this.noPan ) {
+
+			_this.panCamera();
+
+		}
+
+		_this.object.position.addVectors( _this.target, _eye );
+
+		_this.checkDistances();
+
+		_this.object.lookAt( _this.target );
+
+		if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
+
+			_this.dispatchEvent( changeEvent );
+
+			lastPosition.copy( _this.object.position );
+
+		}
+
+	};
+
+	this.reset = function () {
+
+		_state = STATE.NONE;
+		_prevState = STATE.NONE;
+
+		_this.target.copy( _this.target0 );
+		_this.object.position.copy( _this.position0 );
+		_this.object.up.copy( _this.up0 );
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		_this.object.lookAt( _this.target );
+
+		_this.dispatchEvent( changeEvent );
+
+		lastPosition.copy( _this.object.position );
+
+	};
+
+	// listeners
+
+	function keydown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		window.removeEventListener( 'keydown', keydown );
+
+		_prevState = _state;
+
+		if ( _state !== STATE.NONE ) {
+
+			return;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
+
+			_state = STATE.ROTATE;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
+
+			_state = STATE.ZOOM;
+
+		} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
+
+			_state = STATE.PAN;
+
+		}
+
+	}
+
+	function keyup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		_state = _prevState;
+
+		window.addEventListener( 'keydown', keydown, false );
+
+	}
+
+	function mousedown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.NONE ) {
+
+			_state = event.button;
+
+		}
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		}
+
+		document.addEventListener( 'mousemove', mousemove, false );
+		document.addEventListener( 'mouseup', mouseup, false );
+
+	}
+
+	function mousemove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		}
+
+	}
+
+	function mouseup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		_state = STATE.NONE;
+
+		document.removeEventListener( 'mousemove', mousemove );
+		document.removeEventListener( 'mouseup', mouseup );
+
+	}
+
+	function mousewheel( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		var delta = 0;
+
+		if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
+
+			delta = event.wheelDelta / 40;
+
+		} else if ( event.detail ) { // Firefox
+
+			delta = - event.detail / 3;
+
+		}
+
+		_zoomStart.y += ( 1 / delta ) * 0.05;
+
+	}
+
+	function touchstart( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_state = STATE.TOUCH_ROTATE;
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				_state = STATE.TOUCH_ZOOM;
+				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 );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchmove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateEnd = _this.getMouseProjectionOnBall( 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;
+
+			case 3:
+				_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchend( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( 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 );
+				break;
+
+		}
+
+		_state = STATE.NONE;
+
+	}
+
+	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
+
+	this.domElement.addEventListener( 'mousedown', mousedown, false );
+
+	this.domElement.addEventListener( 'mousewheel', mousewheel, false );
+	this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
+
+	this.domElement.addEventListener( 'touchstart', touchstart, false );
+	this.domElement.addEventListener( 'touchend', touchend, false );
+	this.domElement.addEventListener( 'touchmove', touchmove, false );
+
+	window.addEventListener( 'keydown', keydown, false );
+	window.addEventListener( 'keyup', keyup, false );
+
+	this.handleResize();
+
+};
diff --git a/DUBREUIL/lib/dat.gui.min.js b/DUBREUIL/lib/dat.gui.min.js
new file mode 100644
index 0000000..e7318e8
--- /dev/null
+++ b/DUBREUIL/lib/dat.gui.min.js
@@ -0,0 +1,94 @@
+/**
+ * dat-gui JavaScript Controller Library
+ * http://code.google.com/p/dat-gui
+ *
+ * Copyright 2011 Data Arts Team, Google Creative Lab
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+export var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}();
+dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}},
+each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b<n;b++){if(b in a&&d.call(f,a[b],b)===this.BREAK)break}else for(b in a)if(d.call(f,a[b],b)===this.BREAK)break},defer:function(a){setTimeout(a,0)},toArray:function(c){return c.toArray?c.toArray():a.call(c)},isUndefined:function(a){return a===void 0},isNull:function(a){return a===null},isNaN:function(a){return a!==a},isArray:Array.isArray||function(a){return a.constructor===Array},isObject:function(a){return a===
+Object(a)},isNumber:function(a){return a===a+0},isString:function(a){return a===a+""},isBoolean:function(a){return a===false||a===true},isFunction:function(a){return Object.prototype.toString.call(a)==="[object Function]"}}}();
+dat.controllers.Controller=function(e){var a=function(a,d){this.initialValue=a[d];this.domElement=document.createElement("div");this.object=a;this.property=d;this.__onFinishChange=this.__onChange=void 0};e.extend(a.prototype,{onChange:function(a){this.__onChange=a;return this},onFinishChange:function(a){this.__onFinishChange=a;return this},setValue:function(a){this.object[this.property]=a;this.__onChange&&this.__onChange.call(this,a);this.updateDisplay();return this},getValue:function(){return this.object[this.property]},
+updateDisplay:function(){return this},isModified:function(){return this.initialValue!==this.getValue()}});return a}(dat.utils.common);
+dat.dom.dom=function(e){function a(b){if(b==="0"||e.isUndefined(b))return 0;b=b.match(d);return!e.isNull(b)?parseFloat(b[1]):0}var c={};e.each({HTMLEvents:["change"],MouseEvents:["click","mousemove","mousedown","mouseup","mouseover"],KeyboardEvents:["keydown"]},function(b,a){e.each(b,function(b){c[b]=a})});var d=/(\d+(\.\d+)?)px/,f={makeSelectable:function(b,a){if(!(b===void 0||b.style===void 0))b.onselectstart=a?function(){return false}:function(){},b.style.MozUserSelect=a?"auto":"none",b.style.KhtmlUserSelect=
+a?"auto":"none",b.unselectable=a?"on":"off"},makeFullscreen:function(b,a,d){e.isUndefined(a)&&(a=true);e.isUndefined(d)&&(d=true);b.style.position="absolute";if(a)b.style.left=0,b.style.right=0;if(d)b.style.top=0,b.style.bottom=0},fakeEvent:function(b,a,d,f){var d=d||{},m=c[a];if(!m)throw Error("Event type "+a+" not supported.");var l=document.createEvent(m);switch(m){case "MouseEvents":l.initMouseEvent(a,d.bubbles||false,d.cancelable||true,window,d.clickCount||1,0,0,d.x||d.clientX||0,d.y||d.clientY||
+0,false,false,false,false,0,null);break;case "KeyboardEvents":m=l.initKeyboardEvent||l.initKeyEvent;e.defaults(d,{cancelable:true,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false,keyCode:void 0,charCode:void 0});m(a,d.bubbles||false,d.cancelable,window,d.ctrlKey,d.altKey,d.shiftKey,d.metaKey,d.keyCode,d.charCode);break;default:l.initEvent(a,d.bubbles||false,d.cancelable||true)}e.defaults(l,f);b.dispatchEvent(l)},bind:function(b,a,d,c){b.addEventListener?b.addEventListener(a,d,c||false):b.attachEvent&&
+b.attachEvent("on"+a,d);return f},unbind:function(b,a,d,c){b.removeEventListener?b.removeEventListener(a,d,c||false):b.detachEvent&&b.detachEvent("on"+a,d);return f},addClass:function(b,a){if(b.className===void 0)b.className=a;else if(b.className!==a){var d=b.className.split(/ +/);if(d.indexOf(a)==-1)d.push(a),b.className=d.join(" ").replace(/^\s+/,"").replace(/\s+$/,"")}return f},removeClass:function(b,a){if(a){if(b.className!==void 0)if(b.className===a)b.removeAttribute("class");else{var d=b.className.split(/ +/),
+c=d.indexOf(a);if(c!=-1)d.splice(c,1),b.className=d.join(" ")}}else b.className=void 0;return f},hasClass:function(a,d){return RegExp("(?:^|\\s+)"+d+"(?:\\s+|$)").test(a.className)||false},getWidth:function(b){b=getComputedStyle(b);return a(b["border-left-width"])+a(b["border-right-width"])+a(b["padding-left"])+a(b["padding-right"])+a(b.width)},getHeight:function(b){b=getComputedStyle(b);return a(b["border-top-width"])+a(b["border-bottom-width"])+a(b["padding-top"])+a(b["padding-bottom"])+a(b.height)},
+getOffset:function(a){var d={left:0,top:0};if(a.offsetParent){do d.left+=a.offsetLeft,d.top+=a.offsetTop;while(a=a.offsetParent)}return d},isActive:function(a){return a===document.activeElement&&(a.type||a.href)}};return f}(dat.utils.common);
+dat.controllers.OptionController=function(e,a,c){var d=function(f,b,e){d.superclass.call(this,f,b);var h=this;this.__select=document.createElement("select");if(c.isArray(e)){var j={};c.each(e,function(a){j[a]=a});e=j}c.each(e,function(a,b){var d=document.createElement("option");d.innerHTML=b;d.setAttribute("value",a);h.__select.appendChild(d)});this.updateDisplay();a.bind(this.__select,"change",function(){h.setValue(this.options[this.selectedIndex].value)});this.domElement.appendChild(this.__select)};
+d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue());return a},updateDisplay:function(){this.__select.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.controllers.NumberController=function(e,a){var c=function(d,f,b){c.superclass.call(this,d,f);b=b||{};this.__min=b.min;this.__max=b.max;this.__step=b.step;d=this.__impliedStep=a.isUndefined(this.__step)?this.initialValue==0?1:Math.pow(10,Math.floor(Math.log(this.initialValue)/Math.LN10))/10:this.__step;d=d.toString();this.__precision=d.indexOf(".")>-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&a<this.__min)a=this.__min;
+else if(this.__max!==void 0&&a>this.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common);
+dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h);
+a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input,
+b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
+dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div");
+this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width=
+(this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".slider {\n  box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n  height: 1em;\n  border-radius: 1em;\n  background-color: #eee;\n  padding: 0 0.5em;\n  overflow: hidden;\n}\n\n.slider-fg {\n  padding: 1px 0 2px 0;\n  background-color: #aaa;\n  height: 1em;\n  margin-left: -0.5em;\n  padding-right: 0.5em;\n  border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n  display: inline-block;\n  border-radius: 1em;\n  background-color: #fff;\n  border:  1px solid #aaa;\n  content: '';\n  float: right;\n  margin-right: -1em;\n  margin-top: -1px;\n  height: 0.9em;\n  width: 0.9em;\n}");
+dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this,
+this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
+this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
+dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
+return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!=
+3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&&
+a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d=
+false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common);
+dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW);
+g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property,
+{before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}});
+g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()});
+else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)}
+function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement,
+"has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML="&nbsp;";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select");
+a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b<a.__preset_select.length;b++)a.__preset_select[b].innerHTML=a.__preset_select[b].value;a.preset=this.value});b.appendChild(m);b.appendChild(c);b.appendChild(d);b.appendChild(e);b.appendChild(f);if(u){var b=document.getElementById("dg-save-locally"),l=document.getElementById("dg-local-explain");b.style.display="block";b=document.getElementById("dg-local-storage");
+localStorage.getItem(document.location.href+".isLocal")==="true"&&b.setAttribute("checked","checked");var o=function(){l.style.display=a.useLocalStorage?"block":"none"};o();g.bind(b,"change",function(){a.useLocalStorage=!a.useLocalStorage;o()})}var h=document.getElementById("dg-new-constructor");g.bind(h,"keydown",function(a){a.metaKey&&(a.which===67||a.keyCode==67)&&x.hide()});g.bind(c,"click",function(){h.innerHTML=JSON.stringify(a.getSaveObject(),void 0,2);x.show();h.focus();h.select()});g.bind(d,
+"click",function(){a.save()});g.bind(e,"click",function(){var b=prompt("Enter a new preset name.");b&&a.saveAs(b)});g.bind(f,"click",function(){a.revert()})}function J(a){function b(f){f.preventDefault();e=f.clientX;g.addClass(a.__closeButton,k.CLASS_DRAG);g.bind(window,"mousemove",c);g.bind(window,"mouseup",d);return false}function c(b){b.preventDefault();a.width+=e-b.clientX;a.onResize();e=b.clientX;return false}function d(){g.removeClass(a.__closeButton,k.CLASS_DRAG);g.unbind(window,"mousemove",
+c);g.unbind(window,"mouseup",d)}a.__resize_handle=document.createElement("div");i.extend(a.__resize_handle.style,{width:"6px",marginLeft:"-3px",height:"200px",cursor:"ew-resize",position:"absolute"});var e;g.bind(a.__resize_handle,"mousedown",b);g.bind(a.__closeButton,"mousedown",b);a.domElement.insertBefore(a.__resize_handle,a.domElement.firstElementChild)}function D(a,b){a.domElement.style.width=b+"px";if(a.__save_row&&a.autoPlace)a.__save_row.style.width=b+"px";if(a.__closeButton)a.__closeButton.style.width=
+b+"px"}function z(a,b){var c={};i.each(a.__rememberedObjects,function(d,e){var f={};i.each(a.__rememberedObjectIndecesToControllers[e],function(a,c){f[c]=b?a.initialValue:a.getValue()});c[e]=f});return c}function C(a,b,c){var d=document.createElement("option");d.innerHTML=b;d.value=b;a.__preset_select.appendChild(d);if(c)a.__preset_select.selectedIndex=a.__preset_select.length-1}function B(a,b){var c=a.__preset_select[a.__preset_select.selectedIndex];c.innerHTML=b?c.value+"*":c.value}function E(a){a.length!=
+0&&o(function(){E(a)});i.each(a,function(a){a.updateDisplay()})}e.inject(c);var w="Default",u;try{u="localStorage"in window&&window.localStorage!==null}catch(K){u=false}var x,F=true,v,A=false,G=[],k=function(a){function b(){localStorage.setItem(document.location.href+".gui",JSON.stringify(d.getSaveObject()))}function c(){var a=d.getRoot();a.width+=1;i.defer(function(){a.width-=1})}var d=this;this.domElement=document.createElement("div");this.__ul=document.createElement("ul");this.domElement.appendChild(this.__ul);
+g.addClass(this.domElement,"dg");this.__folders={};this.__controllers=[];this.__rememberedObjects=[];this.__rememberedObjectIndecesToControllers=[];this.__listening=[];a=a||{};a=i.defaults(a,{autoPlace:true,width:k.DEFAULT_WIDTH});a=i.defaults(a,{resizable:a.autoPlace,hideable:a.autoPlace});if(i.isUndefined(a.load))a.load={preset:w};else if(a.preset)a.load.preset=a.preset;i.isUndefined(a.parent)&&a.hideable&&G.push(this);a.resizable=i.isUndefined(a.parent)&&a.resizable;if(a.autoPlace&&i.isUndefined(a.scrollable))a.scrollable=
+true;var e=u&&localStorage.getItem(document.location.href+".isLocal")==="true";Object.defineProperties(this,{parent:{get:function(){return a.parent}},scrollable:{get:function(){return a.scrollable}},autoPlace:{get:function(){return a.autoPlace}},preset:{get:function(){return d.parent?d.getRoot().preset:a.load.preset},set:function(b){d.parent?d.getRoot().preset=b:a.load.preset=b;for(b=0;b<this.__preset_select.length;b++)if(this.__preset_select[b].value==this.preset)this.__preset_select.selectedIndex=
+b;d.revert()}},width:{get:function(){return a.width},set:function(b){a.width=b;D(d,b)}},name:{get:function(){return a.name},set:function(b){a.name=b;if(m)m.innerHTML=a.name}},closed:{get:function(){return a.closed},set:function(b){a.closed=b;a.closed?g.addClass(d.__ul,k.CLASS_CLOSED):g.removeClass(d.__ul,k.CLASS_CLOSED);this.onResize();if(d.__closeButton)d.__closeButton.innerHTML=b?k.TEXT_OPEN:k.TEXT_CLOSED}},load:{get:function(){return a.load}},useLocalStorage:{get:function(){return e},set:function(a){u&&
+((e=a)?g.bind(window,"unload",b):g.unbind(window,"unload",b),localStorage.setItem(document.location.href+".isLocal",a))}}});if(i.isUndefined(a.parent)){a.closed=false;g.addClass(this.domElement,k.CLASS_MAIN);g.makeSelectable(this.domElement,false);if(u&&e){d.useLocalStorage=true;var f=localStorage.getItem(document.location.href+".gui");if(f)a.load=JSON.parse(f)}this.__closeButton=document.createElement("div");this.__closeButton.innerHTML=k.TEXT_CLOSED;g.addClass(this.__closeButton,k.CLASS_CLOSE_BUTTON);
+this.domElement.appendChild(this.__closeButton);g.bind(this.__closeButton,"click",function(){d.closed=!d.closed})}else{if(a.closed===void 0)a.closed=true;var m=document.createTextNode(a.name);g.addClass(m,"controller-name");f=s(d,m);g.addClass(this.__ul,k.CLASS_CLOSED);g.addClass(f,"title");g.bind(f,"click",function(a){a.preventDefault();d.closed=!d.closed;return false});if(!a.closed)this.closed=false}a.autoPlace&&(i.isUndefined(a.parent)&&(F&&(v=document.createElement("div"),g.addClass(v,"dg"),g.addClass(v,
+k.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(v),F=false),v.appendChild(this.domElement),g.addClass(this.domElement,k.CLASS_AUTO_PLACE)),this.parent||D(d,a.width));g.bind(window,"resize",function(){d.onResize()});g.bind(this.__ul,"webkitTransitionEnd",function(){d.onResize()});g.bind(this.__ul,"transitionend",function(){d.onResize()});g.bind(this.__ul,"oTransitionEnd",function(){d.onResize()});this.onResize();a.resizable&&J(this);d.getRoot();a.parent||c()};k.toggleHide=function(){A=!A;i.each(G,
+function(a){a.domElement.style.zIndex=A?-999:999;a.domElement.style.opacity=A?0:1})};k.CLASS_AUTO_PLACE="a";k.CLASS_AUTO_PLACE_CONTAINER="ac";k.CLASS_MAIN="main";k.CLASS_CONTROLLER_ROW="cr";k.CLASS_TOO_TALL="taller-than-window";k.CLASS_CLOSED="closed";k.CLASS_CLOSE_BUTTON="close-button";k.CLASS_DRAG="drag";k.DEFAULT_WIDTH=245;k.TEXT_CLOSED="Close Controls";k.TEXT_OPEN="Open Controls";g.bind(window,"keydown",function(a){document.activeElement.type!=="text"&&(a.which===72||a.keyCode==72)&&k.toggleHide()},
+false);i.extend(k.prototype,{add:function(a,b){return q(this,a,b,{factoryArgs:Array.prototype.slice.call(arguments,2)})},addColor:function(a,b){return q(this,a,b,{color:true})},remove:function(a){this.__ul.removeChild(a.__li);this.__controllers.slice(this.__controllers.indexOf(a),1);var b=this;i.defer(function(){b.onResize()})},destroy:function(){this.autoPlace&&v.removeChild(this.domElement)},addFolder:function(a){if(this.__folders[a]!==void 0)throw Error('You already have a folder in this GUI by the name "'+
+a+'"');var b={name:a,parent:this};b.autoPlace=this.autoPlace;if(this.load&&this.load.folders&&this.load.folders[a])b.closed=this.load.folders[a].closed,b.load=this.load.folders[a];b=new k(b);this.__folders[a]=b;a=s(this,b.domElement);g.addClass(a,"folder");return b},open:function(){this.closed=false},close:function(){this.closed=true},onResize:function(){var a=this.getRoot();if(a.scrollable){var b=g.getOffset(a.__ul).top,c=0;i.each(a.__ul.childNodes,function(b){a.autoPlace&&b===a.__save_row||(c+=
+g.getHeight(b))});window.innerHeight-b-20<c?(g.addClass(a.domElement,k.CLASS_TOO_TALL),a.__ul.style.height=window.innerHeight-b-20+"px"):(g.removeClass(a.domElement,k.CLASS_TOO_TALL),a.__ul.style.height="auto")}a.__resize_handle&&i.defer(function(){a.__resize_handle.style.height=a.__ul.offsetHeight+"px"});if(a.__closeButton)a.__closeButton.style.width=a.width+"px"},remember:function(){if(i.isUndefined(x))x=new y,x.domElement.innerHTML=a;if(this.parent)throw Error("You can only call remember on a top level GUI.");
+var b=this;i.each(Array.prototype.slice.call(arguments),function(a){b.__rememberedObjects.length==0&&I(b);b.__rememberedObjects.indexOf(a)==-1&&b.__rememberedObjects.push(a)});this.autoPlace&&D(this,this.width)},getRoot:function(){for(var a=this;a.parent;)a=a.parent;return a},getSaveObject:function(){var a=this.load;a.closed=this.closed;if(this.__rememberedObjects.length>0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b,
+c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders,
+function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'<div id="dg-save" class="dg dialogue">\n\n  Here\'s the new load parameter for your <code>GUI</code>\'s constructor:\n\n  <textarea id="dg-new-constructor"></textarea>\n\n  <div id="dg-save-locally">\n\n    <input id="dg-local-storage" type="checkbox"/> Automatically save\n    values to <code>localStorage</code> on exit.\n\n    <div id="dg-local-explain">The values saved to <code>localStorage</code> will\n      override those passed to <code>dat.GUI</code>\'s constructor. This makes it\n      easier to work incrementally, but <code>localStorage</code> is fragile,\n      and your friends may not see the same values you do.\n      \n    </div>\n    \n  </div>\n\n</div>',
+".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n",
+dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d=
+function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,
+e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,
+dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top,  #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";
+a.style.cssText+="background: -o-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window,
+"mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=
+1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,
+false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=
+document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});
+f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style,
+{width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown",
+function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue());
+if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+
+"rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a,
+b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space===
+"HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||
+1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex=
+a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0};
+a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<<e)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||
+window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1E3/60)}}(),dat.dom.CenteredDiv=function(e,a){var c=function(){this.backgroundElement=document.createElement("div");a.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear"});e.makeFullscreen(this.backgroundElement);this.backgroundElement.style.position="fixed";this.domElement=
+document.createElement("div");a.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear"});document.body.appendChild(this.backgroundElement);document.body.appendChild(this.domElement);var c=this;e.bind(this.backgroundElement,"click",function(){c.hide()})};c.prototype.show=function(){var c=this;this.backgroundElement.style.display="block";this.domElement.style.display="block";this.domElement.style.opacity=
+0;this.domElement.style.webkitTransform="scale(1.1)";this.layout();a.defer(function(){c.backgroundElement.style.opacity=1;c.domElement.style.opacity=1;c.domElement.style.webkitTransform="scale(1)"})};c.prototype.hide=function(){var a=this,c=function(){a.domElement.style.display="none";a.backgroundElement.style.display="none";e.unbind(a.domElement,"webkitTransitionEnd",c);e.unbind(a.domElement,"transitionend",c);e.unbind(a.domElement,"oTransitionEnd",c)};e.bind(this.domElement,"webkitTransitionEnd",
+c);e.bind(this.domElement,"transitionend",c);e.bind(this.domElement,"oTransitionEnd",c);this.backgroundElement.style.opacity=0;this.domElement.style.opacity=0;this.domElement.style.webkitTransform="scale(1.1)"};c.prototype.layout=function(){this.domElement.style.left=window.innerWidth/2-e.getWidth(this.domElement)/2+"px";this.domElement.style.top=window.innerHeight/2-e.getHeight(this.domElement)/2+"px"};return c}(dat.dom.dom,dat.utils.common),dat.dom.dom,dat.utils.common);
diff --git a/DUBREUIL/lib/jquery-1.8.3.min.js b/DUBREUIL/lib/jquery-1.8.3.min.js
new file mode 100644
index 0000000..83589da
--- /dev/null
+++ b/DUBREUIL/lib/jquery-1.8.3.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v1.8.3 jquery.com | jquery.org/license */
+(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r<i;r++)v.event.add(t,n,u[n][r])}o.data&&(o.data=v.extend({},o.data))}function Ot(e,t){var n;if(t.nodeType!==1)return;t.clearAttributes&&t.clearAttributes(),t.mergeAttributes&&t.mergeAttributes(e),n=t.nodeName.toLowerCase(),n==="object"?(t.parentNode&&(t.outerHTML=e.outerHTML),v.support.html5Clone&&e.innerHTML&&!v.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):n==="input"&&Et.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):n==="option"?t.selected=e.defaultSelected:n==="input"||n==="textarea"?t.defaultValue=e.defaultValue:n==="script"&&t.text!==e.text&&(t.text=e.text),t.removeAttribute(v.expando)}function Mt(e){return typeof e.getElementsByTagName!="undefined"?e.getElementsByTagName("*"):typeof e.querySelectorAll!="undefined"?e.querySelectorAll("*"):[]}function _t(e){Et.test(e.type)&&(e.defaultChecked=e.checked)}function Qt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Jt.length;while(i--){t=Jt[i]+n;if(t in e)return t}return r}function Gt(e,t){return e=t||e,v.css(e,"display")==="none"||!v.contains(e.ownerDocument,e)}function Yt(e,t){var n,r,i=[],s=0,o=e.length;for(;s<o;s++){n=e[s];if(!n.style)continue;i[s]=v._data(n,"olddisplay"),t?(!i[s]&&n.style.display==="none"&&(n.style.display=""),n.style.display===""&&Gt(n)&&(i[s]=v._data(n,"olddisplay",nn(n.nodeName)))):(r=Dt(n,"display"),!i[s]&&r!=="none"&&v._data(n,"olddisplay",r))}for(s=0;s<o;s++){n=e[s];if(!n.style)continue;if(!t||n.style.display==="none"||n.style.display==="")n.style.display=t?i[s]||"":"none"}return e}function Zt(e,t,n){var r=Rt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function en(e,t,n,r){var i=n===(r?"border":"content")?4:t==="width"?1:0,s=0;for(;i<4;i+=2)n==="margin"&&(s+=v.css(e,n+$t[i],!0)),r?(n==="content"&&(s-=parseFloat(Dt(e,"padding"+$t[i]))||0),n!=="margin"&&(s-=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0)):(s+=parseFloat(Dt(e,"padding"+$t[i]))||0,n!=="padding"&&(s+=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0));return s}function tn(e,t,n){var r=t==="width"?e.offsetWidth:e.offsetHeight,i=!0,s=v.support.boxSizing&&v.css(e,"boxSizing")==="border-box";if(r<=0||r==null){r=Dt(e,t);if(r<0||r==null)r=e.style[t];if(Ut.test(r))return r;i=s&&(v.support.boxSizingReliable||r===e.style[t]),r=parseFloat(r)||0}return r+en(e,t,n||(s?"border":"content"),i)+"px"}function nn(e){if(Wt[e])return Wt[e];var t=v("<"+e+">").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write("<!doctype html><html><body>"),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u<a;u++)r=o[u],s=/^\+/.test(r),s&&(r=r.substr(1)||"*"),i=e[r]=e[r]||[],i[s?"unshift":"push"](n)}}function kn(e,n,r,i,s,o){s=s||n.dataTypes[0],o=o||{},o[s]=!0;var u,a=e[s],f=0,l=a?a.length:0,c=e===Sn;for(;f<l&&(c||!u);f++)u=a[f](n,r,i),typeof u=="string"&&(!c||o[u]?u=t:(n.dataTypes.unshift(u),u=kn(e,n,r,i,u,o)));return(c||!u)&&!o["*"]&&(u=kn(e,n,r,i,"*",o)),u}function Ln(e,n){var r,i,s=v.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((s[r]?e:i||(i={}))[r]=n[r]);i&&v.extend(!0,e,i)}function An(e,n,r){var i,s,o,u,a=e.contents,f=e.dataTypes,l=e.responseFields;for(s in l)s in r&&(n[l[s]]=r[s]);while(f[0]==="*")f.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("content-type"));if(i)for(s in a)if(a[s]&&a[s].test(i)){f.unshift(s);break}if(f[0]in r)o=f[0];else{for(s in r){if(!f[0]||e.converters[s+" "+f[0]]){o=s;break}u||(u=s)}o=o||u}if(o)return o!==f[0]&&f.unshift(o),r[o]}function On(e,t){var n,r,i,s,o=e.dataTypes.slice(),u=o[0],a={},f=0;e.dataFilter&&(t=e.dataFilter(t,e.dataType));if(o[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=o[++f];)if(i!=="*"){if(u!=="*"&&u!==i){n=a[u+" "+i]||a["* "+i];if(!n)for(r in a){s=r.split(" ");if(s[1]===i){n=a[u+" "+s[0]]||a["* "+s[0]];if(n){n===!0?n=a[r]:a[r]!==!0&&(i=s[0],o.splice(f--,0,i));break}}}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(l){return{state:"parsererror",error:n?l:"No conversion from "+u+" to "+i}}}u=i}return{state:"success",data:t}}function Fn(){try{return new e.XMLHttpRequest}catch(t){}}function In(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function $n(){return setTimeout(function(){qn=t},0),qn=v.now()}function Jn(e,t){v.each(t,function(t,n){var r=(Vn[t]||[]).concat(Vn["*"]),i=0,s=r.length;for(;i<s;i++)if(r[i].call(e,t,n))return})}function Kn(e,t,n){var r,i=0,s=0,o=Xn.length,u=v.Deferred().always(function(){delete a.elem}),a=function(){var t=qn||$n(),n=Math.max(0,f.startTime+f.duration-t),r=n/f.duration||0,i=1-r,s=0,o=f.tweens.length;for(;s<o;s++)f.tweens[s].run(i);return u.notifyWith(e,[f,i,n]),i<1&&o?n:(u.resolveWith(e,[f]),!1)},f=u.promise({elem:e,props:v.extend({},t),opts:v.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:qn||$n(),duration:n.duration,tweens:[],createTween:function(t,n,r){var i=v.Tween(e,f.opts,t,n,f.opts.specialEasing[t]||f.opts.easing);return f.tweens.push(i),i},stop:function(t){var n=0,r=t?f.tweens.length:0;for(;n<r;n++)f.tweens[n].run(1);return t?u.resolveWith(e,[f,t]):u.rejectWith(e,[f,t]),this}}),l=f.props;Qn(l,f.opts.specialEasing);for(;i<o;i++){r=Xn[i].call(f,e,l,f.opts);if(r)return r}return Jn(f,l),v.isFunction(f.opts.start)&&f.opts.start.call(e,f),v.fx.timer(v.extend(a,{anim:f,queue:f.opts.queue,elem:e})),f.progress(f.opts.progress).done(f.opts.done,f.opts.complete).fail(f.opts.fail).always(f.opts.always)}function Qn(e,t){var n,r,i,s,o;for(n in e){r=v.camelCase(n),i=t[r],s=e[n],v.isArray(s)&&(i=s[1],s=e[n]=s[0]),n!==r&&(e[r]=s,delete e[n]),o=v.cssHooks[r];if(o&&"expand"in o){s=o.expand(s),delete e[r];for(n in s)n in e||(e[n]=s[n],t[n]=i)}else t[r]=i}}function Gn(e,t,n){var r,i,s,o,u,a,f,l,c,h=this,p=e.style,d={},m=[],g=e.nodeType&&Gt(e);n.queue||(l=v._queueHooks(e,"fx"),l.unqueued==null&&(l.unqueued=0,c=l.empty.fire,l.empty.fire=function(){l.unqueued||c()}),l.unqueued++,h.always(function(){h.always(function(){l.unqueued--,v.queue(e,"fx").length||l.empty.fire()})})),e.nodeType===1&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],v.css(e,"display")==="inline"&&v.css(e,"float")==="none"&&(!v.support.inlineBlockNeedsLayout||nn(e.nodeName)==="inline"?p.display="inline-block":p.zoom=1)),n.overflow&&(p.overflow="hidden",v.support.shrinkWrapBlocks||h.done(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t){s=t[r];if(Un.exec(s)){delete t[r],a=a||s==="toggle";if(s===(g?"hide":"show"))continue;m.push(r)}}o=m.length;if(o){u=v._data(e,"fxshow")||v._data(e,"fxshow",{}),"hidden"in u&&(g=u.hidden),a&&(u.hidden=!g),g?v(e).show():h.done(function(){v(e).hide()}),h.done(function(){var t;v.removeData(e,"fxshow",!0);for(t in d)v.style(e,t,d[t])});for(r=0;r<o;r++)i=m[r],f=h.createTween(i,g?u[i]:0),d[i]=u[i]||v.style(e,i),i in u||(u[i]=f.start,g&&(f.end=f.start,f.start=i==="width"||i==="height"?1:0))}}function Yn(e,t,n,r,i){return new Yn.prototype.init(e,t,n,r,i)}function Zn(e,t){var n,r={height:e},i=0;t=t?1:0;for(;i<4;i+=2-t)n=$t[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function tr(e){return v.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:!1}var n,r,i=e.document,s=e.location,o=e.navigator,u=e.jQuery,a=e.$,f=Array.prototype.push,l=Array.prototype.slice,c=Array.prototype.indexOf,h=Object.prototype.toString,p=Object.prototype.hasOwnProperty,d=String.prototype.trim,v=function(e,t){return new v.fn.init(e,t,n)},m=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,g=/\S/,y=/\s+/,b=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,w=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a<f;a++)if((e=arguments[a])!=null)for(n in e){r=u[n],i=e[n];if(u===i)continue;l&&i&&(v.isPlainObject(i)||(s=v.isArray(i)))?(s?(s=!1,o=r&&v.isArray(r)?r:[]):o=r&&v.isPlainObject(r)?r:{},u[n]=v.extend(l,o,i)):i!==t&&(u[n]=i)}return u},v.extend({noConflict:function(t){return e.$===v&&(e.$=a),t&&e.jQuery===v&&(e.jQuery=u),v},isReady:!1,readyWait:1,holdReady:function(e){e?v.readyWait++:v.ready(!0)},ready:function(e){if(e===!0?--v.readyWait:v.isReady)return;if(!i.body)return setTimeout(v.ready,1);v.isReady=!0;if(e!==!0&&--v.readyWait>0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s<o;)if(n.apply(e[s++],r)===!1)break}else if(u){for(i in e)if(n.call(e[i],i,e[i])===!1)break}else for(;s<o;)if(n.call(e[s],s,e[s++])===!1)break;return e},trim:d&&!d.call("\ufeff\u00a0")?function(e){return e==null?"":d.call(e)}:function(e){return e==null?"":(e+"").replace(b,"")},makeArray:function(e,t){var n,r=t||[];return e!=null&&(n=v.type(e),e.length==null||n==="string"||n==="function"||n==="regexp"||v.isWindow(e)?f.call(r,e):v.merge(r,e)),r},inArray:function(e,t,n){var r;if(t){if(c)return c.call(t,e,n);r=t.length,n=n?n<0?Math.max(0,r+n):n:0;for(;n<r;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,s=0;if(typeof r=="number")for(;s<r;s++)e[i++]=n[s];else while(n[s]!==t)e[i++]=n[s++];return e.length=i,e},grep:function(e,t,n){var r,i=[],s=0,o=e.length;n=!!n;for(;s<o;s++)r=!!t(e[s],s),n!==r&&i.push(e[s]);return i},map:function(e,n,r){var i,s,o=[],u=0,a=e.length,f=e instanceof v||a!==t&&typeof a=="number"&&(a>0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u<a;u++)i=n(e[u],u,r),i!=null&&(o[o.length]=i);else for(s in e)i=n(e[s],s,r),i!=null&&(o[o.length]=i);return o.concat.apply([],o)},guid:1,proxy:function(e,n){var r,i,s;return typeof n=="string"&&(r=e[n],n=e,e=r),v.isFunction(e)?(i=l.call(arguments,2),s=function(){return e.apply(n,i.concat(l.call(arguments)))},s.guid=e.guid=e.guid||v.guid++,s):t},access:function(e,n,r,i,s,o,u){var a,f=r==null,l=0,c=e.length;if(r&&typeof r=="object"){for(l in r)v.access(e,n,l,r[l],1,o,i);s=1}else if(i!==t){a=u===t&&v.isFunction(i),f&&(a?(a=n,n=function(e,t,n){return a.call(v(e),n)}):(n.call(e,i),n=null));if(n)for(;l<c;l++)n(e[l],r,a?i.call(e[l],l,n(e[l],r)):i,u);s=1}return s?e:f?n.call(e):c?n(e[0],r):o},now:function(){return(new Date).getTime()}}),v.ready.promise=function(t){if(!r){r=v.Deferred();if(i.readyState==="complete")setTimeout(v.ready,1);else if(i.addEventListener)i.addEventListener("DOMContentLoaded",A,!1),e.addEventListener("load",v.ready,!1);else{i.attachEvent("onreadystatechange",A),e.attachEvent("onload",v.ready);var n=!1;try{n=e.frameElement==null&&i.documentElement}catch(s){}n&&n.doScroll&&function o(){if(!v.isReady){try{n.doScroll("left")}catch(e){return setTimeout(o,50)}v.ready()}}()}}return r.promise(t)},v.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(e,t){O["[object "+t+"]"]=t.toLowerCase()}),n=v(i);var M={};v.Callbacks=function(e){e=typeof e=="string"?M[e]||_(e):v.extend({},e);var n,r,i,s,o,u,a=[],f=!e.once&&[],l=function(t){n=e.memory&&t,r=!0,u=s||0,s=0,o=a.length,i=!0;for(;a&&u<o;u++)if(a[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}i=!1,a&&(f?f.length&&l(f.shift()):n?a=[]:c.disable())},c={add:function(){if(a){var t=a.length;(function r(t){v.each(t,function(t,n){var i=v.type(n);i==="function"?(!e.unique||!c.has(n))&&a.push(n):n&&n.length&&i!=="string"&&r(n)})})(arguments),i?o=a.length:n&&(s=t,l(n))}return this},remove:function(){return a&&v.each(arguments,function(e,t){var n;while((n=v.inArray(t,a,n))>-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t<r;t++)n[t]&&v.isFunction(n[t].promise)?n[t].promise().done(o(t,f,n)).fail(s.reject).progress(o(t,a,u)):--i}return i||s.resolveWith(f,n),s.promise()}}),v.support=function(){var t,n,r,s,o,u,a,f,l,c,h,p=i.createElement("div");p.setAttribute("className","t"),p.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="<table><tr><td></td><td>t</td></tr></table>",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="<div></div>",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i<s;i++)delete r[t[i]];if(!(n?B:v.isEmptyObject)(r))return}}if(!n){delete u[a].data;if(!B(u[a]))return}o?v.cleanData([e],!0):v.support.deleteExpando||u!=u.window?delete u[a]:u[a]=null},_data:function(e,t,n){return v.data(e,t,n,!0)},acceptData:function(e){var t=e.nodeName&&v.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),v.fn.extend({data:function(e,n){var r,i,s,o,u,a=this[0],f=0,l=null;if(e===t){if(this.length){l=v.data(a);if(a.nodeType===1&&!v._data(a,"parsedAttrs")){s=a.attributes;for(u=s.length;f<u;f++)o=s[f].name,o.indexOf("data-")||(o=v.camelCase(o.substring(5)),H(a,o,l[o]));v._data(a,"parsedAttrs",!0)}}return l}return typeof e=="object"?this.each(function(){v.data(this,e)}):(r=e.split(".",2),r[1]=r[1]?"."+r[1]:"",i=r[1]+"!",v.access(this,function(n){if(n===t)return l=this.triggerHandler("getData"+i,[r[0]]),l===t&&a&&(l=v.data(a,e),l=H(a,e,l)),l===t&&r[1]?this.data(r[0]):l;r[1]=n,this.each(function(){var t=v(this);t.triggerHandler("setData"+i,r),v.data(this,e,n),t.triggerHandler("changeData"+i,r)})},null,n,arguments.length>1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length<r?v.queue(this[0],e):n===t?this:this.each(function(){var t=v.queue(this,e,n);v._queueHooks(this,e),e==="fx"&&t[0]!=="inprogress"&&v.dequeue(this,e)})},dequeue:function(e){return this.each(function(){v.dequeue(this,e)})},delay:function(e,t){return e=v.fx?v.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,s=v.Deferred(),o=this,u=this.length,a=function(){--i||s.resolveWith(o,[o])};typeof e!="string"&&(n=e,e=t),e=e||"fx";while(u--)r=v._data(o[u],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(a));return a(),s.promise(n)}});var j,F,I,q=/[\t\r\n]/g,R=/\r/g,U=/^(?:button|input)$/i,z=/^(?:button|input|object|select|textarea)$/i,W=/^a(?:rea|)$/i,X=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,V=v.support.getSetAttribute;v.fn.extend({attr:function(e,t){return v.access(this,v.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n<r;n++){i=this[n];if(i.nodeType===1)if(!i.className&&t.length===1)i.className=e;else{s=" "+i.className+" ";for(o=0,u=t.length;o<u;o++)s.indexOf(" "+t[o]+" ")<0&&(s+=t[o]+" ");i.className=v.trim(s)}}}return this},removeClass:function(e){var n,r,i,s,o,u,a;if(v.isFunction(e))return this.each(function(t){v(this).removeClass(e.call(this,t,this.className))});if(e&&typeof e=="string"||e===t){n=(e||"").split(y);for(u=0,a=this.length;u<a;u++){i=this[u];if(i.nodeType===1&&i.className){r=(" "+i.className+" ").replace(q," ");for(s=0,o=n.length;s<o;s++)while(r.indexOf(" "+n[s]+" ")>=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n<r;n++)if(this[n].nodeType===1&&(" "+this[n].className+" ").replace(q," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a<u;a++){n=r[a];if((n.selected||a===i)&&(v.support.optDisabled?!n.disabled:n.getAttribute("disabled")===null)&&(!n.parentNode.disabled||!v.nodeName(n.parentNode,"optgroup"))){t=v(n).val();if(s)return t;o.push(t)}}return o},set:function(e,t){var n=v.makeArray(t);return v(e).find("option").each(function(){this.selected=v.inArray(v(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o<r.length;o++)i=r[o],i&&(n=v.propFix[i]||i,s=X.test(i),s||v.attr(e,i,""),e.removeAttribute(V?i:n),s&&n in e&&(e[n]=!1))}},attrHooks:{type:{set:function(e,t){if(U.test(e.nodeName)&&e.parentNode)v.error("type property can't be changed");else if(!v.support.radioValue&&t==="radio"&&v.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}},value:{get:function(e,t){return j&&v.nodeName(e,"button")?j.get(e,t):t in e?e.value:null},set:function(e,t,n){if(j&&v.nodeName(e,"button"))return j.set(e,t,n);e.value=t}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,s,o,u=e.nodeType;if(!e||u===3||u===8||u===2)return;return o=u!==1||!v.isXMLDoc(e),o&&(n=v.propFix[n]||n,s=v.propHooks[n]),r!==t?s&&"set"in s&&(i=s.set(e,r,n))!==t?i:e[n]=r:s&&"get"in s&&(i=s.get(e,n))!==null?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):z.test(e.nodeName)||W.test(e.nodeName)&&e.href?0:t}}}}),F={get:function(e,n){var r,i=v.prop(e,n);return i===!0||typeof i!="boolean"&&(r=e.getAttributeNode(n))&&r.nodeValue!==!1?n.toLowerCase():t},set:function(e,t,n){var r;return t===!1?v.removeAttr(e,n):(r=v.propFix[n]||n,r in e&&(e[r]=!0),e.setAttribute(n,n.toLowerCase())),n}},V||(I={name:!0,id:!0,coords:!0},j=v.valHooks.button={get:function(e,n){var r;return r=e.getAttributeNode(n),r&&(I[n]?r.value!=="":r.specified)?r.value:t},set:function(e,t,n){var r=e.getAttributeNode(n);return r||(r=i.createAttribute(n),e.setAttributeNode(r)),r.value=t+""}},v.each(["width","height"],function(e,t){v.attrHooks[t]=v.extend(v.attrHooks[t],{set:function(e,n){if(n==="")return e.setAttribute(t,"auto"),n}})}),v.attrHooks.contenteditable={get:j.get,set:function(e,t,n){t===""&&(t="false"),j.set(e,t,n)}}),v.support.hrefNormalized||v.each(["href","src","width","height"],function(e,n){v.attrHooks[n]=v.extend(v.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return r===null?t:r}})}),v.support.style||(v.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||t},set:function(e,t){return e.style.cssText=t+""}}),v.support.optSelected||(v.propHooks.selected=v.extend(v.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),v.support.enctype||(v.propFix.enctype="encoding"),v.support.checkOn||v.each(["radio","checkbox"],function(){v.valHooks[this]={get:function(e){return e.getAttribute("value")===null?"on":e.value}}}),v.each(["radio","checkbox"],function(){v.valHooks[this]=v.extend(v.valHooks[this],{set:function(e,t){if(v.isArray(t))return e.checked=v.inArray(v(e).val(),t)>=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f<n.length;f++){l=J.exec(n[f])||[],c=l[1],h=(l[2]||"").split(".").sort(),g=v.event.special[c]||{},c=(s?g.delegateType:g.bindType)||c,g=v.event.special[c]||{},p=v.extend({type:c,origType:l[1],data:i,handler:r,guid:r.guid,selector:s,needsContext:s&&v.expr.match.needsContext.test(s),namespace:h.join(".")},d),m=a[c];if(!m){m=a[c]=[],m.delegateCount=0;if(!g.setup||g.setup.call(e,i,h,u)===!1)e.addEventListener?e.addEventListener(c,u,!1):e.attachEvent&&e.attachEvent("on"+c,u)}g.add&&(g.add.call(e,p),p.handler.guid||(p.handler.guid=r.guid)),s?m.splice(m.delegateCount++,0,p):m.push(p),v.event.global[c]=!0}e=null},global:{},remove:function(e,t,n,r,i){var s,o,u,a,f,l,c,h,p,d,m,g=v.hasData(e)&&v._data(e);if(!g||!(h=g.events))return;t=v.trim(Z(t||"")).split(" ");for(s=0;s<t.length;s++){o=J.exec(t[s])||[],u=a=o[1],f=o[2];if(!u){for(u in h)v.event.remove(e,u+t[s],n,r,!0);continue}p=v.event.special[u]||{},u=(r?p.delegateType:p.bindType)||u,d=h[u]||[],l=d.length,f=f?new RegExp("(^|\\.)"+f.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(c=0;c<d.length;c++)m=d[c],(i||a===m.origType)&&(!n||n.guid===m.guid)&&(!f||f.test(m.namespace))&&(!r||r===m.selector||r==="**"&&m.selector)&&(d.splice(c--,1),m.selector&&d.delegateCount--,p.remove&&p.remove.call(e,m));d.length===0&&l!==d.length&&((!p.teardown||p.teardown.call(e,f,g.handle)===!1)&&v.removeEvent(e,u,g.handle),delete h[u])}v.isEmptyObject(h)&&(delete g.handle,v.removeData(e,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(n,r,s,o){if(!s||s.nodeType!==3&&s.nodeType!==8){var u,a,f,l,c,h,p,d,m,g,y=n.type||n,b=[];if(Y.test(y+v.event.triggered))return;y.indexOf("!")>=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f<m.length&&!n.isPropagationStopped();f++)l=m[f][0],n.type=m[f][1],d=(v._data(l,"events")||{})[n.type]&&v._data(l,"handle"),d&&d.apply(l,r),d=h&&l[h],d&&v.acceptData(l)&&d.apply&&d.apply(l,r)===!1&&n.preventDefault();return n.type=y,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(s.ownerDocument,r)===!1)&&(y!=="click"||!v.nodeName(s,"a"))&&v.acceptData(s)&&h&&s[y]&&(y!=="focus"&&y!=="blur"||n.target.offsetWidth!==0)&&!v.isWindow(s)&&(c=s[h],c&&(s[h]=null),v.event.triggered=y,s[y](),v.event.triggered=t,c&&(s[h]=c)),n.result}return},dispatch:function(n){n=v.event.fix(n||e.event);var r,i,s,o,u,a,f,c,h,p,d=(v._data(this,"events")||{})[n.type]||[],m=d.delegateCount,g=l.call(arguments),y=!n.exclusive&&!n.namespace,b=v.event.special[n.type]||{},w=[];g[0]=n,n.delegateTarget=this;if(b.preDispatch&&b.preDispatch.call(this,n)===!1)return;if(m&&(!n.button||n.type!=="click"))for(s=n.target;s!=this;s=s.parentNode||this)if(s.disabled!==!0||n.type!=="click"){u={},f=[];for(r=0;r<m;r++)c=d[r],h=c.selector,u[h]===t&&(u[h]=c.needsContext?v(h,this).index(s)>=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r<w.length&&!n.isPropagationStopped();r++){a=w[r],n.currentTarget=a.elem;for(i=0;i<a.matches.length&&!n.isImmediatePropagationStopped();i++){c=a.matches[i];if(y||!n.namespace&&!c.namespace||n.namespace_re&&n.namespace_re.test(c.namespace))n.data=c.data,n.handleObj=c,o=((v.event.special[c.origType]||{}).handle||c.handler).apply(a.elem,g),o!==t&&(n.result=o,o===!1&&(n.preventDefault(),n.stopPropagation()))}}return b.postDispatch&&b.postDispatch.call(this,n),n.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return e.which==null&&(e.which=t.charCode!=null?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,s,o,u=n.button,a=n.fromElement;return e.pageX==null&&n.clientX!=null&&(r=e.target.ownerDocument||i,s=r.documentElement,o=r.body,e.pageX=n.clientX+(s&&s.scrollLeft||o&&o.scrollLeft||0)-(s&&s.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(s&&s.scrollTop||o&&o.scrollTop||0)-(s&&s.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?n.toElement:a),!e.which&&u!==t&&(e.which=u&1?1:u&2?3:u&4?2:0),e}},fix:function(e){if(e[v.expando])return e;var t,n,r=e,s=v.event.fixHooks[e.type]||{},o=s.props?this.props.concat(s.props):this.props;e=v.Event(r);for(t=o.length;t;)n=o[--t],e[n]=r[n];return e.target||(e.target=r.srcElement||i),e.target.nodeType===3&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,r):e},special:{load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(e,t,n){v.isWindow(this)&&(this.onbeforeunload=n)},teardown:function(e,t){this.onbeforeunload===t&&(this.onbeforeunload=null)}}},simulate:function(e,t,n,r){var i=v.extend(new v.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?v.event.trigger(i,null,t):v.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},v.event.handle=v.event.dispatch,v.removeEvent=i.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]=="undefined"&&(e[r]=null),e.detachEvent(r,n))},v.Event=function(e,t){if(!(this instanceof v.Event))return new v.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?tt:et):this.type=e,t&&v.extend(this,t),this.timeStamp=e&&e.timeStamp||v.now(),this[v.expando]=!0},v.Event.prototype={preventDefault:function(){this.isDefaultPrevented=tt;var e=this.originalEvent;if(!e)return;e.preventDefault?e.preventDefault():e.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=tt;var e=this.originalEvent;if(!e)return;e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=tt,this.stopPropagation()},isDefaultPrevented:et,isPropagationStopped:et,isImmediatePropagationStopped:et},v.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){v.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,s=e.handleObj,o=s.selector;if(!i||i!==r&&!v.contains(r,i))e.type=s.origType,n=s.handler.apply(this,arguments),e.type=t;return n}}}),v.support.submitBubbles||(v.event.special.submit={setup:function(){if(v.nodeName(this,"form"))return!1;v.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=v.nodeName(n,"input")||v.nodeName(n,"button")?n.form:t;r&&!v._data(r,"_submit_attached")&&(v.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),v._data(r,"_submit_attached",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&v.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){if(v.nodeName(this,"form"))return!1;v.event.remove(this,"._submit")}}),v.support.changeBubbles||(v.event.special.change={setup:function(){if($.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")v.event.add(this,"propertychange._change",function(e){e.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),v.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),v.event.simulate("change",this,e,!0)});return!1}v.event.add(this,"beforeactivate._change",function(e){var t=e.target;$.test(t.nodeName)&&!v._data(t,"_change_attached")&&(v.event.add(t,"change._change",function(e){this.parentNode&&!e.isSimulated&&!e.isTrigger&&v.event.simulate("change",this.parentNode,e,!0)}),v._data(t,"_change_attached",!0))})},handle:function(e){var t=e.target;if(this!==t||e.isSimulated||e.isTrigger||t.type!=="radio"&&t.type!=="checkbox")return e.handleObj.handler.apply(this,arguments)},teardown:function(){return v.event.remove(this,"._change"),!$.test(this.nodeName)}}),v.support.focusinBubbles||v.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){v.event.simulate(t,e.target,v.event.fix(e),!0)};v.event.special[t]={setup:function(){n++===0&&i.addEventListener(e,r,!0)},teardown:function(){--n===0&&i.removeEventListener(e,r,!0)}}}),v.fn.extend({on:function(e,n,r,i,s){var o,u;if(typeof e=="object"){typeof n!="string"&&(r=r||n,n=t);for(u in e)this.on(u,n,r,e[u],s);return this}r==null&&i==null?(i=n,r=n=t):i==null&&(typeof n=="string"?(i=r,r=t):(i=r,r=n,n=t));if(i===!1)i=et;else if(!i)return this;return s===1&&(o=i,i=function(e){return v().off(e),o.apply(this,arguments)},i.guid=o.guid||(o.guid=v.guid++)),this.each(function(){v.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,s;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,v(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if(typeof e=="object"){for(s in e)this.off(s,n,e[s]);return this}if(n===!1||typeof n=="function")r=n,n=t;return r===!1&&(r=et),this.each(function(){v.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},live:function(e,t,n){return v(this.context).on(e,this.selector,t,n),this},die:function(e,t){return v(this.context).off(e,this.selector||"**",t),this},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return arguments.length===1?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){v.event.trigger(e,t,this)})},triggerHandler:function(e,t){if(this[0])return v.event.trigger(e,t,this[0],!0)},toggle:function(e){var t=arguments,n=e.guid||v.guid++,r=0,i=function(n){var i=(v._data(this,"lastToggle"+e.guid)||0)%r;return v._data(this,"lastToggle"+e.guid,i+1),n.preventDefault(),t[i].apply(this,arguments)||!1};i.guid=n;while(r<t.length)t[r++].guid=n;return this.click(i)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),v.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){v.fn[t]=function(e,n){return n==null&&(n=e,e=null),arguments.length>0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u<a;u++)if(s=e[u])if(!n||n(s,r,i))o.push(s),f&&t.push(u);return o}function ct(e,t,n,r,i,s){return r&&!r[d]&&(r=ct(r)),i&&!i[d]&&(i=ct(i,s)),N(function(s,o,u,a){var f,l,c,h=[],p=[],d=o.length,v=s||dt(t||"*",u.nodeType?[u]:u,[]),m=e&&(s||!t)?lt(v,h,e,u,a):v,g=n?i||(s?e:d||r)?[]:o:m;n&&n(m,g,u,a);if(r){f=lt(g,p),r(f,[],u,a),l=f.length;while(l--)if(c=f[l])g[p[l]]=!(m[p[l]]=c)}if(s){if(i||e){if(i){f=[],l=g.length;while(l--)(c=g[l])&&f.push(m[l]=c);i(null,g=[],f,a)}l=g.length;while(l--)(c=g[l])&&(f=i?T.call(s,c):h[l])>-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a<s;a++)if(n=i.relative[e[a].type])h=[at(ft(h),n)];else{n=i.filter[e[a].type].apply(null,e[a].matches);if(n[d]){r=++a;for(;r<s;r++)if(i.relative[e[r].type])break;return ct(a>1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a<r&&ht(e.slice(a,r)),r<s&&ht(e=e.slice(r)),r<s&&e.join(""))}h.push(n)}return ft(h)}function pt(e,t){var r=t.length>0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r<i;r++)nt(e,t[r],n);return n}function vt(e,t,n,r,s){var o,u,f,l,c,h=ut(e),p=h.length;if(!r&&h.length===1){u=h[0]=h[0].slice(0);if(u.length>2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;t<n;t++)if(this[t]===e)return t;return-1},N=function(e,t){return e[d]=t==null||t,e},C=function(){var e={},t=[];return N(function(n,r){return t.push(n)>i.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="<a name='"+d+"'></a><div name='"+d+"'></div>",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:st(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:st(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},f=y.compareDocumentPosition?function(e,t){return e===t?(l=!0,0):(!e.compareDocumentPosition||!t.compareDocumentPosition?e.compareDocumentPosition:e.compareDocumentPosition(t)&4)?-1:1}:function(e,t){if(e===t)return l=!0,0;if(e.sourceIndex&&t.sourceIndex)return e.sourceIndex-t.sourceIndex;var n,r,i=[],s=[],o=e.parentNode,u=t.parentNode,a=o;if(o===u)return ot(e,t);if(!o)return-1;if(!u)return 1;while(a)i.unshift(a),a=a.parentNode;a=u;while(a)s.unshift(a),a=a.parentNode;n=i.length,r=s.length;for(var f=0;f<n&&f<r;f++)if(i[f]!==s[f])return ot(i[f],s[f]);return f===n?ot(e,s[f],-1):ot(i[f],t,1)},[0,0].sort(f),h=!l,nt.uniqueSort=function(e){var t,n=[],r=1,i=0;l=h,e.sort(f);if(l){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e},nt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},a=nt.compile=function(e,t){var n,r=[],i=[],s=A[d][e+" "];if(!s){t||(t=ut(e)),n=t.length;while(n--)s=ht(t[n]),s[d]?r.push(s):i.push(s);s=A(e,pt(i,r))}return s},g.querySelectorAll&&function(){var e,t=vt,n=/'|\\/g,r=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,i=[":focus"],s=[":active"],u=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector||y.oMatchesSelector||y.msMatchesSelector;K(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="<p test=''></p>",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="<input type='hidden'/>",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t<n;t++)if(v.contains(u[t],this))return!0});o=this.pushStack("","find",e);for(t=0,n=this.length;t<n;t++){r=o.length,v.find(e,this[t],o);if(t>0)for(i=r;i<o.length;i++)for(s=0;s<r;s++)if(o[s]===o[i]){o.splice(i--,1);break}}return o},has:function(e){var t,n=v(e,this),r=n.length;return this.filter(function(){for(t=0;t<r;t++)if(v.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1),"not",e)},filter:function(e){return this.pushStack(ft(this,e,!0),"filter",e)},is:function(e){return!!e&&(typeof e=="string"?st.test(e)?v(e,this.context).index(this[0])>=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r<i;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&n.nodeType!==11){if(o?o.index(n)>-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/<tbody/i,gt=/<|&#?\w+;/,yt=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,wt=new RegExp("<(?:"+ct+")[\\s/>]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,Nt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X<div>","</div>"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1></$2>");try{for(;r<i;r++)n=this[r]||{},n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),n.innerHTML=e);n=0}catch(s){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){return ut(this[0])?this.length?this.pushStack(v(v.isFunction(e)?e():e),"replaceWith",e):this:v.isFunction(e)?this.each(function(t){var n=v(this),r=n.html();n.replaceWith(e.call(this,t,r))}):(typeof e!="string"&&(e=v(e).detach()),this.each(function(){var t=this.nextSibling,n=this.parentNode;v(this).remove(),t?v(t).before(e):v(n).append(e)}))},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=[].concat.apply([],e);var i,s,o,u,a=0,f=e[0],l=[],c=this.length;if(!v.support.checkClone&&c>1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a<c;a++)r.call(n&&v.nodeName(this[a],"table")?Lt(this[a],"tbody"):this[a],a===u?o:v.clone(o,!0,!0))}o=s=null,l.length&&v.each(l,function(e,t){t.src?v.ajax?v.ajax({url:t.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):v.error("no ajax"):v.globalEval((t.text||t.textContent||t.innerHTML||"").replace(Tt,"")),t.parentNode&&t.parentNode.removeChild(t)})}return this}}),v.buildFragment=function(e,n,r){var s,o,u,a=e[0];return n=n||i,n=!n.nodeType&&n[0]||n,n=n.ownerDocument||n,e.length===1&&typeof a=="string"&&a.length<512&&n===i&&a.charAt(0)==="<"&&!bt.test(a)&&(v.support.checkClone||!St.test(a))&&(v.support.html5Clone||!wt.test(a))&&(o=!0,s=v.fragments[a],u=s!==t),s||(s=n.createDocumentFragment(),v.clean(e,n,s,r),o&&(v.fragments[a]=u&&s)),{fragment:s,cacheable:o}},v.fragments={},v.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){v.fn[e]=function(n){var r,i=0,s=[],o=v(n),u=o.length,a=this.length===1&&this[0].parentNode;if((a==null||a&&a.nodeType===11&&a.childNodes.length===1)&&u===1)return o[t](this[0]),this;for(;i<u;i++)r=(i>0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1></$2>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]==="<table>"&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("<div>").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r<i;r++)n=e[r],Vn[n]=Vn[n]||[],Vn[n].unshift(t)},prefilter:function(e,t){t?Xn.unshift(e):Xn.push(e)}}),v.Tween=Yn,Yn.prototype={constructor:Yn,init:function(e,t,n,r,i,s){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=s||(v.cssNumber[n]?"":"px")},cur:function(){var e=Yn.propHooks[this.prop];return e&&e.get?e.get(this):Yn.propHooks._default.get(this)},run:function(e){var t,n=Yn.propHooks[this.prop];return this.options.duration?this.pos=t=v.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Yn.propHooks._default.set(this),this}},Yn.prototype.init.prototype=Yn.prototype,Yn.propHooks={_default:{get:function(e){var t;return e.elem[e.prop]==null||!!e.elem.style&&e.elem.style[e.prop]!=null?(t=v.css(e.elem,e.prop,!1,""),!t||t==="auto"?0:t):e.elem[e.prop]},set:function(e){v.fx.step[e.prop]?v.fx.step[e.prop](e):e.elem.style&&(e.elem.style[v.cssProps[e.prop]]!=null||v.cssHooks[e.prop])?v.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Yn.propHooks.scrollTop=Yn.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},v.each(["toggle","show","hide"],function(e,t){var n=v.fn[t];v.fn[t]=function(r,i,s){return r==null||typeof r=="boolean"||!e&&v.isFunction(r)&&v.isFunction(i)?n.apply(this,arguments):this.animate(Zn(t,!0),r,i,s)}}),v.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Gt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=v.isEmptyObject(e),s=v.speed(t,n,r),o=function(){var t=Kn(this,v.extend({},e),s);i&&t.stop(!0)};return i||s.queue===!1?this.each(o):this.queue(s.queue,o)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return typeof e!="string"&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=e!=null&&e+"queueHooks",s=v.timers,o=v._data(this);if(n)o[n]&&o[n].stop&&i(o[n]);else for(n in o)o[n]&&o[n].stop&&Wn.test(n)&&i(o[n]);for(n=s.length;n--;)s[n].elem===this&&(e==null||s[n].queue===e)&&(s[n].anim.stop(r),t=!1,s.splice(n,1));(t||!r)&&v.dequeue(this,e)})}}),v.each({slideDown:Zn("show"),slideUp:Zn("hide"),slideToggle:Zn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){v.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),v.speed=function(e,t,n){var r=e&&typeof e=="object"?v.extend({},e):{complete:n||!n&&t||v.isFunction(e)&&e,duration:e,easing:n&&t||t&&!v.isFunction(t)&&t};r.duration=v.fx.off?0:typeof r.duration=="number"?r.duration:r.duration in v.fx.speeds?v.fx.speeds[r.duration]:v.fx.speeds._default;if(r.queue==null||r.queue===!0)r.queue="fx";return r.old=r.complete,r.complete=function(){v.isFunction(r.old)&&r.old.call(this),r.queue&&v.dequeue(this,r.queue)},r},v.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},v.timers=[],v.fx=Yn.prototype.init,v.fx.tick=function(){var e,n=v.timers,r=0;qn=v.now();for(;r<n.length;r++)e=n[r],!e()&&n[r]===e&&n.splice(r--,1);n.length||v.fx.stop(),qn=t},v.fx.timer=function(e){e()&&v.timers.push(e)&&!Rn&&(Rn=setInterval(v.fx.tick,v.fx.interval))},v.fx.interval=13,v.fx.stop=function(){clearInterval(Rn),Rn=null},v.fx.speeds={slow:600,fast:200,_default:400},v.fx.step={},v.expr&&v.expr.filters&&(v.expr.filters.animated=function(e){return v.grep(v.timers,function(t){return e===t.elem}).length});var er=/^(?:body|html)$/i;v.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){v.offset.setOffset(this,e,t)});var n,r,i,s,o,u,a,f={top:0,left:0},l=this[0],c=l&&l.ownerDocument;if(!c)return;return(r=c.body)===l?v.offset.bodyOffset(l):(n=c.documentElement,v.contains(n,l)?(typeof l.getBoundingClientRect!="undefined"&&(f=l.getBoundingClientRect()),i=tr(c),s=n.clientTop||r.clientTop||0,o=n.clientLeft||r.clientLeft||0,u=i.pageYOffset||n.scrollTop,a=i.pageXOffset||n.scrollLeft,{top:f.top+u-s,left:f.left+a-o}):f)},v.offset={bodyOffset:function(e){var t=e.offsetTop,n=e.offsetLeft;return v.support.doesNotIncludeMarginInBodyOffset&&(t+=parseFloat(v.css(e,"marginTop"))||0,n+=parseFloat(v.css(e,"marginLeft"))||0),{top:t,left:n}},setOffset:function(e,t,n){var r=v.css(e,"position");r==="static"&&(e.style.position="relative");var i=v(e),s=i.offset(),o=v.css(e,"top"),u=v.css(e,"left"),a=(r==="absolute"||r==="fixed")&&v.inArray("auto",[o,u])>-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window);
\ No newline at end of file
diff --git a/DUBREUIL/lib/sprintf.js b/DUBREUIL/lib/sprintf.js
new file mode 100644
index 0000000..019324e
--- /dev/null
+++ b/DUBREUIL/lib/sprintf.js
@@ -0,0 +1,134 @@
+/*! sprintf.js | Copyright (c) 2007-2013 Alexandru Marasteanu <hello at alexei dot ro> | 3 clause BSD license */
+
+(function(ctx) {
+	var sprintf = function() {
+		if (!sprintf.cache.hasOwnProperty(arguments[0])) {
+			sprintf.cache[arguments[0]] = sprintf.parse(arguments[0]);
+		}
+		return sprintf.format.call(null, sprintf.cache[arguments[0]], arguments);
+	};
+
+	sprintf.format = function(parse_tree, argv) {
+		var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
+		for (i = 0; i < tree_length; i++) {
+			node_type = get_type(parse_tree[i]);
+			if (node_type === 'string') {
+				output.push(parse_tree[i]);
+			}
+			else if (node_type === 'array') {
+				match = parse_tree[i]; // convenience purposes only
+				if (match[2]) { // keyword argument
+					arg = argv[cursor];
+					for (k = 0; k < match[2].length; k++) {
+						if (!arg.hasOwnProperty(match[2][k])) {
+							throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
+						}
+						arg = arg[match[2][k]];
+					}
+				}
+				else if (match[1]) { // positional argument (explicit)
+					arg = argv[match[1]];
+				}
+				else { // positional argument (implicit)
+					arg = argv[cursor++];
+				}
+
+				if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
+					throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
+				}
+				switch (match[8]) {
+					case 'b': arg = arg.toString(2); break;
+					case 'c': arg = String.fromCharCode(arg); break;
+					case 'd': arg = parseInt(arg, 10); break;
+					case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
+					case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
+					case 'o': arg = arg.toString(8); break;
+					case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
+					case 'u': arg = arg >>> 0; break;
+					case 'x': arg = arg.toString(16); break;
+					case 'X': arg = arg.toString(16).toUpperCase(); break;
+				}
+				arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
+				pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
+				pad_length = match[6] - String(arg).length;
+				pad = match[6] ? str_repeat(pad_character, pad_length) : '';
+				output.push(match[5] ? arg + pad : pad + arg);
+			}
+		}
+		return output.join('');
+	};
+
+	sprintf.cache = {};
+
+	sprintf.parse = function(fmt) {
+		var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
+		while (_fmt) {
+			if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
+				parse_tree.push(match[0]);
+			}
+			else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
+				parse_tree.push('%');
+			}
+			else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
+				if (match[2]) {
+					arg_names |= 1;
+					var field_list = [], replacement_field = match[2], field_match = [];
+					if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+						field_list.push(field_match[1]);
+						while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
+							if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+								field_list.push(field_match[1]);
+							}
+							else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
+								field_list.push(field_match[1]);
+							}
+							else {
+								throw('[sprintf] huh?');
+							}
+						}
+					}
+					else {
+						throw('[sprintf] huh?');
+					}
+					match[2] = field_list;
+				}
+				else {
+					arg_names |= 2;
+				}
+				if (arg_names === 3) {
+					throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
+				}
+				parse_tree.push(match);
+			}
+			else {
+				throw('[sprintf] huh?');
+			}
+			_fmt = _fmt.substring(match[0].length);
+		}
+		return parse_tree;
+	};
+
+	var vsprintf = function(fmt, argv, _argv) {
+		_argv = argv.slice(0);
+		_argv.splice(0, 0, fmt);
+		return sprintf.apply(null, _argv);
+	};
+
+	/**
+	 * helpers
+	 */
+	function get_type(variable) {
+		return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
+	}
+
+	function str_repeat(input, multiplier) {
+		for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
+		return output.join('');
+	}
+
+	/**
+	 * export to either browser or node.js
+	 */
+	ctx.sprintf = sprintf;
+	ctx.vsprintf = vsprintf;
+})(typeof exports != "undefined" ? exports : window);
\ No newline at end of file
diff --git a/DUBREUIL/lib/stats.min.js b/DUBREUIL/lib/stats.min.js
new file mode 100644
index 0000000..73744ef
--- /dev/null
+++ b/DUBREUIL/lib/stats.min.js
@@ -0,0 +1,6 @@
+// stats.js - http://github.com/mrdoob/stats.js
+var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
+i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
+k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
+"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
+a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};
diff --git a/DUBREUIL/lib/three.js b/DUBREUIL/lib/three.js
new file mode 100644
index 0000000..2d01d65
--- /dev/null
+++ b/DUBREUIL/lib/three.js
@@ -0,0 +1,35996 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author Larry Battle / http://bateru.com/news
+ */
+
+var THREE = THREE || { REVISION: '56' };
+
+self.console = self.console || {
+
+	info: function () {},
+	log: function () {},
+	debug: function () {},
+	warn: function () {},
+	error: function () {}
+
+};
+
+self.Int32Array = self.Int32Array || Array;
+self.Float32Array = self.Float32Array || Array;
+
+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;
+
+};
+
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+
+// requestAnimationFrame polyfill by Erik M�ller
+// fixes from Paul Irish and Tino Zijdel
+
+( function () {
+
+	var lastTime = 0;
+	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
+
+	for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {
+
+		window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
+		window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
+
+	}
+
+	if ( window.requestAnimationFrame === undefined ) {
+
+		window.requestAnimationFrame = function ( callback ) {
+
+			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
+			var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
+			lastTime = currTime + timeToCall;
+			return id;
+
+		};
+
+	}
+
+	window.cancelAnimationFrame = window.cancelAnimationFrame || function ( id ) { window.clearTimeout( id ) };
+
+}() );
+
+// GL STATE CONSTANTS
+
+THREE.CullFaceNone = 0;
+THREE.CullFaceBack = 1;
+THREE.CullFaceFront = 2;
+THREE.CullFaceFrontBack = 3;
+
+THREE.FrontFaceDirectionCW = 0;
+THREE.FrontFaceDirectionCCW = 1;
+
+// SHADOWING TYPES
+
+THREE.BasicShadowMap = 0;
+THREE.PCFShadowMap = 1;
+THREE.PCFSoftShadowMap = 2;
+
+// MATERIAL CONSTANTS
+
+// side
+
+THREE.FrontSide = 0;
+THREE.BackSide = 1;
+THREE.DoubleSide = 2;
+
+// shading
+
+THREE.NoShading = 0;
+THREE.FlatShading = 1;
+THREE.SmoothShading = 2;
+
+// colors
+
+THREE.NoColors = 0;
+THREE.FaceColors = 1;
+THREE.VertexColors = 2;
+
+// blending modes
+
+THREE.NoBlending = 0;
+THREE.NormalBlending = 1;
+THREE.AdditiveBlending = 2;
+THREE.SubtractiveBlending = 3;
+THREE.MultiplyBlending = 4;
+THREE.CustomBlending = 5;
+
+// custom blending equations
+// (numbers start from 100 not to clash with other
+//  mappings to OpenGL constants defined in Texture.js)
+
+THREE.AddEquation = 100;
+THREE.SubtractEquation = 101;
+THREE.ReverseSubtractEquation = 102;
+
+// custom blending destination factors
+
+THREE.ZeroFactor = 200;
+THREE.OneFactor = 201;
+THREE.SrcColorFactor = 202;
+THREE.OneMinusSrcColorFactor = 203;
+THREE.SrcAlphaFactor = 204;
+THREE.OneMinusSrcAlphaFactor = 205;
+THREE.DstAlphaFactor = 206;
+THREE.OneMinusDstAlphaFactor = 207;
+
+// custom blending source factors
+
+//THREE.ZeroFactor = 200;
+//THREE.OneFactor = 201;
+//THREE.SrcAlphaFactor = 204;
+//THREE.OneMinusSrcAlphaFactor = 205;
+//THREE.DstAlphaFactor = 206;
+//THREE.OneMinusDstAlphaFactor = 207;
+THREE.DstColorFactor = 208;
+THREE.OneMinusDstColorFactor = 209;
+THREE.SrcAlphaSaturateFactor = 210;
+
+
+// TEXTURE CONSTANTS
+
+THREE.MultiplyOperation = 0;
+THREE.MixOperation = 1;
+THREE.AddOperation = 2;
+
+// Mapping modes
+
+THREE.UVMapping = function () {};
+
+THREE.CubeReflectionMapping = function () {};
+THREE.CubeRefractionMapping = function () {};
+
+THREE.SphericalReflectionMapping = function () {};
+THREE.SphericalRefractionMapping = function () {};
+
+// Wrapping modes
+
+THREE.RepeatWrapping = 1000;
+THREE.ClampToEdgeWrapping = 1001;
+THREE.MirroredRepeatWrapping = 1002;
+
+// Filters
+
+THREE.NearestFilter = 1003;
+THREE.NearestMipMapNearestFilter = 1004;
+THREE.NearestMipMapLinearFilter = 1005;
+THREE.LinearFilter = 1006;
+THREE.LinearMipMapNearestFilter = 1007;
+THREE.LinearMipMapLinearFilter = 1008;
+
+// Data types
+
+THREE.UnsignedByteType = 1009;
+THREE.ByteType = 1010;
+THREE.ShortType = 1011;
+THREE.UnsignedShortType = 1012;
+THREE.IntType = 1013;
+THREE.UnsignedIntType = 1014;
+THREE.FloatType = 1015;
+
+// Pixel types
+
+//THREE.UnsignedByteType = 1009;
+THREE.UnsignedShort4444Type = 1016;
+THREE.UnsignedShort5551Type = 1017;
+THREE.UnsignedShort565Type = 1018;
+
+// Pixel formats
+
+THREE.AlphaFormat = 1019;
+THREE.RGBFormat = 1020;
+THREE.RGBAFormat = 1021;
+THREE.LuminanceFormat = 1022;
+THREE.LuminanceAlphaFormat = 1023;
+
+// 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
+THREE.RGB_PVRTC_4BPPV1_Format = 2100;
+THREE.RGB_PVRTC_2BPPV1_Format = 2101;
+THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
+THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
+*/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Color = function ( value ) {
+
+	if ( value !== undefined ) this.set( value );
+
+	return this;
+
+};
+
+THREE.extend( THREE.Color.prototype, {
+
+	r: 1, g: 1, b: 1,
+
+	set: function ( value ) {
+
+		switch ( typeof value ) {
+
+			case "number":
+				this.setHex( value );
+				break;
+
+			case "string":
+				this.setStyle( value );
+				break;
+
+		}
+
+	},
+
+	setHex: function ( hex ) {
+
+		hex = Math.floor( hex );
+
+		this.r = ( hex >> 16 & 255 ) / 255;
+		this.g = ( hex >> 8 & 255 ) / 255;
+		this.b = ( hex & 255 ) / 255;
+
+		return this;
+
+	},
+
+	setRGB: function ( r, g, b ) {
+
+		this.r = r;
+		this.g = g;
+		this.b = b;
+
+		return this;
+
+	},
+
+	setHSV: function ( h, s, v ) {
+
+		console.log( 'DEPRECATED: Color\'s .setHSV() will be removed. Use .setHSL( h, s, l ) instead.' );
+		return this.setHSL(h,s*v/((h=(2-s)*v)<1?h:2-h),h/2); // https://gist.github.com/xpansive/1337890
+
+	},
+
+	setHSL: function ( h, s, l ) {
+
+		// h,s,l ranges are in 0.0 - 1.0
+
+		if ( s === 0 ) {
+
+			this.r = this.g = this.b = l;
+
+		} else {
+
+			var hue2rgb = function ( p, q, t ) {
+
+				if ( t < 0 ) t += 1;
+				if ( t > 1 ) t -= 1;
+				if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
+				if ( t < 1 / 2 ) return q;
+				if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
+				return p;
+
+			};
+
+			var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
+			var q = ( 2 * l ) - p;
+
+			this.r = hue2rgb( q, p, h + 1 / 3 );
+			this.g = hue2rgb( q, p, h );
+			this.b = hue2rgb( q, p, h - 1 / 3 );
+
+		}
+
+		return this;
+
+	},
+
+	setStyle: function ( style ) {
+
+		// rgb(255,0,0)
+
+		if ( /^rgb\((\d+),(\d+),(\d+)\)$/i.test( style ) ) {
+
+			var color = /^rgb\((\d+),(\d+),(\d+)\)$/i.exec( style );
+
+			this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
+			this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
+			this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+
+			return this;
+
+		}
+
+		// rgb(100%,0%,0%)
+
+		if ( /^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.test( style ) ) {
+
+			var color = /^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.exec( style );
+
+			this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
+			this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
+			this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+
+			return this;
+
+		}
+
+		// #ff0000
+
+		if ( /^\#([0-9a-f]{6})$/i.test( style ) ) {
+
+			var color = /^\#([0-9a-f]{6})$/i.exec( style );
+
+			this.setHex( parseInt( color[ 1 ], 16 ) );
+
+			return this;
+
+		}
+
+		// #f00
+
+		if ( /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test( style ) ) {
+
+			var color = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec( style );
+
+			this.setHex( parseInt( color[ 1 ] + color[ 1 ] + color[ 2 ] + color[ 2 ] + color[ 3 ] + color[ 3 ], 16 ) );
+
+			return this;
+
+		}
+
+		// red
+
+		if ( /^(\w+)$/i.test( style ) ) {
+
+			this.setHex( THREE.ColorKeywords[ style ] );
+
+			return this;
+
+		}
+
+
+	},
+
+	copy: function ( color ) {
+
+		this.r = color.r;
+		this.g = color.g;
+		this.b = color.b;
+
+		return this;
+
+	},
+
+	copyGammaToLinear: function ( color ) {
+
+		this.r = color.r * color.r;
+		this.g = color.g * color.g;
+		this.b = color.b * color.b;
+
+		return this;
+
+	},
+
+	copyLinearToGamma: function ( color ) {
+
+		this.r = Math.sqrt( color.r );
+		this.g = Math.sqrt( color.g );
+		this.b = Math.sqrt( color.b );
+
+		return this;
+
+	},
+
+	convertGammaToLinear: function () {
+
+		var r = this.r, g = this.g, b = this.b;
+
+		this.r = r * r;
+		this.g = g * g;
+		this.b = b * b;
+
+		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 ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
+
+	},
+
+	getHexString: function () {
+
+		return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+
+	},
+
+	getHSL: function () {
+
+		var hsl = { h: 0, s: 0, l: 0 };
+
+		return function () {
+
+			// h,s,l ranges are in 0.0 - 1.0
+
+			var r = this.r, g = this.g, b = this.b;
+
+			var max = Math.max( r, g, b );
+			var min = Math.min( r, g, b );
+
+			var hue, saturation;
+			var lightness = ( min + max ) / 2.0;
+
+			if ( min === max ) {
+
+				hue = 0;
+				saturation = 0;
+
+			} else {
+
+				var delta = max - min;
+
+				saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+
+				switch ( max ) {
+
+					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;
+
+				}
+
+				hue /= 6;
+
+			}
+
+			hsl.h = hue;
+			hsl.s = saturation;
+			hsl.l = lightness;
+
+			return hsl;
+
+		};
+
+	}(),
+
+	getStyle: function () {
+
+		return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+
+	},
+
+	offsetHSL: function ( h, s, l ) {
+
+		var hsl = this.getHSL();
+
+		hsl.h += h; hsl.s += s; hsl.l += l;
+
+		this.setHSL( hsl.h, hsl.s, hsl.l );
+
+		return this;
+
+	},
+
+	add: function ( color ) {
+
+		this.r += color.r;
+		this.g += color.g;
+		this.b += color.b;
+
+		return this;
+
+	},
+
+	addColors: function ( color1, color2 ) {
+
+		this.r = color1.r + color2.r;
+		this.g = color1.g + color2.g;
+		this.b = color1.b + color2.b;
+
+		return this;
+
+	},
+
+	addScalar: function ( s ) {
+
+		this.r += s;
+		this.g += s;
+		this.b += s;
+
+		return this;
+
+	},
+
+	multiply: function ( color ) {
+
+		this.r *= color.r;
+		this.g *= color.g;
+		this.b *= color.b;
+
+		return this;
+
+	},
+
+	multiplyScalar: function ( s ) {
+
+		this.r *= s;
+		this.g *= s;
+		this.b *= s;
+
+		return this;
+
+	},
+
+	lerp: function ( color, alpha ) {
+
+		this.r += ( color.r - this.r ) * alpha;
+		this.g += ( color.g - this.g ) * alpha;
+		this.b += ( color.b - this.b ) * alpha;
+
+		return this;
+
+	},
+
+	clone: function () {
+
+		return new THREE.Color().setRGB( this.r, this.g, this.b );
+
+	}
+
+} );
+
+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 };
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Quaternion = function( x, y, z, w ) {
+
+	this.x = x || 0;
+	this.y = y || 0;
+	this.z = z || 0;
+	this.w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.extend( THREE.Quaternion.prototype, {
+
+	set: function ( x, y, z, w ) {
+
+		this.x = x;
+		this.y = y;
+		this.z = z;
+		this.w = w;
+
+		return this;
+
+	},
+
+	copy: function ( q ) {
+
+		this.x = q.x;
+		this.y = q.y;
+		this.z = q.z;
+		this.w = q.w;
+
+		return this;
+
+	},
+
+	setFromEuler: function ( v, order ) {
+
+		// http://www.mathworks.com/matlabcentral/fileexchange/
+		// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+		//	content/SpinCalc.m
+
+		var c1 = Math.cos( v.x / 2 );
+		var c2 = Math.cos( v.y / 2 );
+		var c3 = Math.cos( v.z / 2 );
+		var s1 = Math.sin( v.x / 2 );
+		var s2 = Math.sin( v.y / 2 );
+		var s3 = Math.sin( v.z / 2 );
+
+		if ( order === undefined || order === 'XYZ' ) {
+
+			this.x = s1 * c2 * c3 + c1 * s2 * s3;
+			this.y = c1 * s2 * c3 - s1 * c2 * s3;
+			this.z = c1 * c2 * s3 + s1 * s2 * c3;
+			this.w = c1 * c2 * c3 - s1 * s2 * s3;
+
+		} else if ( order === 'YXZ' ) {
+
+			this.x = s1 * c2 * c3 + c1 * s2 * s3;
+			this.y = c1 * s2 * c3 - s1 * c2 * s3;
+			this.z = c1 * c2 * s3 - s1 * s2 * c3;
+			this.w = c1 * c2 * c3 + s1 * s2 * s3;
+
+		} else if ( order === 'ZXY' ) {
+
+			this.x = s1 * c2 * c3 - c1 * s2 * s3;
+			this.y = c1 * s2 * c3 + s1 * c2 * s3;
+			this.z = c1 * c2 * s3 + s1 * s2 * c3;
+			this.w = c1 * c2 * c3 - s1 * s2 * s3;
+
+		} else if ( order === 'ZYX' ) {
+
+			this.x = s1 * c2 * c3 - c1 * s2 * s3;
+			this.y = c1 * s2 * c3 + s1 * c2 * s3;
+			this.z = c1 * c2 * s3 - s1 * s2 * c3;
+			this.w = c1 * c2 * c3 + s1 * s2 * s3;
+
+		} else if ( order === 'YZX' ) {
+
+			this.x = s1 * c2 * c3 + c1 * s2 * s3;
+			this.y = c1 * s2 * c3 + s1 * c2 * s3;
+			this.z = c1 * c2 * s3 - s1 * s2 * c3;
+			this.w = c1 * c2 * c3 - s1 * s2 * s3;
+
+		} else if ( order === 'XZY' ) {
+
+			this.x = s1 * c2 * c3 - c1 * s2 * s3;
+			this.y = c1 * s2 * c3 - s1 * c2 * s3;
+			this.z = c1 * c2 * s3 + s1 * s2 * c3;
+			this.w = c1 * c2 * c3 + s1 * s2 * s3;
+
+		}
+
+		return this;
+
+	},
+
+	setFromAxisAngle: function ( axis, angle ) {
+
+		// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+		// axis have to be normalized
+
+		var halfAngle = angle / 2,
+			s = Math.sin( halfAngle );
+
+		this.x = axis.x * s;
+		this.y = axis.y * s;
+		this.z = axis.z * s;
+		this.w = Math.cos( halfAngle );
+
+		return this;
+
+	},
+
+	setFromRotationMatrix: function ( m ) {
+
+		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+		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],
+
+			trace = m11 + m22 + m33,
+			s;
+
+		if ( trace > 0 ) {
+
+			s = 0.5 / Math.sqrt( trace + 1.0 );
+
+			this.w = 0.25 / s;
+			this.x = ( m32 - m23 ) * s;
+			this.y = ( m13 - m31 ) * s;
+			this.z = ( m21 - m12 ) * s;
+
+		} else if ( m11 > m22 && m11 > m33 ) {
+
+			s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+			this.w = (m32 - m23 ) / s;
+			this.x = 0.25 * 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.y = 0.25 * s;
+			this.z = (m23 + m32 ) / s;
+
+		} else {
+
+			s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+			this.w = ( m21 - m12 ) / s;
+			this.x = ( m13 + m31 ) / s;
+			this.y = ( m23 + m32 ) / s;
+			this.z = 0.25 * s;
+
+		}
+
+		return this;
+
+	},
+
+	inverse: function () {
+
+		this.conjugate().normalize();
+
+		return this;
+
+	},
+
+	conjugate: function () {
+
+		this.x *= -1;
+		this.y *= -1;
+		this.z *= -1;
+
+		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 l = this.length();
+
+		if ( l === 0 ) {
+
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+			this.w = 1;
+
+		} else {
+
+			l = 1 / l;
+
+			this.x = this.x * l;
+			this.y = this.y * l;
+			this.z = this.z * l;
+			this.w = this.w * l;
+
+		}
+
+		return this;
+
+	},
+
+	multiply: function ( q, p ) {
+
+		if ( p !== undefined ) {
+
+			console.warn( 'DEPRECATED: Quaternion\'s .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+			return this.multiplyQuaternions( q, p );
+
+		}
+
+		return this.multiplyQuaternions( this, q );
+
+	},
+
+	multiplyQuaternions: function ( a, b ) {
+
+		// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+		var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w;
+		var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
+
+		this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+		this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+		this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+		this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+		return this;
+
+	},
+
+	multiplyVector3: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Quaternion\'s .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+		return vector.applyQuaternion( this );
+
+	},
+
+	slerp: function ( qb, t ) {
+
+		var x = this.x, y = this.y, z = this.z, w = this.w;
+
+		// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+		var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
+
+		if ( cosHalfTheta < 0 ) {
+
+			this.w = -qb.w;
+			this.x = -qb.x;
+			this.y = -qb.y;
+			this.z = -qb.z;
+
+			cosHalfTheta = -cosHalfTheta;
+
+		} else {
+
+			this.copy( qb );
+
+		}
+
+		if ( cosHalfTheta >= 1.0 ) {
+
+			this.w = w;
+			this.x = x;
+			this.y = y;
+			this.z = z;
+
+			return this;
+
+		}
+
+		var halfTheta = Math.acos( cosHalfTheta );
+		var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+		if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+			this.w = 0.5 * ( w + this.w );
+			this.x = 0.5 * ( x + this.x );
+			this.y = 0.5 * ( y + this.y );
+			this.z = 0.5 * ( z + this.z );
+
+			return this;
+
+		}
+
+		var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+		ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+		this.w = ( w * ratioA + this.w * ratioB );
+		this.x = ( x * ratioA + this.x * ratioB );
+		this.y = ( y * ratioA + this.y * ratioB );
+		this.z = ( z * ratioA + this.z * ratioB );
+
+		return this;
+
+	},
+
+	equals: function ( v ) {
+
+		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Quaternion( this.x, this.y, this.z, this.w );
+
+	}
+
+} );
+
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
+
+	return qm.copy( qa ).slerp( qb, t );
+
+}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.Vector2 = function ( x, y ) {
+
+	this.x = x || 0;
+	this.y = y || 0;
+
+};
+
+THREE.extend( THREE.Vector2.prototype, {
+
+	set: function ( x, y ) {
+
+		this.x = x;
+		this.y = y;
+
+		return this;
+
+	},
+
+	setX: function ( x ) {
+
+		this.x = x;
+
+		return this;
+
+	},
+
+	setY: function ( y ) {
+
+		this.y = y;
+
+		return this;
+
+	},
+
+
+	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 );
+
+		}
+
+	},
+
+	getComponent: function ( index ) {
+
+		switch ( index ) {
+
+			case 0: return this.x;
+			case 1: return this.y;
+			default: throw new Error( "index is out of range: " + index );
+
+		}
+
+	},
+
+	copy: function ( v ) {
+
+		this.x = v.x;
+		this.y = v.y;
+
+		return this;
+
+	},
+
+	add: function ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector2\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			return this.addVectors( v, w );
+
+		}
+
+		this.x += v.x;
+		this.y += v.y;
+
+		return this;
+
+	},
+
+	addVectors: function ( a, b ) {
+
+		this.x = a.x + b.x;
+		this.y = a.y + b.y;
+
+		return this;
+
+	},
+
+	addScalar: function ( s ) {
+
+		this.x += s;
+		this.y += s;
+
+		return this;
+
+	},
+
+	sub: function ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector2\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			return this.subVectors( v, w );
+
+		}
+
+		this.x -= v.x;
+		this.y -= v.y;
+
+		return this;
+
+	},
+
+	subVectors: function ( a, b ) {
+
+		this.x = a.x - b.x;
+		this.y = a.y - b.y;
+
+		return this;
+
+	},
+
+	multiplyScalar: function ( s ) {
+
+		this.x *= s;
+		this.y *= s;
+
+		return this;
+
+	},
+
+	divideScalar: function ( s ) {
+
+		if ( s !== 0 ) {
+
+			this.x /= s;
+			this.y /= s;
+
+		} else {
+
+			this.set( 0, 0 );
+
+		}
+
+		return this;
+
+	},
+
+	min: function ( v ) {
+
+		if ( this.x > v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y > v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		return this;
+
+	},
+
+	max: function ( v ) {
+
+		if ( this.x < v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y < v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		return this;
+
+	},
+
+	clamp: function ( min, max ) {
+
+		// This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+		if ( this.x < min.x ) {
+
+			this.x = min.x;
+
+		} else if ( this.x > max.x ) {
+
+			this.x = max.x;
+
+		}
+
+		if ( this.y < min.y ) {
+
+			this.y = min.y;
+
+		} else if ( this.y > max.y ) {
+
+			this.y = max.y;
+
+		}
+
+		return this;
+
+	},
+
+	negate: function() {
+
+		return this.multiplyScalar( - 1 );
+
+	},
+
+	dot: function ( v ) {
+
+		return this.x * v.x + this.y * v.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 ( v ) {
+
+		return Math.sqrt( this.distanceToSquared( v ) );
+
+	},
+
+	distanceToSquared: function ( v ) {
+
+		var dx = this.x - v.x, dy = this.y - v.y;
+		return dx * dx + dy * dy;
+
+	},
+
+	setLength: function ( l ) {
+
+		var oldLength = this.length();
+
+		if ( oldLength !== 0 && l !== oldLength ) {
+
+			this.multiplyScalar( l / oldLength );
+		}
+
+		return this;
+
+	},
+
+	lerp: function ( v, alpha ) {
+
+		this.x += ( v.x - this.x ) * alpha;
+		this.y += ( v.y - this.y ) * alpha;
+
+		return this;
+
+	},
+
+	equals: function( v ) {
+
+		return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y ];
+		
+	},
+
+	clone: function () {
+
+		return new THREE.Vector2( this.x, this.y );
+
+	}
+
+} );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author *kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector3 = function ( x, y, z ) {
+
+	this.x = x || 0;
+	this.y = y || 0;
+	this.z = z || 0;
+
+};
+
+THREE.extend( THREE.Vector3.prototype, {
+
+	set: function ( x, y, z ) {
+
+		this.x = x;
+		this.y = y;
+		this.z = z;
+
+		return this;
+
+	},
+
+	setX: function ( x ) {
+
+		this.x = x;
+
+		return this;
+
+	},
+
+	setY: function ( y ) {
+
+		this.y = y;
+
+		return this;
+
+	},
+
+	setZ: function ( z ) {
+
+		this.z = z;
+
+		return this;
+
+	},
+
+	setComponent: function ( index, value ) {
+
+		switch ( index ) {
+
+			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 );
+
+		}
+
+	},
+
+	getComponent: function ( index ) {
+
+		switch ( index ) {
+
+			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 );
+
+		}
+
+	},
+
+	copy: function ( v ) {
+
+		this.x = v.x;
+		this.y = v.y;
+		this.z = v.z;
+
+		return this;
+
+	},
+
+	add: function ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector3\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			return this.addVectors( v, w );
+
+		}
+
+		this.x += v.x;
+		this.y += v.y;
+		this.z += v.z;
+
+		return this;
+
+	},
+
+	addScalar: function ( s ) {
+
+		this.x += s;
+		this.y += s;
+		this.z += s;
+
+		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 ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector3\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			return this.subVectors( v, w );
+
+		}
+
+		this.x -= v.x;
+		this.y -= v.y;
+		this.z -= v.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 ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector3\'s .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+			return this.multiplyVectors( v, w );
+
+		}
+
+		this.x *= v.x;
+		this.y *= v.y;
+		this.z *= v.z;
+
+		return this;
+
+	},
+
+	multiplyScalar: function ( s ) {
+
+		this.x *= s;
+		this.y *= s;
+		this.z *= s;
+
+		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 ( m ) {
+
+		var x = this.x;
+		var y = this.y;
+		var z = this.z;
+
+		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;
+
+		return this;
+
+	},
+
+	applyMatrix4: function ( m ) {
+
+		// input: THREE.Matrix4 affine matrix
+
+		var x = this.x, y = this.y, z = this.z;
+
+		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];
+
+		return this;
+
+	},
+
+	applyProjection: function ( m ) {
+
+		// input: THREE.Matrix4 projection matrix
+
+		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
+
+		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;
+
+	},
+
+	applyQuaternion: function ( q ) {
+
+		var x = this.x;
+		var y = this.y;
+		var z = this.z;
+
+		var qx = q.x;
+		var qy = q.y;
+		var qz = q.z;
+		var qw = q.w;
+
+		// calculate quat * vector
+
+		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;
+
+		// 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;
+
+		return this;
+
+	},
+
+	applyEuler: function () {
+
+		var q1 = new THREE.Quaternion();
+
+		return function ( v, eulerOrder ) {
+
+			var quaternion = q1.setFromEuler( v, eulerOrder );
+
+			this.applyQuaternion( quaternion );
+
+			return this;
+
+		};
+
+	}(),
+
+	applyAxisAngle: function () {
+
+		var q1 = new THREE.Quaternion();
+
+		return function ( axis, angle ) {
+
+			var quaternion = q1.setFromAxisAngle( axis, angle );
+
+			this.applyQuaternion( quaternion );
+
+			return this;
+
+		};
+
+	}(),
+
+	transformDirection: function ( m ) {
+
+		// input: THREE.Matrix4 affine matrix
+		// vector interpreted as a direction
+
+		var x = this.x, y = this.y, z = this.z;
+
+		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.normalize();
+
+		return this;
+
+	},
+
+	divide: function ( v ) {
+
+		this.x /= v.x;
+		this.y /= v.y;
+		this.z /= v.z;
+
+		return this;
+
+	},
+
+	divideScalar: function ( s ) {
+
+		if ( s !== 0 ) {
+
+			this.x /= s;
+			this.y /= s;
+			this.z /= s;
+
+		} else {
+
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+
+		}
+
+		return this;
+
+	},
+
+	min: function ( v ) {
+
+		if ( this.x > v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y > v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		if ( this.z > v.z ) {
+
+			this.z = v.z;
+
+		}
+
+		return this;
+
+	},
+
+	max: function ( v ) {
+
+		if ( this.x < v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y < v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		if ( this.z < v.z ) {
+
+			this.z = v.z;
+
+		}
+
+		return this;
+
+	},
+
+	clamp: function ( min, max ) {
+
+		// This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+		if ( this.x < min.x ) {
+
+			this.x = min.x;
+
+		} else if ( this.x > max.x ) {
+
+			this.x = max.x;
+
+		}
+
+		if ( this.y < min.y ) {
+
+			this.y = min.y;
+
+		} else if ( this.y > max.y ) {
+
+			this.y = max.y;
+
+		}
+
+		if ( this.z < min.z ) {
+
+			this.z = min.z;
+
+		} else if ( this.z > max.z ) {
+
+			this.z = max.z;
+
+		}
+
+		return this;
+
+	},
+
+	negate: function () {
+
+		return this.multiplyScalar( - 1 );
+
+	},
+
+	dot: function ( v ) {
+
+		return this.x * v.x + this.y * v.y + this.z * v.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 ( l ) {
+
+		var oldLength = this.length();
+
+		if ( oldLength !== 0 && l !== oldLength  ) {
+
+			this.multiplyScalar( l / oldLength );
+		}
+
+		return this;
+
+	},
+
+	lerp: function ( v, alpha ) {
+
+		this.x += ( v.x - this.x ) * alpha;
+		this.y += ( v.y - this.y ) * alpha;
+		this.z += ( v.z - this.z ) * alpha;
+
+		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.' );
+			return this.crossVectors( v, w );
+
+		}
+
+		var x = this.x, y = this.y, z = this.z;
+
+		this.x = y * v.z - z * v.y;
+		this.y = z * v.x - x * v.z;
+		this.z = x * v.y - y * v.x;
+
+		return this;
+
+	},
+
+	crossVectors: function ( a, b ) {
+
+		this.x = a.y * b.z - a.z * b.y;
+		this.y = a.z * b.x - a.x * b.z;
+		this.z = a.x * b.y - a.y * b.x;
+
+		return this;
+
+	},
+
+	projectOnVector: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function( vector ) {
+
+			v1.copy( vector ).normalize();
+			var d = this.dot( v1 );
+			return this.copy( v1 ).multiplyScalar( d );
+
+		};
+
+	}(),
+
+	projectOnPlane: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function( planeNormal ) {
+
+			v1.copy( this ).projectOnVector( planeNormal );
+
+			return this.sub( v1 );
+
+		}
+
+	}(),
+
+	reflect: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( vector ) {
+
+		    v1.copy( this ).projectOnVector( vector ).multiplyScalar( 2 );
+
+		    return this.subVectors( v1, this );
+
+		}
+
+	}(),
+
+	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 ) );
+
+	},
+
+	distanceTo: function ( v ) {
+
+		return Math.sqrt( this.distanceToSquared( v ) );
+
+	},
+
+	distanceToSquared: function ( v ) {
+
+		var dx = this.x - v.x;
+		var dy = this.y - v.y;
+		var dz = this.z - v.z;
+
+		return dx * dx + dy * dy + dz * dz;
+
+	},
+
+	getPositionFromMatrix: function ( m ) {
+
+		this.x = m.elements[12];
+		this.y = m.elements[13];
+		this.z = m.elements[14];
+
+		return this;
+
+	},
+
+	setEulerFromRotationMatrix: 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 ) {
+
+			return Math.min( Math.max( x, -1 ), 1 );
+
+		}
+
+		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];
+
+		if ( order === undefined || order === 'XYZ' ) {
+
+			this.y = Math.asin( clamp( m13 ) );
+
+			if ( Math.abs( m13 ) < 0.99999 ) {
+
+				this.x = Math.atan2( - m23, m33 );
+				this.z = Math.atan2( - m12, m11 );
+
+			} else {
+
+				this.x = Math.atan2( m32, m22 );
+				this.z = 0;
+
+			}
+
+		} else if ( order === 'YXZ' ) {
+
+			this.x = Math.asin( - clamp( m23 ) );
+
+			if ( Math.abs( m23 ) < 0.99999 ) {
+
+				this.y = Math.atan2( m13, m33 );
+				this.z = Math.atan2( m21, m22 );
+
+			} else {
+
+				this.y = Math.atan2( - m31, m11 );
+				this.z = 0;
+
+			}
+
+		} else if ( order === 'ZXY' ) {
+
+			this.x = Math.asin( clamp( m32 ) );
+
+			if ( Math.abs( m32 ) < 0.99999 ) {
+
+				this.y = Math.atan2( - m31, m33 );
+				this.z = Math.atan2( - m12, m22 );
+
+			} else {
+
+				this.y = 0;
+				this.z = Math.atan2( m21, m11 );
+
+			}
+
+		} else if ( order === 'ZYX' ) {
+
+			this.y = Math.asin( - clamp( m31 ) );
+
+			if ( Math.abs( m31 ) < 0.99999 ) {
+
+				this.x = Math.atan2( m32, m33 );
+				this.z = Math.atan2( m21, m11 );
+
+			} else {
+
+				this.x = 0;
+				this.z = Math.atan2( - m12, m22 );
+
+			}
+
+		} else if ( order === 'YZX' ) {
+
+			this.z = Math.asin( clamp( m21 ) );
+
+			if ( Math.abs( m21 ) < 0.99999 ) {
+
+				this.x = Math.atan2( - m23, m22 );
+				this.y = Math.atan2( - m31, m11 );
+
+			} else {
+
+				this.x = 0;
+				this.y = Math.atan2( m13, m33 );
+
+			}
+
+		} else if ( order === 'XZY' ) {
+
+			this.z = Math.asin( - clamp( m12 ) );
+
+			if ( Math.abs( m12 ) < 0.99999 ) {
+
+				this.x = Math.atan2( m32, m22 );
+				this.y = Math.atan2( m13, m11 );
+
+			} else {
+
+				this.x = Math.atan2( - m23, m33 );
+				this.y = 0;
+
+			}
+
+		}
+
+		return this;
+
+	},
+
+	setEulerFromQuaternion: function ( q, order ) {
+
+		// 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;
+
+		if ( order === undefined || 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' ) {
+
+			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 ) );
+
+		} 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 ) ) );
+
+		} 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 ) ) );
+
+		}
+
+		return this;
+
+	},
+
+	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;
+
+		return this;
+	},
+
+	equals: function ( v ) {
+
+		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y, this.z ];
+		
+	},
+
+	clone: function () {
+
+		return new THREE.Vector3( this.x, this.y, this.z );
+
+	}
+
+} );
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector4 = function ( x, y, z, w ) {
+
+	this.x = x || 0;
+	this.y = y || 0;
+	this.z = z || 0;
+	this.w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.extend( THREE.Vector4.prototype, {
+
+	set: function ( x, y, z, w ) {
+
+		this.x = x;
+		this.y = y;
+		this.z = z;
+		this.w = w;
+
+		return this;
+
+	},
+
+	setX: function ( x ) {
+
+		this.x = x;
+
+		return this;
+
+	},
+
+	setY: function ( y ) {
+
+		this.y = y;
+
+		return this;
+
+	},
+
+	setZ: function ( z ) {
+
+		this.z = z;
+
+		return this;
+
+	},
+
+	setW: function ( w ) {
+
+		this.w = w;
+
+		return this;
+
+	},
+
+	setComponent: function ( index, value ) {
+
+		switch ( index ) {
+
+			case 0: this.x = value; break;
+			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 );
+
+		}
+
+	},
+
+	getComponent: function ( index ) {
+
+		switch ( index ) {
+
+			case 0: return this.x;
+			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 );
+
+		}
+
+	},
+
+	copy: function ( v ) {
+
+		this.x = v.x;
+		this.y = v.y;
+		this.z = v.z;
+		this.w = ( v.w !== undefined ) ? v.w : 1;
+
+		return this;
+
+	},
+
+	add: function ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector4\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+			return this.addVectors( v, w );
+
+		}
+
+		this.x += v.x;
+		this.y += v.y;
+		this.z += v.z;
+		this.w += v.w;
+
+		return this;
+
+	},
+
+	addScalar: function ( s ) {
+
+		this.x += s;
+		this.y += s;
+		this.z += s;
+		this.w += s;
+
+		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 ( v, w ) {
+
+		if ( w !== undefined ) {
+
+			console.warn( 'DEPRECATED: Vector4\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+			return this.subVectors( v, w );
+
+		}
+
+		this.x -= v.x;
+		this.y -= v.y;
+		this.z -= v.z;
+		this.w -= v.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 ( s ) {
+
+		this.x *= s;
+		this.y *= s;
+		this.z *= s;
+		this.w *= s;
+
+		return this;
+
+	},
+
+	applyMatrix4: function ( m ) {
+
+		var x = this.x;
+		var y = this.y;
+		var z = this.z;
+		var w = this.w;
+
+		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;
+
+		return this;
+
+	},
+
+	divideScalar: function ( s ) {
+
+		if ( s !== 0 ) {
+
+			this.x /= s;
+			this.y /= s;
+			this.z /= s;
+			this.w /= s;
+
+		} else {
+
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+			this.w = 1;
+
+		}
+
+		return this;
+
+	},
+
+	setAxisAngleFromQuaternion: function ( q ) {
+
+		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+
+		// q is assumed to be normalized
+
+		this.w = 2 * Math.acos( q.w );
+
+		var s = Math.sqrt( 1 - q.w * q.w );
+
+		if ( s < 0.0001 ) {
+
+			 this.x = 1;
+			 this.y = 0;
+			 this.z = 0;
+
+		} else {
+
+			 this.x = q.x / s;
+			 this.y = q.y / s;
+			 this.z = q.z / s;
+
+		}
+
+		return this;
+
+	},
+
+	setAxisAngleFromRotationMatrix: function ( m ) {
+
+		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+
+		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+		var angle, x, y, z,		// variables for result
+			epsilon = 0.01,		// margin to allow for rounding errors
+			epsilon2 = 0.1,		// margin to distinguish between 0 and 180 degrees
+
+			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];
+
+		if ( ( Math.abs( m12 - m21 ) < 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 ) ) {
+
+				// this singularity is identity matrix so angle = 0
+
+				this.set( 1, 0, 0, 0 );
+
+				return this; // zero angle, arbitrary axis
+
+			}
+
+			// otherwise this singularity is angle = 180
+
+			angle = Math.PI;
+
+			var xx = ( m11 + 1 ) / 2;
+			var yy = ( m22 + 1 ) / 2;
+			var zz = ( m33 + 1 ) / 2;
+			var xy = ( m12 + m21 ) / 4;
+			var xz = ( m13 + m31 ) / 4;
+			var yz = ( m23 + m32 ) / 4;
+
+			if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term
+
+				if ( xx < epsilon ) {
+
+					x = 0;
+					y = 0.707106781;
+					z = 0.707106781;
+
+				} else {
+
+					x = Math.sqrt( xx );
+					y = xy / x;
+					z = xz / x;
+
+				}
+
+			} else if ( yy > zz ) { // m22 is the largest diagonal term
+
+				if ( yy < epsilon ) {
+
+					x = 0.707106781;
+					y = 0;
+					z = 0.707106781;
+
+				} else {
+
+					y = Math.sqrt( yy );
+					x = xy / y;
+					z = yz / y;
+
+				}
+
+			} else { // m33 is the largest diagonal term so base result on this
+
+				if ( zz < epsilon ) {
+
+					x = 0.707106781;
+					y = 0.707106781;
+					z = 0;
+
+				} else {
+
+					z = Math.sqrt( zz );
+					x = xz / z;
+					y = yz / z;
+
+				}
+
+			}
+
+			this.set( x, y, z, angle );
+
+			return this; // return 180 deg rotation
+
+		}
+
+		// 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
+
+		if ( Math.abs( s ) < 0.001 ) s = 1;
+
+		// prevent divide by zero, should not happen if matrix is orthogonal and should be
+		// caught by singularity test above, but I've left it in just in case
+
+		this.x = ( m32 - m23 ) / s;
+		this.y = ( m13 - m31 ) / s;
+		this.z = ( m21 - m12 ) / s;
+		this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+		return this;
+
+	},
+
+	min: function ( v ) {
+
+		if ( this.x > v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y > v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		if ( this.z > v.z ) {
+
+			this.z = v.z;
+
+		}
+
+		if ( this.w > v.w ) {
+
+			this.w = v.w;
+
+		}
+
+		return this;
+
+	},
+
+	max: function ( v ) {
+
+		if ( this.x < v.x ) {
+
+			this.x = v.x;
+
+		}
+
+		if ( this.y < v.y ) {
+
+			this.y = v.y;
+
+		}
+
+		if ( this.z < v.z ) {
+
+			this.z = v.z;
+
+		}
+
+		if ( this.w < v.w ) {
+
+			this.w = v.w;
+
+		}
+
+		return this;
+
+	},
+
+	clamp: function ( min, max ) {
+
+		// This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+		if ( this.x < min.x ) {
+
+			this.x = min.x;
+
+		} else if ( this.x > max.x ) {
+
+			this.x = max.x;
+
+		}
+
+		if ( this.y < min.y ) {
+
+			this.y = min.y;
+
+		} else if ( this.y > max.y ) {
+
+			this.y = max.y;
+
+		}
+
+		if ( this.z < min.z ) {
+
+			this.z = min.z;
+
+		} else if ( this.z > max.z ) {
+
+			this.z = max.z;
+
+		}
+
+		if ( this.w < min.w ) {
+
+			this.w = min.w;
+
+		} else if ( this.w > max.w ) {
+
+			this.w = max.w;
+
+		}
+
+		return this;
+
+	},
+
+	negate: function() {
+
+		return this.multiplyScalar( -1 );
+
+	},
+
+	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;
+
+	},
+
+	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 ( l ) {
+
+		var oldLength = this.length();
+
+		if ( oldLength !== 0 && l !== oldLength ) {
+
+			this.multiplyScalar( l / oldLength );
+		}
+
+		return this;
+
+	},
+
+	lerp: function ( v, alpha ) {
+
+		this.x += ( v.x - this.x ) * alpha;
+		this.y += ( v.y - this.y ) * alpha;
+		this.z += ( v.z - this.z ) * alpha;
+		this.w += ( v.w - this.w ) * alpha;
+
+		return this;
+
+	},
+
+	equals: function ( v ) {
+
+		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+	},
+
+	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 );
+
+	}
+
+} );
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+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 );
+
+};
+
+THREE.extend( THREE.Box2.prototype, {
+
+	set: function ( min, max ) {
+
+		this.min.copy( min );
+		this.max.copy( max );
+
+		return this;
+
+	},
+
+	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;
+
+				}
+
+			}
+
+		} else {
+
+			this.makeEmpty();
+
+		}
+
+		return this;
+
+	},
+
+	setFromCenterAndSize: function() {
+
+		var v1 = new THREE.Vector2();
+
+		return function ( center, size ) {
+
+			var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+			this.min.copy( center ).sub( halfSize );
+			this.max.copy( center ).add( halfSize );
+
+			return this;
+
+		};
+
+	}(),
+
+	copy: function ( box ) {
+
+		this.min.copy( box.min );
+		this.max.copy( box.max );
+
+		return this;
+
+	},
+
+	makeEmpty: function () {
+
+		this.min.x = this.min.y = Infinity;
+		this.max.x = this.max.y = -Infinity;
+
+		return this;
+
+	},
+
+	empty: function () {
+
+		// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+		return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
+
+	},
+
+	center: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector2();
+		return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+	},
+
+	size: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector2();
+		return result.subVectors( this.max, this.min );
+
+	},
+
+	expandByPoint: function ( point ) {
+
+		this.min.min( point );
+		this.max.max( point );
+
+		return this;
+	},
+
+	expandByVector: function ( vector ) {
+
+		this.min.sub( vector );
+		this.max.add( vector );
+
+		return this;
+	},
+
+	expandByScalar: function ( scalar ) {
+
+		this.min.addScalar( -scalar );
+		this.max.addScalar( scalar );
+
+		return this;
+	},
+
+	containsPoint: function ( point ) {
+
+		if ( point.x < this.min.x || point.x > this.max.x ||
+		     point.y < this.min.y || point.y > this.max.y ) {
+
+			return false;
+
+		}
+
+		return true;
+
+	},
+
+	containsBox: function ( box ) {
+
+		if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+		     ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {
+
+			return true;
+
+		}
+
+		return false;
+
+	},
+
+	getParameter: function ( point ) {
+
+		// This can potentially have a divide by zero if the box
+		// has a size dimension of 0.
+
+		return new THREE.Vector2(
+			( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+			( point.y - this.min.y ) / ( this.max.y - this.min.y )
+		);
+
+	},
+
+	isIntersectionBox: function ( box ) {
+
+		// using 6 splitting planes to rule out intersections.
+
+		if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+		     box.max.y < this.min.y || box.min.y > this.max.y ) {
+
+			return false;
+
+		}
+
+		return true;
+
+	},
+
+	clampPoint: function ( point, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector2();
+		return result.copy( point ).clamp( this.min, this.max );
+
+	},
+
+	distanceToPoint: function() {
+
+		var v1 = new THREE.Vector2();
+
+		return function ( point ) {
+
+			var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+			return clampedPoint.sub( point ).length();
+
+		};
+
+	}(),
+
+	intersect: function ( box ) {
+
+		this.min.max( box.min );
+		this.max.min( box.max );
+
+		return this;
+
+	},
+
+	union: function ( box ) {
+
+		this.min.min( box.min );
+		this.max.max( box.max );
+
+		return this;
+
+	},
+
+	translate: function ( offset ) {
+
+		this.min.add( offset );
+		this.max.add( offset );
+
+		return this;
+
+	},
+
+	equals: function ( box ) {
+
+		return box.min.equals( this.min ) && box.max.equals( this.max );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Box2().copy( this );
+
+	}
+
+} );
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+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 );
+
+};
+
+THREE.extend( THREE.Box3.prototype, {
+
+	set: function ( min, max ) {
+
+		this.min.copy( min );
+		this.max.copy( max );
+
+		return this;
+
+	},
+
+	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;
+
+				}
+
+				if ( point.z < this.min.z ) {
+
+					this.min.z = point.z;
+
+				} else if ( point.z > this.max.z ) {
+
+					this.max.z = point.z;
+
+				}
+
+			}
+
+		} else {
+
+			this.makeEmpty();
+
+		}
+
+		return this;
+
+	},
+
+	setFromCenterAndSize: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( center, size ) {
+
+			var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+
+			this.min.copy( center ).sub( halfSize );
+			this.max.copy( center ).add( halfSize );
+
+			return this;
+
+		};
+
+	}(),
+
+	copy: function ( box ) {
+
+		this.min.copy( box.min );
+		this.max.copy( box.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 () {
+
+		// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+		return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
+
+	},
+
+	center: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+	},
+
+	size: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.subVectors( this.max, this.min );
+
+	},
+
+	expandByPoint: function ( point ) {
+
+		this.min.min( point );
+		this.max.max( point );
+
+		return this;
+
+	},
+
+	expandByVector: function ( vector ) {
+
+		this.min.sub( vector );
+		this.max.add( vector );
+
+		return this;
+
+	},
+
+	expandByScalar: function ( scalar ) {
+
+		this.min.addScalar( -scalar );
+		this.max.addScalar( scalar );
+
+		return this;
+
+	},
+
+	containsPoint: function ( point ) {
+
+		if ( point.x < this.min.x || point.x > this.max.x ||
+		     point.y < this.min.y || point.y > this.max.y ||
+		     point.z < this.min.z || point.z > this.max.z ) {
+
+			return false;
+
+		}
+
+		return true;
+
+	},
+
+	containsBox: function ( box ) {
+
+		if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+			 ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
+			 ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {
+
+			return true;
+
+		}
+
+		return false;
+
+	},
+
+	getParameter: function ( point ) {
+
+		// This can potentially have a divide by zero if the box
+		// has a size dimension of 0.
+
+		return new THREE.Vector3(
+			( 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 )
+		);
+
+	},
+
+	isIntersectionBox: function ( box ) {
+
+		// using 6 splitting planes to rule out intersections.
+
+		if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+		     box.max.y < this.min.y || box.min.y > this.max.y ||
+		     box.max.z < this.min.z || box.min.z > this.max.z ) {
+
+			return false;
+
+		}
+
+		return true;
+
+	},
+
+	clampPoint: function ( point, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.copy( point ).clamp( this.min, this.max );
+
+	},
+
+	distanceToPoint: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( point ) {
+
+			var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+			return clampedPoint.sub( point ).length();
+
+		};
+
+	}(),
+
+	getBoundingSphere: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( optionalTarget ) {
+
+			var result = optionalTarget || new THREE.Sphere();
+
+			result.center = this.center();
+			result.radius = this.size( v1 ).length() * 0.5;
+
+			return result;
+
+		};
+
+	}(),
+
+	intersect: function ( box ) {
+
+		this.min.max( box.min );
+		this.max.min( box.max );
+
+		return this;
+
+	},
+
+	union: function ( box ) {
+
+		this.min.min( box.min );
+		this.max.max( box.max );
+
+		return this;
+
+	},
+
+	applyMatrix4: function() {
+
+		var points = [
+			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 ( 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
+
+			this.makeEmpty();
+			this.setFromPoints( points );
+
+			return this;
+
+		};
+
+	}(),
+
+	translate: function ( offset ) {
+
+		this.min.add( offset );
+		this.max.add( offset );
+
+		return this;
+
+	},
+
+	equals: function ( box ) {
+
+		return box.min.equals( this.min ) && box.max.equals( this.max );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Box3().copy( this );
+
+	}
+
+} );
+/**
+ * @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 ) {
+
+	this.elements = new Float32Array(9);
+
+	this.set(
+
+		( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0,
+		n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0,
+		n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1
+
+	);
+};
+
+THREE.extend( THREE.Matrix3.prototype, {
+
+	set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+		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;
+
+		return this;
+
+	},
+
+	identity: function () {
+
+		this.set(
+
+			1, 0, 0,
+			0, 1, 0,
+			0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	copy: function ( m ) {
+
+		var me = m.elements;
+
+		this.set(
+
+			me[0], me[3], me[6],
+			me[1], me[4], me[7],
+			me[2], me[5], me[8]
+
+		);
+
+		return this;
+
+	},
+
+	multiplyVector3: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Matrix3\'s .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
+		return vector.applyMatrix3( this );
+
+	},
+
+	multiplyVector3Array: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( a ) {
+
+			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+
+				v1.x = a[ i ];
+				v1.y = a[ i + 1 ];
+				v1.z = a[ i + 2 ];
+
+				v1.applyMatrix3(this);
+
+				a[ i ]     = v1.x;
+				a[ i + 1 ] = v1.y;
+				a[ i + 2 ] = v1.z;
+
+			}
+
+			return a;
+
+		};
+
+	}(),
+
+	multiplyScalar: function ( s ) {
+
+		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;
+
+		return this;
+
+	},
+
+	determinant: function () {
+
+		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];
+
+		return a*e*i - a*f*h - b*d*i + b*f*g + c*d*h - c*e*g;
+
+	},
+
+	getInverse: function ( matrix, throwOnInvertible ) {
+
+		// input: THREE.Matrix4
+		// ( based on http://code.google.com/p/webgl-mjs/ )
+
+		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];
+
+		var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
+
+		// no inverse
+
+		if ( det === 0 ) {
+
+			var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
+
+			if ( throwOnInvertible || false ) {
+
+				throw new Error( msg ); 
+
+			} else {
+
+				console.warn( msg );
+
+			}
+
+			this.identity();
+
+			return this;
+
+		}
+
+		this.multiplyScalar( 1.0 / det );
+
+		return this;
+
+	},
+
+	transpose: function () {
+
+		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;
+
+		return this;
+
+	},
+
+	getNormalMatrix: function ( m ) {
+
+		// input: THREE.Matrix4
+
+		this.getInverse( m ).transpose();
+
+		return this;
+
+	},
+
+	transposeIntoArray: function ( r ) {
+
+		var m = this.elements;
+
+		r[ 0 ] = m[ 0 ];
+		r[ 1 ] = m[ 3 ];
+		r[ 2 ] = m[ 6 ];
+		r[ 3 ] = m[ 1 ];
+		r[ 4 ] = m[ 4 ];
+		r[ 5 ] = m[ 7 ];
+		r[ 6 ] = m[ 2 ];
+		r[ 7 ] = m[ 5 ];
+		r[ 8 ] = m[ 8 ];
+
+		return this;
+
+	},
+
+	clone: function () {
+
+		var te = this.elements;
+
+		return new THREE.Matrix3(
+
+			te[0], te[3], te[6],
+			te[1], te[4], te[7],
+			te[2], te[5], te[8]
+
+		);
+
+	}
+
+} );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author jordi_ros / http://plattsoft.com
+ * @author D1plo1d / http://github.com/D1plo1d
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://exocortex.com
+ */
+
+
+THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+	var te = 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.
+
+	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;
+
+};
+
+THREE.extend( THREE.Matrix4.prototype, {
+
+	set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+		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;
+
+		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 ( m ) {
+
+		var me = m.elements;
+
+		this.set(
+
+			me[0], me[4], me[8], me[12],
+			me[1], me[5], me[9], me[13],
+			me[2], me[6], me[10], me[14],
+			me[3], me[7], me[11], me[15]
+
+		);
+
+		return this;
+
+	},
+
+	setRotationFromEuler: function ( v, order ) {
+
+		var te = this.elements;
+
+		var x = v.x, y = v.y, z = v.z;
+		var a = Math.cos( x ), b = Math.sin( x );
+		var c = Math.cos( y ), d = Math.sin( y );
+		var e = Math.cos( z ), f = Math.sin( z );
+
+		if ( order === undefined || order === 'XYZ' ) {
+
+			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[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;
+
+		} else if ( 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[1] = a * f;
+			te[5] = a * e;
+			te[9] = - b;
+
+			te[2] = cf * b - de;
+			te[6] = df + ce * b;
+			te[10] = a * c;
+
+		} else if ( 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[1] = cf + de * b;
+			te[5] = a * e;
+			te[9] = df - ce * b;
+
+			te[2] = - a * d;
+			te[6] = b;
+			te[10] = a * c;
+
+		} else if ( 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[1] = c * f;
+			te[5] = bf * d + ae;
+			te[9] = af * d - be;
+
+			te[2] = - d;
+			te[6] = b * c;
+			te[10] = a * c;
+
+		} else if ( 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[1] = f;
+			te[5] = a * e;
+			te[9] = - b * e;
+
+			te[2] = - d * e;
+			te[6] = ad * f + bc;
+			te[10] = ac - bd * f;
+
+		} else if ( 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[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;
+
+		}
+
+		return this;
+
+	},
+
+	setRotationFromQuaternion: function ( q ) {
+
+		var te = this.elements;
+
+		var x = q.x, y = q.y, z = q.z, w = q.w;
+		var x2 = x + x, y2 = y + y, z2 = z + z;
+		var xx = x * x2, xy = x * y2, xz = x * z2;
+		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[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 );
+
+		return this;
+
+	},
+
+	lookAt: function() {
+
+		var x = new THREE.Vector3();
+		var y = new THREE.Vector3();
+		var z = new THREE.Vector3();
+
+		return function ( eye, target, up ) {
+
+			var te = this.elements;
+
+			z.subVectors( eye, target ).normalize();
+
+			if ( z.length() === 0 ) {
+
+				z.z = 1;
+
+			}
+
+			x.crossVectors( up, z ).normalize();
+
+			if ( x.length() === 0 ) {
+
+				z.x += 0.0001;
+				x.crossVectors( up, z ).normalize();
+
+			}
+
+			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;
+
+			return this;
+
+		};
+
+	}(),
+
+	multiply: function ( m, n ) {
+
+		if ( n !== undefined ) {
+
+			console.warn( 'DEPRECATED: Matrix4\'s .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+			return this.multiplyMatrices( m, n );
+
+		}
+
+		return this.multiplyMatrices( this, m );
+
+	},
+
+	multiplyMatrices: function ( a, b ) {
+
+		var ae = a.elements;
+		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 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[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[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;
+
+	},
+
+	multiplyToArray: function ( a, b, r ) {
+
+		var te = this.elements;
+
+		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];
+
+		return this;
+
+	},
+
+	multiplyScalar: function ( s ) {
+
+		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;
+
+		return this;
+
+	},
+
+	multiplyVector3: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .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.' );
+		return vector.applyMatrix4( this );
+
+	},
+
+	multiplyVector3Array: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( a ) {
+
+			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+
+				v1.x = a[ i ];
+				v1.y = a[ i + 1 ];
+				v1.z = a[ i + 2 ];
+
+				v1.applyProjection( this );
+
+				a[ i ]     = v1.x;
+				a[ i + 1 ] = v1.y;
+				a[ i + 2 ] = v1.z;
+
+			}
+
+			return a;
+
+		};
+
+	}(),
+
+	rotateAxis: function ( v ) {
+
+		var te = this.elements;
+		var vx = v.x, vy = v.y, vz = v.z;
+
+		v.x = vx * te[0] + vy * te[4] + vz * te[8];
+		v.y = vx * te[1] + vy * te[5] + vz * te[9];
+		v.z = vx * te[2] + vy * te[6] + vz * te[10];
+
+		v.normalize();
+
+		return v;
+
+	},
+
+	crossVector: function ( a ) {
+
+		var te = this.elements;
+		var v = new THREE.Vector4();
+
+		v.x = te[0] * a.x + te[4] * a.y + te[8] * a.z + te[12] * a.w;
+		v.y = te[1] * a.x + te[5] * a.y + te[9] * a.z + te[13] * a.w;
+		v.z = te[2] * a.x + te[6] * a.y + te[10] * a.z + te[14] * a.w;
+
+		v.w = ( a.w ) ? te[3] * a.x + te[7] * a.y + te[11] * a.z + te[15] * a.w : 1;
+
+		return v;
+
+	},
+
+	determinant: function () {
+
+		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];
+
+		//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
+			) +
+			n42 * (
+				+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
+			) +
+			n44 * (
+				-n13 * n22 * n31
+				-n11 * n23 * n32
+				+n11 * n22 * n33
+				+n13 * n21 * n32
+				-n12 * n21 * n33
+				+n12 * n23 * n31
+			)
+
+		);
+
+	},
+
+	transpose: function () {
+
+		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[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 ) {
+
+		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;
+
+	},
+
+	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];
+
+		flat[ offset + 4 ] = te[4];
+		flat[ offset + 5 ] = te[5];
+		flat[ offset + 6 ] = te[6];
+		flat[ offset + 7 ] = te[7];
+
+		flat[ offset + 8 ]  = te[8];
+		flat[ offset + 9 ]  = te[9];
+		flat[ offset + 10 ] = te[10];
+		flat[ offset + 11 ] = te[11];
+
+		flat[ offset + 12 ] = te[12];
+		flat[ offset + 13 ] = te[13];
+		flat[ offset + 14 ] = te[14];
+		flat[ offset + 15 ] = te[15];
+
+		return flat;
+
+	},
+
+	getPosition: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function () {
+
+			console.warn( 'DEPRECATED: Matrix4\'s .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.' );
+
+			var te = this.elements;
+			return v1.set( te[12], te[13], te[14] );
+
+		};
+
+	}(),
+
+	setPosition: function ( v ) {
+
+		var te = this.elements;
+
+		te[12] = v.x;
+		te[13] = v.y;
+		te[14] = v.z;
+
+		return this;
+
+	},
+
+	getInverse: function ( m, throwOnInvertible ) {
+
+		// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+		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 det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 4 ] + me[ 2 ] * te[ 8 ] + me[ 3 ] * te[ 12 ];
+
+		if ( det == 0 ) {
+
+			var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0";
+
+			if ( throwOnInvertible || false ) {
+
+				throw new Error( msg ); 
+
+			} else {
+
+				console.warn( msg );
+
+			}
+
+			this.identity();
+
+			return this;
+		}
+
+		this.multiplyScalar( 1 / det );
+
+		return this;
+
+	},
+
+	compose: function() {
+
+		var mRotation = new THREE.Matrix4(),
+			mScale = new THREE.Matrix4();
+		
+		return function ( translation, rotation, scale ) {
+
+			var te = this.elements;
+
+			mRotation.identity();
+			mRotation.setRotationFromQuaternion( rotation );
+
+			mScale.makeScale( scale.x, scale.y, scale.z );
+
+			this.multiplyMatrices( mRotation, mScale );
+
+			te[12] = translation.x;
+			te[13] = translation.y;
+			te[14] = translation.z;
+
+			return this;
+
+		};
+
+	}(),
+
+	decompose: function() {
+
+		var x = new THREE.Vector3(),
+			y = new THREE.Vector3(),
+			z = new THREE.Vector3(),
+			matrix = new THREE.Matrix4();
+
+		return function ( translation, rotation, scale ) {
+
+			var te = this.elements;
+
+			// grab the axis vectors
+			x.set( te[0], te[1], te[2] );
+			y.set( te[4], te[5], te[6] );
+			z.set( te[8], te[9], te[10] );
+
+			translation = ( translation instanceof THREE.Vector3 ) ? translation : new THREE.Vector3();
+			rotation = ( rotation instanceof THREE.Quaternion ) ? rotation : new THREE.Quaternion();
+			scale = ( scale instanceof THREE.Vector3 ) ? scale : new THREE.Vector3();
+
+			scale.x = x.length();
+			scale.y = y.length();
+			scale.z = z.length();
+
+			translation.x = te[12];
+			translation.y = te[13];
+			translation.z = te[14];
+
+			// scale the rotation part
+
+			matrix.copy( this );
+
+			matrix.elements[0] /= scale.x;
+			matrix.elements[1] /= scale.x;
+			matrix.elements[2] /= scale.x;
+
+			matrix.elements[4] /= scale.y;
+			matrix.elements[5] /= scale.y;
+			matrix.elements[6] /= scale.y;
+
+			matrix.elements[8] /= scale.z;
+			matrix.elements[9] /= scale.z;
+			matrix.elements[10] /= scale.z;
+
+			rotation.setFromRotationMatrix( matrix );
+
+			return [ translation, rotation, scale ];
+
+		};
+
+	}(),
+
+	extractPosition: function ( m ) {
+
+		var te = this.elements;
+		var me = m.elements;
+
+		te[12] = me[12];
+		te[13] = me[13];
+		te[14] = me[14];
+
+		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();
+
+			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[8] = me[8] * scaleZ;
+			te[9] = me[9] * scaleZ;
+			te[10] = me[10] * scaleZ;
+
+			return this;
+
+		};
+
+	}(),
+
+	translate: function ( v ) {
+
+		var te = this.elements;
+		var x = v.x, y = v.y, z = v.z;
+
+		te[12] = te[0] * x + te[4] * y + te[8] * z + te[12];
+		te[13] = te[1] * x + te[5] * y + te[9] * z + te[13];
+		te[14] = te[2] * x + te[6] * y + te[10] * z + te[14];
+		te[15] = te[3] * x + te[7] * y + te[11] * z + te[15];
+
+		return this;
+
+	},
+
+	rotateX: function ( angle ) {
+
+		var te = this.elements;
+		var m12 = te[4];
+		var m22 = te[5];
+		var m32 = te[6];
+		var m42 = te[7];
+		var m13 = te[8];
+		var m23 = te[9];
+		var m33 = te[10];
+		var m43 = te[11];
+		var c = Math.cos( angle );
+		var s = Math.sin( angle );
+
+		te[4] = c * m12 + s * m13;
+		te[5] = c * m22 + s * m23;
+		te[6] = c * m32 + s * m33;
+		te[7] = c * m42 + s * m43;
+
+		te[8] = c * m13 - s * m12;
+		te[9] = c * m23 - s * m22;
+		te[10] = c * m33 - s * m32;
+		te[11] = c * m43 - s * m42;
+
+		return this;
+
+	},
+
+	rotateY: function ( angle ) {
+
+		var te = this.elements;
+		var m11 = te[0];
+		var m21 = te[1];
+		var m31 = te[2];
+		var m41 = te[3];
+		var m13 = te[8];
+		var m23 = te[9];
+		var m33 = te[10];
+		var m43 = te[11];
+		var c = Math.cos( angle );
+		var s = Math.sin( angle );
+
+		te[0] = c * m11 - s * m13;
+		te[1] = c * m21 - s * m23;
+		te[2] = c * m31 - s * m33;
+		te[3] = c * m41 - s * m43;
+
+		te[8] = c * m13 + s * m11;
+		te[9] = c * m23 + s * m21;
+		te[10] = c * m33 + s * m31;
+		te[11] = c * m43 + s * m41;
+
+		return this;
+
+	},
+
+	rotateZ: function ( angle ) {
+
+		var te = this.elements;
+		var m11 = te[0];
+		var m21 = te[1];
+		var m31 = te[2];
+		var m41 = te[3];
+		var m12 = te[4];
+		var m22 = te[5];
+		var m32 = te[6];
+		var m42 = te[7];
+		var c = Math.cos( angle );
+		var s = Math.sin( angle );
+
+		te[0] = c * m11 + s * m12;
+		te[1] = c * m21 + s * m22;
+		te[2] = c * m31 + s * m32;
+		te[3] = c * m41 + s * m42;
+
+		te[4] = c * m12 - s * m11;
+		te[5] = c * m22 - s * m21;
+		te[6] = c * m32 - s * m31;
+		te[7] = c * m42 - s * m41;
+
+		return this;
+
+	},
+
+	rotateByAxis: function ( axis, angle ) {
+
+		var te = this.elements;
+
+		// optimize by checking axis
+
+		if ( axis.x === 1 && axis.y === 0 && axis.z === 0 ) {
+
+			return this.rotateX( angle );
+
+		} else if ( axis.x === 0 && axis.y === 1 && axis.z === 0 ) {
+
+			return this.rotateY( angle );
+
+		} else if ( axis.x === 0 && axis.y === 0 && axis.z === 1 ) {
+
+			return this.rotateZ( angle );
+
+		}
+
+		var x = axis.x, y = axis.y, z = axis.z;
+		var n = Math.sqrt(x * x + y * y + z * z);
+
+		x /= n;
+		y /= n;
+		z /= n;
+
+		var xx = x * x, yy = y * y, zz = z * z;
+		var c = Math.cos( angle );
+		var s = Math.sin( angle );
+		var oneMinusCosine = 1 - c;
+		var xy = x * y * oneMinusCosine;
+		var xz = x * z * oneMinusCosine;
+		var yz = y * z * oneMinusCosine;
+		var xs = x * s;
+		var ys = y * s;
+		var zs = z * s;
+
+		var r11 = xx + (1 - xx) * c;
+		var r21 = xy + zs;
+		var r31 = xz - ys;
+		var r12 = xy - zs;
+		var r22 = yy + (1 - yy) * c;
+		var r32 = yz + xs;
+		var r13 = xz + ys;
+		var r23 = yz - xs;
+		var r33 = zz + (1 - zz) * c;
+
+		var m11 = te[0], m21 = te[1], m31 = te[2], m41 = te[3];
+		var m12 = te[4], m22 = te[5], m32 = te[6], m42 = te[7];
+		var m13 = te[8], m23 = te[9], m33 = te[10], m43 = te[11];
+
+		te[0] = r11 * m11 + r21 * m12 + r31 * m13;
+		te[1] = r11 * m21 + r21 * m22 + r31 * m23;
+		te[2] = r11 * m31 + r21 * m32 + r31 * m33;
+		te[3] = r11 * m41 + r21 * m42 + r31 * m43;
+
+		te[4] = r12 * m11 + r22 * m12 + r32 * m13;
+		te[5] = r12 * m21 + r22 * m22 + r32 * m23;
+		te[6] = r12 * m31 + r22 * m32 + r32 * m33;
+		te[7] = r12 * m41 + r22 * m42 + r32 * m43;
+
+		te[8] = r13 * m11 + r23 * m12 + r33 * m13;
+		te[9] = r13 * m21 + r23 * m22 + r33 * m23;
+		te[10] = r13 * m31 + r23 * m32 + r33 * m33;
+		te[11] = r13 * m41 + r23 * m42 + r33 * m43;
+
+		return this;
+
+	},
+
+	scale: function ( v ) {
+
+		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;
+
+		return this;
+
+	},
+
+	getMaxScaleOnAxis: function () {
+
+		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];
+
+		return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) );
+
+	},
+
+	makeTranslation: function ( x, y, z ) {
+
+		this.set(
+
+			1, 0, 0, x,
+			0, 1, 0, y,
+			0, 0, 1, z,
+			0, 0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	makeRotationX: function ( theta ) {
+
+		var c = Math.cos( theta ), s = Math.sin( theta );
+
+		this.set(
+
+			1, 0,  0, 0,
+			0, c, -s, 0,
+			0, s,  c, 0,
+			0, 0,  0, 1
+
+		);
+
+		return this;
+
+	},
+
+	makeRotationY: function ( theta ) {
+
+		var c = Math.cos( theta ), s = Math.sin( theta );
+
+		this.set(
+
+			 c, 0, s, 0,
+			 0, 1, 0, 0,
+			-s, 0, c, 0,
+			 0, 0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	makeRotationZ: function ( theta ) {
+
+		var c = Math.cos( theta ), s = Math.sin( theta );
+
+		this.set(
+
+			c, -s, 0, 0,
+			s,  c, 0, 0,
+			0,  0, 1, 0,
+			0,  0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	makeRotationAxis: function ( axis, angle ) {
+
+		// Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+		var c = Math.cos( angle );
+		var s = Math.sin( angle );
+		var t = 1 - c;
+		var x = axis.x, y = axis.y, z = axis.z;
+		var tx = t * x, ty = t * y;
+
+		this.set(
+
+			tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+			tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+			tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+			0, 0, 0, 1
+
+		);
+
+		 return this;
+
+	},
+
+	makeScale: function ( x, y, z ) {
+
+		this.set(
+
+			x, 0, 0, 0,
+			0, y, 0, 0,
+			0, 0, z, 0,
+			0, 0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	makeFrustum: function ( left, right, bottom, top, near, far ) {
+
+		var te = this.elements;
+		var x = 2 * near / ( right - left );
+		var y = 2 * near / ( top - bottom );
+
+		var a = ( right + left ) / ( right - left );
+		var b = ( top + bottom ) / ( top - bottom );
+		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;
+
+		return this;
+
+	},
+
+	makePerspective: function ( fov, aspect, near, far ) {
+
+		var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) );
+		var ymin = - ymax;
+		var xmin = ymin * aspect;
+		var xmax = ymax * aspect;
+
+		return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );
+
+	},
+
+	makeOrthographic: function ( left, right, top, bottom, near, far ) {
+
+		var te = this.elements;
+		var w = right - left;
+		var h = top - bottom;
+		var p = far - near;
+
+		var x = ( right + left ) / w;
+		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;
+
+		return this;
+
+	},
+
+	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]
+
+		);
+
+	}
+
+} );
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Ray = function ( origin, direction ) {
+
+	this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
+	this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
+
+};
+
+THREE.extend( THREE.Ray.prototype, {
+
+	set: function ( origin, direction ) {
+
+		this.origin.copy( origin );
+		this.direction.copy( direction );
+
+		return this;
+
+	},
+
+	copy: function ( ray ) {
+
+		this.origin.copy( ray.origin );
+		this.direction.copy( ray.direction );
+
+		return this;
+
+	},
+
+	at: function( t, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+
+		return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
+
+	},
+
+	recast: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( t ) {
+
+			this.origin.copy( this.at( t, v1 ) );
+
+			return this;
+
+		};
+
+	}(),
+
+	closestPointToPoint: function ( point, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		result.subVectors( point, this.origin );
+		var directionDistance = result.dot( this.direction );
+
+		return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+	},
+
+	distanceToPoint: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( point ) {
+
+			var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
+			v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+			return v1.distanceTo( point );
+
+		};
+
+	}(),
+
+	isIntersectionSphere: function( sphere ) {
+
+		return ( this.distanceToPoint( sphere.center ) <= sphere.radius );
+
+	},
+
+	isIntersectionPlane: function ( plane ) {
+
+		// check if the line and plane are non-perpendicular, if they
+		// eventually they will intersect.
+		var denominator = plane.normal.dot( this.direction );
+		if ( denominator != 0 ) {
+
+			return true;
+
+		}
+
+		// line is coplanar, return origin
+		if( plane.distanceToPoint( this.origin ) == 0 ) {
+
+			return true;
+
+		}
+
+		return false;
+
+	},
+
+	distanceToPlane: function ( plane ) {
+
+		var denominator = plane.normal.dot( this.direction );
+		if ( denominator == 0 ) {
+
+			// line is coplanar, return origin
+			if( plane.distanceToPoint( this.origin ) == 0 ) {
+
+				return 0;
+
+			}
+
+			// Unsure if this is the correct method to handle this case.
+			return undefined;
+
+		}
+
+		var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
+
+		return t;
+
+	},
+
+	intersectPlane: function ( plane, optionalTarget ) {
+
+		var t = this.distanceToPlane( plane );
+
+		if ( t === undefined ) {
+
+			return undefined;
+		}
+
+		return this.at( t, optionalTarget );
+
+	},
+
+	applyMatrix4: function ( matrix4 ) {
+
+		this.direction.add( this.origin ).applyMatrix4( matrix4 );
+		this.origin.applyMatrix4( matrix4 );
+		this.direction.sub( this.origin );
+
+		return this;
+	},
+
+	equals: function ( ray ) {
+
+		return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Ray().copy( this );
+
+	}
+
+} );
+/**
+ * @author bhouston / http://exocortex.com
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Sphere = function ( center, radius ) {
+
+	this.center = ( center !== undefined ) ? center : new THREE.Vector3();
+	this.radius = ( radius !== undefined ) ? radius : 0;
+
+};
+
+THREE.extend( THREE.Sphere.prototype, {
+
+	set: function ( center, radius ) {
+
+		this.center.copy( center );
+		this.radius = radius;
+
+		return this;
+	},
+
+	setFromCenterAndPoints: function ( center, points ) {
+
+		var maxRadiusSq = 0;
+
+		for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+			var radiusSq = center.distanceToSquared( points[ i ] );
+			maxRadiusSq = Math.max( maxRadiusSq, radiusSq );
+
+		}
+
+		this.center = center;
+		this.radius = Math.sqrt( maxRadiusSq );
+
+		return this;
+
+	},
+
+	copy: function ( sphere ) {
+
+		this.center.copy( sphere.center );
+		this.radius = sphere.radius;
+
+		return this;
+
+	},
+
+	empty: function () {
+
+		return ( this.radius <= 0 );
+
+	},
+
+	containsPoint: function ( point ) {
+
+		return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
+
+	},
+
+	distanceToPoint: function ( point ) {
+
+		return ( point.distanceTo( this.center ) - this.radius );
+
+	},
+
+	intersectsSphere: function ( sphere ) {
+
+		var radiusSum = this.radius + sphere.radius;
+
+		return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
+
+	},
+
+	clampPoint: function ( point, optionalTarget ) {
+
+		var deltaLengthSq = this.center.distanceToSquared( point );
+
+		var result = optionalTarget || new THREE.Vector3();
+		result.copy( point );
+
+		if ( deltaLengthSq > ( this.radius * this.radius ) ) {
+
+			result.sub( this.center ).normalize();
+			result.multiplyScalar( this.radius ).add( this.center );
+
+		}
+
+		return result;
+
+	},
+
+	getBoundingBox: function ( optionalTarget ) {
+
+		var box = optionalTarget || new THREE.Box3();
+
+		box.set( this.center, this.center );
+		box.expandByScalar( this.radius );
+
+		return box;
+
+	},
+
+	applyMatrix4: function ( matrix ) {
+
+		this.center.applyMatrix4( matrix );
+		this.radius = this.radius * matrix.getMaxScaleOnAxis();
+
+		return this;
+
+	},
+
+	translate: function ( offset ) {
+
+		this.center.add( offset );
+
+		return this;
+
+	},
+
+	equals: function ( sphere ) {
+
+		return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Sphere().copy( this );
+
+	}
+
+} );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) {
+
+	this.planes = [
+
+		( p0 !== undefined ) ? p0 : new THREE.Plane(),
+		( p1 !== undefined ) ? p1 : new THREE.Plane(),
+		( p2 !== undefined ) ? p2 : new THREE.Plane(),
+		( p3 !== undefined ) ? p3 : new THREE.Plane(),
+		( p4 !== undefined ) ? p4 : new THREE.Plane(),
+		( p5 !== undefined ) ? p5 : new THREE.Plane()
+
+	];
+
+};
+
+THREE.extend( THREE.Frustum.prototype, {
+
+	set: function ( p0, p1, p2, p3, p4, p5 ) {
+
+		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 );
+
+		return this;
+
+	},
+
+	copy: function ( frustum ) {
+
+		var planes = this.planes;
+
+		for( var i = 0; i < 6; i ++ ) {
+
+			planes[i].copy( frustum.planes[i] );
+
+		}
+
+		return this;
+
+	},
+
+	setFromMatrix: function ( m ) {
+
+		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];
+
+		planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+		planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+		planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+		planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+		planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+		planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
+
+		return this;
+
+	},
+
+	intersectsObject: function () {
+
+		var center = new THREE.Vector3();
+
+		return function ( object ) {
+
+			// this method is expanded inlined for performance reasons.
+
+			var matrix = object.matrixWorld;
+			var planes = this.planes;
+			var negRadius = - object.geometry.boundingSphere.radius * matrix.getMaxScaleOnAxis();
+
+			center.getPositionFromMatrix( matrix );
+
+			for ( var i = 0; i < 6; i ++ ) {
+
+				var distance = planes[ i ].distanceToPoint( center );
+
+				if ( distance < negRadius ) {
+
+					return false;
+
+				}
+
+			}
+
+			return true;
+
+		};
+
+	}(),
+
+	intersectsSphere: function ( sphere ) {
+
+		var planes = this.planes;
+		var center = sphere.center;
+		var negRadius = -sphere.radius;
+
+		for ( var i = 0; i < 6; i ++ ) {
+
+			var distance = planes[ i ].distanceToPoint( center );
+
+			if ( distance < negRadius ) {
+
+				return false;
+
+			}
+
+		}
+
+		return true;
+
+	},
+
+	containsPoint: function ( point ) {
+
+		var planes = this.planes;
+
+		for ( var i = 0; i < 6; i ++ ) {
+
+			if ( planes[ i ].distanceToPoint( point ) < 0 ) {
+
+				return false;
+
+			}
+
+		}
+
+		return true;
+
+	},
+
+	clone: function () {
+
+		return new THREE.Frustum().copy( this );
+
+	}
+
+} );
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Plane = function ( normal, constant ) {
+
+	this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 );
+	this.constant = ( constant !== undefined ) ? constant : 0;
+
+};
+
+THREE.extend( THREE.Plane.prototype, {
+
+	set: function ( normal, constant ) {
+
+		this.normal.copy( normal );
+		this.constant = constant;
+
+		return this;
+
+	},
+
+	setComponents: function ( x, y, z, w ) {
+
+		this.normal.set( x, y, z );
+		this.constant = w;
+
+		return this;
+
+	},
+
+	setFromNormalAndCoplanarPoint: function ( normal, point ) {
+
+		this.normal.copy( normal );
+		this.constant = - point.dot( this.normal );	// must be this.normal, not normal, as this.normal is normalized
+
+		return this;
+
+	},
+
+	setFromCoplanarPoints: function() {
+
+		var v1 = new THREE.Vector3();
+		var v2 = new THREE.Vector3();
+
+		return function ( a, b, c ) {
+
+			var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
+
+			// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+			this.setFromNormalAndCoplanarPoint( normal, a );
+
+			return this;
+
+		};
+
+	}(),
+
+
+	copy: function ( plane ) {
+
+		this.normal.copy( plane.normal );
+		this.constant = plane.constant;
+
+		return this;
+
+	},
+
+	normalize: function () {
+
+		// Note: will lead to a divide by zero if the plane is invalid.
+
+		var inverseNormalLength = 1.0 / this.normal.length();
+		this.normal.multiplyScalar( inverseNormalLength );
+		this.constant *= inverseNormalLength;
+
+		return this;
+
+	},
+
+	negate: function () {
+
+		this.constant *= -1;
+		this.normal.negate();
+
+		return this;
+
+	},
+
+	distanceToPoint: function ( point ) {
+
+		return this.normal.dot( point ) + this.constant;
+
+	},
+
+	distanceToSphere: function ( sphere ) {
+
+		return this.distanceToPoint( sphere.center ) - sphere.radius;
+
+	},
+
+	projectPoint: function ( point, optionalTarget ) {
+
+		return this.orthoPoint( point, optionalTarget ).sub( point ).negate();
+
+	},
+
+	orthoPoint: function ( point, optionalTarget ) {
+
+		var perpendicularMagnitude = this.distanceToPoint( point );
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );
+
+	},
+
+	isIntersectionLine: function ( line ) {
+
+		// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+		var startSign = this.distanceToPoint( line.start );
+		var endSign = this.distanceToPoint( line.end );
+
+		return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+	},
+
+	intersectLine: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( line, optionalTarget ) {
+
+			var result = optionalTarget || new THREE.Vector3();
+
+			var direction = line.delta( v1 );
+
+			var denominator = this.normal.dot( direction );
+
+			if ( denominator == 0 ) {
+
+				// line is coplanar, return origin
+				if( this.distanceToPoint( line.start ) == 0 ) {
+
+					return result.copy( line.start );
+
+				}
+
+				// Unsure if this is the correct method to handle this case.
+				return undefined;
+
+			}
+
+			var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
+
+			if( t < 0 || t > 1 ) {
+
+				return undefined;
+
+			}
+
+			return result.copy( direction ).multiplyScalar( t ).add( line.start );
+
+		};
+
+	}(),
+
+
+	coplanarPoint: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.copy( this.normal ).multiplyScalar( - this.constant );
+
+	},
+
+	applyMatrix4: function() {
+
+		var v1 = new THREE.Vector3();
+		var v2 = new THREE.Vector3();
+
+		return function ( matrix, optionalNormalMatrix ) {
+
+			// compute new normal based on theory here:
+			// http://www.songho.ca/opengl/gl_normaltransform.html
+			optionalNormalMatrix = optionalNormalMatrix || new THREE.Matrix3().getInverse( matrix ).transpose();
+			var newNormal = v1.copy( this.normal ).applyMatrix3( optionalNormalMatrix );
+
+			var newCoplanarPoint = this.coplanarPoint( v2 );
+			newCoplanarPoint.applyMatrix4( matrix );
+
+			this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );
+
+			return this;
+
+		};
+
+	}(),
+
+	translate: function ( offset ) {
+
+		this.constant = this.constant - offset.dot( this.normal );
+
+		return this;
+
+	},
+
+	equals: function ( plane ) {
+
+		return plane.normal.equals( this.normal ) && ( plane.constant == this.constant );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Plane().copy( this );
+
+	}
+
+} );
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Math = {
+
+	// Clamp value to range <a, b>
+
+	clamp: function ( x, a, b ) {
+
+		return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+	},
+
+	// Clamp value to range <a, inf)
+
+	clampBottom: function ( x, a ) {
+
+		return x < a ? a : x;
+
+	},
+
+	// Linear mapping from range <a1, a2> to range <b1, b2>
+
+	mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+		return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+	},
+
+	// http://en.wikipedia.org/wiki/Smoothstep
+
+	smoothstep: function ( x, min, max ) {
+
+		if ( x <= min ) return 0;
+		if ( x >= max ) return 1;
+
+		x = ( x - min )/( max - min );
+
+		return x*x*(3 - 2*x);
+
+	},
+
+	smootherstep: function ( x, min, max ) {
+
+		if ( x <= min ) return 0;
+		if ( x >= max ) return 1;
+
+		x = ( x - min )/( max - min );
+
+		return x*x*x*(x*(x*6 - 15) + 10);
+
+	},
+
+	// Random float from <0, 1> with 16 bits of randomness
+	// (standard Math.random() creates repetitive patterns when applied over larger space)
+
+	random16: function () {
+
+		return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
+
+	},
+
+	// Random integer from <low, high> interval
+
+	randInt: function ( low, high ) {
+
+		return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+	},
+
+	// Random float from <low, high> interval
+
+	randFloat: function ( low, high ) {
+
+		return low + Math.random() * ( high - low );
+
+	},
+
+	// Random float from <-range/2, range/2> interval
+
+	randFloatSpread: function ( range ) {
+
+		return range * ( 0.5 - Math.random() );
+
+	},
+
+	sign: function ( x ) {
+
+		return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 );
+
+	},
+
+	degToRad: function() {
+
+		var degreeToRadiansFactor = Math.PI / 180;
+
+		return function ( degrees ) {
+
+			return degrees * degreeToRadiansFactor;
+
+		};
+
+	}(),
+
+	radToDeg: function() {
+
+		var radianToDegreesFactor = 180 / Math.PI;
+
+		return function ( radians ) {
+
+			return radians * radianToDegreesFactor;
+
+		};
+
+	}()
+
+};
+/**
+ * Spline from Tween.js, slightly optimized (and trashed)
+ * http://sole.github.com/tween.js/examples/05_spline.html
+ *
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Spline = function ( points ) {
+
+	this.points = points;
+
+	var c = [], v3 = { x: 0, y: 0, z: 0 },
+	point, intPoint, weight, w2, w3,
+	pa, pb, pc, pd;
+
+	this.initFromArray = function( a ) {
+
+		this.points = [];
+
+		for ( var i = 0; i < a.length; i++ ) {
+
+			this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
+
+		}
+
+	};
+
+	this.getPoint = function ( k ) {
+
+		point = ( this.points.length - 1 ) * k;
+		intPoint = Math.floor( point );
+		weight = point - intPoint;
+
+		c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
+		c[ 1 ] = intPoint;
+		c[ 2 ] = intPoint  > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
+		c[ 3 ] = intPoint  > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;
+
+		pa = this.points[ c[ 0 ] ];
+		pb = this.points[ c[ 1 ] ];
+		pc = this.points[ c[ 2 ] ];
+		pd = this.points[ c[ 3 ] ];
+
+		w2 = weight * weight;
+		w3 = weight * w2;
+
+		v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
+		v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
+		v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );
+
+		return v3;
+
+	};
+
+	this.getControlPointsArray = function () {
+
+		var i, p, l = this.points.length,
+			coords = [];
+
+		for ( i = 0; i < l; i ++ ) {
+
+			p = this.points[ i ];
+			coords[ i ] = [ p.x, p.y, p.z ];
+
+		}
+
+		return coords;
+
+	};
+
+	// approximate length by summing linear segments
+
+	this.getLength = function ( nSubDivisions ) {
+
+		var i, index, nSamples, position,
+			point = 0, intPoint = 0, oldIntPoint = 0,
+			oldPosition = new THREE.Vector3(),
+			tmpVec = new THREE.Vector3(),
+			chunkLengths = [],
+			totalLength = 0;
+
+		// first point has 0 length
+
+		chunkLengths[ 0 ] = 0;
+
+		if ( !nSubDivisions ) nSubDivisions = 100;
+
+		nSamples = this.points.length * nSubDivisions;
+
+		oldPosition.copy( this.points[ 0 ] );
+
+		for ( i = 1; i < nSamples; i ++ ) {
+
+			index = i / nSamples;
+
+			position = this.getPoint( index );
+			tmpVec.copy( position );
+
+			totalLength += tmpVec.distanceTo( oldPosition );
+
+			oldPosition.copy( position );
+
+			point = ( this.points.length - 1 ) * index;
+			intPoint = Math.floor( point );
+
+			if ( intPoint != oldIntPoint ) {
+
+				chunkLengths[ intPoint ] = totalLength;
+				oldIntPoint = intPoint;
+
+			}
+
+		}
+
+		// last point ends with total length
+
+		chunkLengths[ chunkLengths.length ] = totalLength;
+
+		return { chunks: chunkLengths, total: totalLength };
+
+	};
+
+	this.reparametrizeByArcLength = function ( samplingCoef ) {
+
+		var i, j,
+			index, indexCurrent, indexNext,
+			linearDistance, realDistance,
+			sampling, position,
+			newpoints = [],
+			tmpVec = new THREE.Vector3(),
+			sl = this.getLength();
+
+		newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
+
+		for ( i = 1; i < this.points.length; i++ ) {
+
+			//tmpVec.copy( this.points[ i - 1 ] );
+			//linearDistance = tmpVec.distanceTo( this.points[ i ] );
+
+			realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];
+
+			sampling = Math.ceil( samplingCoef * realDistance / sl.total );
+
+			indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
+			indexNext = i / ( this.points.length - 1 );
+
+			for ( j = 1; j < sampling - 1; j++ ) {
+
+				index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
+
+				position = this.getPoint( index );
+				newpoints.push( tmpVec.copy( position ).clone() );
+
+			}
+
+			newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );
+
+		}
+
+		this.points = newpoints;
+
+	};
+
+	// Catmull-Rom
+
+	function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
+
+		var v0 = ( p2 - p0 ) * 0.5,
+			v1 = ( p3 - p1 ) * 0.5;
+
+		return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+	};
+
+};
+/**
+ * @author bhouston / http://exocortex.com
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Triangle = function ( a, b, c ) {
+
+	this.a = ( a !== undefined ) ? a : new THREE.Vector3();
+	this.b = ( b !== undefined ) ? b : new THREE.Vector3();
+	this.c = ( c !== undefined ) ? c : new THREE.Vector3();
+
+};
+
+THREE.Triangle.normal = function() {
+
+	var v0 = new THREE.Vector3();
+
+	return function( a, b, c, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+
+		result.subVectors( c, b );
+		v0.subVectors( a, b );
+		result.cross( v0 );
+
+		var resultLengthSq = result.lengthSq();
+		if( resultLengthSq > 0 ) {
+
+			return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
+
+		}
+
+		return result.set( 0, 0, 0 );
+
+	};
+
+}();
+
+// static/instance method to calculate barycoordinates
+// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+THREE.Triangle.barycoordFromPoint = function() {
+
+	var v0 = new THREE.Vector3(),
+		v1 = new THREE.Vector3(),
+		v2 = new THREE.Vector3();
+
+	return function ( point, a, b, c, optionalTarget ) {
+
+		v0.subVectors( c, a );
+		v1.subVectors( b, a );
+		v2.subVectors( point, a );
+
+		var dot00 = v0.dot( v0 );
+		var dot01 = v0.dot( v1 );
+		var dot02 = v0.dot( v2 );
+		var dot11 = v1.dot( v1 );
+		var dot12 = v1.dot( v2 );
+
+		var denom = ( dot00 * dot11 - dot01 * dot01 );
+
+		var result = optionalTarget || new THREE.Vector3();
+
+		// colinear or singular triangle
+		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 );
+		}
+
+		var invDenom = 1 / denom;
+		var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+		var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+
+		// barycoordinates must always sum to 1
+		return result.set( 1 - u - v, v, u );
+
+	};
+
+}();
+
+THREE.Triangle.containsPoint = function() {
+
+	var v1 = new THREE.Vector3();
+
+	return function ( point, a, b, c ) {
+
+		var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 );
+
+		return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
+
+	};
+
+}();
+
+THREE.extend( 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 ( points, i0, i1, i2 ) {
+
+		this.a.copy( points[i0] );
+		this.b.copy( points[i1] );
+		this.c.copy( points[i2] );
+
+		return this;
+
+	},
+
+	copy: function ( triangle ) {
+
+		this.a.copy( triangle.a );
+		this.b.copy( triangle.b );
+		this.c.copy( triangle.c );
+
+		return this;
+
+	},
+
+	area: function() {
+
+		var v0 = new THREE.Vector3();
+		var v1 = new THREE.Vector3();
+
+		return function () {
+
+			v0.subVectors( this.c, this.b );
+			v1.subVectors( this.a, this.b );
+
+			return v0.cross( v1 ).length() * 0.5;
+
+		};
+
+	}(),
+
+	midpoint: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+		return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
+
+	},
+
+	normal: function ( optionalTarget ) {
+
+		return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget );
+
+	},
+
+	plane: function ( optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Plane();
+
+		return result.setFromCoplanarPoints( this.a, this.b, this.c );
+
+	},
+
+	barycoordFromPoint: function ( point, optionalTarget ) {
+
+		return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );
+
+	},
+
+	containsPoint: function ( point ) {
+
+		return THREE.Triangle.containsPoint( point, this.a, this.b, this.c );
+
+	},
+
+	equals: function ( triangle ) {
+
+		return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Triangle().copy( this );
+
+	}
+
+} );
+/**
+ * @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 );
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Clock = function ( autoStart ) {
+
+	this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
+
+	this.startTime = 0;
+	this.oldTime = 0;
+	this.elapsedTime = 0;
+
+	this.running = false;
+
+};
+
+THREE.extend( THREE.Clock.prototype, {
+
+	start: function () {
+
+		this.startTime = window.performance !== undefined && window.performance.now !== undefined
+					? window.performance.now()
+					: Date.now();
+
+		this.oldTime = this.startTime;
+		this.running = true;
+	},
+
+	stop: function () {
+
+		this.getElapsedTime();
+		this.running = false;
+
+	},
+
+	getElapsedTime: function () {
+
+		this.getDelta();
+		return this.elapsedTime;
+
+	},
+
+	getDelta: function () {
+
+		var diff = 0;
+
+		if ( this.autoStart && ! this.running ) {
+
+			this.start();
+
+		}
+
+		if ( this.running ) {
+
+			var newTime = window.performance !== undefined && window.performance.now !== undefined
+					? window.performance.now()
+					: Date.now();
+
+			diff = 0.001 * ( newTime - this.oldTime );
+			this.oldTime = newTime;
+
+			this.elapsedTime += diff;
+
+		}
+
+		return diff;
+
+	}
+
+} );
+/**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+THREE.EventDispatcher = function () {
+
+	var listeners = {};
+
+	this.addEventListener = function ( type, listener ) {
+
+		if ( listeners[ type ] === undefined ) {
+
+			listeners[ type ] = [];
+
+		}
+
+		if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+			listeners[ type ].push( listener );
+
+		}
+
+	};
+
+	this.removeEventListener = function ( type, listener ) {
+
+		var index = listeners[ type ].indexOf( listener );
+
+		if ( index !== - 1 ) {
+
+			listeners[ type ].splice( index, 1 );
+
+		}
+
+	};
+
+	this.dispatchEvent = function ( event ) {
+
+		var listenerArray = listeners[ event.type ];
+
+		if ( listenerArray !== undefined ) {
+
+			event.target = this;
+
+			for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
+
+				listenerArray[ i ].call( this, event );
+
+			}
+
+		}
+
+	};
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author bhouston / http://exocortex.com/
+ */
+
+( function ( THREE ) {
+
+	THREE.Raycaster = function ( origin, direction, near, far ) {
+
+		this.ray = new THREE.Ray( origin, direction );
+
+		// normalized ray.direction required for accurate distance calculations
+		if( this.ray.direction.lengthSq() > 0 ) {
+
+			this.ray.direction.normalize();
+
+		}
+
+		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();
+
+	var inverseMatrix = new THREE.Matrix4();
+
+	var descSort = function ( a, b ) {
+
+		return a.distance - b.distance;
+
+	};
+
+	var intersectObject = function ( object, raycaster, intersects ) {
+
+		if ( object instanceof THREE.Particle ) {
+
+			matrixPosition.getPositionFromMatrix( object.matrixWorld );
+			var distance = raycaster.ray.distanceToPoint( matrixPosition );
+
+			if ( distance > object.scale.x ) {
+
+				return intersects;
+
+			}
+
+			intersects.push( {
+
+				distance: distance,
+				point: object.position,
+				face: null,
+				object: object
+
+			} );
+
+		} else if ( object instanceof THREE.Mesh ) {
+
+			// Checking boundingSphere distance to ray
+			matrixPosition.getPositionFromMatrix( object.matrixWorld );
+			sphere.set(
+				matrixPosition,
+				object.geometry.boundingSphere.radius * object.matrixWorld.getMaxScaleOnAxis() );
+
+			if ( ! raycaster.ray.isIntersectionSphere( sphere ) ) {
+
+				return intersects;
+
+			}
+
+			// Checking faces
+
+			var geometry = object.geometry;
+			var vertices = geometry.vertices;
+
+			var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
+			var objectMaterials = isFaceMaterial === true ? object.material.materials : null;
+
+			var side = object.material.side;
+
+			var a, b, c, d;
+			var precision = raycaster.precision;
+
+			object.matrixRotationWorld.extractRotation( object.matrixWorld );
+
+			inverseMatrix.getInverse( object.matrixWorld );
+
+			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+			for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+
+				var face = geometry.faces[ f ];
+
+				var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : object.material;
+
+				if ( material === undefined ) continue;
+
+				facePlane.setFromNormalAndCoplanarPoint( face.normal, vertices[face.a] );
+
+				var planeDistance = localRay.distanceToPlane( facePlane );
+
+				// bail if raycaster and plane are parallel
+				if ( Math.abs( planeDistance ) < precision ) continue;
+
+				// if negative distance, then plane is behind raycaster
+				if ( planeDistance < 0 ) continue;
+
+				// check if we hit the wrong side of a single sided face
+				side = material.side;
+				if( side !== THREE.DoubleSide ) {
+
+					var planeSign = localRay.direction.dot( facePlane.normal );
+
+					if( ! ( side === THREE.FrontSide ? planeSign < 0 : planeSign > 0 ) ) continue;
+
+				}
+
+				// this can be done using the planeDistance from localRay because localRay wasn't normalized, but ray was
+				if ( planeDistance < raycaster.near || planeDistance > raycaster.far ) continue;
+
+				intersectPoint = localRay.at( planeDistance, intersectPoint ); // passing in intersectPoint avoids a copy
+
+				if ( face instanceof THREE.Face3 ) {
+
+					a = vertices[ face.a ];
+					b = vertices[ face.b ];
+					c = vertices[ face.c ];
+
+					if ( ! THREE.Triangle.containsPoint( intersectPoint, a, b, c ) ) continue;
+
+				} else if ( face instanceof THREE.Face4 ) {
+
+					a = vertices[ face.a ];
+					b = vertices[ face.b ];
+					c = vertices[ face.c ];
+					d = vertices[ face.d ];
+
+					if ( ( ! THREE.Triangle.containsPoint( intersectPoint, a, b, d ) ) &&
+						 ( ! THREE.Triangle.containsPoint( intersectPoint, b, c, d ) ) ) continue;
+
+				} else {
+
+					// This is added because if we call out of this if/else group when none of the cases
+					//    match it will add a point to the intersection list erroneously.
+					throw Error( "face type not supported" );
+
+				}
+
+				intersects.push( {
+
+					distance: planeDistance,	// this works because the original ray was normalized, and the transformed localRay wasn't
+					point: raycaster.ray.at( planeDistance ),
+					face: face,
+					faceIndex: f,
+					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.set = function ( origin, direction ) {
+
+		this.ray.set( origin, direction );
+
+		// normalized ray.direction required for accurate distance calculations
+		if( this.ray.direction.length() > 0 ) {
+
+			this.ray.direction.normalize();
+
+		}
+
+	};
+
+	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/
+ */
+
+THREE.Object3D = function () {
+
+	this.id = THREE.Object3DIdCount ++;
+
+	this.name = '';
+	this.properties = {};
+
+	this.parent = undefined;
+	this.children = [];
+
+	this.up = new THREE.Vector3( 0, 1, 0 );
+
+	this.position = new THREE.Vector3();
+	this.rotation = new THREE.Vector3();
+	this.eulerOrder = THREE.Object3D.defaultEulerOrder;
+	this.scale = new THREE.Vector3( 1, 1, 1 );
+
+	this.renderDepth = null;
+
+	this.rotationAutoUpdate = true;
+
+	this.matrix = new THREE.Matrix4();
+	this.matrixWorld = new THREE.Matrix4();
+	this.matrixRotationWorld = new THREE.Matrix4();
+
+	this.matrixAutoUpdate = true;
+	this.matrixWorldNeedsUpdate = true;
+
+	this.quaternion = new THREE.Quaternion();
+	this.useQuaternion = false;
+
+	this.visible = true;
+
+	this.castShadow = false;
+	this.receiveShadow = false;
+
+	this.frustumCulled = true;
+
+	this._vector = new THREE.Vector3();
+
+};
+
+
+THREE.Object3D.prototype = {
+
+	constructor: THREE.Object3D,
+
+	applyMatrix: function ( matrix ) {
+
+		this.matrix.multiplyMatrices( matrix, this.matrix );
+
+		this.scale.getScaleFromMatrix( this.matrix );
+
+		var mat = new THREE.Matrix4().extractRotation( this.matrix );
+		this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder );
+
+		this.position.getPositionFromMatrix( this.matrix );
+
+	},
+
+	translate: function ( distance, axis ) {
+
+		this.matrix.rotateAxis( axis );
+		this.position.add( axis.multiplyScalar( distance ) );
+
+	},
+
+	translateX: function ( distance ) {
+
+		this.translate( distance, this._vector.set( 1, 0, 0 ) );
+
+	},
+
+	translateY: function ( distance ) {
+
+		this.translate( distance, this._vector.set( 0, 1, 0 ) );
+
+	},
+
+	translateZ: function ( distance ) {
+
+		this.translate( distance, this._vector.set( 0, 0, 1 ) );
+
+	},
+
+	localToWorld: function ( vector ) {
+
+		return vector.applyMatrix4( this.matrixWorld );
+
+	},
+
+	worldToLocal: function ( vector ) {
+
+		return vector.applyMatrix4( THREE.Object3D.__m1.getInverse( this.matrixWorld ) );
+
+	},
+
+	lookAt: function ( vector ) {
+
+		// TODO: Add hierarchy support.
+
+		this.matrix.lookAt( vector, this.position, this.up );
+
+		if ( this.rotationAutoUpdate ) {
+
+			if ( this.useQuaternion === false )  {
+
+				this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
+
+			} else {
+
+				this.quaternion.copy( this.matrix.decompose()[ 1 ] );
+
+			}
+
+		}
+
+	},
+
+	add: function ( object ) {
+
+		if ( object === this ) {
+
+			console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
+			return;
+
+		}
+
+		if ( object instanceof THREE.Object3D ) {
+
+			if ( object.parent !== undefined ) {
+
+				object.parent.remove( object );
+
+			}
+
+			object.parent = this;
+			this.children.push( object );
+
+			// add to scene
+
+			var scene = this;
+
+			while ( scene.parent !== undefined ) {
+
+				scene = scene.parent;
+
+			}
+
+			if ( scene !== undefined && scene instanceof THREE.Scene )  {
+
+				scene.__addObject( object );
+
+			}
+
+		}
+
+	},
+
+	remove: function ( object ) {
+
+		var index = this.children.indexOf( object );
+
+		if ( index !== - 1 ) {
+
+			object.parent = undefined;
+			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 ) {
+
+		callback( this );
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].traverse( callback );
+
+		}
+
+	},
+
+	getChildByName: function ( name, recursive ) {
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			var child = this.children[ i ];
+
+			if ( child.name === name ) {
+
+				return child;
+
+			}
+
+			if ( recursive === true ) {
+
+				child = child.getChildByName( name, recursive );
+
+				if ( child !== undefined ) {
+
+					return child;
+
+				}
+
+			}
+
+		}
+
+		return undefined;
+
+	},
+
+	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;
+
+	},
+
+	updateMatrix: function () {
+
+		this.matrix.setPosition( this.position );
+
+		if ( this.useQuaternion === false )  {
+
+			this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder );
+
+		} else {
+
+			this.matrix.setRotationFromQuaternion( this.quaternion );
+
+		}
+
+		if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) {
+
+			this.matrix.scale( this.scale );
+
+		}
+
+		this.matrixWorldNeedsUpdate = true;
+
+	},
+
+	updateMatrixWorld: function ( force ) {
+
+		if ( this.matrixAutoUpdate === true ) this.updateMatrix();
+
+		if ( this.matrixWorldNeedsUpdate === true || force === true ) {
+
+			if ( this.parent === undefined ) {
+
+				this.matrixWorld.copy( this.matrix );
+
+			} else {
+
+				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+			}
+
+			this.matrixWorldNeedsUpdate = false;
+
+			force = true;
+
+		}
+
+		// update children
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].updateMatrixWorld( force );
+
+		}
+
+	},
+
+	clone: function ( object ) {
+
+		if ( object === undefined ) object = new THREE.Object3D();
+
+		object.name = this.name;
+
+		object.up.copy( this.up );
+
+		object.position.copy( this.position );
+		if ( object.rotation instanceof THREE.Vector3 ) object.rotation.copy( this.rotation ); // because of Sprite madness
+		object.eulerOrder = this.eulerOrder;
+		object.scale.copy( this.scale );
+
+		object.renderDepth = this.renderDepth;
+
+		object.rotationAutoUpdate = this.rotationAutoUpdate;
+
+		object.matrix.copy( this.matrix );
+		object.matrixWorld.copy( this.matrixWorld );
+		object.matrixRotationWorld.copy( this.matrixRotationWorld );
+
+		object.matrixAutoUpdate = this.matrixAutoUpdate;
+		object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
+
+		object.quaternion.copy( this.quaternion );
+		object.useQuaternion = this.useQuaternion;
+
+		object.visible = this.visible;
+
+		object.castShadow = this.castShadow;
+		object.receiveShadow = this.receiveShadow;
+
+		object.frustumCulled = this.frustumCulled;
+
+		for ( var i = 0; i < this.children.length; i ++ ) {
+
+			var child = this.children[ i ];
+			object.add( child.clone() );
+
+		}
+
+		return object;
+
+	}
+
+};
+
+THREE.Object3D.__m1 = new THREE.Matrix4();
+THREE.Object3D.defaultEulerOrder = 'XYZ',
+
+THREE.Object3DIdCount = 0;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author julianwa / https://github.com/julianwa
+ */
+
+THREE.Projector = function () {
+
+	var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
+	_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
+	_face, _face3Count, _face3Pool = [], _face3PoolLength = 0,
+	_face4Count, _face4Pool = [], _face4PoolLength = 0,
+	_line, _lineCount, _linePool = [], _linePoolLength = 0,
+	_particle, _particleCount, _particlePool = [], _particlePoolLength = 0,
+
+	_renderData = { objects: [], sprites: [], 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(),
+	_normalViewMatrix = new THREE.Matrix3(),
+
+	_centroid = new THREE.Vector3(),
+
+	_frustum = new THREE.Frustum(),
+
+	_clippedVertex1PositionScreen = new THREE.Vector4(),
+	_clippedVertex2PositionScreen = new THREE.Vector4();
+
+	this.projectVector = function ( vector, camera ) {
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+
+		return vector.applyProjection( _viewProjectionMatrix );
+
+	};
+
+	this.unprojectVector = function ( vector, camera ) {
+
+		camera.projectionMatrixInverse.getInverse( camera.projectionMatrix );
+
+		_viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, camera.projectionMatrixInverse );
+
+		return vector.applyProjection( _viewProjectionMatrix );
+
+	};
+
+	this.pickingRay = function ( vector, camera ) {
+
+		// set two vectors with opposing z values
+		vector.z = -1.0;
+		var end = new THREE.Vector3( vector.x, vector.y, 1.0 );
+
+		this.unprojectVector( vector, camera );
+		this.unprojectVector( end, camera );
+
+		// find direction from vector to end
+		end.sub( vector ).normalize();
+
+		return new THREE.Raycaster( vector, end );
+
+	};
+
+	var projectGraph = function ( root, sortObjects ) {
+
+		_objectCount = 0;
+
+		_renderData.objects.length = 0;
+		_renderData.sprites.length = 0;
+		_renderData.lights.length = 0;
+
+		var projectObject = function ( parent ) {
+
+			for ( var c = 0, cl = parent.children.length; c < cl; c ++ ) {
+
+				var object = parent.children[ c ];
+
+				if ( object.visible === false ) continue;
+
+				if ( object instanceof THREE.Light ) {
+
+					_renderData.lights.push( object );
+
+				} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line ) {
+
+					if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+
+						_object = getNextObjectInPool();
+						_object.object = object;
+
+						if ( object.renderDepth !== null ) {
+
+							_object.z = object.renderDepth;
+
+						} else {
+
+							_vector3.getPositionFromMatrix( object.matrixWorld );
+							_vector3.applyProjection( _viewProjectionMatrix );
+							_object.z = _vector3.z;
+
+						}
+
+						_renderData.objects.push( _object );
+
+					}
+
+				} else if ( object instanceof THREE.Sprite || object instanceof THREE.Particle ) {
+
+					_object = getNextObjectInPool();
+					_object.object = object;
+
+					// TODO: Find an elegant and performant solution and remove this dupe code.
+
+					if ( object.renderDepth !== null ) {
+
+						_object.z = object.renderDepth;
+
+					} else {
+
+						_vector3.getPositionFromMatrix( object.matrixWorld );
+						_vector3.applyProjection( _viewProjectionMatrix );
+						_object.z = _vector3.z;
+
+					}
+
+					_renderData.sprites.push( _object );
+
+				} else {
+
+					_object = getNextObjectInPool();
+					_object.object = object;
+
+					if ( object.renderDepth !== null ) {
+
+						_object.z = object.renderDepth;
+
+					} else {
+
+						_vector3.getPositionFromMatrix( object.matrixWorld );
+						_vector3.applyProjection( _viewProjectionMatrix );
+						_object.z = _vector3.z;
+
+					}
+
+					_renderData.objects.push( _object );
+
+				}
+
+				projectObject( object );
+
+			}
+
+		};
+
+		projectObject( root );
+
+		if ( sortObjects === true ) _renderData.objects.sort( painterSort );
+
+		return _renderData;
+
+	};
+
+	this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
+
+		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;
+
+		_face3Count = 0;
+		_face4Count = 0;
+		_lineCount = 0;
+		_particleCount = 0;
+
+		_renderData.elements.length = 0;
+
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
+		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
+
+		_normalViewMatrix.getInverse( _viewMatrix );
+		_normalViewMatrix.transpose();
+
+		_frustum.setFromMatrix( _viewProjectionMatrix );
+
+		_renderData = projectGraph( scene, sortObjects );
+
+		for ( o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
+
+			object = _renderData.objects[ o ].object;
+
+			_modelMatrix = object.matrixWorld;
+
+			_vertexCount = 0;
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+
+				vertices = geometry.vertices;
+				faces = geometry.faces;
+				faceVertexUvs = geometry.faceVertexUvs;
+
+				_normalMatrix.getInverse( _modelMatrix );
+				_normalMatrix.transpose();
+
+				isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
+				objectMaterials = isFaceMaterial === true ? object.material : null;
+
+				for ( v = 0, vl = vertices.length; v < vl; v ++ ) {
+
+					_vertex = getNextVertexInPool();
+
+					_vertex.positionWorld.copy( vertices[ v ] ).applyMatrix4( _modelMatrix );
+					_vertex.positionScreen.copy( _vertex.positionWorld ).applyMatrix4( _viewProjectionMatrix );
+
+					_vertex.positionScreen.x /= _vertex.positionScreen.w;
+					_vertex.positionScreen.y /= _vertex.positionScreen.w;
+					_vertex.positionScreen.z /= _vertex.positionScreen.w;
+
+					_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 );
+
+				}
+
+				for ( f = 0, fl = faces.length; f < fl; f ++ ) {
+
+					face = faces[ f ];
+
+					var material = isFaceMaterial === true
+						? objectMaterials.materials[ face.materialIndex ]
+						: object.material;
+
+					if ( material === undefined ) continue;
+
+					var side = material.side;
+
+					if ( face instanceof THREE.Face3 ) {
+
+						v1 = _vertexPool[ face.a ];
+						v2 = _vertexPool[ face.b ];
+						v3 = _vertexPool[ face.c ];
+
+						_points3[ 0 ] = v1.positionScreen;
+						_points3[ 1 ] = v2.positionScreen;
+						_points3[ 2 ] = v3.positionScreen;
+
+						if ( v1.visible === true || v2.visible === true || v3.visible === true ||
+							_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ) ) {
+
+							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 ) ) {
+
+								_face = getNextFace3InPool();
+
+								_face.v1.copy( v1 );
+								_face.v2.copy( v2 );
+								_face.v3.copy( v3 );
+
+							} else {
+
+								continue;
+
+							}
+
+						} else {
+
+							continue;
+
+						}
+
+					} else if ( face instanceof THREE.Face4 ) {
+
+						v1 = _vertexPool[ face.a ];
+						v2 = _vertexPool[ face.b ];
+						v3 = _vertexPool[ face.c ];
+						v4 = _vertexPool[ face.d ];
+
+						_points4[ 0 ] = v1.positionScreen;
+						_points4[ 1 ] = v2.positionScreen;
+						_points4[ 2 ] = v3.positionScreen;
+						_points4[ 3 ] = v4.positionScreen;
+
+						if ( v1.visible === true || v2.visible === true || v3.visible === true || v4.visible === true ||
+							_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points4 ) ) ) {
+
+							visible = ( v4.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) -
+								( v4.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) < 0 ||
+								( v2.positionScreen.x - v3.positionScreen.x ) * ( v4.positionScreen.y - v3.positionScreen.y ) -
+								( v2.positionScreen.y - v3.positionScreen.y ) * ( v4.positionScreen.x - v3.positionScreen.x ) < 0;
+
+
+							if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) {
+
+								_face = getNextFace4InPool();
+
+								_face.v1.copy( v1 );
+								_face.v2.copy( v2 );
+								_face.v3.copy( v3 );
+								_face.v4.copy( v4 );
+
+							} else {
+
+								continue;
+
+							}
+
+						} else {
+
+							continue;
+
+						}
+
+					}
+
+					_face.normalModel.copy( face.normal );
+
+					if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+
+						_face.normalModel.negate();
+
+					}
+
+					_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
+
+					_face.normalModelView.copy( _face.normalModel ).applyMatrix3( _normalViewMatrix );
+
+					_face.centroidModel.copy( face.centroid ).applyMatrix4( _modelMatrix );
+
+					faceVertexNormals = face.vertexNormals;
+
+					for ( n = 0, nl = faceVertexNormals.length; 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();
+
+						var normalModelView = _face.vertexNormalsModelView[ n ];
+						normalModelView.copy( normalModel ).applyMatrix3( _normalViewMatrix );
+
+					}
+
+					_face.vertexNormalsLength = faceVertexNormals.length;
+
+					for ( c = 0, cl = faceVertexUvs.length; c < cl; c ++ ) {
+
+						uvs = faceVertexUvs[ c ][ f ];
+
+						if ( uvs === undefined ) continue;
+
+						for ( u = 0, ul = uvs.length; u < ul; u ++ ) {
+
+							_face.uvs[ c ][ u ] = uvs[ u ];
+
+						}
+
+					}
+
+					_face.color = face.color;
+					_face.material = material;
+
+					_centroid.copy( _face.centroidModel ).applyProjection( _viewProjectionMatrix );
+
+					_face.z = _centroid.z;
+
+					_renderData.elements.push( _face );
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
+
+				vertices = object.geometry.vertices;
+
+				v1 = getNextVertexInPool();
+				v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
+
+				// Handle LineStrip and LinePieces
+				var step = object.type === THREE.LinePieces ? 2 : 1;
+
+				for ( 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.v1.positionScreen.copy( _clippedVertex1PositionScreen );
+						_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
+
+						_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
+
+						_line.material = object.material;
+
+						_renderData.elements.push( _line );
+
+					}
+
+				}
+
+			}
+
+		}
+
+		for ( o = 0, ol = _renderData.sprites.length; o < ol; o++ ) {
+
+			object = _renderData.sprites[ o ].object;
+
+			_modelMatrix = object.matrixWorld;
+
+			if ( object instanceof THREE.Particle ) {
+
+				_vector4.set( _modelMatrix.elements[12], _modelMatrix.elements[13], _modelMatrix.elements[14], 1 );
+				_vector4.applyMatrix4( _viewProjectionMatrix );
+
+				_vector4.z /= _vector4.w;
+
+				if ( _vector4.z > 0 && _vector4.z < 1 ) {
+
+					_particle = getNextParticleInPool();
+					_particle.object = object;
+					_particle.x = _vector4.x / _vector4.w;
+					_particle.y = _vector4.y / _vector4.w;
+					_particle.z = _vector4.z;
+
+					_particle.rotation = object.rotation.z;
+
+					_particle.scale.x = object.scale.x * Math.abs( _particle.x - ( _vector4.x + camera.projectionMatrix.elements[0] ) / ( _vector4.w + camera.projectionMatrix.elements[12] ) );
+					_particle.scale.y = object.scale.y * Math.abs( _particle.y - ( _vector4.y + camera.projectionMatrix.elements[5] ) / ( _vector4.w + camera.projectionMatrix.elements[13] ) );
+
+					_particle.material = object.material;
+
+					_renderData.elements.push( _particle );
+
+				}
+
+			}
+
+		}
+
+		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 getNextFace3InPool() {
+
+		if ( _face3Count === _face3PoolLength ) {
+
+			var face = new THREE.RenderableFace3();
+			_face3Pool.push( face );
+			_face3PoolLength ++;
+			_face3Count ++;
+			return face;
+
+		}
+
+		return _face3Pool[ _face3Count ++ ];
+
+
+	}
+
+	function getNextFace4InPool() {
+
+		if ( _face4Count === _face4PoolLength ) {
+
+			var face = new THREE.RenderableFace4();
+			_face4Pool.push( face );
+			_face4PoolLength ++;
+			_face4Count ++;
+			return face;
+
+		}
+
+		return _face4Pool[ _face4Count ++ ];
+
+	}
+
+	function getNextLineInPool() {
+
+		if ( _lineCount === _linePoolLength ) {
+
+			var line = new THREE.RenderableLine();
+			_linePool.push( line );
+			_linePoolLength ++;
+			_lineCount ++
+			return line;
+
+		}
+
+		return _linePool[ _lineCount ++ ];
+
+	}
+
+	function getNextParticleInPool() {
+
+		if ( _particleCount === _particlePoolLength ) {
+
+			var particle = new THREE.RenderableParticle();
+			_particlePool.push( particle );
+			_particlePoolLength ++;
+			_particleCount ++
+			return particle;
+
+		}
+
+		return _particlePool[ _particleCount ++ ];
+
+	}
+
+	//
+
+	function painterSort( a, b ) {
+
+		return b.z - a.z;
+
+	}
+
+	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;
+
+			}
+
+		}
+
+	}
+
+};
+/**
+ * @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;
+
+	this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
+	this.vertexNormals = normal instanceof Array ? normal : [ ];
+
+	this.color = color instanceof THREE.Color ? color : new THREE.Color();
+	this.vertexColors = color instanceof Array ? color : [];
+
+	this.vertexTangents = [];
+
+	this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+
+	this.centroid = new THREE.Vector3();
+
+};
+
+THREE.Face3.prototype = {
+
+	constructor: THREE.Face3,
+
+	clone: function () {
+
+		var face = new THREE.Face3( this.a, this.b, this.c );
+
+		face.normal.copy( this.normal );
+		face.color.copy( this.color );
+		face.centroid.copy( this.centroid );
+
+		face.materialIndex = this.materialIndex;
+
+		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;
+
+	}
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
+
+	this.a = a;
+	this.b = b;
+	this.c = c;
+	this.d = d;
+
+	this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
+	this.vertexNormals = normal instanceof Array ? normal : [ ];
+
+	this.color = color instanceof THREE.Color ? color : new THREE.Color();
+	this.vertexColors = color instanceof Array ? color : [];
+
+	this.vertexTangents = [];
+
+	this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+
+	this.centroid = new THREE.Vector3();
+
+};
+
+THREE.Face4.prototype = {
+
+	constructor: THREE.Face4,
+
+	clone: function () {
+
+		var face = new THREE.Face4( this.a, this.b, this.c, this.d );
+
+		face.normal.copy( this.normal );
+		face.color.copy( this.color );
+		face.centroid.copy( this.centroid );
+
+		face.materialIndex = this.materialIndex;
+
+		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;
+
+	}
+
+};
+/**
+ * @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.Geometry = function () {
+
+	THREE.EventDispatcher.call( this );
+
+	this.id = THREE.GeometryIdCount ++;
+
+	this.name = '';
+
+	this.vertices = [];
+	this.colors = [];  // one-to-one vertex colors, used in ParticleSystem, Line and Ribbon
+	this.normals = []; // one-to-one vertex normals, used in Ribbon
+
+	this.faces = [];
+
+	this.faceUvs = [[]];
+	this.faceVertexUvs = [[]];
+
+	this.morphTargets = [];
+	this.morphColors = [];
+	this.morphNormals = [];
+
+	this.skinWeights = [];
+	this.skinIndices = [];
+
+	this.lineDistances = [];
+
+	this.boundingBox = null;
+	this.boundingSphere = null;
+
+	this.hasTangents = false;
+
+	this.dynamic = true; // the intermediate typed arrays will be deleted when set to false
+
+	// update flags
+
+	this.verticesNeedUpdate = false;
+	this.elementsNeedUpdate = false;
+	this.uvsNeedUpdate = false;
+	this.normalsNeedUpdate = false;
+	this.tangentsNeedUpdate = false;
+	this.colorsNeedUpdate = false;
+	this.lineDistancesNeedUpdate = false;
+
+	this.buffersNeedUpdate = false;
+
+};
+
+THREE.Geometry.prototype = {
+
+	constructor: THREE.Geometry,
+
+	applyMatrix: function ( matrix ) {
+
+		var normalMatrix = new THREE.Matrix3().getInverse( matrix ).transpose();
+
+		for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
+
+			var vertex = this.vertices[ i ];
+			vertex.applyMatrix4( matrix );
+
+		}
+
+		for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
+
+			var face = this.faces[ i ];
+			face.normal.applyMatrix3( normalMatrix ).normalize();
+
+			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+
+				face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
+
+			}
+
+			face.centroid.applyMatrix4( matrix );
+
+		}
+
+	},
+
+	computeCentroids: function () {
+
+		var f, fl, face;
+
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			face = this.faces[ f ];
+			face.centroid.set( 0, 0, 0 );
+
+			if ( face instanceof THREE.Face3 ) {
+
+				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 );
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				face.centroid.add( this.vertices[ face.a ] );
+				face.centroid.add( this.vertices[ face.b ] );
+				face.centroid.add( this.vertices[ face.c ] );
+				face.centroid.add( this.vertices[ face.d ] );
+				face.centroid.divideScalar( 4 );
+
+			}
+
+		}
+
+	},
+
+	computeFaceNormals: function () {
+
+		var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+
+		for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			var face = this.faces[ f ];
+
+			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 );
+
+			cb.normalize();
+
+			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)
+
+		if ( this.__tmpVertices === undefined ) {
+
+			this.__tmpVertices = new Array( this.vertices.length );
+			vertices = this.__tmpVertices;
+
+			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+				vertices[ v ] = new THREE.Vector3();
+
+			}
+
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+				face = this.faces[ f ];
+
+				if ( face instanceof THREE.Face3 ) {
+
+					face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+
+				} else if ( face instanceof THREE.Face4 ) {
+
+					face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+
+				}
+
+			}
+
+		} else {
+
+			vertices = this.__tmpVertices;
+
+			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+				vertices[ v ].set( 0, 0, 0 );
+
+			}
+
+		}
+
+		if ( areaWeighted ) {
+
+			// vertex normals weighted by triangle areas
+			// http://www.iquilezles.org/www/articles/normals/normals.htm
+
+			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();
+
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+				face = this.faces[ f ];
+
+				if ( face instanceof THREE.Face3 ) {
+
+					vA = this.vertices[ face.a ];
+					vB = this.vertices[ face.b ];
+					vC = this.vertices[ face.c ];
+
+					cb.subVectors( vC, vB );
+					ab.subVectors( vA, vB );
+					cb.cross( ab );
+
+					vertices[ face.a ].add( cb );
+					vertices[ face.b ].add( cb );
+					vertices[ face.c ].add( cb );
+
+				} else if ( face instanceof THREE.Face4 ) {
+
+					vA = this.vertices[ face.a ];
+					vB = this.vertices[ face.b ];
+					vC = this.vertices[ face.c ];
+					vD = this.vertices[ face.d ];
+
+					// abd
+
+					db.subVectors( vD, vB );
+					ab.subVectors( vA, vB );
+					db.cross( ab );
+
+					vertices[ face.a ].add( db );
+					vertices[ face.b ].add( db );
+					vertices[ face.d ].add( db );
+
+					// bcd
+
+					dc.subVectors( vD, vC );
+					bc.subVectors( vB, vC );
+					dc.cross( bc );
+
+					vertices[ face.b ].add( dc );
+					vertices[ face.c ].add( dc );
+					vertices[ face.d ].add( dc );
+
+				}
+
+			}
+
+		} else {
+
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+				face = this.faces[ f ];
+
+				if ( face instanceof THREE.Face3 ) {
+
+					vertices[ face.a ].add( face.normal );
+					vertices[ face.b ].add( face.normal );
+					vertices[ face.c ].add( face.normal );
+
+				} else if ( face instanceof THREE.Face4 ) {
+
+					vertices[ face.a ].add( face.normal );
+					vertices[ face.b ].add( face.normal );
+					vertices[ face.c ].add( face.normal );
+					vertices[ face.d ].add( face.normal );
+
+				}
+
+			}
+
+		}
+
+		for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+			vertices[ v ].normalize();
+
+		}
+
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			face = this.faces[ f ];
+
+			if ( face instanceof THREE.Face3 ) {
+
+				face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
+				face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
+				face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
+				face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
+				face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
+				face.vertexNormals[ 3 ].copy( vertices[ face.d ] );
+
+			}
+
+		}
+
+	},
+
+	computeMorphNormals: function () {
+
+		var i, il, f, fl, face;
+
+		// 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 ];
+
+			if ( ! face.__originalFaceNormal ) {
+
+				face.__originalFaceNormal = face.normal.clone();
+
+			} else {
+
+				face.__originalFaceNormal.copy( face.normal );
+
+			}
+
+			if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
+
+			for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
+
+				if ( ! face.__originalVertexNormals[ i ] ) {
+
+					face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
+
+				} else {
+
+					face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
+
+				}
+
+			}
+
+		}
+
+		// use temp geometry to compute face and vertex normals for each morph
+
+		var tmpGeo = new THREE.Geometry();
+		tmpGeo.faces = this.faces;
+
+		for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
+
+			// create on first access
+
+			if ( ! this.morphNormals[ i ] ) {
+
+				this.morphNormals[ i ] = {};
+				this.morphNormals[ i ].faceNormals = [];
+				this.morphNormals[ i ].vertexNormals = [];
+
+				var dstNormalsFace = this.morphNormals[ i ].faceNormals;
+				var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
+
+				var faceNormal, vertexNormals;
+
+				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+					face = this.faces[ f ];
+
+					faceNormal = new THREE.Vector3();
+
+					if ( face instanceof THREE.Face3 ) {
+
+						vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
+
+					} else {
+
+						vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3(), d: new THREE.Vector3() };
+
+					}
+
+					dstNormalsFace.push( faceNormal );
+					dstNormalsVertex.push( vertexNormals );
+
+				}
+
+			}
+
+			var morphNormals = this.morphNormals[ i ];
+
+			// set vertices to morph target
+
+			tmpGeo.vertices = this.morphTargets[ i ].vertices;
+
+			// compute morph normals
+
+			tmpGeo.computeFaceNormals();
+			tmpGeo.computeVertexNormals();
+
+			// store morph normals
+
+			var faceNormal, vertexNormals;
+
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+				face = this.faces[ f ];
+
+				faceNormal = morphNormals.faceNormals[ f ];
+				vertexNormals = morphNormals.vertexNormals[ f ];
+
+				faceNormal.copy( face.normal );
+
+				if ( face instanceof THREE.Face3 ) {
+
+					vertexNormals.a.copy( face.vertexNormals[ 0 ] );
+					vertexNormals.b.copy( face.vertexNormals[ 1 ] );
+					vertexNormals.c.copy( face.vertexNormals[ 2 ] );
+
+				} else {
+
+					vertexNormals.a.copy( face.vertexNormals[ 0 ] );
+					vertexNormals.b.copy( face.vertexNormals[ 1 ] );
+					vertexNormals.c.copy( face.vertexNormals[ 2 ] );
+					vertexNormals.d.copy( face.vertexNormals[ 3 ] );
+
+				}
+
+			}
+
+		}
+
+		// restore original normals
+
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			face = this.faces[ f ];
+
+			face.normal = face.__originalFaceNormal;
+			face.vertexNormals = face.__originalVertexNormals;
+
+		}
+
+	},
+
+	computeTangents: function () {
+
+		// based on http://www.terathon.com/code/tangent.html
+		// tangents go to vertices
+
+		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();
+
+		}
+
+		function handleTriangle( context, a, b, c, ua, ub, uc ) {
+
+			vA = context.vertices[ a ];
+			vB = context.vertices[ b ];
+			vC = context.vertices[ c ];
+
+			uvA = uv[ ua ];
+			uvB = uv[ ub ];
+			uvC = uv[ uc ];
+
+			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 );
+
+			tan1[ a ].add( sdir );
+			tan1[ b ].add( sdir );
+			tan1[ c ].add( sdir );
+
+			tan2[ a ].add( tdir );
+			tan2[ b ].add( tdir );
+			tan2[ c ].add( tdir );
+
+		}
+
+		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
+
+			if ( face instanceof THREE.Face3 ) {
+
+				handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				handleTriangle( this, face.a, face.b, face.d, 0, 1, 3 );
+				handleTriangle( this, face.b, face.c, face.d, 1, 2, 3 );
+
+			}
+
+		}
+
+		var faceIndex = [ 'a', 'b', 'c', 'd' ];
+
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			face = this.faces[ f ];
+
+			for ( i = 0; i < face.vertexNormals.length; i++ ) {
+
+				n.copy( face.vertexNormals[ i ] );
+
+				vertexIndex = face[ faceIndex[ i ] ];
+
+				t = tan1[ vertexIndex ];
+
+				// Gram-Schmidt orthogonalize
+
+				tmp.copy( t );
+				tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
+
+				// Calculate handedness
+
+				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 );
+
+			}
+
+		}
+
+		this.hasTangents = true;
+
+	},
+
+	computeLineDistances: function ( ) {
+
+		var d = 0;
+		var vertices = this.vertices;
+
+		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+			if ( i > 0 ) {
+
+				d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
+
+			}
+
+			this.lineDistances[ i ] = d;
+
+		}
+
+	},
+
+	computeBoundingBox: function () {
+
+		if ( this.boundingBox === null ) {
+
+			this.boundingBox = new THREE.Box3();
+
+		}
+
+		this.boundingBox.setFromPoints( this.vertices );
+
+	},
+
+	computeBoundingSphere: function () {
+
+		if ( this.boundingSphere === null ) {
+
+			this.boundingSphere = new THREE.Sphere();
+
+		}
+
+		this.boundingSphere.setFromCenterAndPoints( this.boundingSphere.center, this.vertices );
+
+	},
+
+	/*
+	 * Checks for duplicate vertices with hashmap.
+	 * Duplicated vertices are removed
+	 * and faces' vertices are updated.
+	 */
+
+	mergeVertices: function () {
+
+		var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique)
+		var unique = [], changes = [];
+
+		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;
+
+		// reset cache of vertices as it now will be changing.
+		this.__tmpVertices = undefined;
+
+		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 ) ].join( '_' );
+
+			if ( verticesMap[ key ] === undefined ) {
+
+				verticesMap[ key ] = i;
+				unique.push( this.vertices[ i ] );
+				changes[ i ] = unique.length - 1;
+
+			} else {
+
+				//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
+				changes[ i ] = changes[ verticesMap[ key ] ];
+
+			}
+
+		};
+
+
+		// if faces are completely degenerate after merging vertices, we
+		// have to remove them from the geometry.
+		var faceIndicesToRemove = [];
+
+		for( i = 0, il = this.faces.length; i < il; i ++ ) {
+
+			face = this.faces[ i ];
+
+			if ( face instanceof THREE.Face3 ) {
+
+				face.a = changes[ face.a ];
+				face.b = changes[ face.b ];
+				face.c = changes[ face.c ];
+
+				indices = [ face.a, face.b, face.c ];
+
+				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;
+
+					}
+				}
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				face.a = changes[ face.a ];
+				face.b = changes[ face.b ];
+				face.c = changes[ face.c ];
+				face.d = changes[ face.d ];
+
+				// check dups in (a, b, c, d) and convert to -> face3
+
+				indices = [ face.a, face.b, face.c, face.d ];
+
+				var dupIndex = -1;
+
+				for ( var n = 0; n < 4; n ++ ) {
+
+					if ( indices[ n ] == indices[ ( n + 1 ) % 4 ] ) {
+
+						// if more than one duplicated vertex is found
+						// we can't generate any valid Face3's, thus
+						// we need to remove this face complete.
+						if ( dupIndex >= 0 ) {
+
+							faceIndicesToRemove.push( i );
+
+						}
+
+						dupIndex = n;
+
+					}
+				}
+
+				if ( dupIndex >= 0 ) {
+
+					indices.splice( dupIndex, 1 );
+
+					var newFace = new THREE.Face3( indices[0], indices[1], indices[2], face.normal, face.color, face.materialIndex );
+
+					for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
+
+						u = this.faceVertexUvs[ j ][ i ];
+
+						if ( u ) {
+							u.splice( dupIndex, 1 );
+						}
+
+					}
+
+					if( face.vertexNormals && face.vertexNormals.length > 0) {
+
+						newFace.vertexNormals = face.vertexNormals;
+						newFace.vertexNormals.splice( dupIndex, 1 );
+
+					}
+
+					if( face.vertexColors && face.vertexColors.length > 0 ) {
+
+						newFace.vertexColors = face.vertexColors;
+						newFace.vertexColors.splice( dupIndex, 1 );
+					}
+
+					this.faces[ i ] = newFace;
+				}
+
+			}
+
+		}
+
+		for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
+
+			this.faces.splice( i, 1 );
+
+			for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
+
+				this.faceVertexUvs[ j ].splice( i, 1 );
+
+			}
+
+		}
+
+		// Use unique set of vertices
+
+		var diff = this.vertices.length - unique.length;
+		this.vertices = unique;
+		return diff;
+
+	},
+
+	clone: function () {
+
+		var geometry = new THREE.Geometry();
+
+		var vertices = this.vertices;
+
+		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+			geometry.vertices.push( vertices[ i ].clone() );
+
+		}
+
+		var faces = this.faces;
+
+		for ( var i = 0, il = faces.length; i < il; i ++ ) {
+
+			geometry.faces.push( faces[ i ].clone() );
+
+		}
+
+		var uvs = this.faceVertexUvs[ 0 ];
+
+		for ( var i = 0, il = uvs.length; i < il; i ++ ) {
+
+			var uv = uvs[ i ], uvCopy = [];
+
+			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+
+			}
+
+			geometry.faceVertexUvs[ 0 ].push( uvCopy );
+
+		}
+
+		return geometry;
+
+	},
+
+	dispose: function () {
+
+		this.dispatchEvent( { type: 'dispose' } );
+
+	}
+
+};
+
+THREE.GeometryIdCount = 0;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.BufferGeometry = function () {
+
+	THREE.EventDispatcher.call( this );
+
+	this.id = THREE.GeometryIdCount ++;
+
+	// attributes
+
+	this.attributes = {};
+
+	// attributes typed arrays are kept only if dynamic flag is set
+
+	this.dynamic = false;
+
+	// offsets for chunks when using indexed elements
+
+	this.offsets = [];
+
+	// boundings
+
+	this.boundingBox = null;
+	this.boundingSphere = null;
+
+	this.hasTangents = false;
+
+	// for compatibility
+
+	this.morphTargets = [];
+
+};
+
+THREE.BufferGeometry.prototype = {
+
+	constructor : THREE.BufferGeometry,
+
+	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;
+
+		if ( positionArray !== undefined ) {
+
+			matrix.multiplyVector3Array( positionArray );
+			this.verticesNeedUpdate = true;
+
+		}
+
+		if ( normalArray !== undefined ) {
+
+			var normalMatrix = new THREE.Matrix3();
+			normalMatrix.getInverse( matrix ).transpose();
+
+			normalMatrix.multiplyVector3Array( normalArray );
+
+			this.normalizeNormals();
+
+			this.normalsNeedUpdate = true;
+
+		}
+
+	},
+
+	computeBoundingBox: function () {
+
+		if ( this.boundingBox === null ) {
+
+			this.boundingBox = new THREE.Box3();
+
+		}
+
+		var positions = this.attributes[ "position" ].array;
+
+		if ( positions ) {
+
+			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 ];
+			}
+
+			for ( var i = 3, il = positions.length; i < il; i += 3 ) {
+
+				x = positions[ i ];
+				y = positions[ i + 1 ];
+				z = positions[ i + 2 ];
+
+				// bounding box
+
+				if ( x < bb.min.x ) {
+
+					bb.min.x = x;
+
+				} else if ( x > bb.max.x ) {
+
+					bb.max.x = x;
+
+				}
+
+				if ( y < bb.min.y ) {
+
+					bb.min.y = y;
+
+				} else if ( y > bb.max.y ) {
+
+					bb.max.y = y;
+
+				}
+
+				if ( z < bb.min.z ) {
+
+					bb.min.z = z;
+
+				} else if ( z > bb.max.z ) {
+
+					bb.max.z = z;
+
+				}
+
+			}
+
+		}
+
+		if ( positions === undefined || positions.length === 0 ) {
+
+			this.boundingBox.min.set( 0, 0, 0 );
+			this.boundingBox.max.set( 0, 0, 0 );
+
+		}
+
+	},
+
+	computeBoundingSphere: function () {
+
+		if ( this.boundingSphere === null ) {
+
+			this.boundingSphere = new THREE.Sphere();
+
+		}
+
+		var positions = this.attributes[ "position" ].array;
+
+		if ( positions ) {
+
+			var radiusSq, maxRadiusSq = 0;
+			var x, y, z;
+
+			for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+				x = positions[ i ];
+				y = positions[ i + 1 ];
+				z = positions[ i + 2 ];
+
+				radiusSq =  x * x + y * y + z * z;
+				if ( radiusSq > maxRadiusSq ) maxRadiusSq = radiusSq;
+
+			}
+
+			this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
+
+		}
+
+	},
+
+	computeVertexNormals: function () {
+
+		if ( this.attributes[ "position" ] ) {
+
+			var i, il;
+			var j, jl;
+
+			var nVertexElements = this.attributes[ "position" ].array.length;
+
+			if ( this.attributes[ "normal" ] === undefined ) {
+
+				this.attributes[ "normal" ] = {
+
+					itemSize: 3,
+					array: new Float32Array( nVertexElements ),
+					numItems: nVertexElements
+
+				};
+
+			} else {
+
+				// reset existing normals to zero
+
+				for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
+
+					this.attributes[ "normal" ].array[ i ] = 0;
+
+				}
+
+			}
+
+			var positions = this.attributes[ "position" ].array;
+			var normals = this.attributes[ "normal" ].array;
+
+			var vA, vB, vC, x, y, z,
+
+			pA = new THREE.Vector3(),
+			pB = new THREE.Vector3(),
+			pC = new THREE.Vector3(),
+
+			cb = new THREE.Vector3(),
+			ab = new THREE.Vector3();
+
+			// indexed elements
+
+			if ( this.attributes[ "index" ] ) {
+
+				var indices = this.attributes[ "index" ].array;
+
+				var offsets = this.offsets;
+
+				for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+
+					var start = offsets[ j ].start;
+					var count = offsets[ j ].count;
+					var index = offsets[ j ].index;
+
+					for ( i = start, il = start + count; i < il; i += 3 ) {
+
+						vA = index + indices[ i ];
+						vB = index + indices[ i + 1 ];
+						vC = index + indices[ i + 2 ];
+
+						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 );
+
+						cb.subVectors( pC, pB );
+						ab.subVectors( pA, pB );
+						cb.cross( ab );
+
+						normals[ vA * 3 ]     += cb.x;
+						normals[ vA * 3 + 1 ] += cb.y;
+						normals[ vA * 3 + 2 ] += cb.z;
+
+						normals[ vB * 3 ]     += cb.x;
+						normals[ vB * 3 + 1 ] += cb.y;
+						normals[ vB * 3 + 2 ] += cb.z;
+
+						normals[ vC * 3 ]     += cb.x;
+						normals[ vC * 3 + 1 ] += cb.y;
+						normals[ vC * 3 + 2 ] += cb.z;
+
+					}
+
+				}
+
+			// non-indexed elements (unconnected triangle soup)
+
+			} else {
+
+				for ( i = 0, il = positions.length; i < il; i += 9 ) {
+
+					x = positions[ i ];
+					y = positions[ i + 1 ];
+					z = positions[ i + 2 ];
+					pA.set( x, y, z );
+
+					x = positions[ i + 3 ];
+					y = positions[ i + 4 ];
+					z = positions[ i + 5 ];
+					pB.set( x, y, z );
+
+					x = positions[ i + 6 ];
+					y = positions[ i + 7 ];
+					z = positions[ i + 8 ];
+					pC.set( x, y, z );
+
+					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;
+
+					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;
+
+				}
+
+			}
+
+			this.normalizeNormals();
+
+			this.normalsNeedUpdate = true;
+
+		}
+
+	},
+
+	normalizeNormals: function () {
+
+		var normals = this.attributes[ "normal" ].array;
+
+		var x, y, z, n;
+
+		for ( var i = 0, il = normals.length; i < il; i += 3 ) {
+
+			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;
+
+		}
+
+	},
+
+	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;
+
+		}
+
+		var indices = this.attributes[ "index" ].array;
+		var positions = this.attributes[ "position" ].array;
+		var normals = this.attributes[ "normal" ].array;
+		var uvs = this.attributes[ "uv" ].array;
+
+		var nVertices = positions.length / 3;
+
+		if ( this.attributes[ "tangent" ] === undefined ) {
+
+			var nTangentElements = 4 * nVertices;
+
+			this.attributes[ "tangent" ] = {
+
+				itemSize: 4,
+				array: new Float32Array( nTangentElements ),
+				numItems: nTangentElements
+
+			};
+
+		}
+
+		var tangents = this.attributes[ "tangent" ].array;
+
+		var tan1 = [], tan2 = [];
+
+		for ( var k = 0; k < nVertices; k ++ ) {
+
+			tan1[ k ] = new THREE.Vector3();
+			tan2[ k ] = new THREE.Vector3();
+
+		}
+
+		var xA, yA, zA,
+			xB, yB, zB,
+			xC, yC, zC,
+
+			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 ) {
+
+			xA = positions[ a * 3 ];
+			yA = positions[ a * 3 + 1 ];
+			zA = positions[ a * 3 + 2 ];
+
+			xB = positions[ b * 3 ];
+			yB = positions[ b * 3 + 1 ];
+			zB = positions[ b * 3 + 2 ];
+
+			xC = positions[ c * 3 ];
+			yC = positions[ c * 3 + 1 ];
+			zC = positions[ c * 3 + 2 ];
+
+			uA = uvs[ a * 2 ];
+			vA = uvs[ a * 2 + 1 ];
+
+			uB = uvs[ b * 2 ];
+			vB = uvs[ b * 2 + 1 ];
+
+			uC = uvs[ c * 2 ];
+			vC = uvs[ c * 2 + 1 ];
+
+			x1 = xB - xA;
+			x2 = xC - xA;
+
+			y1 = yB - yA;
+			y2 = yC - yA;
+
+			z1 = zB - zA;
+			z2 = zC - zA;
+
+			s1 = uB - uA;
+			s2 = uC - uA;
+
+			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
+			);
+
+			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 );
+
+		}
+
+		var i, il;
+		var j, jl;
+		var iA, iB, iC;
+
+		var offsets = this.offsets;
+
+		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+
+			var start = offsets[ j ].start;
+			var count = offsets[ j ].count;
+			var index = offsets[ j ].index;
+
+			for ( i = start, il = start + count; i < il; i += 3 ) {
+
+				iA = index + indices[ i ];
+				iB = index + indices[ i + 1 ];
+				iC = index + indices[ i + 2 ];
+
+				handleTriangle( iA, iB, iC );
+
+			}
+
+		}
+
+		var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
+		var n = new THREE.Vector3(), n2 = new THREE.Vector3();
+		var w, t, test;
+
+		function handleVertex( v ) {
+
+			n.x = normals[ v * 3 ];
+			n.y = normals[ v * 3 + 1 ];
+			n.z = normals[ v * 3 + 2 ];
+
+			n2.copy( n );
+
+			t = tan1[ v ];
+
+			// Gram-Schmidt orthogonalize
+
+			tmp.copy( t );
+			tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
+
+			// Calculate handedness
+
+			tmp2.crossVectors( n2, t );
+			test = tmp2.dot( tan2[ v ] );
+			w = ( test < 0.0 ) ? -1.0 : 1.0;
+
+			tangents[ v * 4 ] 	  = tmp.x;
+			tangents[ v * 4 + 1 ] = tmp.y;
+			tangents[ v * 4 + 2 ] = tmp.z;
+			tangents[ v * 4 + 3 ] = w;
+
+		}
+
+		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+
+			var start = offsets[ j ].start;
+			var count = offsets[ j ].count;
+			var index = offsets[ j ].index;
+
+			for ( i = start, il = start + count; i < il; i += 3 ) {
+
+				iA = index + indices[ i ];
+				iB = index + indices[ i + 1 ];
+				iC = index + indices[ i + 2 ];
+
+				handleVertex( iA );
+				handleVertex( iB );
+				handleVertex( iC );
+
+			}
+
+		}
+
+		this.hasTangents = true;
+		this.tangentsNeedUpdate = true;
+
+	},
+
+	dispose: function () {
+
+		this.dispatchEvent( { type: 'dispose' } );
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+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 ( vector ) {
+
+	// TODO: Add hierarchy support.
+
+	this.matrix.lookAt( this.position, vector, this.up );
+
+	if ( this.rotationAutoUpdate === true ) {
+
+		if ( this.useQuaternion === false )  {
+
+			this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
+
+		} else {
+
+			this.quaternion.copy( this.matrix.decompose()[ 1 ] );
+
+		}
+
+	}
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+
+	THREE.Camera.call( this );
+
+	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;
+
+	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 );
+
+};
+/**
+ * @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 ) {
+
+	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;
+
+	this.updateProjectionMatrix();
+
+};
+
+THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
+
+
+/**
+ * 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 ( frameHeight === undefined ) frameHeight = 24;
+
+	this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+	this.updateProjectionMatrix();
+
+}
+
+
+/**
+ * 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.
+ */
+
+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;
+
+	this.updateProjectionMatrix();
+
+};
+
+
+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 );
+
+		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
+		);
+
+	} else {
+
+		this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far );
+
+	}
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+ 
+THREE.Light = function ( hex ) {
+
+	THREE.Object3D.call( this );
+
+	this.color = new THREE.Color( hex );
+
+};
+
+THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AmbientLight = function ( hex ) {
+
+	THREE.Light.call( this, hex );
+
+};
+
+THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+/**
+ * @author MPanknin / http://www.redplant.de/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.AreaLight = function ( hex, intensity ) {
+
+	THREE.Light.call( this, hex );
+
+	this.normal = new THREE.Vector3( 0, -1, 0 );
+	this.right = new THREE.Vector3( 1, 0, 0 );
+
+	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;
+
+};
+
+THREE.AreaLight.prototype = Object.create( THREE.Light.prototype );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DirectionalLight = function ( hex, intensity ) {
+
+	THREE.Light.call( this, hex );
+
+	this.position = new THREE.Vector3( 0, 1, 0 );
+	this.target = new THREE.Object3D();
+
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+	this.castShadow = false;
+	this.onlyShadow = false;
+
+	//
+
+	this.shadowCameraNear = 50;
+	this.shadowCameraFar = 5000;
+
+	this.shadowCameraLeft = -500;
+	this.shadowCameraRight = 500;
+	this.shadowCameraTop = 500;
+	this.shadowCameraBottom = -500;
+
+	this.shadowCameraVisible = false;
+
+	this.shadowBias = 0;
+	this.shadowDarkness = 0.5;
+
+	this.shadowMapWidth = 512;
+	this.shadowMapHeight = 512;
+
+	//
+
+	this.shadowCascade = false;
+
+	this.shadowCascadeOffset = new THREE.Vector3( 0, 0, -1000 );
+	this.shadowCascadeCount = 2;
+
+	this.shadowCascadeBias = [ 0, 0, 0 ];
+	this.shadowCascadeWidth = [ 512, 512, 512 ];
+	this.shadowCascadeHeight = [ 512, 512, 512 ];
+
+	this.shadowCascadeNearZ = [ -1.000, 0.990, 0.998 ];
+	this.shadowCascadeFarZ  = [  0.990, 0.998, 1.000 ];
+
+	this.shadowCascadeArray = [];
+
+	//
+
+	this.shadowMap = null;
+	this.shadowMapSize = null;
+	this.shadowCamera = null;
+	this.shadowMatrix = null;
+
+};
+
+THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) {
+
+	THREE.Light.call( this, skyColorHex );
+
+	this.groundColor = new THREE.Color( groundColorHex );
+
+	this.position = new THREE.Vector3( 0, 100, 0 );
+
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+};
+
+THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.PointLight = function ( hex, intensity, distance ) {
+
+	THREE.Light.call( this, hex );
+
+	this.position = new THREE.Vector3( 0, 0, 0 );
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+	this.distance = ( distance !== undefined ) ? distance : 0;
+
+};
+
+THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) {
+
+	THREE.Light.call( this, hex );
+
+	this.position = new THREE.Vector3( 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 / 2;
+	this.exponent = ( exponent !== undefined ) ? exponent : 10;
+
+	this.castShadow = false;
+	this.onlyShadow = false;
+
+	//
+
+	this.shadowCameraNear = 50;
+	this.shadowCameraFar = 5000;
+	this.shadowCameraFov = 50;
+
+	this.shadowCameraVisible = false;
+
+	this.shadowBias = 0;
+	this.shadowDarkness = 0.5;
+
+	this.shadowMapWidth = 512;
+	this.shadowMapHeight = 512;
+
+	//
+
+	this.shadowMap = null;
+	this.shadowMapSize = null;
+	this.shadowCamera = null;
+	this.shadowMatrix = null;
+
+};
+
+THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Loader = function ( showStatus ) {
+
+	this.showStatus = showStatus;
+	this.statusDomElement = showStatus ? 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 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;
+
+		e.innerHTML = "Loading ...";
+
+		return e;
+
+	},
+
+	updateProgress: function ( progress ) {
+
+		var message = "Loaded ";
+
+		if ( progress.total ) {
+
+			message += ( 100 * progress.loaded / progress.total ).toFixed(0) + "%";
+
+
+		} else {
+
+			message += ( progress.loaded / 1000 ).toFixed(2) + " KB";
+
+		}
+
+		this.statusDomElement.innerHTML = message;
+
+	},
+
+	extractUrlBase: function ( url ) {
+
+		var parts = url.split( '/' );
+		parts.pop();
+		return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
+
+	},
+
+	initMaterials: function ( materials, texturePath ) {
+
+		var array = [];
+
+		for ( var i = 0; i < materials.length; ++ i ) {
+
+			array[ i ] = THREE.Loader.prototype.createMaterial( materials[ i ], texturePath );
+
+		}
+
+		return array;
+
+	},
+
+	needsTangents: function ( materials ) {
+
+		for( var i = 0, il = materials.length; i < il; i ++ ) {
+
+			var m = materials[ i ];
+
+			if ( m instanceof THREE.ShaderMaterial ) return true;
+
+		}
+
+		return false;
+
+	},
+
+	createMaterial: function ( m, texturePath ) {
+
+		var _this = this;
+
+		function is_pow2( n ) {
+
+			var l = Math.log( n ) / Math.LN2;
+			return Math.floor( l ) == l;
+
+		}
+
+		function nearest_pow2( n ) {
+
+			var l = Math.log( n ) / Math.LN2;
+			return Math.pow( 2, Math.round(  l ) );
+
+		}
+
+		function load_image( where, url ) {
+
+			var image = new Image();
+
+			image.onload = function () {
+
+				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 );
+
+				} else {
+
+					where.image = this;
+
+				}
+
+				where.needsUpdate = true;
+
+			};
+
+			image.crossOrigin = _this.crossOrigin;
+			image.src = url;
+
+		}
+
+		function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
+
+			var isCompressed = /\.dds$/i.test( sourceFile );
+			var fullPath = texturePath + "/" + sourceFile;
+
+			if ( isCompressed ) {
+
+				var texture = THREE.ImageUtils.loadCompressedTexture( fullPath );
+
+				where[ name ] = texture;
+
+			} else {
+
+				var texture = document.createElement( 'canvas' );
+
+				where[ name ] = new THREE.Texture( texture );
+
+			}
+
+			where[ name ].sourceFile = sourceFile;
+
+			if( repeat ) {
+
+				where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] );
+
+				if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping;
+				if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping;
+
+			}
+
+			if ( offset ) {
+
+				where[ name ].offset.set( offset[ 0 ], offset[ 1 ] );
+
+			}
+
+			if ( wrap ) {
+
+				var wrapMap = {
+					"repeat": THREE.RepeatWrapping,
+					"mirror": THREE.MirroredRepeatWrapping
+				}
+
+				if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ];
+				if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ];
+
+			}
+
+			if ( anisotropy ) {
+
+				where[ name ].anisotropy = anisotropy;
+
+			}
+
+			if ( ! isCompressed ) {
+
+				load_image( where[ name ], fullPath );
+
+			}
+
+		}
+
+		function rgb2hex( rgb ) {
+
+			return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255;
+
+		}
+
+		// defaults
+
+		var mtype = "MeshLambertMaterial";
+		var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false };
+
+		// parameters from model file
+
+		if ( m.shading ) {
+
+			var shading = m.shading.toLowerCase();
+
+			if ( shading === "phong" ) mtype = "MeshPhongMaterial";
+			else if ( shading === "basic" ) mtype = "MeshBasicMaterial";
+
+		}
+
+		if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) {
+
+			mpars.blending = THREE[ m.blending ];
+
+		}
+
+		if ( m.transparent !== undefined || m.opacity < 1.0 ) {
+
+			mpars.transparent = m.transparent;
+
+		}
+
+		if ( m.depthTest !== undefined ) {
+
+			mpars.depthTest = m.depthTest;
+
+		}
+
+		if ( m.depthWrite !== undefined ) {
+
+			mpars.depthWrite = m.depthWrite;
+
+		}
+
+		if ( m.visible !== undefined ) {
+
+			mpars.visible = m.visible;
+
+		}
+
+		if ( m.flipSided !== undefined ) {
+
+			mpars.side = THREE.BackSide;
+
+		}
+
+		if ( m.doubleSided !== undefined ) {
+
+			mpars.side = THREE.DoubleSide;
+
+		}
+
+		if ( m.wireframe !== undefined ) {
+
+			mpars.wireframe = m.wireframe;
+
+		}
+
+		if ( m.vertexColors !== undefined ) {
+
+			if ( m.vertexColors === "face" ) {
+
+				mpars.vertexColors = THREE.FaceColors;
+
+			} else if ( m.vertexColors ) {
+
+				mpars.vertexColors = THREE.VertexColors;
+
+			}
+
+		}
+
+		// colors
+
+		if ( m.colorDiffuse ) {
+
+			mpars.color = rgb2hex( m.colorDiffuse );
+
+		} else if ( m.DbgColor ) {
+
+			mpars.color = m.DbgColor;
+
+		}
+
+		if ( m.colorSpecular ) {
+
+			mpars.specular = rgb2hex( m.colorSpecular );
+
+		}
+
+		if ( m.colorAmbient ) {
+
+			mpars.ambient = rgb2hex( m.colorAmbient );
+
+		}
+
+		// modifiers
+
+		if ( m.transparency ) {
+
+			mpars.opacity = m.transparency;
+
+		}
+
+		if ( m.specularCoef ) {
+
+			mpars.shininess = m.specularCoef;
+
+		}
+
+		// textures
+
+		if ( m.mapDiffuse && texturePath ) {
+
+			create_texture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
+
+		}
+
+		if ( m.mapLight && texturePath ) {
+
+			create_texture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
+
+		}
+
+		if ( m.mapBump && texturePath ) {
+
+			create_texture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
+
+		}
+
+		if ( m.mapNormal && texturePath ) {
+
+			create_texture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
+
+		}
+
+		if ( m.mapSpecular && texturePath ) {
+
+			create_texture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
+
+		}
+
+		//
+
+		if ( m.mapBumpScale ) {
+
+			mpars.bumpScale = m.mapBumpScale;
+
+		}
+
+		// special case for normal mapped material
+
+		if ( m.mapNormal ) {
+
+			var shader = THREE.ShaderLib[ "normalmap" ];
+			var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+			uniforms[ "tNormal" ].value = mpars.normalMap;
+
+			if ( m.mapNormalFactor ) {
+
+				uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor );
+
+			}
+
+			if ( mpars.map ) {
+
+				uniforms[ "tDiffuse" ].value = mpars.map;
+				uniforms[ "enableDiffuse" ].value = true;
+
+			}
+
+			if ( mpars.specularMap ) {
+
+				uniforms[ "tSpecular" ].value = mpars.specularMap;
+				uniforms[ "enableSpecular" ].value = true;
+
+			}
+
+			if ( mpars.lightMap ) {
+
+				uniforms[ "tAO" ].value = mpars.lightMap;
+				uniforms[ "enableAO" ].value = true;
+
+			}
+
+			// for the moment don't handle displacement texture
+
+			uniforms[ "uDiffuseColor" ].value.setHex( mpars.color );
+			uniforms[ "uSpecularColor" ].value.setHex( mpars.specular );
+			uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient );
+
+			uniforms[ "uShininess" ].value = mpars.shininess;
+
+			if ( mpars.opacity !== undefined ) {
+
+				uniforms[ "uOpacity" ].value = mpars.opacity;
+
+			}
+
+			var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
+			var material = new THREE.ShaderMaterial( parameters );
+
+			if ( mpars.transparent ) {
+
+				material.transparent = true;
+
+			}
+
+		} else {
+
+			var material = new THREE[ mtype ]( mpars );
+
+		}
+
+		if ( m.DbgName !== undefined ) material.name = m.DbgName;
+
+		return material;
+
+	}
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageLoader = function () {
+
+	THREE.EventDispatcher.call( this );
+
+	this.crossOrigin = null;
+
+};
+
+THREE.ImageLoader.prototype = {
+
+	constructor: THREE.ImageLoader,
+
+	load: function ( url, image ) {
+
+		var scope = this;
+
+		if ( image === undefined ) image = new Image();
+
+		image.addEventListener( 'load', function () {
+
+			scope.dispatchEvent( { type: 'load', content: image } );
+
+		}, false );
+
+		image.addEventListener( 'error', function () {
+
+			scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
+
+		}, false );
+
+		if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin;
+
+		image.src = url;
+
+	}
+
+}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.JSONLoader = function ( showStatus ) {
+
+	THREE.Loader.call( this, showStatus );
+
+	this.withCredentials = false;
+
+};
+
+THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype );
+
+THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) {
+
+	var scope = this;
+
+	// todo: unify load API to for easier SceneLoader use
+
+	texturePath = texturePath && ( typeof texturePath === "string" ) ? texturePath : this.extractUrlBase( url );
+
+	this.onLoadStart();
+	this.loadAjaxJSON( this, url, callback, texturePath );
+
+};
+
+THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) {
+
+	var xhr = new XMLHttpRequest();
+
+	var length = 0;
+
+	xhr.onreadystatechange = function () {
+
+		if ( xhr.readyState === xhr.DONE ) {
+
+			if ( xhr.status === 200 || xhr.status === 0 ) {
+
+				if ( xhr.responseText ) {
+
+					var json = JSON.parse( xhr.responseText );
+					context.createModel( json, callback, texturePath );
+
+				} else {
+
+					console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there is empty" );
+
+				}
+
+				// in context of more complex asset initialization
+				// do not block on single failed file
+				// maybe should go even one more level up
+
+				context.onLoadComplete();
+
+			} else {
+
+				console.error( "THREE.JSONLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
+
+			}
+
+		} else if ( xhr.readyState === xhr.LOADING ) {
+
+			if ( callbackProgress ) {
+
+				if ( length === 0 ) {
+
+					length = xhr.getResponseHeader( "Content-Length" );
+
+				}
+
+				callbackProgress( { total: length, loaded: xhr.responseText.length } );
+
+			}
+
+		} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
+
+			length = xhr.getResponseHeader( "Content-Length" );
+
+		}
+
+	};
+
+	xhr.open( "GET", url, true );
+	xhr.withCredentials = this.withCredentials;
+	xhr.send( null );
+
+};
+
+THREE.JSONLoader.prototype.createModel = function ( json, callback, texturePath ) {
+
+	var scope = this,
+	geometry = new THREE.Geometry(),
+	scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
+
+	parseModel( scale );
+
+	parseSkin();
+	parseMorphing( scale );
+
+	geometry.computeCentroids();
+	geometry.computeFaceNormals();
+
+	function parseModel( scale ) {
+
+		function isBitSet( value, position ) {
+
+			return value & ( 1 << position );
+
+		}
+
+		var i, j, fi,
+
+		offset, zLength, nVertices,
+
+		colorIndex, normalIndex, uvIndex, materialIndex,
+
+		type,
+		isQuad,
+		hasMaterial,
+		hasFaceUv, hasFaceVertexUv,
+		hasFaceNormal, hasFaceVertexNormal,
+		hasFaceColor, hasFaceVertexColor,
+
+		vertex, face, color, normal,
+
+		uvLayer, uvs, u, v,
+
+		faces = json.faces,
+		vertices = json.vertices,
+		normals = json.normals,
+		colors = json.colors,
+
+		nUvLayers = 0;
+
+		// disregard empty arrays
+
+		for ( i = 0; i < json.uvs.length; i++ ) {
+
+			if ( json.uvs[ i ].length ) nUvLayers ++;
+
+		}
+
+		for ( i = 0; i < nUvLayers; i++ ) {
+
+			geometry.faceUvs[ i ] = [];
+			geometry.faceVertexUvs[ i ] = [];
+
+		}
+
+		offset = 0;
+		zLength = vertices.length;
+
+		while ( offset < zLength ) {
+
+			vertex = new THREE.Vector3();
+
+			vertex.x = vertices[ offset ++ ] * scale;
+			vertex.y = vertices[ offset ++ ] * scale;
+			vertex.z = vertices[ offset ++ ] * scale;
+
+			geometry.vertices.push( vertex );
+
+		}
+
+		offset = 0;
+		zLength = faces.length;
+
+		while ( offset < zLength ) {
+
+			type = faces[ offset ++ ];
+
+
+			isQuad          	= isBitSet( type, 0 );
+			hasMaterial         = isBitSet( type, 1 );
+			hasFaceUv           = isBitSet( type, 2 );
+			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, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
+
+			if ( isQuad ) {
+
+				face = new THREE.Face4();
+
+				face.a = faces[ offset ++ ];
+				face.b = faces[ offset ++ ];
+				face.c = faces[ offset ++ ];
+				face.d = faces[ offset ++ ];
+
+				nVertices = 4;
+
+			} else {
+
+				face = new THREE.Face3();
+
+				face.a = faces[ offset ++ ];
+				face.b = faces[ offset ++ ];
+				face.c = faces[ offset ++ ];
+
+				nVertices = 3;
+
+			}
+
+			if ( hasMaterial ) {
+
+				materialIndex = faces[ offset ++ ];
+				face.materialIndex = materialIndex;
+
+			}
+
+			// to get face <=> uv index correspondence
+
+			fi = geometry.faces.length;
+
+			if ( hasFaceUv ) {
+
+				for ( i = 0; i < nUvLayers; i++ ) {
+
+					uvLayer = json.uvs[ i ];
+
+					uvIndex = faces[ offset ++ ];
+
+					u = uvLayer[ uvIndex * 2 ];
+					v = uvLayer[ uvIndex * 2 + 1 ];
+
+					geometry.faceUvs[ i ][ fi ] = new THREE.Vector2( u, v );
+
+				}
+
+			}
+
+			if ( hasFaceVertexUv ) {
+
+				for ( i = 0; i < nUvLayers; i++ ) {
+
+					uvLayer = json.uvs[ i ];
+
+					uvs = [];
+
+					for ( j = 0; j < nVertices; j ++ ) {
+
+						uvIndex = faces[ offset ++ ];
+
+						u = uvLayer[ uvIndex * 2 ];
+						v = uvLayer[ uvIndex * 2 + 1 ];
+
+						uvs[ j ] = new THREE.Vector2( u, v );
+
+					}
+
+					geometry.faceVertexUvs[ i ][ fi ] = uvs;
+
+				}
+
+			}
+
+			if ( hasFaceNormal ) {
+
+				normalIndex = faces[ offset ++ ] * 3;
+
+				normal = new THREE.Vector3();
+
+				normal.x = normals[ normalIndex ++ ];
+				normal.y = normals[ normalIndex ++ ];
+				normal.z = normals[ normalIndex ];
+
+				face.normal = normal;
+
+			}
+
+			if ( hasFaceVertexNormal ) {
+
+				for ( i = 0; i < nVertices; i++ ) {
+
+					normalIndex = faces[ offset ++ ] * 3;
+
+					normal = new THREE.Vector3();
+
+					normal.x = normals[ normalIndex ++ ];
+					normal.y = normals[ normalIndex ++ ];
+					normal.z = normals[ normalIndex ];
+
+					face.vertexNormals.push( normal );
+
+				}
+
+			}
+
+
+			if ( hasFaceColor ) {
+
+				colorIndex = faces[ offset ++ ];
+
+				color = new THREE.Color( colors[ colorIndex ] );
+				face.color = color;
+
+			}
+
+
+			if ( hasFaceVertexColor ) {
+
+				for ( i = 0; i < nVertices; i++ ) {
+
+					colorIndex = faces[ offset ++ ];
+
+					color = new THREE.Color( colors[ colorIndex ] );
+					face.vertexColors.push( color );
+
+				}
+
+			}
+
+			geometry.faces.push( face );
+
+		}
+
+	};
+
+	function parseSkin() {
+
+		var i, l, x, y, z, w, a, b, c, d;
+
+		if ( json.skinWeights ) {
+
+			for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) {
+
+				x = json.skinWeights[ i     ];
+				y = json.skinWeights[ i + 1 ];
+				z = 0;
+				w = 0;
+
+				geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
+
+			}
+
+		}
+
+		if ( json.skinIndices ) {
+
+			for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) {
+
+				a = json.skinIndices[ i     ];
+				b = json.skinIndices[ i + 1 ];
+				c = 0;
+				d = 0;
+
+				geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
+
+			}
+
+		}
+
+		geometry.bones = json.bones;
+		geometry.animation = json.animation;
+
+	};
+
+	function parseMorphing( scale ) {
+
+		if ( json.morphTargets !== undefined ) {
+
+			var i, l, v, vl, dstVertices, srcVertices;
+
+			for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
+
+				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;
+
+				for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
+
+					var vertex = new THREE.Vector3();
+					vertex.x = srcVertices[ v ] * scale;
+					vertex.y = srcVertices[ v + 1 ] * scale;
+					vertex.z = srcVertices[ v + 2 ] * scale;
+
+					dstVertices.push( vertex );
+
+				}
+
+			}
+
+		}
+
+		if ( json.morphColors !== undefined ) {
+
+			var i, l, c, cl, dstColors, srcColors, color;
+
+			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 = [];
+
+				dstColors = geometry.morphColors[ i ].colors;
+				srcColors = json.morphColors [ i ].colors;
+
+				for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) {
+
+					color = new THREE.Color( 0xffaa00 );
+					color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );
+					dstColors.push( color );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	var materials = this.initMaterials( json.materials, texturePath );
+
+	if ( this.needsTangents( materials ) ) geometry.computeTangents();
+
+	callback( geometry, materials );
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LoadingMonitor = function () {
+
+	THREE.EventDispatcher.call( this );
+
+	var scope = this;
+
+	var loaded = 0;
+	var total = 0;
+
+	var onLoad = function ( event ) {
+
+		loaded ++;
+
+		scope.dispatchEvent( { type: 'progress', loaded: loaded, total: total } );
+
+		if ( loaded === total ) {
+
+			scope.dispatchEvent( { type: 'load' } );
+
+		}
+
+	};
+
+	this.add = function ( loader ) {
+
+		total ++;
+
+		loader.addEventListener( 'load', onLoad, false );
+
+	};
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneLoader = function () {
+
+	this.onLoadStart = function () {};
+	this.onLoadProgress = function() {};
+	this.onLoadComplete = function () {};
+
+	this.callbackSync = function () {};
+	this.callbackProgress = function () {};
+
+	this.geometryHandlerMap = {};
+	this.hierarchyHandlerMap = {};
+
+	this.addGeometryHandler( "ascii", THREE.JSONLoader );
+
+};
+
+THREE.SceneLoader.prototype.constructor = THREE.SceneLoader;
+
+THREE.SceneLoader.prototype.load = function ( url, callbackFinished ) {
+
+	var scope = this;
+
+	var xhr = new XMLHttpRequest();
+
+	xhr.onreadystatechange = function () {
+
+		if ( xhr.readyState === 4 ) {
+
+			if ( xhr.status === 200 || xhr.status === 0 ) {
+
+				var json = JSON.parse( xhr.responseText );
+				scope.parse( json, callbackFinished, url );
+
+			} else {
+
+				console.error( "THREE.SceneLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
+
+			}
+
+		}
+
+	};
+
+	xhr.open( "GET", url, true );
+	xhr.send( null );
+
+};
+
+THREE.SceneLoader.prototype.addGeometryHandler = function ( typeID, loaderClass ) {
+
+	this.geometryHandlerMap[ typeID ] = { "loaderClass": loaderClass };
+
+};
+
+THREE.SceneLoader.prototype.addHierarchyHandler = function ( typeID, loaderClass ) {
+
+	this.hierarchyHandlerMap[ typeID ] = { "loaderClass": loaderClass };
+
+};
+
+THREE.SceneLoader.prototype.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.geometryHandlerMap ) {
+
+		var loaderClass = this.geometryHandlerMap[ typeID ][ "loaderClass" ];
+		this.geometryHandlerMap[ typeID ][ "loaderObject" ] = new loaderClass();
+
+	}
+
+	// async hierachy loaders
+
+	for ( var typeID in this.hierarchyHandlerMap ) {
+
+		var loaderClass = this.hierarchyHandlerMap[ typeID ][ "loaderClass" ];
+		this.hierarchyHandlerMap[ 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.set( position[ 0 ], position[ 1 ], position [ 2 ] );
+
+		if ( rotation )
+			result.scene.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation [ 2 ] );
+
+		if ( scale )
+			result.scene.scale.set( scale[ 0 ], scale[ 1 ], scale [ 2 ] );
+
+		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
+
+			if ( result.objects[ objID ] === undefined ) {
+
+				var objJSON = children[ objID ];
+
+				var object = null;
+
+				// meshes
+
+				if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlerMap ) ) {
+
+					if ( objJSON.loading === undefined ) {
+
+						var reservedTypes = { "type": 1, "url": 1, "material": 1,
+											  "position": 1, "rotation": 1, "scale" : 1,
+											  "visible": 1, "children": 1, "properties": 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.hierarchyHandlerMap[ 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.set( pos[0], pos[1], pos[2] );
+
+							if ( quat ) {
+
+								object.quaternion.set( quat[0], quat[1], quat[2], quat[3] );
+								object.useQuaternion = true;
+
+							} else {
+
+								object.rotation.set( rot[0], rot[1], rot[2] );
+
+							}
+
+							object.scale.set( scl[0], scl[1], scl[2] );
+
+						}
+
+						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.set( pos[0], pos[1], pos[2] );
+
+						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.set( pos[0], pos[1], pos[2] );
+
+					} 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" ) {
+
+					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 );
+
+					}
+
+					pos = objJSON.position;
+					camera.position.set( pos[0], pos[1], pos[2] );
+					parent.add( camera );
+
+					camera.name = objID;
+					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.set( pos[0], pos[1], pos[2] );
+
+					if ( quat ) {
+
+						object.quaternion.set( quat[0], quat[1], quat[2], quat[3] );
+						object.useQuaternion = true;
+
+					} else {
+
+						object.rotation.set( rot[0], rot[1], rot[2] );
+
+					}
+
+					object.scale.set( scl[0], scl[1], scl[2] );
+					object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
+
+					parent.add( object );
+
+					result.objects[ objID ] = object;
+					result.empties[ objID ] = object;
+
+				}
+
+				if ( object ) {
+
+					if ( objJSON.properties !== undefined )  {
+
+						for ( var key in objJSON.properties ) {
+
+							var value = objJSON.properties[ key ];
+							object.properties[ 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 ( 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.set( p[0], p[1], p[2] );
+
+		if ( q ) {
+
+			node.quaternion.set( q[0], q[1], q[2], q[3] );
+			node.useQuaternion = true;
+
+		} else {
+
+			node.rotation.set( r[0], r[1], r[2] );
+
+		}
+
+		node.scale.set( s[0], s[1], s[2] );
+
+		// 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 ) {
+
+			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 ) {
+
+			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.properties.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 );
+
+		};
+
+	};
+
+	// 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.geometryHandlerMap ) {
+
+			counter_models += 1;
+
+			scope.onLoadStart();
+
+		}
+
+	}
+
+	// count how many hierarchies will be loaded asynchronously
+
+	var objID, objJSON;
+
+	for ( objID in data.objects ) {
+
+		objJSON = data.objects[ objID ];
+
+		if ( objJSON.type && ( objJSON.type in this.hierarchyHandlerMap ) ) {
+
+			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 );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type === "plane" ) {
+
+			geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type === "sphere" ) {
+
+			geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type === "cylinder" ) {
+
+			geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type === "torus" ) {
+
+			geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type === "icosahedron" ) {
+
+			geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
+			result.geometries[ geoID ] = geometry;
+
+		} else if ( geoJSON.type in this.geometryHandlerMap ) {
+
+			var loaderParameters = {};
+
+			for ( var parType in geoJSON ) {
+
+				if ( parType !== "type" && parType !== "url" ) {
+
+					loaderParameters[ parType ] = geoJSON[ parType ];
+
+				}
+
+			}
+
+			var loader = this.geometryHandlerMap[ 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.geometryHandlerMap[ "ascii" ][ "loaderObject" ];
+				jsonLoader.createModel( modelJson, create_callback_embed( geoID ), texture_path );
+
+			}
+
+		}
+
+	}
+
+	// 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 );
+
+		}
+
+		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 () {
+
+	THREE.EventDispatcher.call( this );
+
+	this.crossOrigin = null;
+
+};
+
+THREE.TextureLoader.prototype = {
+
+	constructor: THREE.TextureLoader,
+
+	load: function ( url ) {
+
+		var scope = this;
+
+		var image = new Image();
+
+		image.addEventListener( 'load', function () {
+
+			var texture = new THREE.Texture( image );
+			texture.needsUpdate = true;
+
+			scope.dispatchEvent( { type: 'load', content: texture } );
+
+		}, false );
+
+		image.addEventListener( 'error', function () {
+
+			scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
+
+		}, false );
+
+		if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin;
+
+		image.src = url;
+
+	}
+
+}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Material = function () {
+
+	THREE.EventDispatcher.call( this );
+
+	this.id = THREE.MaterialIdCount ++;
+
+	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 = false; // Boolean for fixing antialiasing gaps in CanvasRenderer
+
+	this.visible = true;
+
+	this.needsUpdate = true;
+
+};
+
+THREE.Material.prototype.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 && newValue instanceof THREE.Color ) {
+
+				currentValue.copy( newValue );
+
+			} else if ( currentValue instanceof THREE.Color ) {
+
+				currentValue.set( newValue );
+
+			} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
+
+				currentValue.copy( newValue );
+
+			} else {
+
+				this[ key ] = newValue;
+
+			}
+
+		}
+
+	}
+
+};
+
+THREE.Material.prototype.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;
+
+};
+
+THREE.Material.prototype.dispose = function () {
+
+	this.dispatchEvent( { type: 'dispose' } );
+
+};
+
+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>
+ * }
+ */
+
+THREE.MeshLambertMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.color = new THREE.Color( 0xffffff ); // diffuse
+	this.ambient = new THREE.Color( 0xffffff );
+	this.emissive = new THREE.Color( 0x000000 );
+
+	this.wrapAround = false;
+	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
+
+	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.morphNormals = false;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.MeshLambertMaterial.prototype.clone = function () {
+
+	var material = new THREE.MeshLambertMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.color.copy( this.color );
+	material.ambient.copy( this.ambient );
+	material.emissive.copy( this.emissive );
+
+	material.wrapAround = this.wrapAround;
+	material.wrapRGB.copy( this.wrapRGB );
+
+	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;
+	material.morphNormals = this.morphNormals;
+
+	return material;
+
+};
+/**
+ * @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.Material.call( this );
+
+	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;
+
+	this.wrapAround = false;
+	this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
+
+	this.map = null;
+
+	this.lightMap = null;
+
+	this.bumpMap = null;
+	this.bumpScale = 1;
+
+	this.normalMap = null;
+	this.normalScale = new THREE.Vector2( 1, 1 );
+
+	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.morphNormals = false;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.MeshPhongMaterial.prototype.clone = function () {
+
+	var material = new THREE.MeshPhongMaterial();
+
+	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;
+
+	material.metal = this.metal;
+	material.perPixel = this.perPixel;
+
+	material.wrapAround = this.wrapAround;
+	material.wrapRGB.copy( this.wrapRGB );
+
+	material.map = this.map;
+
+	material.lightMap = this.lightMap;
+
+	material.bumpMap = this.bumpMap;
+	material.bumpScale = this.bumpScale;
+
+	material.normalMap = this.normalMap;
+	material.normalScale.copy( this.normalScale );
+
+	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;
+	material.morphNormals = this.morphNormals;
+
+	return material;
+
+};
+/**
+ * @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;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.MeshDepthMaterial.prototype.clone = function () {
+
+	var material = new THREE.LineBasicMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+
+	return material;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ *  opacity: <float>,
+ *
+ *  shading: THREE.FlatShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>
+ * }
+ */
+
+THREE.MeshNormalMaterial = function ( parameters ) {
+
+	THREE.Material.call( this, parameters );
+
+	this.shading = THREE.FlatShading;
+
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.MeshNormalMaterial.prototype.clone = function () {
+
+	var material = new THREE.MeshNormalMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.shading = this.shading;
+
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+
+	return material;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MeshFaceMaterial = function ( materials ) {
+
+	this.materials = materials instanceof Array ? materials : [];
+
+};
+
+THREE.MeshFaceMaterial.prototype.clone = function () {
+
+	return new THREE.MeshFaceMaterial( this.materials.slice( 0 ) );
+
+};
+/**
+ * @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.ParticleBasicMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.color = new THREE.Color( 0xffffff );
+
+	this.map = null;
+
+	this.size = 1;
+	this.sizeAttenuation = true;
+
+	this.vertexColors = false;
+
+	this.fog = true;
+
+	this.setValues( parameters );
+
+};
+
+THREE.ParticleBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.ParticleBasicMaterial.prototype.clone = function () {
+
+	var material = new THREE.ParticleBasicMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.color.copy( this.color );
+
+	material.map = this.map;
+
+	material.size = this.size;
+	material.sizeAttenuation = this.sizeAttenuation;
+
+	material.vertexColors = this.vertexColors;
+
+	material.fog = this.fog;
+
+	return material;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  program: <function>,
+ *  opacity: <float>,
+ *  blending: THREE.NormalBlending
+ * }
+ */
+
+THREE.ParticleCanvasMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.color = new THREE.Color( 0xffffff );
+	this.program = function ( context, color ) {};
+
+	this.setValues( parameters );
+
+};
+
+THREE.ParticleCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.ParticleCanvasMaterial.prototype.clone = function () {
+
+	var material = new THREE.ParticleCanvasMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.color.copy( this.color );
+	material.program = this.program;
+
+	return material;
+
+};
+/**
+ * @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.ShaderMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.fragmentShader = "void main() {}";
+	this.vertexShader = "void main() {}";
+	this.uniforms = {};
+	this.defines = {};
+	this.attributes = null;
+
+	this.shading = THREE.SmoothShading;
+
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+
+	this.fog = false; // set to use scene fog
+
+	this.lights = false; // set to use scene lights
+
+	this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
+
+	this.skinning = false; // set to use skinning attribute streams
+
+	this.morphTargets = false; // set to use morph targets
+	this.morphNormals = false; // set to use morph normals
+
+	this.setValues( parameters );
+
+};
+
+THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.ShaderMaterial.prototype.clone = function () {
+
+	var material = new THREE.ShaderMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.fragmentShader = this.fragmentShader;
+	material.vertexShader = this.vertexShader;
+
+	material.uniforms = THREE.UniformsUtils.clone( this.uniforms );
+
+	material.attributes = this.attributes;
+	material.defines = this.defines;
+
+	material.shading = this.shading;
+
+	material.wireframe = this.wireframe;
+	material.wireframeLinewidth = this.wireframeLinewidth;
+
+	material.fog = this.fog;
+
+	material.lights = this.lights;
+
+	material.vertexColors = this.vertexColors;
+
+	material.skinning = this.skinning;
+
+	material.morphTargets = this.morphTargets;
+	material.morphNormals = this.morphNormals;
+
+	return material;
+
+};
+/**
+ * @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>,
+ *  scaleByViewport: <bool>,
+ *  alignment: THREE.SpriteAlignment.center,
+ *
+ *	uvOffset: new THREE.Vector2(),
+ *	uvScale: new THREE.Vector2(),
+ *
+ *  fog: <bool>
+ * }
+ */
+
+THREE.SpriteMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	// defaults
+
+	this.color = new THREE.Color( 0xffffff );
+	this.map = new THREE.Texture();
+
+	this.useScreenCoordinates = true;
+	this.depthTest = !this.useScreenCoordinates;
+	this.sizeAttenuation = !this.useScreenCoordinates;
+	this.scaleByViewport = !this.sizeAttenuation;
+	this.alignment = THREE.SpriteAlignment.center.clone();
+
+	this.fog = false;
+
+	this.uvOffset = new THREE.Vector2( 0, 0 );
+	this.uvScale  = new THREE.Vector2( 1, 1 );
+
+	// set parameters
+
+	this.setValues( parameters );
+
+	// override coupled defaults if not specified explicitly by parameters
+
+	parameters = parameters || {};
+
+	if ( parameters.depthTest === undefined ) this.depthTest = !this.useScreenCoordinates;
+	if ( parameters.sizeAttenuation === undefined ) this.sizeAttenuation = !this.useScreenCoordinates;
+	if ( parameters.scaleByViewport === undefined ) this.scaleByViewport = !this.sizeAttenuation;
+
+};
+
+THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.SpriteMaterial.prototype.clone = function () {
+
+	var material = new THREE.SpriteMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.color.copy( this.color );
+	material.map = this.map;
+
+	material.useScreenCoordinates = this.useScreenCoordinates;
+	material.sizeAttenuation = this.sizeAttenuation;
+	material.scaleByViewport = this.scaleByViewport;
+	material.alignment.copy( this.alignment );
+
+	material.uvOffset.copy( this.uvOffset );
+	material.uvScale.copy( this.uvScale );
+
+	material.fog = this.fog;
+
+	return material;
+
+};
+
+// Alignment enums
+
+THREE.SpriteAlignment = {};
+THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 );
+THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 );
+THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 );
+THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 );
+THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
+THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 );
+THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 );
+THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 );
+THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+	THREE.EventDispatcher.call( this );
+
+	this.id = THREE.TextureIdCount ++;
+
+	this.name = '';
+
+	this.image = image;
+	this.mipmaps = [];
+
+	this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping();
+
+	this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
+	this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
+
+	this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
+	this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
+
+	this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+
+	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 );
+
+	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;
+
+};
+
+THREE.Texture.prototype = {
+
+	constructor: THREE.Texture,
+
+	clone: function ( texture ) {
+
+		if ( texture === undefined ) texture = new THREE.Texture();
+
+		texture.image = this.image;
+		texture.mipmaps = this.mipmaps.slice(0);
+
+		texture.mapping = this.mapping;
+
+		texture.wrapS = this.wrapS;
+		texture.wrapT = this.wrapT;
+
+		texture.magFilter = this.magFilter;
+		texture.minFilter = this.minFilter;
+
+		texture.anisotropy = this.anisotropy;
+
+		texture.format = this.format;
+		texture.type = this.type;
+
+		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;
+
+	},
+
+	dispose: function () {
+
+		this.dispatchEvent( { type: 'dispose' } );
+
+	}
+
+};
+
+THREE.TextureIdCount = 0;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+	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
+
+};
+
+THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+
+THREE.CompressedTexture.prototype.clone = function () {
+
+	var texture = new THREE.CompressedTexture();
+
+	THREE.Texture.prototype.clone.call( this, texture );
+
+	return texture;
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+	THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+	this.image = { data: data, width: width, height: height };
+
+};
+
+THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
+
+THREE.DataTexture.prototype.clone = function () {
+
+	var texture = new THREE.DataTexture();
+
+	THREE.Texture.prototype.clone.call( this, texture );
+
+	return texture;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Particle = function ( material ) {
+
+	THREE.Object3D.call( this );
+
+	this.material = material;
+
+};
+
+THREE.Particle.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Particle.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Particle( this.material );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ParticleSystem = function ( geometry, material ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry;
+	this.material = ( material !== undefined ) ? material : new THREE.ParticleBasicMaterial( { color: Math.random() * 0xffffff } );
+
+	this.sortParticles = false;
+
+	if ( this.geometry ) {
+
+		// calc bound radius
+
+		if( this.geometry.boundingSphere === null ) {
+
+			this.geometry.computeBoundingSphere();
+
+		}
+
+	}
+
+	this.frustumCulled = false;
+
+};
+
+THREE.ParticleSystem.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.ParticleSystem.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.ParticleSystem( this.geometry, this.material );
+	object.sortParticles = this.sortParticles;
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Line = function ( geometry, material, type ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry;
+	this.material = ( material !== undefined ) ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
+	this.type = ( type !== undefined ) ? type : THREE.LineStrip;
+
+	if ( this.geometry ) {
+
+		if ( ! this.geometry.boundingSphere ) {
+
+			this.geometry.computeBoundingSphere();
+
+		}
+
+	}
+
+};
+
+THREE.LineStrip = 0;
+THREE.LinePieces = 1;
+
+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 );
+
+	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/
+ */
+
+THREE.Mesh = function ( geometry, material ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry;
+	this.material = ( material !== undefined ) ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, wireframe: true } );
+
+	if ( this.geometry !== undefined ) {
+
+		if ( this.geometry.boundingSphere === null ) {
+
+			this.geometry.computeBoundingSphere();
+
+		}
+
+		this.updateMorphTargets();
+
+	}
+
+};
+
+THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Mesh.prototype.updateMorphTargets = function () {
+
+	if ( this.geometry.morphTargets.length > 0 ) {
+
+		this.morphTargetBase = -1;
+		this.morphTargetForcedOrder = [];
+		this.morphTargetInfluences = [];
+		this.morphTargetDictionary = {};
+
+		for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
+
+			this.morphTargetInfluences.push( 0 );
+			this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
+
+		}
+
+	}
+
+};
+
+THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
+
+	if ( this.morphTargetDictionary[ name ] !== undefined ) {
+
+		return this.morphTargetDictionary[ name ];
+
+	}
+
+	console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." );
+
+	return 0;
+
+};
+
+THREE.Mesh.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Bone = function( belongsToSkin ) {
+
+	THREE.Object3D.call( this );
+
+	this.skin = belongsToSkin;
+	this.skinMatrix = new THREE.Matrix4();
+
+};
+
+THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
+
+	// update local
+
+	if ( this.matrixAutoUpdate ) {
+
+		forceUpdate |= this.updateMatrix();
+
+	}
+
+	// update skin matrix
+
+	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
+
+		if( parentSkinMatrix ) {
+
+			this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
+
+		} else {
+
+			this.skinMatrix.copy( this.matrix );
+
+		}
+
+		this.matrixWorldNeedsUpdate = false;
+		forceUpdate = true;
+
+	}
+
+	// update children
+
+	var child, i, l = this.children.length;
+
+	for ( i = 0; i < l; i ++ ) {
+
+		this.children[ i ].update( this.skinMatrix, forceUpdate );
+
+	}
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	//
+
+	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+
+	// init bones
+
+	this.identityMatrix = new THREE.Matrix4();
+
+	this.bones = [];
+	this.boneMatrices = [];
+
+	var b, bone, gbone, p, q, s;
+
+	if ( this.geometry && this.geometry.bones !== undefined ) {
+
+		for ( b = 0; b < this.geometry.bones.length; b ++ ) {
+
+			gbone = this.geometry.bones[ b ];
+
+			p = gbone.pos;
+			q = gbone.rotq;
+			s = gbone.scl;
+
+			bone = this.addBone();
+
+			bone.name = gbone.name;
+			bone.position.set( p[0], p[1], p[2] );
+			bone.quaternion.set( q[0], q[1], q[2], q[3] );
+			bone.useQuaternion = true;
+
+			if ( s !== undefined ) {
+
+				bone.scale.set( s[0], s[1], s[2] );
+
+			} else {
+
+				bone.scale.set( 1, 1, 1 );
+
+			}
+
+		}
+
+		for ( b = 0; b < this.bones.length; b ++ ) {
+
+			gbone = this.geometry.bones[ b ];
+			bone = this.bones[ b ];
+
+			if ( gbone.parent === -1 ) {
+
+				this.add( bone );
+
+			} else {
+
+				this.bones[ gbone.parent ].add( bone );
+
+			}
+
+		}
+
+		//
+
+		var nBones = this.bones.length;
+
+		if ( this.useVertexTexture ) {
+
+			// 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;
+
+		} else {
+
+			this.boneMatrices = new Float32Array( 16 * nBones );
+
+		}
+
+		this.pose();
+
+	}
+
+};
+
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+
+THREE.SkinnedMesh.prototype.addBone = function( bone ) {
+
+	if ( bone === undefined ) {
+
+		bone = new THREE.Bone( this );
+
+	}
+
+	this.bones.push( bone );
+
+	return bone;
+
+};
+
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function ( force ) {
+
+	this.matrixAutoUpdate && this.updateMatrix();
+
+	// update matrixWorld
+
+	if ( this.matrixWorldNeedsUpdate || force ) {
+
+		if ( this.parent ) {
+
+			this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+		} else {
+
+			this.matrixWorld.copy( this.matrix );
+
+		}
+
+		this.matrixWorldNeedsUpdate = false;
+
+		force = true;
+
+	}
+
+	// update children
+
+	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+		var child = this.children[ i ];
+
+		if ( child instanceof THREE.Bone ) {
+
+			child.update( this.identityMatrix, false );
+
+		} else {
+
+			child.updateMatrixWorld( true );
+
+		}
+
+	}
+
+	// make a snapshot of the bones' rest position
+
+	if ( this.boneInverses == undefined ) {
+
+		this.boneInverses = [];
+
+		for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+			var inverse = new THREE.Matrix4();
+
+			inverse.getInverse( this.bones[ b ].skinMatrix );
+
+			this.boneInverses.push( inverse );
+
+		}
+
+	}
+
+	// flatten bone matrices to array
+
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+		// compute the offset between the current and the original transform;
+
+		//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
+
+		THREE.SkinnedMesh.offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );
+
+		THREE.SkinnedMesh.offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+	}
+
+	if ( this.useVertexTexture ) {
+
+		this.boneTexture.needsUpdate = true;
+
+	}
+
+};
+
+THREE.SkinnedMesh.prototype.pose = function () {
+
+	this.updateMatrixWorld( true );
+
+	for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
+
+		// normalize weights
+
+		var sw = this.geometry.skinWeights[ i ];
+
+		var scale = 1.0 / sw.lengthManhattan();
+
+		if ( scale !== Infinity ) {
+
+			sw.multiplyScalar( scale );
+
+		} else {
+
+			sw.set( 1 ); // this will be normalized by the shader anyway
+
+		}
+
+	}
+
+};
+
+THREE.SkinnedMesh.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture );
+
+	THREE.Mesh.prototype.clone.call( this, object );
+
+	return object;
+
+};
+
+THREE.SkinnedMesh.offsetMatrix = new THREE.Matrix4();
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MorphAnimMesh = function ( geometry, material ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	// API
+
+	this.duration = 1000; // milliseconds
+	this.mirroredLoop = false;
+	this.time = 0;
+
+	// internals
+
+	this.lastKeyframe = 0;
+	this.currentKeyframe = 0;
+
+	this.direction = 1;
+	this.directionBackwards = false;
+
+	this.setFrameRange( 0, this.geometry.morphTargets.length - 1 );
+
+};
+
+THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype );
+
+THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) {
+
+	this.startKeyframe = start;
+	this.endKeyframe = end;
+
+	this.length = this.endKeyframe - this.startKeyframe + 1;
+
+};
+
+THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
+
+	this.direction = 1;
+	this.directionBackwards = false;
+
+};
+
+THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
+
+	this.direction = -1;
+	this.directionBackwards = true;
+
+};
+
+THREE.MorphAnimMesh.prototype.parseAnimations = function () {
+
+	var geometry = this.geometry;
+
+	if ( ! geometry.animations ) geometry.animations = {};
+
+	var firstAnimation, animations = geometry.animations;
+
+	var pattern = /([a-z]+)(\d+)/;
+
+	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+
+		var morph = geometry.morphTargets[ i ];
+		var parts = morph.name.match( pattern );
+
+		if ( parts && parts.length > 1 ) {
+
+			var label = parts[ 1 ];
+			var num = parts[ 2 ];
+
+			if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: -Infinity };
+
+			var animation = animations[ label ];
+
+			if ( i < animation.start ) animation.start = i;
+			if ( i > animation.end ) animation.end = i;
+
+			if ( ! firstAnimation ) firstAnimation = label;
+
+		}
+
+	}
+
+	geometry.firstAnimation = firstAnimation;
+
+};
+
+THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) {
+
+	if ( ! this.geometry.animations ) this.geometry.animations = {};
+
+	this.geometry.animations[ label ] = { start: start, end: end };
+
+};
+
+THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) {
+
+	var animation = this.geometry.animations[ label ];
+
+	if ( animation ) {
+
+		this.setFrameRange( animation.start, animation.end );
+		this.duration = 1000 * ( ( animation.end - animation.start ) / fps );
+		this.time = 0;
+
+	} else {
+
+		console.warn( "animation[" + label + "] undefined" );
+
+	}
+
+};
+
+THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) {
+
+	var frameTime = this.duration / this.length;
+
+	this.time += this.direction * delta;
+
+	if ( this.mirroredLoop ) {
+
+		if ( this.time > this.duration || this.time < 0 ) {
+
+			this.direction *= -1;
+
+			if ( this.time > this.duration ) {
+
+				this.time = this.duration;
+				this.directionBackwards = true;
+
+			}
+
+			if ( this.time < 0 ) {
+
+				this.time = 0;
+				this.directionBackwards = false;
+
+			}
+
+		}
+
+	} else {
+
+		this.time = this.time % this.duration;
+
+		if ( this.time < 0 ) this.time += this.duration;
+
+	}
+
+	var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 );
+
+	if ( keyframe !== this.currentKeyframe ) {
+
+		this.morphTargetInfluences[ this.lastKeyframe ] = 0;
+		this.morphTargetInfluences[ this.currentKeyframe ] = 1;
+
+		this.morphTargetInfluences[ keyframe ] = 0;
+
+		this.lastKeyframe = this.currentKeyframe;
+		this.currentKeyframe = keyframe;
+
+	}
+
+	var mix = ( this.time % frameTime ) / frameTime;
+
+	if ( this.directionBackwards ) {
+
+		mix = 1 - mix;
+
+	}
+
+	this.morphTargetInfluences[ this.currentKeyframe ] = mix;
+	this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix;
+
+};
+
+THREE.MorphAnimMesh.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material );
+
+	object.duration = this.duration;
+	object.mirroredLoop = this.mirroredLoop;
+	object.time = this.time;
+
+	object.lastKeyframe = this.lastKeyframe;
+	object.currentKeyframe = this.currentKeyframe;
+
+	object.direction = this.direction;
+	object.directionBackwards = this.directionBackwards;
+
+	THREE.Mesh.prototype.clone.call( this, object );
+
+	return object;
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Ribbon = function ( geometry, material ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry;
+	this.material = material;
+
+};
+
+THREE.Ribbon.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Ribbon.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Ribbon( this.geometry, this.material );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LOD = function () {
+
+	THREE.Object3D.call( this );
+
+	this.LODs = [];
+
+};
+
+
+THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.LOD.prototype.addLevel = function ( object3D, visibleAtDistance ) {
+
+	if ( visibleAtDistance === undefined ) {
+
+		visibleAtDistance = 0;
+
+	}
+
+	visibleAtDistance = Math.abs( visibleAtDistance );
+
+	for ( var l = 0; l < this.LODs.length; l ++ ) {
+
+		if ( visibleAtDistance < this.LODs[ l ].visibleAtDistance ) {
+
+			break;
+
+		}
+
+	}
+
+	this.LODs.splice( l, 0, { visibleAtDistance: visibleAtDistance, object3D: object3D } );
+	this.add( object3D );
+
+};
+
+THREE.LOD.prototype.update = function ( camera ) {
+
+	if ( this.LODs.length > 1 ) {
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		var inverse  = camera.matrixWorldInverse;
+		var distance = -( inverse.elements[2] * this.matrixWorld.elements[12] + inverse.elements[6] * this.matrixWorld.elements[13] + inverse.elements[10] * this.matrixWorld.elements[14] + inverse.elements[14] );
+
+		this.LODs[ 0 ].object3D.visible = true;
+
+		for ( var l = 1; l < this.LODs.length; l ++ ) {
+
+			if( distance >= this.LODs[ l ].visibleAtDistance ) {
+
+				this.LODs[ l - 1 ].object3D.visible = false;
+				this.LODs[ l     ].object3D.visible = true;
+
+			} else {
+
+				break;
+
+			}
+
+		}
+
+		for( ; l < this.LODs.length; l ++ ) {
+
+			this.LODs[ l ].object3D.visible = false;
+
+		}
+
+	}
+
+};
+
+THREE.LOD.prototype.clone = function () {
+
+	// TODO
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Sprite = function ( material ) {
+
+	THREE.Object3D.call( this );
+
+	this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
+
+	this.rotation3d = this.rotation;
+	this.rotation = 0;
+
+};
+
+THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+
+/*
+ * Custom update matrix
+ */
+
+THREE.Sprite.prototype.updateMatrix = function () {
+
+	this.matrix.setPosition( this.position );
+
+	this.rotation3d.set( 0, 0, this.rotation );
+	this.matrix.setRotationFromEuler( this.rotation3d );
+
+	if ( this.scale.x !== 1 || this.scale.y !== 1 ) {
+
+		this.matrix.scale( this.scale );
+
+	}
+
+	this.matrixWorldNeedsUpdate = true;
+
+};
+
+THREE.Sprite.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Sprite( this.material );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Scene = function () {
+
+	THREE.Object3D.call( this );
+
+	this.fog = null;
+	this.overrideMaterial = null;
+
+	this.matrixAutoUpdate = false;
+
+	this.__objects = [];
+	this.__lights = [];
+
+	this.__objectsAdded = [];
+	this.__objectsRemoved = [];
+
+};
+
+THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Scene.prototype.__addObject = function ( object ) {
+
+	if ( object instanceof THREE.Light ) {
+
+		if ( this.__lights.indexOf( object ) === - 1 ) {
+
+			this.__lights.push( object );
+
+		}
+
+		if ( object.target && object.target.parent === undefined ) {
+
+			this.add( object.target );
+
+		}
+
+	} else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) {
+
+		if ( this.__objects.indexOf( object ) === - 1 ) {
+
+			this.__objects.push( object );
+			this.__objectsAdded.push( object );
+
+			// check if previously removed
+
+			var i = this.__objectsRemoved.indexOf( object );
+
+			if ( i !== -1 ) {
+
+				this.__objectsRemoved.splice( i, 1 );
+
+			}
+
+		}
+
+	}
+
+	for ( var c = 0; c < object.children.length; c ++ ) {
+
+		this.__addObject( object.children[ c ] );
+
+	}
+
+};
+
+THREE.Scene.prototype.__removeObject = function ( object ) {
+
+	if ( object instanceof THREE.Light ) {
+
+		var i = this.__lights.indexOf( object );
+
+		if ( i !== -1 ) {
+
+			this.__lights.splice( i, 1 );
+
+		}
+
+	} else if ( !( object instanceof THREE.Camera ) ) {
+
+		var i = this.__objects.indexOf( object );
+
+		if( i !== -1 ) {
+
+			this.__objects.splice( i, 1 );
+			this.__objectsRemoved.push( object );
+
+			// check if previously added
+
+			var ai = this.__objectsAdded.indexOf( object );
+
+			if ( ai !== -1 ) {
+
+				this.__objectsAdded.splice( ai, 1 );
+
+			}
+
+		}
+
+	}
+
+	for ( var c = 0; c < object.children.length; c ++ ) {
+
+		this.__removeObject( object.children[ c ] );
+
+	}
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Fog = function ( hex, near, far ) {
+
+	this.name = '';
+
+	this.color = new THREE.Color( hex );
+
+	this.near = ( near !== undefined ) ? near : 1;
+	this.far = ( far !== undefined ) ? far : 1000;
+
+};
+
+THREE.Fog.prototype.clone = function () {
+
+	return new THREE.Fog( this.color.getHex(), this.near, this.far );
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.FogExp2 = function ( hex, density ) {
+
+	this.name = '';
+	this.color = new THREE.Color( hex );
+	this.density = ( density !== undefined ) ? density : 0.00025;
+
+};
+
+THREE.FogExp2.prototype.clone = function () {
+
+	return new THREE.FogExp2( this.color.getHex(), this.density );
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CanvasRenderer = function ( parameters ) {
+
+	console.log( 'THREE.CanvasRenderer', THREE.REVISION );
+
+	var smoothstep = THREE.Math.smoothstep;
+
+	parameters = parameters || {};
+
+	var _this = this,
+	_renderData, _elements, _lights,
+	_projector = new THREE.Projector(),
+
+	_canvas = parameters.canvas !== undefined
+			? parameters.canvas
+			: document.createElement( 'canvas' ),
+
+	_canvasWidth, _canvasHeight, _canvasWidthHalf, _canvasHeightHalf,
+	_context = _canvas.getContext( '2d' ),
+
+	_clearColor = new THREE.Color( 0x000000 ),
+	_clearOpacity = 0,
+
+	_contextGlobalAlpha = 1,
+	_contextGlobalCompositeOperation = 0,
+	_contextStrokeStyle = null,
+	_contextFillStyle = null,
+	_contextLineWidth = null,
+	_contextLineCap = null,
+	_contextLineJoin = null,
+	_contextDashSize = null,
+	_contextGapSize = 0,
+
+	_v1, _v2, _v3, _v4,
+	_v5 = new THREE.RenderableVertex(),
+	_v6 = new THREE.RenderableVertex(),
+
+	_v1x, _v1y, _v2x, _v2y, _v3x, _v3y,
+	_v4x, _v4y, _v5x, _v5y, _v6x, _v6y,
+
+	_color = new THREE.Color(),
+	_color1 = new THREE.Color(),
+	_color2 = new THREE.Color(),
+	_color3 = new THREE.Color(),
+	_color4 = new THREE.Color(),
+
+	_diffuseColor = new THREE.Color(),
+	_emissiveColor = new THREE.Color(),
+
+	_lightColor = new THREE.Color(),
+
+	_patterns = {}, _imagedatas = {},
+
+	_near, _far,
+
+	_image, _uvs,
+	_uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y,
+
+	_clipBox = new THREE.Box2(),
+	_clearBox = new THREE.Box2(),
+	_elemBox = new THREE.Box2(),
+
+	_enableLighting = false,
+	_ambientLight = new THREE.Color(),
+	_directionalLights = new THREE.Color(),
+	_pointLights = new THREE.Color(),
+
+	_vector3 = new THREE.Vector3(), // Needed for PointLight
+
+	_pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData,
+	_gradientMap, _gradientMapContext, _gradientMapQuality = 16;
+
+	_pixelMap = document.createElement( 'canvas' );
+	_pixelMap.width = _pixelMap.height = 2;
+
+	_pixelMapContext = _pixelMap.getContext( '2d' );
+	_pixelMapContext.fillStyle = 'rgba(0,0,0,1)';
+	_pixelMapContext.fillRect( 0, 0, 2, 2 );
+
+	_pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 );
+	_pixelMapData = _pixelMapImage.data;
+
+	_gradientMap = document.createElement( 'canvas' );
+	_gradientMap.width = _gradientMap.height = _gradientMapQuality;
+
+	_gradientMapContext = _gradientMap.getContext( '2d' );
+	_gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 );
+	_gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality );
+
+	_gradientMapQuality --; // Fix UVs
+
+	// dash+gap fallbacks for Firefox and everything else
+
+	if ( _context.setLineDash === undefined ) {
+
+		if ( _context.mozDash !== undefined ) {
+
+			_context.setLineDash = function ( values ) {
+
+				_context.mozDash = values[ 0 ] !== null ? values : null;
+
+			}
+
+		} else {
+
+			_context.setLineDash = function () {}
+
+		}
+
+	}
+
+	this.domElement = _canvas;
+
+	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
+				? parameters.devicePixelRatio
+				: window.devicePixelRatio !== undefined
+					? window.devicePixelRatio
+					: 1;
+
+	this.autoClear = true;
+	this.sortObjects = true;
+	this.sortElements = true;
+
+	this.info = {
+
+		render: {
+
+			vertices: 0,
+			faces: 0
+
+		}
+
+	}
+
+	// WebGLRenderer compatibility
+
+	this.supportsVertexTextures = function () {};
+	this.setFaceCulling = function () {};
+
+	this.setSize = function ( width, height ) {
+
+		_canvasWidth = width * this.devicePixelRatio;
+		_canvasHeight = height * this.devicePixelRatio;
+
+		_canvasWidthHalf = Math.floor( _canvasWidth / 2 );
+		_canvasHeightHalf = Math.floor( _canvasHeight / 2 );
+
+		_canvas.width = _canvasWidth;
+		_canvas.height = _canvasHeight;
+
+		_canvas.style.width = width + 'px';
+		_canvas.style.height = height + 'px';
+
+		_clipBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+		_clearBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+		_contextGlobalAlpha = 1;
+		_contextGlobalCompositeOperation = 0;
+		_contextStrokeStyle = null;
+		_contextFillStyle = null;
+		_contextLineWidth = null;
+		_contextLineCap = null;
+		_contextLineJoin = null;
+
+	};
+
+	this.setClearColor = function ( color, opacity ) {
+
+		_clearColor.copy( color );
+		_clearOpacity = opacity !== undefined ? opacity : 1;
+
+		_clearBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+	};
+
+	this.setClearColorHex = function ( hex, opacity ) {
+
+		_clearColor.setHex( hex );
+		_clearOpacity = opacity !== undefined ? opacity : 1;
+
+		_clearBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+	};
+
+	this.getMaxAnisotropy  = function () {
+
+		return 0;
+
+	};
+
+	this.clear = function () {
+
+		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+
+		if ( _clearBox.empty() === false ) {
+
+			_clearBox.intersect( _clipBox );
+			_clearBox.expandByScalar( 2 );
+
+			if ( _clearOpacity < 1 ) {
+
+				_context.clearRect(
+					_clearBox.min.x | 0,
+					_clearBox.min.y | 0,
+					( _clearBox.max.x - _clearBox.min.x ) | 0,
+					( _clearBox.max.y - _clearBox.min.y ) | 0
+				);
+
+			}
+
+			if ( _clearOpacity > 0 ) {
+
+				setBlending( THREE.NormalBlending );
+				setOpacity( 1 );
+
+				setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearOpacity + ')' );
+
+				_context.fillRect(
+					_clearBox.min.x | 0,
+					_clearBox.min.y | 0,
+					( _clearBox.max.x - _clearBox.min.x ) | 0,
+					( _clearBox.max.y - _clearBox.min.y ) | 0
+				);
+
+			}
+
+			_clearBox.makeEmpty();
+
+		}
+
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( camera instanceof THREE.Camera === false ) {
+
+			console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
+
+		}
+
+		if ( this.autoClear === true ) {
+
+			this.clear();
+
+		}
+
+		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+
+		_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;
+
+		/* 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 );
+		*/
+
+		_enableLighting = _lights.length > 0;
+
+		if ( _enableLighting === true ) {
+
+			 calculateLights();
+
+		}
+
+		for ( var e = 0, el = _elements.length; e < el; e++ ) {
+
+			var element = _elements[ e ];
+
+			var material = element.material;
+
+			if ( material === undefined || material.visible === false ) continue;
+
+			_elemBox.makeEmpty();
+
+			if ( element instanceof THREE.RenderableParticle ) {
+
+				_v1 = element;
+				_v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf;
+
+				renderParticle( _v1, element, material );
+
+			} else if ( element instanceof THREE.RenderableLine ) {
+
+				_v1 = element.v1; _v2 = element.v2;
+
+				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
+				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
+
+				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+
+					renderLine( _v1, _v2, element, material );
+
+				}
+
+			} else if ( element instanceof THREE.RenderableFace3 ) {
+
+				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
+
+				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;
+
+				_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 === true ) {
+
+					expand( _v1.positionScreen, _v2.positionScreen );
+					expand( _v2.positionScreen, _v3.positionScreen );
+					expand( _v3.positionScreen, _v1.positionScreen );
+
+				}
+
+				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen ] );
+
+				renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material );
+
+			} else if ( element instanceof THREE.RenderableFace4 ) {
+
+				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4;
+
+				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;
+				if ( _v4.positionScreen.z < -1 || _v4.positionScreen.z > 1 ) continue;
+
+				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
+				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
+				_v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf;
+				_v4.positionScreen.x *= _canvasWidthHalf; _v4.positionScreen.y *= _canvasHeightHalf;
+
+				_v5.positionScreen.copy( _v2.positionScreen );
+				_v6.positionScreen.copy( _v4.positionScreen );
+
+				if ( material.overdraw === true ) {
+
+					expand( _v1.positionScreen, _v2.positionScreen );
+					expand( _v2.positionScreen, _v4.positionScreen );
+					expand( _v4.positionScreen, _v1.positionScreen );
+
+					expand( _v3.positionScreen, _v5.positionScreen );
+					expand( _v3.positionScreen, _v6.positionScreen );
+
+				}
+
+				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen, _v4.positionScreen ] );
+
+				renderFace4( _v1, _v2, _v3, _v4, _v5, _v6, element, material, scene );
+
+			}
+
+			/* 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 );
+
+		}
+
+		/* 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 );
+		*/
+
+		_context.setTransform( 1, 0, 0, 1, 0, 0 );
+
+		//
+
+		function calculateLights() {
+
+			_ambientLight.setRGB( 0, 0, 0 );
+			_directionalLights.setRGB( 0, 0, 0 );
+			_pointLights.setRGB( 0, 0, 0 );
+
+			for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+
+				var light = _lights[ l ];
+				var lightColor = light.color;
+
+				if ( light instanceof THREE.AmbientLight ) {
+
+					_ambientLight.add( lightColor );
+
+				} else if ( light instanceof THREE.DirectionalLight ) {
+
+					// for particles
+
+					_directionalLights.add( lightColor );
+
+				} else if ( light instanceof THREE.PointLight ) {
+
+					// for particles
+
+					_pointLights.add( lightColor );
+
+				}
+
+			}
+
+		}
+
+		function calculateLight( position, normal, color ) {
+
+			for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+
+				var light = _lights[ l ];
+
+				_lightColor.copy( light.color );
+
+				if ( light instanceof THREE.DirectionalLight ) {
+
+					var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize();
+
+					var amount = normal.dot( lightPosition );
+
+					if ( amount <= 0 ) continue;
+
+					amount *= light.intensity;
+
+					color.add( _lightColor.multiplyScalar( amount ) );
+
+				} else if ( light instanceof THREE.PointLight ) {
+
+					var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld );
+
+					var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
+
+					if ( amount <= 0 ) continue;
+
+					amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
+
+					if ( amount == 0 ) continue;
+
+					amount *= light.intensity;
+
+					color.add( _lightColor.multiplyScalar( amount ) );
+
+				}
+
+			}
+
+		}
+
+		function renderParticle( v1, element, material ) {
+
+			setOpacity( material.opacity );
+			setBlending( material.blending );
+
+			var width, height, scaleX, scaleY,
+			bitmap, bitmapWidth, bitmapHeight;
+
+			if ( material instanceof THREE.ParticleBasicMaterial ) {
+
+				if ( material.map === null ) {
+
+					scaleX = element.object.scale.x;
+					scaleY = element.object.scale.y;
+
+					// TODO: Be able to disable this
+
+					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 );
+
+					if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+						return;
+
+					}
+
+					setFillStyle( material.color.getStyle() );
+
+					_context.save();
+					_context.translate( v1.x, v1.y );
+					_context.rotate( - element.rotation );
+					_context.scale( scaleX, scaleY );
+					_context.fillRect( -1, -1, 2, 2 );
+					_context.restore();
+
+				} else {
+
+					bitmap = material.map.image;
+					bitmapWidth = bitmap.width >> 1;
+					bitmapHeight = bitmap.height >> 1;
+
+					scaleX = element.scale.x * _canvasWidthHalf;
+					scaleY = element.scale.y * _canvasHeightHalf;
+
+					width = scaleX * bitmapWidth;
+					height = scaleY * bitmapHeight;
+
+					// TODO: Rotations break this...
+
+					_elemBox.min.set( v1.x - width, v1.y - height );
+					_elemBox.max.set( v1.x + width, v1.y + height );
+
+					if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+						return;
+
+					}
+
+					_context.save();
+					_context.translate( v1.x, v1.y );
+					_context.rotate( - element.rotation );
+					_context.scale( scaleX, - scaleY );
+
+					_context.translate( - bitmapWidth, - bitmapHeight );
+					_context.drawImage( bitmap, 0, 0 );
+					_context.restore();
+
+				}
+
+				/* 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();
+				*/
+
+			} else if ( material instanceof THREE.ParticleCanvasMaterial ) {
+
+				width = element.scale.x * _canvasWidthHalf;
+				height = element.scale.y * _canvasHeightHalf;
+
+				_elemBox.min.set( v1.x - width, v1.y - height );
+				_elemBox.max.set( v1.x + width, v1.y + height );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+					return;
+
+				}
+
+				setStrokeStyle( material.color.getStyle() );
+				setFillStyle( material.color.getStyle() );
+
+				_context.save();
+				_context.translate( v1.x, v1.y );
+				_context.rotate( - element.rotation );
+				_context.scale( width, height );
+
+				material.program( _context );
+
+				_context.restore();
+
+			}
+
+		}
+
+		function renderLine( v1, v2, element, material ) {
+
+			setOpacity( material.opacity );
+			setBlending( material.blending );
+
+			_context.beginPath();
+			_context.moveTo( v1.positionScreen.x, v1.positionScreen.y );
+			_context.lineTo( v2.positionScreen.x, v2.positionScreen.y );
+
+			if ( material instanceof THREE.LineBasicMaterial ) {
+
+				setLineWidth( material.linewidth );
+				setLineCap( material.linecap );
+				setLineJoin( material.linejoin );
+				setStrokeStyle( material.color.getStyle() );
+				setDashAndGap( null, null );
+
+				_context.stroke();
+				_elemBox.expandByScalar( material.linewidth * 2 );
+
+			} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+				setLineWidth( material.linewidth );
+				setLineCap( material.linecap );
+				setLineJoin( material.linejoin );
+				setStrokeStyle( material.color.getStyle() );
+				setDashAndGap( material.dashSize, material.gapSize );
+
+				_context.stroke();
+				_elemBox.expandByScalar( material.linewidth * 2 );
+
+			}
+
+		}
+
+		function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) {
+
+			_this.info.render.vertices += 3;
+			_this.info.render.faces ++;
+
+			setOpacity( material.opacity );
+			setBlending( material.blending );
+
+			_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
+			_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
+			_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
+
+			drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y );
+
+			if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) {
+
+				_diffuseColor.copy( material.color );
+				_emissiveColor.copy( material.emissive );
+
+				if ( material.vertexColors === THREE.FaceColors ) {
+
+					_diffuseColor.multiply( element.color );
+
+				}
+
+				if ( _enableLighting === true ) {
+
+					if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) {
+
+						_color1.copy( _ambientLight );
+						_color2.copy( _ambientLight );
+						_color3.copy( _ambientLight );
+
+						calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
+						calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
+						calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color3 );
+
+						_color1.multiply( _diffuseColor ).add( _emissiveColor );
+						_color2.multiply( _diffuseColor ).add( _emissiveColor );
+						_color3.multiply( _diffuseColor ).add( _emissiveColor );
+						_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+
+						_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+						clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+					} else {
+
+						_color.copy( _ambientLight );
+
+						calculateLight( element.centroidModel, element.normalModel, _color );
+
+						_color.multiply( _diffuseColor ).add( _emissiveColor );
+
+						material.wireframe === true
+							? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+							: fillPath( _color );
+
+					}
+
+				} else {
+
+					material.wireframe === true
+						? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+						: fillPath( material.color );
+
+				}
+
+			} else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
+
+				if ( material.map !== null ) {
+
+					if ( material.map.mapping instanceof THREE.UVMapping ) {
+
+						_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 );
+
+					}
+
+
+				} else if ( material.envMap !== null ) {
+
+					if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) {
+
+						_vector3.copy( element.vertexNormalsModelView[ uv1 ] );
+						_uv1x = 0.5 * _vector3.x + 0.5;
+						_uv1y = 0.5 * _vector3.y + 0.5;
+
+						_vector3.copy( element.vertexNormalsModelView[ uv2 ] );
+						_uv2x = 0.5 * _vector3.x + 0.5;
+						_uv2y = 0.5 * _vector3.y + 0.5;
+
+						_vector3.copy( element.vertexNormalsModelView[ uv3 ] );
+						_uv3x = 0.5 * _vector3.x + 0.5;
+						_uv3y = 0.5 * _vector3.y + 0.5;
+
+						patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
+
+					}/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) {
+
+
+
+					}*/
+
+
+				} else {
+
+					_color.copy( material.color );
+
+					if ( material.vertexColors === THREE.FaceColors ) {
+
+						_color.multiply( element.color );
+
+					}
+
+					material.wireframe === true
+						? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+						: fillPath( _color );
+
+				}
+
+			} else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+				_near = camera.near;
+				_far = camera.far;
+
+				_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 );
+
+				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+				clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+			} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+				var normal;
+
+				if ( material.shading == THREE.FlatShading ) {
+
+					normal = element.normalModelView;
+
+					_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 );
+
+				} else if ( material.shading == THREE.SmoothShading ) {
+
+					normal = element.vertexNormalsModelView[ uv1 ];
+					_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					normal = element.vertexNormalsModelView[ uv2 ];
+					_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					normal = element.vertexNormalsModelView[ uv3 ];
+					_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+
+					_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+					clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+				}
+
+			}
+
+		}
+
+		function renderFace4( v1, v2, v3, v4, v5, v6, element, material ) {
+
+			_this.info.render.vertices += 4;
+			_this.info.render.faces ++;
+
+			setOpacity( material.opacity );
+			setBlending( material.blending );
+
+			if ( ( material.map !== undefined && material.map !== null ) || ( material.envMap !== undefined && material.envMap !== null ) ) {
+
+				// Let renderFace3() handle this
+
+				renderFace3( v1, v2, v4, 0, 1, 3, element, material );
+				renderFace3( v5, v3, v6, 1, 2, 3, element, material );
+
+				return;
+
+			}
+
+			_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
+			_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
+			_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
+			_v4x = v4.positionScreen.x; _v4y = v4.positionScreen.y;
+			_v5x = v5.positionScreen.x; _v5y = v5.positionScreen.y;
+			_v6x = v6.positionScreen.x; _v6y = v6.positionScreen.y;
+
+			if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
+
+				_diffuseColor.copy( material.color );
+				_emissiveColor.copy( material.emissive );
+
+				if ( material.vertexColors === THREE.FaceColors ) {
+
+					_diffuseColor.multiply( element.color );
+
+				}
+
+				if ( _enableLighting === true ) {
+
+					if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 4 ) {
+
+						_color1.copy( _ambientLight );
+						_color2.copy( _ambientLight );
+						_color3.copy( _ambientLight );
+						_color4.copy( _ambientLight );
+
+						calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
+						calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
+						calculateLight( element.v4.positionWorld, element.vertexNormalsModel[ 3 ], _color3 );
+						calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color4 );
+
+						_color1.multiply( _diffuseColor ).add( _emissiveColor );
+						_color2.multiply( _diffuseColor ).add( _emissiveColor );
+						_color3.multiply( _diffuseColor ).add( _emissiveColor );
+						_color4.multiply( _diffuseColor ).add( _emissiveColor );
+
+						_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+						// TODO: UVs are incorrect, v4->v3?
+
+						drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
+						clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
+
+						drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
+						clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
+
+					} else {
+
+						_color.copy( _ambientLight );
+
+						calculateLight( element.centroidModel, element.normalModel, _color );
+
+						_color.multiply( _diffuseColor ).add( _emissiveColor );
+
+						drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
+
+						material.wireframe === true
+							? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+							: fillPath( _color );
+
+					}
+
+				} else {
+
+					_color.addColors( _diffuseColor, _emissiveColor );
+
+					drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
+
+					material.wireframe === true
+						? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+						: fillPath( _color );
+
+				}
+
+			} else if ( material instanceof THREE.MeshBasicMaterial ) {
+
+				_color.copy( material.color );
+
+				if ( material.vertexColors === THREE.FaceColors ) {
+
+					_color.multiply( element.color );
+
+				}
+
+				drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
+
+				material.wireframe === true
+					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+					: fillPath( _color );
+
+			} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+				var normal;
+
+				if ( material.shading == THREE.FlatShading ) {
+
+					normal = element.normalModelView;
+					_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
+
+					material.wireframe === true
+						? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+						: fillPath( _color );
+
+				} else if ( material.shading == THREE.SmoothShading ) {
+
+					normal = element.vertexNormalsModelView[ 0 ];
+					_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					normal = element.vertexNormalsModelView[ 1 ];
+					_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					normal = element.vertexNormalsModelView[ 3 ];
+					_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					normal = element.vertexNormalsModelView[ 2 ];
+					_color4.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+					_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+					drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
+					clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
+
+					drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
+					clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
+
+				}
+
+
+
+			} else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+				_near = camera.near;
+				_far = camera.far;
+
+				_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( v4.positionScreen.z * v4.positionScreen.w, _near, _far );
+				_color4.r = _color4.g = _color4.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far );
+
+				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+				// TODO: UVs are incorrect, v4->v3?
+
+				drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
+				clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
+
+				drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
+				clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
+
+			}
+
+		}
+
+		//
+
+		function drawTriangle( x0, y0, x1, y1, x2, y2 ) {
+
+			_context.beginPath();
+			_context.moveTo( x0, y0 );
+			_context.lineTo( x1, y1 );
+			_context.lineTo( x2, y2 );
+			_context.closePath();
+
+		}
+
+		function drawQuad( x0, y0, x1, y1, x2, y2, x3, y3 ) {
+
+			_context.beginPath();
+			_context.moveTo( x0, y0 );
+			_context.lineTo( x1, y1 );
+			_context.lineTo( x2, y2 );
+			_context.lineTo( x3, y3 );
+			_context.closePath();
+
+		}
+
+		function strokePath( color, linewidth, linecap, linejoin ) {
+
+			setLineWidth( linewidth );
+			setLineCap( linecap );
+			setLineJoin( linejoin );
+			setStrokeStyle( color.getStyle() );
+
+			_context.stroke();
+
+			_elemBox.expandByScalar( linewidth * 2 );
+
+		}
+
+		function fillPath( color ) {
+
+			setFillStyle( color.getStyle() );
+			_context.fill();
+
+		}
+
+		function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) {
+
+			if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return;
+
+			if ( texture.needsUpdate === true ) {
+
+				var repeatX = texture.wrapS == THREE.RepeatWrapping;
+				var repeatY = texture.wrapT == THREE.RepeatWrapping;
+
+				_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'
+				);
+
+				texture.needsUpdate = false;
+
+			}
+
+			_patterns[ texture.id ] === undefined
+				? setFillStyle( 'rgba(0,0,0,1)' )
+				: setFillStyle( _patterns[ texture.id ] );
+
+			// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+
+			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;
+
+			u0 = ( u0 + offsetX ) * width;
+			v0 = ( 1.0 - v0 + offsetY ) * height;
+
+			u1 = ( u1 + offsetX ) * width;
+			v1 = ( 1.0 - v1 + offsetY ) * height;
+
+			u2 = ( u2 + offsetX ) * width;
+			v2 = ( 1.0 - v2 + offsetY ) * height;
+
+			x1 -= x0; y1 -= y0;
+			x2 -= x0; y2 -= y0;
+
+			u1 -= u0; v1 -= v0;
+			u2 -= u0; v2 -= v0;
+
+			det = u1 * v2 - u2 * v1;
+
+			if ( det === 0 ) {
+
+				if ( _imagedatas[ texture.id ] === undefined ) {
+
+					var canvas = document.createElement( 'canvas' )
+					canvas.width = texture.image.width;
+					canvas.height = texture.image.height;
+
+					var context = canvas.getContext( '2d' );
+					context.drawImage( texture.image, 0, 0 );
+
+					_imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data;
+
+				}
+
+				var data = _imagedatas[ texture.id ];
+				var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4;
+
+				_color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 );
+				fillPath( _color );
+
+				return;
+
+			}
+
+			idet = 1 / det;
+
+			a = ( v2 * x1 - v1 * x2 ) * idet;
+			b = ( v2 * y1 - v1 * y2 ) * idet;
+			c = ( u1 * x2 - u2 * x1 ) * idet;
+			d = ( u1 * y2 - u2 * y1 ) * idet;
+
+			e = x0 - a * u0 - c * v0;
+			f = y0 - b * u0 - d * v0;
+
+			_context.save();
+			_context.transform( a, b, c, d, e, f );
+			_context.fill();
+			_context.restore();
+
+		}
+
+		function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) {
+
+			// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+
+			var a, b, c, d, e, f, det, idet,
+			width = image.width - 1,
+			height = image.height - 1;
+
+			u0 *= width; v0 *= height;
+			u1 *= width; v1 *= height;
+			u2 *= width; v2 *= height;
+
+			x1 -= x0; y1 -= y0;
+			x2 -= x0; y2 -= y0;
+
+			u1 -= u0; v1 -= v0;
+			u2 -= u0; v2 -= v0;
+
+			det = u1 * v2 - u2 * v1;
+
+			idet = 1 / det;
+
+			a = ( v2 * x1 - v1 * x2 ) * idet;
+			b = ( v2 * y1 - v1 * y2 ) * idet;
+			c = ( u1 * x2 - u2 * x1 ) * idet;
+			d = ( u1 * y2 - u2 * y1 ) * idet;
+
+			e = x0 - a * u0 - c * v0;
+			f = y0 - b * u0 - d * v0;
+
+			_context.save();
+			_context.transform( a, b, c, d, e, f );
+			_context.clip();
+			_context.drawImage( image, 0, 0 );
+			_context.restore();
+
+		}
+
+		function getGradientTexture( color1, color2, color3, color4 ) {
+
+			// http://mrdoob.com/blog/post/710
+
+			_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;
+
+			_pixelMapData[ 8 ] = ( color3.r * 255 ) | 0;
+			_pixelMapData[ 9 ] = ( color3.g * 255 ) | 0;
+			_pixelMapData[ 10 ] = ( color3.b * 255 ) | 0;
+
+			_pixelMapData[ 12 ] = ( color4.r * 255 ) | 0;
+			_pixelMapData[ 13 ] = ( color4.g * 255 ) | 0;
+			_pixelMapData[ 14 ] = ( color4.b * 255 ) | 0;
+
+			_pixelMapContext.putImageData( _pixelMapImage, 0, 0 );
+			_gradientMapContext.drawImage( _pixelMap, 0, 0 );
+
+			return _gradientMap;
+
+		}
+
+		// Hide anti-alias gaps
+
+		function expand( v1, v2 ) {
+
+			var x = v2.x - v1.x, y =  v2.y - v1.y,
+			det = x * x + y * y, idet;
+
+			if ( det === 0 ) return;
+
+			idet = 1 / Math.sqrt( det );
+
+			x *= idet; y *= idet;
+
+			v2.x += x; v2.y += y;
+			v1.x -= x; v1.y -= y;
+
+		}
+	};
+
+	// Context cached methods.
+
+	function setOpacity( value ) {
+
+		if ( _contextGlobalAlpha !== value ) {
+
+			_context.globalAlpha = value;
+			_contextGlobalAlpha = value;
+
+		}
+
+	}
+
+	function setBlending( value ) {
+
+		if ( _contextGlobalCompositeOperation !== value ) {
+
+			if ( value === THREE.NormalBlending ) {
+
+				_context.globalCompositeOperation = 'source-over';
+
+			} else if ( value === THREE.AdditiveBlending ) {
+
+				_context.globalCompositeOperation = 'lighter';
+
+			} else if ( value === THREE.SubtractiveBlending ) {
+
+				_context.globalCompositeOperation = 'darker';
+
+			}
+
+			_contextGlobalCompositeOperation = value;
+
+		}
+
+	}
+
+	function setLineWidth( value ) {
+
+		if ( _contextLineWidth !== value ) {
+
+			_context.lineWidth = value;
+			_contextLineWidth = value;
+
+		}
+
+	}
+
+	function setLineCap( value ) {
+
+		// "butt", "round", "square"
+
+		if ( _contextLineCap !== value ) {
+
+			_context.lineCap = value;
+			_contextLineCap = value;
+
+		}
+
+	}
+
+	function setLineJoin( value ) {
+
+		// "round", "bevel", "miter"
+
+		if ( _contextLineJoin !== value ) {
+
+			_context.lineJoin = value;
+			_contextLineJoin = value;
+
+		}
+
+	}
+
+	function setStrokeStyle( value ) {
+
+		if ( _contextStrokeStyle !== value ) {
+
+			_context.strokeStyle = value;
+			_contextStrokeStyle = value;
+
+		}
+
+	}
+
+	function setFillStyle( value ) {
+
+		if ( _contextFillStyle !== value ) {
+
+			_context.fillStyle = value;
+			_contextFillStyle = value;
+
+		}
+
+	}
+
+	function setDashAndGap( dashSizeValue, gapSizeValue ) {
+
+		if ( _contextDashSize !== dashSizeValue || _contextGapSize !== gapSizeValue ) {
+
+			_context.setLineDash( [ dashSizeValue, gapSizeValue ] );
+			_contextDashSize = dashSizeValue;
+			_contextGapSize = gapSizeValue;
+
+		}
+
+	}
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.ShaderChunk = {
+
+	// FOG
+
+	fog_pars_fragment: [
+
+		"#ifdef USE_FOG",
+
+			"uniform vec3 fogColor;",
+
+			"#ifdef FOG_EXP2",
+
+				"uniform float fogDensity;",
+
+			"#else",
+
+				"uniform float fogNear;",
+				"uniform float fogFar;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	fog_fragment: [
+
+		"#ifdef USE_FOG",
+
+			"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+
+			"#ifdef FOG_EXP2",
+
+				"const float LOG2 = 1.442695;",
+				"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
+				"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
+
+			"#else",
+
+				"float fogFactor = smoothstep( fogNear, fogFar, depth );",
+
+			"#endif",
+
+			"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
+
+		"#endif"
+
+	].join("\n"),
+
+	// ENVIRONMENT MAP
+
+	envmap_pars_fragment: [
+
+		"#ifdef USE_ENVMAP",
+
+			"uniform float reflectivity;",
+			"uniform samplerCube envMap;",
+			"uniform float flipEnvMap;",
+			"uniform int combine;",
+
+			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+
+				"uniform bool useRefract;",
+				"uniform float refractionRatio;",
+
+			"#else",
+
+				"varying vec3 vReflect;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_fragment: [
+
+		"#ifdef USE_ENVMAP",
+
+			"vec3 reflectVec;",
+
+			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+
+				"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+
+				"if ( useRefract ) {",
+
+					"reflectVec = refract( cameraToVertex, normal, refractionRatio );",
+
+				"} else { ",
+
+					"reflectVec = reflect( cameraToVertex, normal );",
+
+				"}",
+
+			"#else",
+
+				"reflectVec = vReflect;",
+
+			"#endif",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
+				"vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+
+			"#else",
+
+				"vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+
+			"#endif",
+
+			"#ifdef GAMMA_INPUT",
+
+				"cubeColor.xyz *= cubeColor.xyz;",
+
+			"#endif",
+
+			"if ( combine == 1 ) {",
+
+				"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );",
+
+			"} else if ( combine == 2 ) {",
+
+				"gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;",
+
+			"} else {",
+
+				"gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_pars_vertex: [
+
+		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+
+			"varying vec3 vReflect;",
+
+			"uniform float refractionRatio;",
+			"uniform bool useRefract;",
+
+		"#endif"
+
+	].join("\n"),
+
+	worldpos_vertex : [
+
+		"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )",
+
+			"#ifdef USE_SKINNING",
+
+				"vec4 worldPosition = modelMatrix * skinned;",
+
+			"#endif",
+
+			"#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+
+				"vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );",
+
+			"#endif",
+
+			"#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+
+				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_vertex : [
+
+		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+
+			"vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;",
+			"worldNormal = normalize( worldNormal );",
+
+			"vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );",
+
+			"if ( useRefract ) {",
+
+				"vReflect = refract( cameraToVertex, worldNormal, refractionRatio );",
+
+			"} else {",
+
+				"vReflect = reflect( cameraToVertex, worldNormal );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// COLOR MAP (particles)
+
+	map_particle_pars_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"uniform sampler2D map;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	map_particle_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );",
+
+		"#endif"
+
+	].join("\n"),
+
+	// COLOR MAP (triangles)
+
+	map_pars_vertex: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"varying vec2 vUv;",
+			"uniform vec4 offsetRepeat;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_pars_fragment: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"varying vec2 vUv;",
+
+		"#endif",
+
+		"#ifdef USE_MAP",
+
+			"uniform sampler2D map;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_vertex: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"vec4 texelColor = texture2D( map, vUv );",
+
+			"#ifdef GAMMA_INPUT",
+
+				"texelColor.xyz *= texelColor.xyz;",
+
+			"#endif",
+
+			"gl_FragColor = gl_FragColor * texelColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHT MAP
+
+	lightmap_pars_fragment: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"varying vec2 vUv2;",
+			"uniform sampler2D lightMap;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_pars_vertex: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"varying vec2 vUv2;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_fragment: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_vertex: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"vUv2 = uv2;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// BUMP MAP
+
+	bumpmap_pars_fragment: [
+
+		"#ifdef USE_BUMPMAP",
+
+			"uniform sampler2D bumpMap;",
+			"uniform float bumpScale;",
+
+			// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen
+			//	http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html
+
+			// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
+
+			"vec2 dHdxy_fwd() {",
+
+				"vec2 dSTdx = dFdx( vUv );",
+				"vec2 dSTdy = dFdy( vUv );",
+
+				"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;",
+
+				"return vec2( dBx, dBy );",
+
+			"}",
+
+			"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
+
+				"vec3 R1 = cross( vSigmaY, vN );",
+				"vec3 R2 = cross( vN, vSigmaX );",
+
+				"float fDet = dot( vSigmaX, R1 );",
+
+				"vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );",
+				"return normalize( abs( fDet ) * surf_norm - vGrad );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// NORMAL MAP
+
+	normalmap_pars_fragment: [
+
+		"#ifdef USE_NORMALMAP",
+
+			"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 ) {",
+
+				"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 );",
+
+				"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 );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// SPECULAR MAP
+
+	specularmap_pars_fragment: [
+
+		"#ifdef USE_SPECULARMAP",
+
+			"uniform sampler2D specularMap;",
+
+		"#endif"
+
+	].join("\n"),
+
+	specularmap_fragment: [
+
+		"float specularStrength;",
+
+		"#ifdef USE_SPECULARMAP",
+
+			"vec4 texelSpecular = texture2D( specularMap, vUv );",
+			"specularStrength = texelSpecular.r;",
+
+		"#else",
+
+			"specularStrength = 1.0;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHTS LAMBERT
+
+	lights_lambert_pars_vertex: [
+
+		"uniform vec3 ambient;",
+		"uniform vec3 diffuse;",
+		"uniform vec3 emissive;",
+
+		"uniform vec3 ambientLightColor;",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"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 ];",
+
+		"#endif",
+
+		"#ifdef WRAP_AROUND",
+
+			"uniform vec3 wrapRGB;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lights_lambert_vertex: [
+
+		"vLightFront = vec3( 0.0 );",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"vLightBack = vec3( 0.0 );",
+
+		"#endif",
+
+		"transformedNormal = normalize( transformedNormal );",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+		"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+
+			"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
+			"vec3 dirVector = normalize( lDirection.xyz );",
+
+			"float dotProduct = dot( transformedNormal, dirVector );",
+			"vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+				"#ifdef WRAP_AROUND",
+
+					"vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+				"#endif",
+
+			"#endif",
+
+			"#ifdef WRAP_AROUND",
+
+				"vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+				"directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );",
+
+				"#endif",
+
+			"#endif",
+
+			"vLightFront += directionalLightColor[ i ] * directionalLightWeighting;",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;",
+
+			"#endif",
+
+		"}",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( pointLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+				"lVector = normalize( lVector );",
+				"float dotProduct = dot( transformedNormal, lVector );",
+
+				"vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+					"#ifdef WRAP_AROUND",
+
+						"vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+					"#endif",
+
+				"#endif",
+
+				"#ifdef WRAP_AROUND",
+
+					"vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+					"pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );",
+
+					"#endif",
+
+				"#endif",
+
+				"vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );",
+
+				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+					"float lDistance = 1.0;",
+					"if ( spotLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+					"float dotProduct = dot( transformedNormal, lVector );",
+					"vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+						"#ifdef WRAP_AROUND",
+
+							"vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+						"#endif",
+
+					"#endif",
+
+					"#ifdef WRAP_AROUND",
+
+						"vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+						"spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );",
+
+						"#ifdef DOUBLE_SIDED",
+
+							"spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );",
+
+						"#endif",
+
+					"#endif",
+
+					"vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;",
+
+					"#endif",
+
+				"}",
+
+			"}",
+
+		"#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 );",
+
+				"float dotProduct = dot( transformedNormal, lVector );",
+
+				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+				"float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;",
+
+				"vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHTS PHONG
+
+	lights_phong_pars_vertex: [
+
+		"#ifndef PHONG_PER_PIXEL",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"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;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	lights_phong_vertex: [
+
+		"#ifndef PHONG_PER_PIXEL",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( pointLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+				"vPointLight[ i ] = vec4( lVector, lDistance );",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( spotLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+				"vSpotLight[ i ] = vec4( lVector, lDistance );",
+
+			"}",
+
+		"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+
+			"vWorldPosition = worldPosition.xyz;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lights_phong_pars_fragment: [
+
+		"uniform vec3 ambientLightColor;",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+
+			"#ifdef PHONG_PER_PIXEL",
+
+				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"#else",
+
+				"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+
+			"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"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 ];",
+
+			"#ifdef PHONG_PER_PIXEL",
+
+				"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+
+			"#else",
+
+				"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
+
+			"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+
+			"varying vec3 vWorldPosition;",
+
+		"#endif",
+
+		"#ifdef WRAP_AROUND",
+
+			"uniform vec3 wrapRGB;",
+
+		"#endif",
+
+		"varying vec3 vViewPosition;",
+		"varying vec3 vNormal;"
+
+	].join("\n"),
+
+	lights_phong_fragment: [
+
+		"vec3 normal = normalize( vNormal );",
+		"vec3 viewPosition = normalize( vViewPosition );",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
+
+		"#endif",
+
+		"#ifdef USE_NORMALMAP",
+
+			"normal = perturbNormal2Arb( -viewPosition, normal );",
+
+		"#elif defined( USE_BUMPMAP )",
+
+			"normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"vec3 pointDiffuse  = vec3( 0.0 );",
+			"vec3 pointSpecular = vec3( 0.0 );",
+
+			"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"#ifdef PHONG_PER_PIXEL",
+
+					"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+
+					"float lDistance = 1.0;",
+					"if ( pointLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+				"#else",
+
+					"vec3 lVector = normalize( vPointLight[ i ].xyz );",
+					"float lDistance = vPointLight[ i ].w;",
+
+				"#endif",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, lVector );",
+
+				"#ifdef WRAP_AROUND",
+
+					"float pointDiffuseWeightFull = max( dotProduct, 0.0 );",
+					"float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+					"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+
+				"#else",
+
+					"float pointDiffuseWeight = max( dotProduct, 0.0 );",
+
+				"#endif",
+
+				"pointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;",
+
+				// specular
+
+				"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+				"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+				"float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
+
+				"#ifdef PHYSICALLY_BASED_SHADING",
+
+					// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );",
+					"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;",
+
+				"#else",
+
+					"pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"vec3 spotDiffuse  = vec3( 0.0 );",
+			"vec3 spotSpecular = vec3( 0.0 );",
+
+			"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"#ifdef PHONG_PER_PIXEL",
+
+					"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+
+					"float lDistance = 1.0;",
+					"if ( spotLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+				"#else",
+
+					"vec3 lVector = normalize( vSpotLight[ i ].xyz );",
+					"float lDistance = vSpotLight[ i ].w;",
+
+				"#endif",
+
+				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+
+				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+					// diffuse
+
+					"float dotProduct = dot( normal, lVector );",
+
+					"#ifdef WRAP_AROUND",
+
+						"float spotDiffuseWeightFull = max( dotProduct, 0.0 );",
+						"float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+						"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+
+					"#else",
+
+						"float spotDiffuseWeight = max( dotProduct, 0.0 );",
+
+					"#endif",
+
+					"spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;",
+
+					// specular
+
+					"vec3 spotHalfVector = normalize( lVector + viewPosition );",
+					"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
+					"float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );",
+
+					"#ifdef PHYSICALLY_BASED_SHADING",
+
+						// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+						"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+						"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );",
+						"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;",
+
+					"#else",
+
+						"spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;",
+
+					"#endif",
+
+				"}",
+
+			"}",
+
+		"#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 );",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, dirVector );",
+
+				"#ifdef WRAP_AROUND",
+
+					"float dirDiffuseWeightFull = max( dotProduct, 0.0 );",
+					"float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+					"vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );",
+
+				"#else",
+
+					"float dirDiffuseWeight = max( dotProduct, 0.0 );",
+
+				"#endif",
+
+				"dirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;",
+
+				// specular
+
+				"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",
+
+					/*
+					// fresnel term from skin shader
+					"const float F0 = 0.128;",
+
+					"float base = 1.0 - dot( viewPosition, dirHalfVector );",
+					"float exponential = pow( base, 5.0 );",
+
+					"float fresnel = exponential + F0 * ( 1.0 - exponential );",
+					*/
+
+					/*
+					// fresnel term from fresnel shader
+					"const float mFresnelBias = 0.08;",
+					"const float mFresnelScale = 0.3;",
+					"const float mFresnelPower = 5.0;",
+
+					"float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );",
+					*/
+
+					// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+					//"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;",
+
+					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
+					"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
+
+				"#else",
+
+					"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"vec3 hemiDiffuse  = vec3( 0.0 );",
+			"vec3 hemiSpecular = vec3( 0.0 );" ,
+
+			"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+
+				"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
+				"vec3 lVector = normalize( lDirection.xyz );",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, lVector );",
+				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+				"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+				"hemiDiffuse += diffuse * hemiColor;",
+
+				// specular (sky light)
+
+				"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
+				"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
+				"float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
+
+				// specular (ground light)
+
+				"vec3 lVectorGround = -lVector;",
+
+				"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
+				"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
+				"float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
+
+				"#ifdef PHYSICALLY_BASED_SHADING",
+
+					"float dotProductGround = dot( normal, lVectorGround );",
+
+					// 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;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"vec3 totalDiffuse = vec3( 0.0 );",
+		"vec3 totalSpecular = vec3( 0.0 );",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"totalDiffuse += dirDiffuse;",
+			"totalSpecular += dirSpecular;",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"totalDiffuse += hemiDiffuse;",
+			"totalSpecular += hemiSpecular;",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"totalDiffuse += pointDiffuse;",
+			"totalSpecular += pointSpecular;",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"totalDiffuse += spotDiffuse;",
+			"totalSpecular += spotSpecular;",
+
+		"#endif",
+
+		"#ifdef METAL",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );",
+
+		"#else",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// VERTEX COLORS
+
+	color_pars_fragment: [
+
+		"#ifdef USE_COLOR",
+
+			"varying vec3 vColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	color_fragment: [
+
+		"#ifdef USE_COLOR",
+
+			"gl_FragColor = gl_FragColor * vec4( vColor, opacity );",
+
+		"#endif"
+
+	].join("\n"),
+
+	color_pars_vertex: [
+
+		"#ifdef USE_COLOR",
+
+			"varying vec3 vColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	color_vertex: [
+
+		"#ifdef USE_COLOR",
+
+			"#ifdef GAMMA_INPUT",
+
+				"vColor = color * color;",
+
+			"#else",
+
+				"vColor = color;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	// SKINNING
+
+	skinning_pars_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"#ifdef BONE_TEXTURE",
+
+				"uniform sampler2D boneTexture;",
+
+				"mat4 getBoneMatrix( const in float i ) {",
+
+					"float j = i * 4.0;",
+					"float x = mod( j, N_BONE_PIXEL_X );",
+					"float y = floor( j / N_BONE_PIXEL_X );",
+
+					"const float dx = 1.0 / N_BONE_PIXEL_X;",
+					"const float dy = 1.0 / N_BONE_PIXEL_Y;",
+
+					"y = dy * ( y + 0.5 );",
+
+					"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 ) );",
+
+					"mat4 bone = mat4( v1, v2, v3, v4 );",
+
+					"return bone;",
+
+				"}",
+
+			"#else",
+
+				"uniform mat4 boneGlobalMatrices[ MAX_BONES ];",
+
+				"mat4 getBoneMatrix( const in float i ) {",
+
+					"mat4 bone = boneGlobalMatrices[ int(i) ];",
+					"return bone;",
+
+				"}",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinbase_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"mat4 boneMatX = getBoneMatrix( skinIndex.x );",
+			"mat4 boneMatY = getBoneMatrix( skinIndex.y );",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinning_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"#ifdef USE_MORPHTARGETS",
+
+			"vec4 skinVertex = vec4( morphed, 1.0 );",
+
+			"#else",
+
+			"vec4 skinVertex = vec4( position, 1.0 );",
+
+			"#endif",
+
+			"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+			"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// MORPHING
+
+	morphtarget_pars_vertex: [
+
+		"#ifdef USE_MORPHTARGETS",
+
+			"#ifndef USE_MORPHNORMALS",
+
+			"uniform float morphTargetInfluences[ 8 ];",
+
+			"#else",
+
+			"uniform float morphTargetInfluences[ 4 ];",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	morphtarget_vertex: [
+
+		"#ifdef USE_MORPHTARGETS",
+
+			"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 ];",
+
+			"#ifndef USE_MORPHNORMALS",
+
+			"morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];",
+			"morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];",
+			"morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];",
+			"morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];",
+
+			"#endif",
+
+			"morphed += position;",
+
+		"#endif"
+
+	].join("\n"),
+
+	default_vertex : [
+
+		"vec4 mvPosition;",
+
+		"#ifdef USE_SKINNING",
+
+			"mvPosition = modelViewMatrix * skinned;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )",
+
+			"mvPosition = modelViewMatrix * vec4( morphed, 1.0 );",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )",
+
+			"mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+		"#endif",
+
+		"gl_Position = projectionMatrix * mvPosition;"
+
+	].join("\n"),
+
+	morphnormal_vertex: [
+
+		"#ifdef USE_MORPHNORMALS",
+
+			"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 ];",
+
+			"morphedNormal += normal;",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinnormal_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"mat4 skinMatrix = skinWeight.x * boneMatX;",
+			"skinMatrix 	+= skinWeight.y * boneMatY;",
+
+			"#ifdef USE_MORPHNORMALS",
+
+			"vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );",
+
+			"#else",
+
+			"vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	defaultnormal_vertex: [
+
+		"vec3 objectNormal;",
+
+		"#ifdef USE_SKINNING",
+
+			"objectNormal = skinnedNormal.xyz;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )",
+
+			"objectNormal = morphedNormal;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )",
+
+			"objectNormal = normal;",
+
+		"#endif",
+
+		"#ifdef FLIP_SIDED",
+
+			"objectNormal = -objectNormal;",
+
+		"#endif",
+
+		"vec3 transformedNormal = normalMatrix * objectNormal;"
+
+	].join("\n"),
+
+	// SHADOW MAP
+
+	// based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples
+	//  http://spidergl.org/example.php?id=6
+	// 	http://fabiensanglard.net/shadowmapping
+
+	shadowmap_pars_fragment: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"uniform sampler2D shadowMap[ MAX_SHADOWS ];",
+			"uniform vec2 shadowMapSize[ MAX_SHADOWS ];",
+
+			"uniform float shadowDarkness[ MAX_SHADOWS ];",
+			"uniform float shadowBias[ MAX_SHADOWS ];",
+
+			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
+
+			"float unpackDepth( const in vec4 rgba_depth ) {",
+
+				"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;",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_fragment: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"#ifdef SHADOWMAP_DEBUG",
+
+				"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 );",
+
+			"#endif",
+
+			"#ifdef SHADOWMAP_CASCADE",
+
+				"int inFrustumCount = 0;",
+
+			"#endif",
+
+			"float fDepth;",
+			"vec3 shadowColor = vec3( 1.0 );",
+
+			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+				"vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;",
+
+				// "if ( something && something )" 		 breaks ATI OpenGL shader compiler
+				// "if ( all( something, something ) )"  using this instead
+
+				"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",
+
+					"inFrustumCount += int( inFrustum );",
+					"bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );",
+
+				"#else",
+
+					"bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );",
+
+				"#endif",
+
+				"bool frustumTest = all( frustumTestVec );",
+
+				"if ( frustumTest ) {",
+
+					"shadowCoord.z += shadowBias[ i ];",
+
+					"#if defined( SHADOWMAP_TYPE_PCF )",
+
+						// Percentage-close filtering
+						// (9 pixel kernel)
+						// http://fabiensanglard.net/shadowmappingPCF/
+
+						"float shadow = 0.0;",
+
+						/*
+						// 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 );",
+
+								// 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 ) );",
+
+								"float fDepth = unpackDepth( rgbaDepth );",
+
+								"if ( fDepth < shadowCoord.z )",
+									"shadow += 1.0;",
+
+						"}",
+
+						"shadow /= 9.0;",
+
+						*/
+
+						"const float shadowDelta = 1.0 / 9.0;",
+
+						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
+						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+
+						"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;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"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;",
+
+						"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;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+
+					"#elif defined( SHADOWMAP_TYPE_PCF_SOFT )",
+
+						// Percentage-close filtering
+						// (9 pixel kernel)
+						// http://fabiensanglard.net/shadowmappingPCF/
+
+						"float shadow = 0.0;",
+
+						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
+						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+
+						"float dx0 = -1.0 * xPixelOffset;",
+						"float dy0 = -1.0 * yPixelOffset;",
+						"float dx1 = 1.0 * xPixelOffset;",
+						"float dy1 = 1.0 * yPixelOffset;",
+
+						"mat3 shadowKernel;",
+						"mat3 depthKernel;",
+
+						"depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
+						"if ( depthKernel[0][0] < shadowCoord.z ) shadowKernel[0][0] = 0.25;",
+						"else shadowKernel[0][0] = 0.0;",
+
+						"depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
+						"if ( depthKernel[0][1] < shadowCoord.z ) shadowKernel[0][1] = 0.25;",
+						"else shadowKernel[0][1] = 0.0;",
+
+						"depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
+						"if ( depthKernel[0][2] < shadowCoord.z ) shadowKernel[0][2] = 0.25;",
+						"else shadowKernel[0][2] = 0.0;",
+
+						"depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
+						"if ( depthKernel[1][0] < shadowCoord.z ) shadowKernel[1][0] = 0.25;",
+						"else shadowKernel[1][0] = 0.0;",
+
+						"depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
+						"if ( depthKernel[1][1] < shadowCoord.z ) shadowKernel[1][1] = 0.25;",
+						"else shadowKernel[1][1] = 0.0;",
+
+						"depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
+						"if ( depthKernel[1][2] < shadowCoord.z ) shadowKernel[1][2] = 0.25;",
+						"else shadowKernel[1][2] = 0.0;",
+
+						"depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
+						"if ( depthKernel[2][0] < shadowCoord.z ) shadowKernel[2][0] = 0.25;",
+						"else shadowKernel[2][0] = 0.0;",
+
+						"depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
+						"if ( depthKernel[2][1] < shadowCoord.z ) shadowKernel[2][1] = 0.25;",
+						"else shadowKernel[2][1] = 0.0;",
+
+						"depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
+						"if ( depthKernel[2][2] < shadowCoord.z ) shadowKernel[2][2] = 0.25;",
+						"else shadowKernel[2][2] = 0.0;",
+
+						"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 );",
+
+						"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 );",
+
+						"shadow = dot( shadowValues, vec4( 1.0 ) );",
+
+						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+
+					"#else",
+
+						"vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );",
+						"float fDepth = unpackDepth( rgbaDepth );",
+
+						"if ( fDepth < shadowCoord.z )",
+
+							// spot with multiple shadows is darker
+
+							"shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );",
+
+							// spot with multiple shadows has the same color as single shadow spot
+
+							//"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );",
+
+					"#endif",
+
+				"}",
+
+
+				"#ifdef SHADOWMAP_DEBUG",
+
+					"#ifdef SHADOWMAP_CASCADE",
+
+						"if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];",
+
+					"#else",
+
+						"if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];",
+
+					"#endif",
+
+				"#endif",
+
+			"}",
+
+			"#ifdef GAMMA_OUTPUT",
+
+				"shadowColor *= shadowColor;",
+
+			"#endif",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_pars_vertex: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
+			"uniform mat4 shadowMatrix[ MAX_SHADOWS ];",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_vertex: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+				"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// ALPHATEST
+
+	alphatest_fragment: [
+
+		"#ifdef ALPHATEST",
+
+			"if ( gl_FragColor.a < ALPHATEST ) discard;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LINEAR SPACE
+
+	linear_to_gamma_fragment: [
+
+		"#ifdef GAMMA_OUTPUT",
+
+			"gl_FragColor.xyz = sqrt( gl_FragColor.xyz );",
+
+		"#endif"
+
+	].join("\n")
+
+
+};
+
+THREE.UniformsUtils = {
+
+	merge: function ( uniforms ) {
+
+		var u, p, tmp, merged = {};
+
+		for ( u = 0; u < uniforms.length; u ++ ) {
+
+			tmp = this.clone( uniforms[ u ] );
+
+			for ( p in tmp ) {
+
+				merged[ p ] = tmp[ p ];
+
+			}
+
+		}
+
+		return merged;
+
+	},
+
+	clone: function ( uniforms_src ) {
+
+		var u, p, parameter, parameter_src, uniforms_dst = {};
+
+		for ( u in uniforms_src ) {
+
+			uniforms_dst[ u ] = {};
+
+			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 ) {
+
+					uniforms_dst[ u ][ p ] = parameter_src.clone();
+
+				} else if ( parameter_src instanceof Array ) {
+
+					uniforms_dst[ u ][ p ] = parameter_src.slice();
+
+				} else {
+
+					uniforms_dst[ u ][ p ] = parameter_src;
+
+				}
+
+			}
+
+		}
+
+		return uniforms_dst;
+
+	}
+
+};
+
+THREE.UniformsLib = {
+
+	common: {
+
+		"diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+		"opacity" : { type: "f", value: 1.0 },
+
+		"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.0 },
+		"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: 0.00025 },
+		"fogNear" : { type: "f", value: 1 },
+		"fogFar" : { type: "f", value: 2000 },
+		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+	},
+
+	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( 0xeeeeee ) },
+		"opacity" : { type: "f", value: 1.0 },
+		"size" : { type: "f", value: 1.0 },
+		"scale" : { type: "f", value: 1.0 },
+		"map" : { type: "t", value: null },
+
+		"fogDensity" : { type: "f", value: 0.00025 },
+		"fogNear" : { type: "f", value: 1 },
+		"fogFar" : { type: "f", value: 2000 },
+		"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+	},
+
+	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;",
+			"uniform 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() {",
+
+				"gl_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( 0xffffff ) },
+				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+				"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
+			}
+
+		] ),
+
+		vertexShader: [
+
+			"#define LAMBERT",
+
+			"varying vec3 vLightFront;",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"varying vec3 vLightBack;",
+
+			"#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;",
+
+			"varying vec3 vLightFront;",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"varying vec3 vLightBack;",
+
+			"#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() {",
+
+				"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
+
+				THREE.ShaderChunk[ "map_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
+				THREE.ShaderChunk[ "specularmap_fragment" ],
+
+				"#ifdef DOUBLE_SIDED",
+
+					//"float isFront = float( gl_FrontFacing );",
+					//"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;",
+
+					"if ( gl_FrontFacing )",
+						"gl_FragColor.xyz *= vLightFront;",
+					"else",
+						"gl_FragColor.xyz *= vLightBack;",
+
+				"#else",
+
+					"gl_FragColor.xyz *= vLightFront;",
+
+				"#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( 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 ) }
+			}
+
+		] ),
+
+		vertexShader: [
+
+			"#define PHONG",
+
+			"varying vec3 vViewPosition;",
+			"varying 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;",
+			"uniform float opacity;",
+
+			"uniform vec3 ambient;",
+			"uniform vec3 emissive;",
+			"uniform vec3 specular;",
+			"uniform 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() {",
+
+				"gl_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;",
+			"uniform 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 );",
+
+				"#ifdef USE_SIZEATTENUATION",
+					"gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
+				"#else",
+					"gl_PointSize = size;",
+				"#endif",
+
+				"gl_Position = projectionMatrix * mvPosition;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform vec3 psColor;",
+			"uniform 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() {",
+
+				"gl_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;",
+			"attribute float lineDistance;",
+
+			"varying float vLineDistance;",
+
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+
+			"void main() {",
+
+				THREE.ShaderChunk[ "color_vertex" ],
+
+				"vLineDistance = scale * lineDistance;",
+
+				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+				"gl_Position = projectionMatrix * mvPosition;",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform vec3 diffuse;",
+			"uniform float opacity;",
+
+			"uniform float dashSize;",
+			"uniform float totalSize;",
+
+			"varying float vLineDistance;",
+
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+
+			"void main() {",
+
+				"if ( mod( vLineDistance, totalSize ) > dashSize ) {",
+
+					"discard;",
+
+				"}",
+
+				"gl_FragColor = vec4( diffuse, opacity );",
+
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "fog_fragment" ],
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	'depth': {
+
+		uniforms: {
+
+			"mNear": { type: "f", value: 1.0 },
+			"mFar" : { type: "f", value: 2000.0 },
+			"opacity" : { type: "f", value: 1.0 }
+
+		},
+
+		vertexShader: [
+
+			"void main() {",
+
+				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform float mNear;",
+			"uniform float mFar;",
+			"uniform float opacity;",
+
+			"void main() {",
+
+				"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+				"float color = 1.0 - smoothstep( mNear, mFar, depth );",
+				"gl_FragColor = vec4( vec3( color ), opacity );",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	'normal': {
+
+		uniforms: {
+
+			"opacity" : { type: "f", value: 1.0 }
+
+		},
+
+		vertexShader: [
+
+			"varying vec3 vNormal;",
+
+			"void main() {",
+
+				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+				"vNormal = normalize( normalMatrix * normal );",
+
+				"gl_Position = projectionMatrix * mvPosition;",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform float opacity;",
+			"varying vec3 vNormal;",
+
+			"void main() {",
+
+				"gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	/* -------------------------------------------------------------------------
+	//	Normal map shader
+	//		- Blinn-Phong
+	//		- normal + diffuse + specular + AO + displacement + reflection + shadow maps
+	//		- point and directional lights (use with "lights: true" material option)
+	 ------------------------------------------------------------------------- */
+
+	'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 }, // 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 ) },
+
+			"uDisplacementBias": { type: "f", value: 0.0 },
+			"uDisplacementScale": { type: "f", value: 1.0 },
+
+			"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 },
+
+			"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;",
+			"uniform vec3 uDiffuseColor;",
+			"uniform vec3 uSpecularColor;",
+			"uniform float uShininess;",
+			"uniform float uOpacity;",
+
+			"uniform bool enableDiffuse;",
+			"uniform bool enableSpecular;",
+			"uniform bool enableAO;",
+			"uniform bool enableReflection;",
+
+			"uniform sampler2D tDiffuse;",
+			"uniform sampler2D tNormal;",
+			"uniform sampler2D tSpecular;",
+			"uniform sampler2D tAO;",
+
+			"uniform samplerCube tCube;",
+
+			"uniform vec2 uNormalScale;",
+
+			"uniform bool useRefract;",
+			"uniform float uRefractionRatio;",
+			"uniform float uReflectivity;",
+
+			"varying vec3 vTangent;",
+			"varying vec3 vBinormal;",
+			"varying vec3 vNormal;",
+			"varying vec2 vUv;",
+
+			"uniform vec3 ambientLightColor;",
+
+			"#if MAX_DIR_LIGHTS > 0",
+
+				"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+				"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_HEMI_LIGHTS > 0",
+
+				"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+				"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+				"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_POINT_LIGHTS > 0",
+
+				"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_SPOT_LIGHTS > 0",
+
+				"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 ];",
+
+			"#endif",
+
+			"#ifdef WRAP_AROUND",
+
+				"uniform vec3 wrapRGB;",
+
+			"#endif",
+
+			"varying vec3 vWorldPosition;",
+			"varying vec3 vViewPosition;",
+
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( vec3( 1.0 ), uOpacity );",
+
+				"vec3 specularTex = vec3( 1.0 );",
+
+				"vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
+				"normalTex.xy *= uNormalScale;",
+				"normalTex = normalize( normalTex );",
+
+				"if( enableDiffuse ) {",
+
+					"#ifdef GAMMA_INPUT",
+
+						"vec4 texelColor = texture2D( tDiffuse, vUv );",
+						"texelColor.xyz *= texelColor.xyz;",
+
+						"gl_FragColor = gl_FragColor * texelColor;",
+
+					"#else",
+
+						"gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );",
+
+					"#endif",
+
+				"}",
+
+				"if( enableAO ) {",
+
+					"#ifdef GAMMA_INPUT",
+
+						"vec4 aoColor = texture2D( tAO, vUv );",
+						"aoColor.xyz *= aoColor.xyz;",
+
+						"gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;",
+
+					"#else",
+
+						"gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;",
+
+					"#endif",
+
+				"}",
+
+				"if( enableSpecular )",
+					"specularTex = texture2D( tSpecular, vUv ).xyz;",
+
+				"mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );",
+				"vec3 finalNormal = tsb * normalTex;",
+
+				"#ifdef FLIP_SIDED",
+
+					"finalNormal = -finalNormal;",
+
+				"#endif",
+
+				"vec3 normal = normalize( finalNormal );",
+				"vec3 viewPosition = normalize( vViewPosition );",
+
+				// point lights
+
+				"#if MAX_POINT_LIGHTS > 0",
+
+					"vec3 pointDiffuse = vec3( 0.0 );",
+					"vec3 pointSpecular = vec3( 0.0 );",
+
+					"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+						"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+						"vec3 pointVector = lPosition.xyz + vViewPosition.xyz;",
+
+						"float pointDistance = 1.0;",
+						"if ( pointLightDistance[ i ] > 0.0 )",
+							"pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+						"pointVector = normalize( pointVector );",
+
+						// diffuse
+
+						"#ifdef WRAP_AROUND",
+
+							"float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );",
+							"float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );",
+
+							"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+
+						"#else",
+
+							"float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
+
+						"#endif",
+
+						"pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;",
+
+						// specular
+
+						"vec3 pointHalfVector = normalize( pointVector + viewPosition );",
+						"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+						"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );",
+
+						"#ifdef PHYSICALLY_BASED_SHADING",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+							"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );",
+							"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;",
+
+						"#else",
+
+							"pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// spot lights
+
+				"#if MAX_SPOT_LIGHTS > 0",
+
+					"vec3 spotDiffuse = vec3( 0.0 );",
+					"vec3 spotSpecular = vec3( 0.0 );",
+
+					"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+						"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+						"vec3 spotVector = lPosition.xyz + vViewPosition.xyz;",
+
+						"float spotDistance = 1.0;",
+						"if ( spotLightDistance[ i ] > 0.0 )",
+							"spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+						"spotVector = normalize( spotVector );",
+
+						"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+
+						"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+							"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+							// diffuse
+
+							"#ifdef WRAP_AROUND",
+
+								"float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );",
+								"float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );",
+
+								"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+
+							"#else",
+
+								"float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );",
+
+							"#endif",
+
+							"spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;",
+
+							// specular
+
+							"vec3 spotHalfVector = normalize( spotVector + viewPosition );",
+							"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
+							"float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );",
+
+							"#ifdef PHYSICALLY_BASED_SHADING",
+
+								// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+								"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+								"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );",
+								"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;",
+
+							"#else",
+
+								"spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;",
+
+							"#endif",
+
+						"}",
+
+					"}",
+
+				"#endif",
+
+				// directional lights
+
+				"#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 );",
+
+						// diffuse
+
+						"#ifdef WRAP_AROUND",
+
+							"float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );",
+							"float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
+
+							"vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );",
+
+						"#else",
+
+							"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
+
+						"#endif",
+
+						"dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;",
+
+						// specular
+
+						"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
+						"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
+						"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );",
+
+						"#ifdef PHYSICALLY_BASED_SHADING",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"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;",
+
+						"#else",
+
+							"dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// hemisphere lights
+
+				"#if MAX_HEMI_LIGHTS > 0",
+
+					"vec3 hemiDiffuse  = vec3( 0.0 );",
+					"vec3 hemiSpecular = vec3( 0.0 );" ,
+
+					"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+
+						"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
+						"vec3 lVector = normalize( lDirection.xyz );",
+
+						// diffuse
+
+						"float dotProduct = dot( normal, lVector );",
+						"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+						"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+						"hemiDiffuse += uDiffuseColor * hemiColor;",
+
+						// specular (sky light)
+
+
+						"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
+						"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
+						"float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );",
+
+						// specular (ground light)
+
+						"vec3 lVectorGround = -lVector;",
+
+						"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",
+
+							"float dotProductGround = dot( normal, lVectorGround );",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+							"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 ) );",
+
+						"#else",
+
+							"hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// all lights contribution summation
+
+				"vec3 totalDiffuse = vec3( 0.0 );",
+				"vec3 totalSpecular = vec3( 0.0 );",
+
+				"#if MAX_DIR_LIGHTS > 0",
+
+					"totalDiffuse += dirDiffuse;",
+					"totalSpecular += dirSpecular;",
+
+				"#endif",
+
+				"#if MAX_HEMI_LIGHTS > 0",
+
+					"totalDiffuse += hemiDiffuse;",
+					"totalSpecular += hemiSpecular;",
+
+				"#endif",
+
+				"#if MAX_POINT_LIGHTS > 0",
+
+					"totalDiffuse += pointDiffuse;",
+					"totalSpecular += pointSpecular;",
+
+				"#endif",
+
+				"#if MAX_SPOT_LIGHTS > 0",
+
+					"totalDiffuse += spotDiffuse;",
+					"totalSpecular += spotSpecular;",
+
+				"#endif",
+
+				"#ifdef METAL",
+
+					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );",
+
+				"#else",
+
+					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;",
+
+				"#endif",
+
+				"if ( enableReflection ) {",
+
+					"vec3 vReflect;",
+					"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+
+					"if ( useRefract ) {",
+
+						"vReflect = refract( cameraToVertex, normal, uRefractionRatio );",
+
+					"} else {",
+
+						"vReflect = reflect( cameraToVertex, normal );",
+
+					"}",
+
+					"vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
+
+					"#ifdef GAMMA_INPUT",
+
+						"cubeColor.xyz *= cubeColor.xyz;",
+
+					"#endif",
+
+					"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );",
+
+				"}",
+
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+				THREE.ShaderChunk[ "fog_fragment" ],
+
+			"}"
+
+		].join("\n"),
+
+		vertexShader: [
+
+			"attribute vec4 tangent;",
+
+			"uniform vec2 uOffset;",
+			"uniform vec2 uRepeat;",
+
+			"uniform bool enableDisplacement;",
+
+			"#ifdef VERTEX_TEXTURES",
+
+				"uniform sampler2D tDisplacement;",
+				"uniform float uDisplacementScale;",
+				"uniform float uDisplacementBias;",
+
+			"#endif",
+
+			"varying vec3 vTangent;",
+			"varying vec3 vBinormal;",
+			"varying vec3 vNormal;",
+			"varying vec2 vUv;",
+
+			"varying vec3 vWorldPosition;",
+			"varying vec3 vViewPosition;",
+
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+
+			"void main() {",
+
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+
+				// normal, tangent and binormal vectors
+
+				"#ifdef USE_SKINNING",
+
+					"vNormal = normalize( normalMatrix * skinnedNormal.xyz );",
+
+					"vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );",
+					"vTangent = normalize( normalMatrix * skinnedTangent.xyz );",
+
+				"#else",
+
+					"vNormal = normalize( normalMatrix * normal );",
+					"vTangent = normalize( normalMatrix * tangent.xyz );",
+
+				"#endif",
+
+				"vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );",
+
+				"vUv = uv * uRepeat + uOffset;",
+
+				// displacement mapping
+
+				"vec3 displacedPosition;",
+
+				"#ifdef VERTEX_TEXTURES",
+
+					"if ( enableDisplacement ) {",
+
+						"vec3 dv = texture2D( tDisplacement, uv ).xyz;",
+						"float df = uDisplacementScale * dv.x + uDisplacementBias;",
+						"displacedPosition = position + normalize( normal ) * df;",
+
+					"} else {",
+
+						"#ifdef USE_SKINNING",
+
+							"vec4 skinVertex = vec4( position, 1.0 );",
+
+							"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+							"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+							"displacedPosition  = skinned.xyz;",
+
+						"#else",
+
+							"displacedPosition = position;",
+
+						"#endif",
+
+					"}",
+
+				"#else",
+
+					"#ifdef USE_SKINNING",
+
+						"vec4 skinVertex = vec4( position, 1.0 );",
+
+						"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+						"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+						"displacedPosition  = skinned.xyz;",
+
+					"#else",
+
+						"displacedPosition = position;",
+
+					"#endif",
+
+				"#endif",
+
+				//
+
+				"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
+				"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
+
+				"gl_Position = projectionMatrix * mvPosition;",
+
+				//
+
+				"vWorldPosition = worldPosition.xyz;",
+				"vViewPosition = -mvPosition.xyz;",
+
+				// shadows
+
+				"#ifdef USE_SHADOWMAP",
+
+					"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+						"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+
+					"}",
+
+				"#endif",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	/* -------------------------------------------------------------------------
+	//	Cube map shader
+	 ------------------------------------------------------------------------- */
+
+	'cube': {
+
+		uniforms: { "tCube": { type: "t", value: null },
+					"tFlip": { type: "f", value: -1 } },
+
+		vertexShader: [
+
+			"varying vec3 vWorldPosition;",
+
+			"void main() {",
+
+				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+				"vWorldPosition = worldPosition.xyz;",
+
+				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform samplerCube tCube;",
+			"uniform float tFlip;",
+
+			"varying vec3 vWorldPosition;",
+
+			"void main() {",
+
+				"gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	// 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/
+
+	'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 ) {",
+
+				"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;",
+
+			"}",
+
+			"void main() {",
+
+				"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+
+				//"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 );",
+				//"gl_FragData[ 0 ] = pack_depth( z );",
+				//"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );",
+
+			"}"
+
+		].join("\n")
+
+	}
+
+};
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.WebGLRenderer = function ( parameters ) {
+
+	console.log( 'THREE.WebGLRenderer', THREE.REVISION );
+
+	parameters = parameters || {};
+
+	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
+
+	_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,
+	_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+	_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+
+	_clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ),
+	_clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0;
+
+	// public properties
+
+	this.domElement = _canvas;
+	this.context = null;
+	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
+				? parameters.devicePixelRatio
+				: window.devicePixelRatio !== undefined
+					? window.devicePixelRatio
+					: 1;
+
+	// clearing
+
+	this.autoClear = true;
+	this.autoClearColor = true;
+	this.autoClearDepth = true;
+	this.autoClearStencil = true;
+
+	// scene graph
+
+	this.sortObjects = true;
+
+	this.autoUpdateObjects = true;
+	this.autoUpdateScene = true;
+
+	// physically based shading
+
+	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;
+	this.shadowMapCascade = false;
+
+	// morphs
+
+	this.maxMorphTargets = 8;
+	this.maxMorphNormals = 4;
+
+	// flags
+
+	this.autoScaleCubemaps = true;
+
+	// custom render plugins
+
+	this.renderPluginsPre = [];
+	this.renderPluginsPost = [];
+
+	// info
+
+	this.info = {
+
+		memory: {
+
+			programs: 0,
+			geometries: 0,
+			textures: 0
+
+		},
+
+		render: {
+
+			calls: 0,
+			vertices: 0,
+			faces: 0,
+			points: 0
+
+		}
+
+	};
+
+	// internal properties
+
+	var _this = this,
+
+	_programs = [],
+	_programs_counter = 0,
+
+	// internal state cache
+
+	_currentProgram = null,
+	_currentFramebuffer = null,
+	_currentMaterialId = -1,
+	_currentGeometryGroupHash = null,
+	_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 = 0,
+	_viewportHeight = 0,
+	_currentWidth = 0,
+	_currentHeight = 0,
+
+	_enabledAttributes = {},
+
+	// frustum
+
+	_frustum = new THREE.Frustum(),
+
+	 // camera matrices cache
+
+	_projScreenMatrix = new THREE.Matrix4(),
+	_projScreenMatrixPS = new THREE.Matrix4(),
+
+	_vector3 = new THREE.Vector3(),
+
+	// light arrays cache
+
+	_direction = new THREE.Vector3(),
+
+	_lightsNeedUpdate = true,
+
+	_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() }
+
+	};
+
+	// initialize
+
+	var _gl;
+
+	var _glExtensionTextureFloat;
+	var _glExtensionStandardDerivatives;
+	var _glExtensionTextureFilterAnisotropic;
+	var _glExtensionCompressedTextureS3TC;
+
+	initGL();
+
+	setDefaultGLState();
+
+	this.context = _gl;
+
+	// GPU capabilities
+
+	var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS );
+	var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
+	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 _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 _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 ( mediumpAvailable ) {
+
+			_precision = "mediump";
+			console.warn( "WebGLRenderer: highp not supported, using mediump" );
+
+		} else {
+
+			_precision = "lowp";
+			console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" );
+
+		}
+
+	}
+
+	if ( _precision === "mediump" && ! mediumpAvailable ) {
+
+		_precision = "lowp";
+		console.warn( "WebGLRenderer: mediump not supported, using lowp" );
+
+	}
+
+	// API
+
+	this.getContext = function () {
+
+		return _gl;
+
+	};
+
+	this.supportsVertexTextures = function () {
+
+		return _supportsVertexTextures;
+
+	};
+
+	this.supportsFloatTextures = function () {
+
+		return _glExtensionTextureFloat;
+
+	};
+	
+	this.supportsStandardDerivatives = function () {
+
+		return _glExtensionStandardDerivatives;
+
+	};
+	
+	this.supportsCompressedTextureS3TC = function () {
+
+		return _glExtensionCompressedTextureS3TC;
+
+	};
+	
+	this.getMaxAnisotropy  = function () {
+
+		return _maxAnisotropy;
+
+	};
+
+	this.getPrecision = function () {
+
+		return _precision;
+
+	};
+
+	this.setSize = function ( width, height ) {
+
+		_canvas.width = width * this.devicePixelRatio;
+		_canvas.height = height * this.devicePixelRatio;
+
+		_canvas.style.width = width + 'px';
+		_canvas.style.height = height + 'px';
+
+		this.setViewport( 0, 0, _canvas.width, _canvas.height );
+
+	};
+
+	this.setViewport = function ( x, y, width, height ) {
+
+		_viewportX = x !== undefined ? x : 0;
+		_viewportY = y !== undefined ? y : 0;
+
+		_viewportWidth = width !== undefined ? width : _canvas.width;
+		_viewportHeight = height !== undefined ? height : _canvas.height;
+
+		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+	};
+
+	this.setScissor = function ( x, y, width, height ) {
+
+		_gl.scissor( x, y, width, height );
+
+	};
+
+	this.enableScissorTest = function ( enable ) {
+
+		enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
+
+	};
+
+	// Clearing
+
+	this.setClearColorHex = function ( hex, alpha ) {
+
+		_clearColor.setHex( hex );
+		_clearAlpha = alpha;
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	this.setClearColor = function ( color, alpha ) {
+
+		_clearColor.copy( color );
+		_clearAlpha = alpha;
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	this.getClearColor = function () {
+
+		return _clearColor;
+
+	};
+
+	this.getClearAlpha = function () {
+
+		return _clearAlpha;
+
+	};
+
+	this.clear = function ( color, depth, stencil ) {
+
+		var bits = 0;
+
+		if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
+		if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
+		if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
+
+		_gl.clear( bits );
+
+	};
+
+	this.clearTarget = function ( renderTarget, color, depth, stencil ) {
+
+		this.setRenderTarget( renderTarget );
+		this.clear( color, depth, stencil );
+
+	};
+
+	// Plugins
+
+	this.addPostPlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPost.push( plugin );
+
+	};
+
+	this.addPrePlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPre.push( plugin );
+
+	};
+
+	// 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.shadowMapPlugin.update( scene, camera );
+
+	};
+
+	// Internal functions
+
+	// Buffer allocation
+
+	function createParticleBuffers ( geometry ) {
+
+		geometry.__webglVertexBuffer = _gl.createBuffer();
+		geometry.__webglColorBuffer = _gl.createBuffer();
+
+		_this.info.memory.geometries ++;
+
+	};
+
+	function createLineBuffers ( geometry ) {
+
+		geometry.__webglVertexBuffer = _gl.createBuffer();
+		geometry.__webglColorBuffer = _gl.createBuffer();
+		geometry.__webglLineDistanceBuffer = _gl.createBuffer();
+
+		_this.info.memory.geometries ++;
+
+	};
+
+	function createRibbonBuffers ( geometry ) {
+
+		geometry.__webglVertexBuffer = _gl.createBuffer();
+		geometry.__webglColorBuffer = _gl.createBuffer();
+		geometry.__webglNormalBuffer = _gl.createBuffer();
+
+		_this.info.memory.geometries ++;
+
+	};
+
+	function createMeshBuffers ( geometryGroup ) {
+
+		geometryGroup.__webglVertexBuffer = _gl.createBuffer();
+		geometryGroup.__webglNormalBuffer = _gl.createBuffer();
+		geometryGroup.__webglTangentBuffer = _gl.createBuffer();
+		geometryGroup.__webglColorBuffer = _gl.createBuffer();
+		geometryGroup.__webglUVBuffer = _gl.createBuffer();
+		geometryGroup.__webglUV2Buffer = _gl.createBuffer();
+
+		geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer();
+		geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer();
+
+		geometryGroup.__webglFaceBuffer = _gl.createBuffer();
+		geometryGroup.__webglLineBuffer = _gl.createBuffer();
+
+		var m, ml;
+
+		if ( geometryGroup.numMorphTargets ) {
+
+			geometryGroup.__webglMorphTargetsBuffers = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+				geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
+
+			}
+
+		}
+
+		if ( geometryGroup.numMorphNormals ) {
+
+			geometryGroup.__webglMorphNormalsBuffers = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+				geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
+
+			}
+
+		}
+
+		_this.info.memory.geometries ++;
+
+	};
+
+	// Events
+
+	var onGeometryDispose = function ( event ) {
+
+		var geometry = event.target;
+
+		geometry.removeEventListener( 'dispose', onGeometryDispose );
+
+		deallocateGeometry( geometry );
+
+		_this.info.memory.geometries --;
+
+	};
+
+	var onTextureDispose = function ( event ) {
+
+		var texture = event.target;
+
+		texture.removeEventListener( 'dispose', onTextureDispose );
+
+		deallocateTexture( texture );
+
+		_this.info.memory.textures --;
+
+
+	};
+
+	var onRenderTargetDispose = function ( event ) {
+
+		var renderTarget = event.target;
+
+		renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
+
+		deallocateRenderTarget( renderTarget );
+
+		_this.info.memory.textures --;
+
+	};
+
+	var onMaterialDispose = function ( event ) {
+
+		var material = event.target;
+
+		material.removeEventListener( 'dispose', onMaterialDispose );
+
+		deallocateMaterial( material );
+
+	};
+
+	// Buffer deallocation
+
+	var deallocateGeometry = function ( geometry ) {
+
+		geometry.__webglInit = undefined;
+
+		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 );
+
+		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 );
+
+		// geometry groups
+
+		if ( geometry.geometryGroups !== undefined ) {
+
+			for ( var g in geometry.geometryGroups ) {
+
+				var geometryGroup = geometry.geometryGroups[ g ];
+
+				if ( geometryGroup.numMorphTargets !== undefined ) {
+
+					for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+						_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
+
+					}
+
+				}
+
+				if ( geometryGroup.numMorphNormals !== undefined ) {
+
+					for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+						_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
+
+					}
+
+				}
+
+				deleteCustomAttributesBuffers( geometryGroup );
+
+			}
+
+		}
+
+		deleteCustomAttributesBuffers( geometry );
+
+	};
+
+	var deallocateTexture = function ( texture ) {
+
+		if ( texture.image && texture.image.__webglTextureCube ) {
+
+			// cube texture
+
+			_gl.deleteTexture( texture.image.__webglTextureCube );
+
+		} else {
+
+			// 2D texture
+
+			if ( ! texture.__webglInit ) return;
+
+			texture.__webglInit = false;
+			_gl.deleteTexture( texture.__webglTexture );
+
+		}
+
+	};
+
+	var deallocateRenderTarget = function ( renderTarget ) {
+
+		if ( !renderTarget || ! renderTarget.__webglTexture ) return;
+
+		_gl.deleteTexture( renderTarget.__webglTexture );
+
+		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+			for ( var i = 0; i < 6; i ++ ) {
+
+				_gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] );
+				_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] );
+
+			}
+
+		} else {
+
+			_gl.deleteFramebuffer( renderTarget.__webglFramebuffer );
+			_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer );
+
+		}
+
+	};
+
+	var deallocateMaterial = function ( material ) {
+
+		var program = material.program;
+
+		if ( program === undefined ) return;
+
+		material.program = undefined;
+
+		// only deallocate GL program if this was the last use of shared program
+		// assumed there is only single copy of any program in the _programs list
+		// (that's how it's constructed)
+
+		var i, il, programInfo;
+		var deleteProgram = false;
+
+		for ( i = 0, il = _programs.length; i < il; i ++ ) {
+
+			programInfo = _programs[ i ];
+
+			if ( programInfo.program === program ) {
+
+				programInfo.usedTimes --;
+
+				if ( programInfo.usedTimes === 0 ) {
+
+					deleteProgram = true;
+
+				}
+
+				break;
+
+			}
+
+		}
+
+		if ( deleteProgram === true ) {
+
+			// avoid using array.splice, this is costlier than creating new array from scratch
+
+			var newPrograms = [];
+
+			for ( i = 0, il = _programs.length; i < il; i ++ ) {
+
+				programInfo = _programs[ i ];
+
+				if ( programInfo.program !== program ) {
+
+					newPrograms.push( programInfo );
+
+				}
+
+			}
+
+			_programs = newPrograms;
+
+			_gl.deleteProgram( program );
+
+			_this.info.memory.programs --;
+
+		}
+
+	};
+
+	//
+
+	/*
+	function deleteParticleBuffers ( geometry ) {
+
+		_gl.deleteBuffer( geometry.__webglVertexBuffer );
+		_gl.deleteBuffer( geometry.__webglColorBuffer );
+
+		deleteCustomAttributesBuffers( geometry );
+
+		_this.info.memory.geometries --;
+
+	};
+
+	function deleteLineBuffers ( geometry ) {
+
+		_gl.deleteBuffer( geometry.__webglVertexBuffer );
+		_gl.deleteBuffer( geometry.__webglColorBuffer );
+		_gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
+
+		deleteCustomAttributesBuffers( geometry );
+
+		_this.info.memory.geometries --;
+
+	};
+
+	function deleteRibbonBuffers ( geometry ) {
+
+		_gl.deleteBuffer( geometry.__webglVertexBuffer );
+		_gl.deleteBuffer( geometry.__webglColorBuffer );
+		_gl.deleteBuffer( geometry.__webglNormalBuffer );
+
+		deleteCustomAttributesBuffers( geometry );
+
+		_this.info.memory.geometries --;
+
+	};
+
+	function deleteMeshBuffers ( geometryGroup ) {
+
+		_gl.deleteBuffer( geometryGroup.__webglVertexBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglNormalBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglTangentBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglColorBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglUVBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglUV2Buffer );
+
+		_gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer );
+
+		_gl.deleteBuffer( geometryGroup.__webglFaceBuffer );
+		_gl.deleteBuffer( geometryGroup.__webglLineBuffer );
+
+		var m, ml;
+
+		if ( geometryGroup.numMorphTargets ) {
+
+			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+				_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
+
+			}
+
+		}
+
+		if ( geometryGroup.numMorphNormals ) {
+
+			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+				_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
+
+			}
+
+		}
+
+		deleteCustomAttributesBuffers( geometryGroup );
+
+		_this.info.memory.geometries --;
+
+	};
+	*/
+
+	function deleteCustomAttributesBuffers( geometry ) {
+
+		if ( geometry.__webglCustomAttributesList ) {
+
+			for ( var id in geometry.__webglCustomAttributesList ) {
+
+				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
+
+			}
+
+		}
+
+	};
+
+	// Buffer initialization
+
+	function initCustomAttributes ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		var material = object.material;
+
+		if ( material.attributes ) {
+
+			if ( geometry.__webglCustomAttributesList === undefined ) {
+
+				geometry.__webglCustomAttributesList = [];
+
+			}
+
+			for ( var a in material.attributes ) {
+
+				var attribute = material.attributes[ a ];
+
+				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+					attribute.__webglInitialized = true;
+
+					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;
+
+					attribute.size = size;
+
+					attribute.array = new Float32Array( nvertices * size );
+
+					attribute.buffer = _gl.createBuffer();
+					attribute.buffer.belongsToAttribute = a;
+
+					attribute.needsUpdate = true;
+
+				}
+
+				geometry.__webglCustomAttributesList.push( attribute );
+
+			}
+
+		}
+
+	};
+
+	function initParticleBuffers ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		geometry.__vertexArray = new Float32Array( nvertices * 3 );
+		geometry.__colorArray = new Float32Array( nvertices * 3 );
+
+		geometry.__sortArray = [];
+
+		geometry.__webglParticleCount = nvertices;
+
+		initCustomAttributes ( geometry, object );
+
+	};
+
+	function initLineBuffers ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		geometry.__vertexArray = new Float32Array( nvertices * 3 );
+		geometry.__colorArray = new Float32Array( nvertices * 3 );
+		geometry.__lineDistanceArray = new Float32Array( nvertices * 1 );
+
+		geometry.__webglLineCount = nvertices;
+
+		initCustomAttributes ( geometry, object );
+
+	};
+
+	function initRibbonBuffers ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		geometry.__vertexArray = new Float32Array( nvertices * 3 );
+		geometry.__colorArray = new Float32Array( nvertices * 3 );
+		geometry.__normalArray = new Float32Array( nvertices * 3 );
+
+		geometry.__webglVertexCount = nvertices;
+
+		initCustomAttributes ( geometry, object );
+
+	};
+
+	function initMeshBuffers ( geometryGroup, object ) {
+
+		var geometry = object.geometry,
+			faces3 = geometryGroup.faces3,
+			faces4 = geometryGroup.faces4,
+
+			nvertices = faces3.length * 3 + faces4.length * 4,
+			ntris     = faces3.length * 1 + faces4.length * 2,
+			nlines    = faces3.length * 3 + faces4.length * 4,
+
+			material = getBufferMaterial( object, geometryGroup ),
+
+			uvType = bufferGuessUVType( material ),
+			normalType = bufferGuessNormalType( material ),
+			vertexColorType = bufferGuessVertexColorType( material );
+
+		//console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
+
+		geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
+
+		if ( normalType ) {
+
+			geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
+
+		}
+
+		if ( geometry.hasTangents ) {
+
+			geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
+
+		}
+
+		if ( vertexColorType ) {
+
+			geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
+
+		}
+
+		if ( uvType ) {
+
+			if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) {
+
+				geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
+
+			}
+
+			if ( geometry.faceUvs.length > 1 || 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 );
+			geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
+
+		}
+
+		geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
+		geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
+
+		var m, ml;
+
+		if ( geometryGroup.numMorphTargets ) {
+
+			geometryGroup.__morphTargetsArrays = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+				geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
+
+			}
+
+		}
+
+		if ( geometryGroup.numMorphNormals ) {
+
+			geometryGroup.__morphNormalsArrays = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+				geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) );
+
+			}
+
+		}
+
+		geometryGroup.__webglFaceCount = ntris * 3;
+		geometryGroup.__webglLineCount = nlines * 2;
+
+
+		// custom attributes
+
+		if ( material.attributes ) {
+
+			if ( geometryGroup.__webglCustomAttributesList === undefined ) {
+
+				geometryGroup.__webglCustomAttributesList = [];
+
+			}
+
+			for ( var a 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 attribute = {};
+
+				for ( var property in originalAttribute ) {
+
+					attribute[ property ] = originalAttribute[ property ];
+
+				}
+
+				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+					attribute.__webglInitialized = true;
+
+					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;
+
+					attribute.size = size;
+
+					attribute.array = new Float32Array( nvertices * size );
+
+					attribute.buffer = _gl.createBuffer();
+					attribute.buffer.belongsToAttribute = a;
+
+					originalAttribute.needsUpdate = true;
+					attribute.__original = originalAttribute;
+
+				}
+
+				geometryGroup.__webglCustomAttributesList.push( attribute );
+
+			}
+
+		}
+
+		geometryGroup.__inittedArrays = true;
+
+	};
+
+	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;
+
+		}
+
+	};
+
+	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 ];
+
+			attribute.buffer = _gl.createBuffer();
+
+			_gl.bindBuffer( type, attribute.buffer );
+			_gl.bufferData( type, attribute.array, _gl.STATIC_DRAW );
+
+		}
+
+	};
+
+	// Buffer setting
+
+	function setParticleBuffers ( geometry, hint, object ) {
+
+		var v, c, vertex, offset, index, color,
+
+		vertices = geometry.vertices,
+		vl = vertices.length,
+
+		colors = geometry.colors,
+		cl = colors.length,
+
+		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,
+		customAttribute;
+
+		if ( object.sortParticles ) {
+
+			_projScreenMatrixPS.copy( _projScreenMatrix );
+			_projScreenMatrixPS.multiply( object.matrixWorld );
+
+			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;
+				vertexArray[ offset + 1 ] = vertex.y;
+				vertexArray[ offset + 2 ] = vertex.z;
+
+			}
+
+			for ( c = 0; c < cl; c ++ ) {
+
+				offset = c * 3;
+
+				color = colors[ sortArray[c][1] ];
+
+				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.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
+
+					offset = 0;
+
+					cal = customAttribute.value.length;
+
+					if ( customAttribute.size === 1 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							customAttribute.array[ ca ] = customAttribute.value[ index ];
+
+						}
+
+					} else if ( customAttribute.size === 2 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							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 ++ ) {
+
+								index = sortArray[ ca ][ 1 ];
+
+								value = customAttribute.value[ index ];
+
+								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 ++ ) {
+
+								index = sortArray[ ca ][ 1 ];
+
+								value = customAttribute.value[ index ];
+
+								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 ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							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;
+
+						}
+
+					}
+
+				}
+
+			}
+
+		} 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 );
+
+				}
+
+			}
+
+		}
+
+
+	};
+
+	function setLineBuffers ( geometry, hint ) {
+
+		var v, c, d, vertex, offset, color,
+
+		vertices = geometry.vertices,
+		colors = geometry.colors,
+		lineDistances = geometry.lineDistances,
+
+		vl = vertices.length,
+		cl = colors.length,
+		dl = lineDistances.length,
+
+		vertexArray = geometry.__vertexArray,
+		colorArray = geometry.__colorArray,
+		lineDistanceArray = geometry.__lineDistanceArray,
+
+		dirtyVertices = geometry.verticesNeedUpdate,
+		dirtyColors = geometry.colorsNeedUpdate,
+		dirtyLineDistances = geometry.lineDistancesNeedUpdate,
+
+		customAttributes = geometry.__webglCustomAttributesList,
+
+		i, il,
+		a, ca, cal, value,
+		customAttribute;
+
+		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;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
+
+		}
+
+		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;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
+
+		}
+
+		if ( dirtyLineDistances ) {
+
+			for ( d = 0; d < dl; d ++ ) {
+
+				lineDistanceArray[ d ] = lineDistances[ d ];
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint );
+
+		}
+
+		if ( customAttributes ) {
+
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+				customAttribute = customAttributes[ i ];
+
+				if ( customAttribute.needsUpdate &&
+					 ( customAttribute.boundTo === undefined ||
+					   customAttribute.boundTo === "vertices" ) ) {
+
+					offset = 0;
+
+					cal = customAttribute.value.length;
+
+					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;
+
+						}
+
+					}
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function setRibbonBuffers ( geometry, hint ) {
+
+		var v, c, n, vertex, offset, color, normal,
+
+		i, il, ca, cal, customAttribute, value,
+
+		vertices = geometry.vertices,
+		colors = geometry.colors,
+		normals = geometry.normals,
+
+		vl = vertices.length,
+		cl = colors.length,
+		nl = normals.length,
+
+		vertexArray = geometry.__vertexArray,
+		colorArray = geometry.__colorArray,
+		normalArray = geometry.__normalArray,
+
+		dirtyVertices = geometry.verticesNeedUpdate,
+		dirtyColors = geometry.colorsNeedUpdate,
+		dirtyNormals = geometry.normalsNeedUpdate,
+
+		customAttributes = geometry.__webglCustomAttributesList;
+
+		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;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
+
+		}
+
+		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;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
+
+		}
+
+		if ( dirtyNormals ) {
+
+			for ( n = 0; n < nl; n ++ ) {
+
+				normal = normals[ n ];
+
+				offset = n * 3;
+
+				normalArray[ offset ]     = normal.x;
+				normalArray[ offset + 1 ] = normal.y;
+				normalArray[ offset + 2 ] = normal.z;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglNormalBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
+
+		}
+
+		if ( customAttributes ) {
+
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+				customAttribute = customAttributes[ i ];
+
+				if ( customAttribute.needsUpdate &&
+					 ( customAttribute.boundTo === undefined ||
+					   customAttribute.boundTo === "vertices" ) ) {
+
+					offset = 0;
+
+					cal = customAttribute.value.length;
+
+					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;
+
+						}
+
+					}
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
+
+		if ( ! geometryGroup.__inittedArrays ) {
+
+			return;
+
+		}
+
+		var normalType = bufferGuessNormalType( material ),
+		vertexColorType = bufferGuessVertexColorType( material ),
+		uvType = bufferGuessUVType( material ),
+
+		needsSmoothNormals = ( normalType === THREE.SmoothShading );
+
+		var f, fl, fi, face,
+		vertexNormals, faceNormal, normal,
+		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,
+		vn, uvi, uv2i,
+		vk, vkl, vka,
+		nka, chf, faceVertexNormals,
+		a,
+
+		vertexIndex = 0,
+
+		offset = 0,
+		offset_uv = 0,
+		offset_uv2 = 0,
+		offset_face = 0,
+		offset_normal = 0,
+		offset_tangent = 0,
+		offset_line = 0,
+		offset_color = 0,
+		offset_skin = 0,
+		offset_morphTarget = 0,
+		offset_custom = 0,
+		offset_customSrc = 0,
+
+		value,
+
+		vertexArray = geometryGroup.__vertexArray,
+		uvArray = geometryGroup.__uvArray,
+		uv2Array = geometryGroup.__uv2Array,
+		normalArray = geometryGroup.__normalArray,
+		tangentArray = geometryGroup.__tangentArray,
+		colorArray = geometryGroup.__colorArray,
+
+		skinIndexArray = geometryGroup.__skinIndexArray,
+		skinWeightArray = geometryGroup.__skinWeightArray,
+
+		morphTargetsArrays = geometryGroup.__morphTargetsArrays,
+		morphNormalsArrays = geometryGroup.__morphNormalsArrays,
+
+		customAttributes = geometryGroup.__webglCustomAttributesList,
+		customAttribute,
+
+		faceArray = geometryGroup.__faceArray,
+		lineArray = geometryGroup.__lineArray,
+
+		geometry = object.geometry, // this is shared for all chunks
+
+		dirtyVertices = geometry.verticesNeedUpdate,
+		dirtyElements = geometry.elementsNeedUpdate,
+		dirtyUvs = geometry.uvsNeedUpdate,
+		dirtyNormals = geometry.normalsNeedUpdate,
+		dirtyTangents = geometry.tangentsNeedUpdate,
+		dirtyColors = geometry.colorsNeedUpdate,
+		dirtyMorphTargets = geometry.morphTargetsNeedUpdate,
+
+		vertices = geometry.vertices,
+		chunk_faces3 = geometryGroup.faces3,
+		chunk_faces4 = geometryGroup.faces4,
+		obj_faces = geometry.faces,
+
+		obj_uvs  = geometry.faceVertexUvs[ 0 ],
+		obj_uvs2 = geometry.faceVertexUvs[ 1 ],
+
+		obj_colors = geometry.colors,
+
+		obj_skinIndices = geometry.skinIndices,
+		obj_skinWeights = geometry.skinWeights,
+
+		morphTargets = geometry.morphTargets,
+		morphNormals = geometry.morphNormals;
+
+		if ( dirtyVertices ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces3[ f ] ];
+
+				v1 = vertices[ face.a ];
+				v2 = vertices[ face.b ];
+				v3 = vertices[ face.c ];
+
+				vertexArray[ offset ]     = v1.x;
+				vertexArray[ offset + 1 ] = v1.y;
+				vertexArray[ offset + 2 ] = v1.z;
+
+				vertexArray[ offset + 3 ] = v2.x;
+				vertexArray[ offset + 4 ] = v2.y;
+				vertexArray[ offset + 5 ] = v2.z;
+
+				vertexArray[ offset + 6 ] = v3.x;
+				vertexArray[ offset + 7 ] = v3.y;
+				vertexArray[ offset + 8 ] = v3.z;
+
+				offset += 9;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces4[ f ] ];
+
+				v1 = vertices[ face.a ];
+				v2 = vertices[ face.b ];
+				v3 = vertices[ face.c ];
+				v4 = vertices[ face.d ];
+
+				vertexArray[ offset ]     = v1.x;
+				vertexArray[ offset + 1 ] = v1.y;
+				vertexArray[ offset + 2 ] = v1.z;
+
+				vertexArray[ offset + 3 ] = v2.x;
+				vertexArray[ offset + 4 ] = v2.y;
+				vertexArray[ offset + 5 ] = v2.z;
+
+				vertexArray[ offset + 6 ] = v3.x;
+				vertexArray[ offset + 7 ] = v3.y;
+				vertexArray[ offset + 8 ] = v3.z;
+
+				vertexArray[ offset + 9 ]  = v4.x;
+				vertexArray[ offset + 10 ] = v4.y;
+				vertexArray[ offset + 11 ] = v4.z;
+
+				offset += 12;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
+
+		}
+
+		if ( dirtyMorphTargets ) {
+
+			for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) {
+
+				offset_morphTarget = 0;
+
+				for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+					chf = chunk_faces3[ f ];
+					face = obj_faces[ chf ];
+
+					// morph positions
+
+					v1 = morphTargets[ vk ].vertices[ face.a ];
+					v2 = morphTargets[ vk ].vertices[ face.b ];
+					v3 = morphTargets[ vk ].vertices[ face.c ];
+
+					vka = morphTargetsArrays[ vk ];
+
+					vka[ offset_morphTarget ] 	  = v1.x;
+					vka[ offset_morphTarget + 1 ] = v1.y;
+					vka[ offset_morphTarget + 2 ] = v1.z;
+
+					vka[ offset_morphTarget + 3 ] = v2.x;
+					vka[ offset_morphTarget + 4 ] = v2.y;
+					vka[ offset_morphTarget + 5 ] = v2.z;
+
+					vka[ offset_morphTarget + 6 ] = v3.x;
+					vka[ offset_morphTarget + 7 ] = v3.y;
+					vka[ offset_morphTarget + 8 ] = v3.z;
+
+					// morph normals
+
+					if ( material.morphNormals ) {
+
+						if ( needsSmoothNormals ) {
+
+							faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
+
+							n1 = faceVertexNormals.a;
+							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 + 1 ] = n1.y;
+						nka[ offset_morphTarget + 2 ] = n1.z;
+
+						nka[ offset_morphTarget + 3 ] = n2.x;
+						nka[ offset_morphTarget + 4 ] = n2.y;
+						nka[ offset_morphTarget + 5 ] = n2.z;
+
+						nka[ offset_morphTarget + 6 ] = n3.x;
+						nka[ offset_morphTarget + 7 ] = n3.y;
+						nka[ offset_morphTarget + 8 ] = n3.z;
+
+					}
+
+					//
+
+					offset_morphTarget += 9;
+
+				}
+
+				for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+					chf = chunk_faces4[ f ];
+					face = obj_faces[ chf ];
+
+					// morph positions
+
+					v1 = morphTargets[ vk ].vertices[ face.a ];
+					v2 = morphTargets[ vk ].vertices[ face.b ];
+					v3 = morphTargets[ vk ].vertices[ face.c ];
+					v4 = morphTargets[ vk ].vertices[ face.d ];
+
+					vka = morphTargetsArrays[ vk ];
+
+					vka[ offset_morphTarget ] 	  = v1.x;
+					vka[ offset_morphTarget + 1 ] = v1.y;
+					vka[ offset_morphTarget + 2 ] = v1.z;
+
+					vka[ offset_morphTarget + 3 ] = v2.x;
+					vka[ offset_morphTarget + 4 ] = v2.y;
+					vka[ offset_morphTarget + 5 ] = v2.z;
+
+					vka[ offset_morphTarget + 6 ] = v3.x;
+					vka[ offset_morphTarget + 7 ] = v3.y;
+					vka[ offset_morphTarget + 8 ] = v3.z;
+
+					vka[ offset_morphTarget + 9 ]  = v4.x;
+					vka[ offset_morphTarget + 10 ] = v4.y;
+					vka[ offset_morphTarget + 11 ] = v4.z;
+
+					// morph normals
+
+					if ( material.morphNormals ) {
+
+						if ( needsSmoothNormals ) {
+
+							faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
+
+							n1 = faceVertexNormals.a;
+							n2 = faceVertexNormals.b;
+							n3 = faceVertexNormals.c;
+							n4 = faceVertexNormals.d;
+
+						} else {
+
+							n1 = morphNormals[ vk ].faceNormals[ chf ];
+							n2 = n1;
+							n3 = n1;
+							n4 = n1;
+
+						}
+
+						nka = morphNormalsArrays[ vk ];
+
+						nka[ offset_morphTarget ] 	  = n1.x;
+						nka[ offset_morphTarget + 1 ] = n1.y;
+						nka[ offset_morphTarget + 2 ] = n1.z;
+
+						nka[ offset_morphTarget + 3 ] = n2.x;
+						nka[ offset_morphTarget + 4 ] = n2.y;
+						nka[ offset_morphTarget + 5 ] = n2.z;
+
+						nka[ offset_morphTarget + 6 ] = n3.x;
+						nka[ offset_morphTarget + 7 ] = n3.y;
+						nka[ offset_morphTarget + 8 ] = n3.z;
+
+						nka[ offset_morphTarget + 9 ]  = n4.x;
+						nka[ offset_morphTarget + 10 ] = n4.y;
+						nka[ offset_morphTarget + 11 ] = n4.z;
+
+					}
+
+					//
+
+					offset_morphTarget += 12;
+
+				}
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] );
+				_gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint );
+
+				if ( material.morphNormals ) {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] );
+					_gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint );
+
+				}
+
+			}
+
+		}
+
+		if ( obj_skinWeights.length ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces3[ f ]	];
+
+				// weights
+
+				sw1 = obj_skinWeights[ face.a ];
+				sw2 = obj_skinWeights[ face.b ];
+				sw3 = obj_skinWeights[ face.c ];
+
+				skinWeightArray[ offset_skin ]     = sw1.x;
+				skinWeightArray[ offset_skin + 1 ] = sw1.y;
+				skinWeightArray[ offset_skin + 2 ] = sw1.z;
+				skinWeightArray[ offset_skin + 3 ] = sw1.w;
+
+				skinWeightArray[ offset_skin + 4 ] = sw2.x;
+				skinWeightArray[ offset_skin + 5 ] = sw2.y;
+				skinWeightArray[ offset_skin + 6 ] = sw2.z;
+				skinWeightArray[ offset_skin + 7 ] = sw2.w;
+
+				skinWeightArray[ offset_skin + 8 ]  = sw3.x;
+				skinWeightArray[ offset_skin + 9 ]  = sw3.y;
+				skinWeightArray[ offset_skin + 10 ] = sw3.z;
+				skinWeightArray[ offset_skin + 11 ] = sw3.w;
+
+				// indices
+
+				si1 = obj_skinIndices[ face.a ];
+				si2 = obj_skinIndices[ face.b ];
+				si3 = obj_skinIndices[ face.c ];
+
+				skinIndexArray[ offset_skin ]     = si1.x;
+				skinIndexArray[ offset_skin + 1 ] = si1.y;
+				skinIndexArray[ offset_skin + 2 ] = si1.z;
+				skinIndexArray[ offset_skin + 3 ] = si1.w;
+
+				skinIndexArray[ offset_skin + 4 ] = si2.x;
+				skinIndexArray[ offset_skin + 5 ] = si2.y;
+				skinIndexArray[ offset_skin + 6 ] = si2.z;
+				skinIndexArray[ offset_skin + 7 ] = si2.w;
+
+				skinIndexArray[ offset_skin + 8 ]  = si3.x;
+				skinIndexArray[ offset_skin + 9 ]  = si3.y;
+				skinIndexArray[ offset_skin + 10 ] = si3.z;
+				skinIndexArray[ offset_skin + 11 ] = si3.w;
+
+				offset_skin += 12;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces4[ f ] ];
+
+				// weights
+
+				sw1 = obj_skinWeights[ face.a ];
+				sw2 = obj_skinWeights[ face.b ];
+				sw3 = obj_skinWeights[ face.c ];
+				sw4 = obj_skinWeights[ face.d ];
+
+				skinWeightArray[ offset_skin ]     = sw1.x;
+				skinWeightArray[ offset_skin + 1 ] = sw1.y;
+				skinWeightArray[ offset_skin + 2 ] = sw1.z;
+				skinWeightArray[ offset_skin + 3 ] = sw1.w;
+
+				skinWeightArray[ offset_skin + 4 ] = sw2.x;
+				skinWeightArray[ offset_skin + 5 ] = sw2.y;
+				skinWeightArray[ offset_skin + 6 ] = sw2.z;
+				skinWeightArray[ offset_skin + 7 ] = sw2.w;
+
+				skinWeightArray[ offset_skin + 8 ]  = sw3.x;
+				skinWeightArray[ offset_skin + 9 ]  = sw3.y;
+				skinWeightArray[ offset_skin + 10 ] = sw3.z;
+				skinWeightArray[ offset_skin + 11 ] = sw3.w;
+
+				skinWeightArray[ offset_skin + 12 ] = sw4.x;
+				skinWeightArray[ offset_skin + 13 ] = sw4.y;
+				skinWeightArray[ offset_skin + 14 ] = sw4.z;
+				skinWeightArray[ offset_skin + 15 ] = sw4.w;
+
+				// indices
+
+				si1 = obj_skinIndices[ face.a ];
+				si2 = obj_skinIndices[ face.b ];
+				si3 = obj_skinIndices[ face.c ];
+				si4 = obj_skinIndices[ face.d ];
+
+				skinIndexArray[ offset_skin ]     = si1.x;
+				skinIndexArray[ offset_skin + 1 ] = si1.y;
+				skinIndexArray[ offset_skin + 2 ] = si1.z;
+				skinIndexArray[ offset_skin + 3 ] = si1.w;
+
+				skinIndexArray[ offset_skin + 4 ] = si2.x;
+				skinIndexArray[ offset_skin + 5 ] = si2.y;
+				skinIndexArray[ offset_skin + 6 ] = si2.z;
+				skinIndexArray[ offset_skin + 7 ] = si2.w;
+
+				skinIndexArray[ offset_skin + 8 ]  = si3.x;
+				skinIndexArray[ offset_skin + 9 ]  = si3.y;
+				skinIndexArray[ offset_skin + 10 ] = si3.z;
+				skinIndexArray[ offset_skin + 11 ] = si3.w;
+
+				skinIndexArray[ offset_skin + 12 ] = si4.x;
+				skinIndexArray[ offset_skin + 13 ] = si4.y;
+				skinIndexArray[ offset_skin + 14 ] = si4.z;
+				skinIndexArray[ offset_skin + 15 ] = si4.w;
+
+				offset_skin += 16;
+
+			}
+
+			if ( offset_skin > 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint );
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint );
+
+			}
+
+		}
+
+		if ( dirtyColors && vertexColorType ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces3[ f ]	];
+
+				vertexColors = face.vertexColors;
+				faceColor = face.color;
+
+				if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
+
+					c1 = vertexColors[ 0 ];
+					c2 = vertexColors[ 1 ];
+					c3 = vertexColors[ 2 ];
+
+				} else {
+
+					c1 = faceColor;
+					c2 = faceColor;
+					c3 = faceColor;
+
+				}
+
+				colorArray[ offset_color ]     = c1.r;
+				colorArray[ offset_color + 1 ] = c1.g;
+				colorArray[ offset_color + 2 ] = c1.b;
+
+				colorArray[ offset_color + 3 ] = c2.r;
+				colorArray[ offset_color + 4 ] = c2.g;
+				colorArray[ offset_color + 5 ] = c2.b;
+
+				colorArray[ offset_color + 6 ] = c3.r;
+				colorArray[ offset_color + 7 ] = c3.g;
+				colorArray[ offset_color + 8 ] = c3.b;
+
+				offset_color += 9;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces4[ f ] ];
+
+				vertexColors = face.vertexColors;
+				faceColor = face.color;
+
+				if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) {
+
+					c1 = vertexColors[ 0 ];
+					c2 = vertexColors[ 1 ];
+					c3 = vertexColors[ 2 ];
+					c4 = vertexColors[ 3 ];
+
+				} else {
+
+					c1 = faceColor;
+					c2 = faceColor;
+					c3 = faceColor;
+					c4 = faceColor;
+
+				}
+
+				colorArray[ offset_color ]     = c1.r;
+				colorArray[ offset_color + 1 ] = c1.g;
+				colorArray[ offset_color + 2 ] = c1.b;
+
+				colorArray[ offset_color + 3 ] = c2.r;
+				colorArray[ offset_color + 4 ] = c2.g;
+				colorArray[ offset_color + 5 ] = c2.b;
+
+				colorArray[ offset_color + 6 ] = c3.r;
+				colorArray[ offset_color + 7 ] = c3.g;
+				colorArray[ offset_color + 8 ] = c3.b;
+
+				colorArray[ offset_color + 9 ]  = c4.r;
+				colorArray[ offset_color + 10 ] = c4.g;
+				colorArray[ offset_color + 11 ] = c4.b;
+
+				offset_color += 12;
+
+			}
+
+			if ( offset_color > 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
+
+			}
+
+		}
+
+		if ( dirtyTangents && geometry.hasTangents ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces3[ f ]	];
+
+				vertexTangents = face.vertexTangents;
+
+				t1 = vertexTangents[ 0 ];
+				t2 = vertexTangents[ 1 ];
+				t3 = vertexTangents[ 2 ];
+
+				tangentArray[ offset_tangent ]     = t1.x;
+				tangentArray[ offset_tangent + 1 ] = t1.y;
+				tangentArray[ offset_tangent + 2 ] = t1.z;
+				tangentArray[ offset_tangent + 3 ] = t1.w;
+
+				tangentArray[ offset_tangent + 4 ] = t2.x;
+				tangentArray[ offset_tangent + 5 ] = t2.y;
+				tangentArray[ offset_tangent + 6 ] = t2.z;
+				tangentArray[ offset_tangent + 7 ] = t2.w;
+
+				tangentArray[ offset_tangent + 8 ]  = t3.x;
+				tangentArray[ offset_tangent + 9 ]  = t3.y;
+				tangentArray[ offset_tangent + 10 ] = t3.z;
+				tangentArray[ offset_tangent + 11 ] = t3.w;
+
+				offset_tangent += 12;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces4[ f ] ];
+
+				vertexTangents = face.vertexTangents;
+
+				t1 = vertexTangents[ 0 ];
+				t2 = vertexTangents[ 1 ];
+				t3 = vertexTangents[ 2 ];
+				t4 = vertexTangents[ 3 ];
+
+				tangentArray[ offset_tangent ]     = t1.x;
+				tangentArray[ offset_tangent + 1 ] = t1.y;
+				tangentArray[ offset_tangent + 2 ] = t1.z;
+				tangentArray[ offset_tangent + 3 ] = t1.w;
+
+				tangentArray[ offset_tangent + 4 ] = t2.x;
+				tangentArray[ offset_tangent + 5 ] = t2.y;
+				tangentArray[ offset_tangent + 6 ] = t2.z;
+				tangentArray[ offset_tangent + 7 ] = t2.w;
+
+				tangentArray[ offset_tangent + 8 ]  = t3.x;
+				tangentArray[ offset_tangent + 9 ]  = t3.y;
+				tangentArray[ offset_tangent + 10 ] = t3.z;
+				tangentArray[ offset_tangent + 11 ] = t3.w;
+
+				tangentArray[ offset_tangent + 12 ] = t4.x;
+				tangentArray[ offset_tangent + 13 ] = t4.y;
+				tangentArray[ offset_tangent + 14 ] = t4.z;
+				tangentArray[ offset_tangent + 15 ] = t4.w;
+
+				offset_tangent += 16;
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint );
+
+		}
+
+		if ( dirtyNormals && normalType ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces3[ f ]	];
+
+				vertexNormals = face.vertexNormals;
+				faceNormal = face.normal;
+
+				if ( vertexNormals.length === 3 && needsSmoothNormals ) {
+
+					for ( i = 0; i < 3; i ++ ) {
+
+						vn = vertexNormals[ i ];
+
+						normalArray[ offset_normal ]     = vn.x;
+						normalArray[ offset_normal + 1 ] = vn.y;
+						normalArray[ offset_normal + 2 ] = vn.z;
+
+						offset_normal += 3;
+
+					}
+
+				} else {
+
+					for ( i = 0; i < 3; i ++ ) {
+
+						normalArray[ offset_normal ]     = faceNormal.x;
+						normalArray[ offset_normal + 1 ] = faceNormal.y;
+						normalArray[ offset_normal + 2 ] = faceNormal.z;
+
+						offset_normal += 3;
+
+					}
+
+				}
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				face = obj_faces[ chunk_faces4[ f ] ];
+
+				vertexNormals = face.vertexNormals;
+				faceNormal = face.normal;
+
+				if ( vertexNormals.length === 4 && needsSmoothNormals ) {
+
+					for ( i = 0; i < 4; i ++ ) {
+
+						vn = vertexNormals[ i ];
+
+						normalArray[ offset_normal ]     = vn.x;
+						normalArray[ offset_normal + 1 ] = vn.y;
+						normalArray[ offset_normal + 2 ] = vn.z;
+
+						offset_normal += 3;
+
+					}
+
+				} else {
+
+					for ( i = 0; i < 4; i ++ ) {
+
+						normalArray[ offset_normal ]     = faceNormal.x;
+						normalArray[ offset_normal + 1 ] = faceNormal.y;
+						normalArray[ offset_normal + 2 ] = faceNormal.z;
+
+						offset_normal += 3;
+
+					}
+
+				}
+
+			}
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
+
+		}
+
+		if ( dirtyUvs && obj_uvs && uvType ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				fi = chunk_faces3[ f ];
+
+				uv = obj_uvs[ fi ];
+
+				if ( uv === undefined ) continue;
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					uvi = uv[ i ];
+
+					uvArray[ offset_uv ]     = uvi.x;
+					uvArray[ offset_uv + 1 ] = uvi.y;
+
+					offset_uv += 2;
+
+				}
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				fi = chunk_faces4[ f ];
+
+				uv = obj_uvs[ fi ];
+
+				if ( uv === undefined ) continue;
+
+				for ( i = 0; i < 4; i ++ ) {
+
+					uvi = uv[ i ];
+
+					uvArray[ offset_uv ]     = uvi.x;
+					uvArray[ offset_uv + 1 ] = uvi.y;
+
+					offset_uv += 2;
+
+				}
+
+			}
+
+			if ( offset_uv > 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint );
+
+			}
+
+		}
+
+		if ( dirtyUvs && obj_uvs2 && uvType ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				fi = chunk_faces3[ f ];
+
+				uv2 = obj_uvs2[ fi ];
+
+				if ( uv2 === undefined ) continue;
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					uv2i = uv2[ i ];
+
+					uv2Array[ offset_uv2 ]     = uv2i.x;
+					uv2Array[ offset_uv2 + 1 ] = uv2i.y;
+
+					offset_uv2 += 2;
+
+				}
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				fi = chunk_faces4[ f ];
+
+				uv2 = obj_uvs2[ fi ];
+
+				if ( uv2 === undefined ) continue;
+
+				for ( i = 0; i < 4; i ++ ) {
+
+					uv2i = uv2[ i ];
+
+					uv2Array[ offset_uv2 ]     = uv2i.x;
+					uv2Array[ offset_uv2 + 1 ] = uv2i.y;
+
+					offset_uv2 += 2;
+
+				}
+
+			}
+
+			if ( offset_uv2 > 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint );
+
+			}
+
+		}
+
+		if ( dirtyElements ) {
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				faceArray[ offset_face ] 	 = vertexIndex;
+				faceArray[ offset_face + 1 ] = vertexIndex + 1;
+				faceArray[ offset_face + 2 ] = vertexIndex + 2;
+
+				offset_face += 3;
+
+				lineArray[ offset_line ]     = vertexIndex;
+				lineArray[ offset_line + 1 ] = vertexIndex + 1;
+
+				lineArray[ offset_line + 2 ] = vertexIndex;
+				lineArray[ offset_line + 3 ] = vertexIndex + 2;
+
+				lineArray[ offset_line + 4 ] = vertexIndex + 1;
+				lineArray[ offset_line + 5 ] = vertexIndex + 2;
+
+				offset_line += 6;
+
+				vertexIndex += 3;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				faceArray[ offset_face ]     = vertexIndex;
+				faceArray[ offset_face + 1 ] = vertexIndex + 1;
+				faceArray[ offset_face + 2 ] = vertexIndex + 3;
+
+				faceArray[ offset_face + 3 ] = vertexIndex + 1;
+				faceArray[ offset_face + 4 ] = vertexIndex + 2;
+				faceArray[ offset_face + 5 ] = vertexIndex + 3;
+
+				offset_face += 6;
+
+				lineArray[ offset_line ]     = vertexIndex;
+				lineArray[ offset_line + 1 ] = vertexIndex + 1;
+
+				lineArray[ offset_line + 2 ] = vertexIndex;
+				lineArray[ offset_line + 3 ] = vertexIndex + 3;
+
+				lineArray[ offset_line + 4 ] = vertexIndex + 1;
+				lineArray[ offset_line + 5 ] = vertexIndex + 2;
+
+				lineArray[ offset_line + 6 ] = vertexIndex + 2;
+				lineArray[ offset_line + 7 ] = vertexIndex + 3;
+
+				offset_line += 8;
+
+				vertexIndex += 4;
+
+			}
+
+			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
+			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint );
+
+			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
+			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint );
+
+		}
+
+		if ( customAttributes ) {
+
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+				customAttribute = customAttributes[ i ];
+
+				if ( ! customAttribute.__original.needsUpdate ) continue;
+
+				offset_custom = 0;
+				offset_customSrc = 0;
+
+				if ( customAttribute.size === 1 ) {
+
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							face = obj_faces[ chunk_faces3[ f ]	];
+
+							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 ];
+
+							offset_custom += 3;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							face = obj_faces[ chunk_faces4[ f ] ];
+
+							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 ];
+							customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ];
+
+							offset_custom += 4;
+
+						}
+
+					} 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 + 1 ] = value;
+							customAttribute.array[ offset_custom + 2 ] = value;
+
+							offset_custom += 3;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							customAttribute.array[ offset_custom ] 	   = value;
+							customAttribute.array[ offset_custom + 1 ] = value;
+							customAttribute.array[ offset_custom + 2 ] = value;
+							customAttribute.array[ offset_custom + 3 ] = value;
+
+							offset_custom += 4;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 2 ) {
+
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; 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 + 1 ] = v1.y;
+
+							customAttribute.array[ offset_custom + 2 ] = v2.x;
+							customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+							customAttribute.array[ offset_custom + 4 ] = v3.x;
+							customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+							offset_custom += 6;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							face = obj_faces[ chunk_faces4[ f ] ];
+
+							v1 = customAttribute.value[ face.a ];
+							v2 = customAttribute.value[ face.b ];
+							v3 = customAttribute.value[ face.c ];
+							v4 = customAttribute.value[ face.d ];
+
+							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+							customAttribute.array[ offset_custom + 2 ] = v2.x;
+							customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+							customAttribute.array[ offset_custom + 4 ] = v3.x;
+							customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+							customAttribute.array[ offset_custom + 6 ] = v4.x;
+							customAttribute.array[ offset_custom + 7 ] = v4.y;
+
+							offset_custom += 8;
+
+						}
+
+					} else if ( customAttribute.boundTo === "faces" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces3[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+
+							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+							customAttribute.array[ offset_custom + 2 ] = v2.x;
+							customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+							customAttribute.array[ offset_custom + 4 ] = v3.x;
+							customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+							offset_custom += 6;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+							v4 = value;
+
+							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+							customAttribute.array[ offset_custom + 2 ] = v2.x;
+							customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+							customAttribute.array[ offset_custom + 4 ] = v3.x;
+							customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+							customAttribute.array[ offset_custom + 6 ] = v4.x;
+							customAttribute.array[ offset_custom + 7 ] = v4.y;
+
+							offset_custom += 8;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 3 ) {
+
+					var pp;
+
+					if ( customAttribute.type === "c" ) {
+
+						pp = [ "r", "g", "b" ];
+
+					} else {
+
+						pp = [ "x", "y", "z" ];
+
+					}
+
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; 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 + 1 ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+							offset_custom += 9;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							face = obj_faces[ chunk_faces4[ f ] ];
+
+							v1 = customAttribute.value[ face.a ];
+							v2 = customAttribute.value[ face.b ];
+							v3 = customAttribute.value[ face.c ];
+							v4 = customAttribute.value[ face.d ];
+
+							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+							offset_custom += 12;
+
+						}
+
+					} else if ( customAttribute.boundTo === "faces" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces3[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+
+							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+							offset_custom += 9;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+							v4 = value;
+
+							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+							offset_custom += 12;
+
+						}
+
+					} else if ( customAttribute.boundTo === "faceVertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces3[ f ] ];
+
+							v1 = value[ 0 ];
+							v2 = value[ 1 ];
+							v3 = value[ 2 ];
+
+							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+							offset_custom += 9;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							v1 = value[ 0 ];
+							v2 = value[ 1 ];
+							v3 = value[ 2 ];
+							v4 = value[ 3 ];
+
+							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+							offset_custom += 12;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 4 ) {
+
+					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; 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 + 1  ] = v1.y;
+							customAttribute.array[ offset_custom + 2  ] = v1.z;
+							customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							offset_custom += 12;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							face = obj_faces[ chunk_faces4[ f ] ];
+
+							v1 = customAttribute.value[ face.a ];
+							v2 = customAttribute.value[ face.b ];
+							v3 = customAttribute.value[ face.c ];
+							v4 = customAttribute.value[ face.d ];
+
+							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;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							customAttribute.array[ offset_custom + 12 ] = v4.x;
+							customAttribute.array[ offset_custom + 13 ] = v4.y;
+							customAttribute.array[ offset_custom + 14 ] = v4.z;
+							customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+							offset_custom += 16;
+
+						}
+
+					} else if ( customAttribute.boundTo === "faces" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces3[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+
+							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;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							offset_custom += 12;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							v1 = value;
+							v2 = value;
+							v3 = value;
+							v4 = value;
+
+							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;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							customAttribute.array[ offset_custom + 12 ] = v4.x;
+							customAttribute.array[ offset_custom + 13 ] = v4.y;
+							customAttribute.array[ offset_custom + 14 ] = v4.z;
+							customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+							offset_custom += 16;
+
+						}
+
+					} else if ( customAttribute.boundTo === "faceVertices" ) {
+
+						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces3[ f ] ];
+
+							v1 = value[ 0 ];
+							v2 = value[ 1 ];
+							v3 = value[ 2 ];
+
+							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;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							offset_custom += 12;
+
+						}
+
+						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+							value = customAttribute.value[ chunk_faces4[ f ] ];
+
+							v1 = value[ 0 ];
+							v2 = value[ 1 ];
+							v3 = value[ 2 ];
+							v4 = value[ 3 ];
+
+							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;
+
+							customAttribute.array[ offset_custom + 4  ] = v2.x;
+							customAttribute.array[ offset_custom + 5  ] = v2.y;
+							customAttribute.array[ offset_custom + 6  ] = v2.z;
+							customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+							customAttribute.array[ offset_custom + 8  ] = v3.x;
+							customAttribute.array[ offset_custom + 9  ] = v3.y;
+							customAttribute.array[ offset_custom + 10 ] = v3.z;
+							customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+							customAttribute.array[ offset_custom + 12 ] = v4.x;
+							customAttribute.array[ offset_custom + 13 ] = v4.y;
+							customAttribute.array[ offset_custom + 14 ] = v4.z;
+							customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+							offset_custom += 16;
+
+						}
+
+					}
+
+				}
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+				_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+
+			}
+
+		}
+
+		if ( dispose ) {
+
+			delete geometryGroup.__inittedArrays;
+			delete geometryGroup.__colorArray;
+			delete geometryGroup.__normalArray;
+			delete geometryGroup.__tangentArray;
+			delete geometryGroup.__uvArray;
+			delete geometryGroup.__uv2Array;
+			delete geometryGroup.__faceArray;
+			delete geometryGroup.__vertexArray;
+			delete geometryGroup.__lineArray;
+			delete geometryGroup.__skinIndexArray;
+			delete geometryGroup.__skinWeightArray;
+
+		}
+
+	};
+
+	function setDirectBuffers ( geometry, hint, dispose ) {
+
+		var attributes = geometry.attributes;
+
+		var index = attributes[ "index" ];
+		var position = attributes[ "position" ];
+		var normal = attributes[ "normal" ];
+		var uv = attributes[ "uv" ];
+		var color = attributes[ "color" ];
+		var tangent = attributes[ "tangent" ];
+
+		if ( geometry.elementsNeedUpdate && index !== undefined ) {
+
+			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
+			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, index.array, hint );
+
+		}
+
+		if ( geometry.verticesNeedUpdate && position !== undefined ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, position.array, hint );
+
+		}
+
+		if ( geometry.normalsNeedUpdate && normal !== undefined ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, normal.array, hint );
+
+		}
+
+		if ( geometry.uvsNeedUpdate && uv !== undefined ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, uv.array, hint );
+
+		}
+
+		if ( geometry.colorsNeedUpdate && color !== undefined ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, color.array, hint );
+
+		}
+
+		if ( geometry.tangentsNeedUpdate && tangent !== undefined ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, tangent.array, hint );
+
+		}
+
+		if ( dispose ) {
+
+			for ( var i in geometry.attributes ) {
+
+				delete geometry.attributes[ i ].array;
+
+			}
+
+		}
+
+	};
+
+	// Buffer rendering
+
+	this.renderBufferImmediate = function ( object, program, material ) {
+
+		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();
+		if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer();
+
+		if ( object.hasPositions ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
+			_gl.enableVertexAttribArray( program.attributes.position );
+			_gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		if ( object.hasNormals ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer );
+
+			if ( 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 ) {
+
+					normalArray = object.normalArray;
+
+					nax  = normalArray[ i ];
+					nay  = normalArray[ i + 1 ];
+					naz  = normalArray[ i + 2 ];
+
+					nbx  = normalArray[ i + 3 ];
+					nby  = normalArray[ i + 4 ];
+					nbz  = normalArray[ i + 5 ];
+
+					ncx  = normalArray[ i + 6 ];
+					ncy  = normalArray[ i + 7 ];
+					ncz  = normalArray[ i + 8 ];
+
+					nx = ( nax + nbx + ncx ) / 3;
+					ny = ( nay + nby + ncy ) / 3;
+					nz = ( naz + nbz + ncz ) / 3;
+
+					normalArray[ i ] 	 = nx;
+					normalArray[ i + 1 ] = ny;
+					normalArray[ i + 2 ] = nz;
+
+					normalArray[ i + 3 ] = nx;
+					normalArray[ i + 4 ] = ny;
+					normalArray[ i + 5 ] = nz;
+
+					normalArray[ i + 6 ] = nx;
+					normalArray[ i + 7 ] = ny;
+					normalArray[ i + 8 ] = nz;
+
+				}
+
+			}
+
+			_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
+			_gl.enableVertexAttribArray( program.attributes.normal );
+			_gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		if ( object.hasUvs && material.map ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
+			_gl.enableVertexAttribArray( program.attributes.uv );
+			_gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
+			_gl.enableVertexAttribArray( program.attributes.color );
+			_gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		_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 program, attributes, linewidth, primitives, a, attribute;
+
+		program = setProgram( camera, lights, fog, material, object );
+
+		attributes = program.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 = geometry.attributes[ "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 ) {
+
+						// vertices
+
+						var position = geometry.attributes[ "position" ];
+						var positionSize = position.itemSize;
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
+						enableAttribute( attributes.position );
+						_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, startIndex * positionSize * 4 ); // 4 bytes per Float32
+
+						// normals
+
+						var normal = geometry.attributes[ "normal" ];
+
+						if ( attributes.normal >= 0 && normal ) {
+
+							var normalSize = normal.itemSize;
+
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer );
+							enableAttribute( attributes.normal );
+							_gl.vertexAttribPointer( attributes.normal, normalSize, _gl.FLOAT, false, 0, startIndex * normalSize * 4 );
+
+						}
+
+						// uvs
+
+						var uv = geometry.attributes[ "uv" ];
+
+						if ( attributes.uv >= 0 && uv ) {
+
+							var uvSize = uv.itemSize;
+
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer );
+							enableAttribute( attributes.uv );
+							_gl.vertexAttribPointer( attributes.uv, uvSize, _gl.FLOAT, false, 0, startIndex * uvSize * 4 );
+
+						}
+
+						// colors
+
+						var color = geometry.attributes[ "color" ];
+
+						if ( attributes.color >= 0 && color ) {
+
+							var colorSize = color.itemSize;
+
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
+							enableAttribute( attributes.color );
+							_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, startIndex * colorSize * 4 );
+
+						}
+
+						// tangents
+
+						var tangent = geometry.attributes[ "tangent" ];
+
+						if ( attributes.tangent >= 0 && tangent ) {
+
+							var tangentSize = tangent.itemSize;
+
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer );
+							enableAttribute( attributes.tangent );
+							_gl.vertexAttribPointer( attributes.tangent, tangentSize, _gl.FLOAT, false, 0, startIndex * tangentSize * 4 );
+
+						}
+
+						// 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 ) {
+
+					// vertices
+
+					var position = geometry.attributes[ "position" ];
+					var positionSize = position.itemSize;
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
+					enableAttribute( attributes.position );
+					_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, 0 );
+
+					// normals
+
+					var normal = geometry.attributes[ "normal" ];
+
+					if ( attributes.normal >= 0 && normal ) {
+
+						var normalSize = normal.itemSize;
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer );
+						enableAttribute( attributes.normal );
+						_gl.vertexAttribPointer( attributes.normal, normalSize, _gl.FLOAT, false, 0, 0 );
+
+					}
+
+					// uvs
+
+					var uv = geometry.attributes[ "uv" ];
+
+					if ( attributes.uv >= 0 && uv ) {
+
+						var uvSize = uv.itemSize;
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer );
+						enableAttribute( attributes.uv );
+						_gl.vertexAttribPointer( attributes.uv, uvSize, _gl.FLOAT, false, 0, 0 );
+
+					}
+
+					// colors
+
+					var color = geometry.attributes[ "color" ];
+
+					if ( attributes.color >= 0 && color ) {
+
+						var colorSize = color.itemSize;
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
+						enableAttribute( attributes.color );
+						_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, 0 );
+
+					}
+
+					// tangents
+
+					var tangent = geometry.attributes[ "tangent" ];
+
+					if ( attributes.tangent >= 0 && tangent ) {
+
+						var tangentSize = tangent.itemSize;
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer );
+						enableAttribute( attributes.tangent );
+						_gl.vertexAttribPointer( attributes.tangent, tangentSize, _gl.FLOAT, false, 0, 0 );
+
+					}
+
+				}
+
+				// 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
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			if ( updateBuffers ) {
+
+				// vertices
+
+				var position = geometry.attributes[ "position" ];
+				var positionSize = position.itemSize;
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
+				enableAttribute( attributes.position );
+				_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, 0 );
+
+				// colors
+
+				var color = geometry.attributes[ "color" ];
+
+				if ( attributes.color >= 0 && color ) {
+
+					var colorSize = color.itemSize;
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
+					enableAttribute( attributes.color );
+					_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, 0 );
+
+				}
+
+				// 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 ) {
+
+				// vertices
+
+				var position = geometry.attributes[ "position" ];
+				var positionSize = position.itemSize;
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
+				enableAttribute( attributes.position );
+				_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, 0 );
+
+				// colors
+
+				var color = geometry.attributes[ "color" ];
+
+				if ( attributes.color >= 0 && color ) {
+
+					var colorSize = color.itemSize;
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
+					enableAttribute( attributes.color );
+					_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, 0 );
+
+				}
+
+				// render lines
+
+				setLineWidth( material.linewidth );
+
+				_gl.drawArrays( _gl.LINE_STRIP, 0, position.numItems / 3 );
+
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems;
+
+			}
+
+    }
+
+	};
+
+	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
+
+		if ( material.visible === false ) return;
+
+		var program, attributes, linewidth, primitives, a, attribute, i, il;
+
+		program = setProgram( camera, lights, fog, material, object );
+
+		attributes = program.attributes;
+
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+
+		if ( geometryGroupHash !== _currentGeometryGroupHash ) {
+
+			_currentGeometryGroupHash = geometryGroupHash;
+			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 );
+
+			}
+
+		}
+
+
+		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 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
+				enableAttribute( attributes.color );
+				_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+			// normals
+
+			if ( attributes.normal >= 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
+				enableAttribute( attributes.normal );
+				_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+			// tangents
+
+			if ( attributes.tangent >= 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
+				enableAttribute( attributes.tangent );
+				_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+			// uvs
+
+			if ( attributes.uv >= 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
+				enableAttribute( attributes.uv );
+				_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+			if ( attributes.uv2 >= 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
+				enableAttribute( attributes.uv2 );
+				_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+			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 );
+
+			}
+
+			// line distances
+
+			if ( attributes.lineDistance >= 0 ) {
+
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer );
+				enableAttribute( attributes.lineDistance );
+				_gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 );
+
+			}
+
+		}
+
+		// render mesh
+
+		if ( object instanceof THREE.Mesh ) {
+
+			// wireframe
+
+			if ( material.wireframe ) {
+
+				setLineWidth( material.wireframeLinewidth );
+
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
+				_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 );
+
+			// triangles
+
+			} else {
+
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
+				_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 );
+
+			}
+
+			_this.info.render.calls ++;
+			_this.info.render.vertices += geometryGroup.__webglFaceCount;
+			_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
+
+		// render lines
+
+		} else if ( object instanceof THREE.Line ) {
+
+			primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+
+			setLineWidth( material.linewidth );
+
+			_gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount );
+
+			_this.info.render.calls ++;
+
+		// render particles
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
+
+			_this.info.render.calls ++;
+			_this.info.render.points += geometryGroup.__webglParticleCount;
+
+		// render ribbon
+
+		} else if ( object instanceof THREE.Ribbon ) {
+
+			_gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount );
+
+			_this.info.render.calls ++;
+
+		}
+
+	};
+
+	function enableAttribute( attribute ) {
+
+		if ( ! _enabledAttributes[ attribute ] ) {
+
+			_gl.enableVertexAttribArray( attribute );
+			_enabledAttributes[ attribute ] = true;
+
+		}
+
+	};
+
+	function disableAttributes() {
+
+		for ( var attribute in _enabledAttributes ) {
+
+			if ( _enabledAttributes[ attribute ] ) {
+
+				_gl.disableVertexAttribArray( attribute );
+				_enabledAttributes[ attribute ] = false;
+
+			}
+
+		}
+
+	};
+
+	function setupMorphTargets ( material, geometryGroup, object ) {
+
+		// set base
+
+		var attributes = material.program.attributes;
+
+		if ( object.morphTargetBase !== -1 && attributes.position >= 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 ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
+			enableAttribute( attributes.position );
+			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		if ( object.morphTargetForcedOrder.length ) {
+
+			// set forced order
+
+			var m = 0;
+			var order = object.morphTargetForcedOrder;
+			var influences = object.morphTargetInfluences;
+
+			while ( m < material.numSupportedMorphTargets && m < order.length ) {
+
+				if ( attributes[ "morphTarget" + m ] >= 0 ) {
+
+					_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 );
+
+				}
+
+				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
+
+				m ++;
+			}
+
+		} else {
+
+			// find the most influencing
+
+			var influence, activeInfluenceIndices = [];
+			var influences = object.morphTargetInfluences;
+			var i, il = influences.length;
+
+			for ( i = 0; i < il; i ++ ) {
+
+				influence = influences[ i ];
+
+				if ( influence > 0 ) {
+
+					activeInfluenceIndices.push( [ influence, i ] );
+
+				}
+
+			}
+
+			if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
+
+				activeInfluenceIndices.sort( numericalSort );
+				activeInfluenceIndices.length = material.numSupportedMorphTargets;
+
+			} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
+
+				activeInfluenceIndices.sort( numericalSort );
+
+			} else if ( activeInfluenceIndices.length === 0 ) {
+
+				activeInfluenceIndices.push( [ 0, 0 ] );
+
+			};
+
+			var influenceIndex, m = 0;
+
+			while ( m < material.numSupportedMorphTargets ) {
+
+				if ( activeInfluenceIndices[ m ] ) {
+
+					influenceIndex = activeInfluenceIndices[ m ][ 1 ];
+
+					if ( attributes[ "morphTarget" + m ] >= 0 ) {
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
+						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[ influenceIndex ] );
+						enableAttribute( attributes[ "morphNormal" + m ] );
+						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+
+					}
+
+					object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
+
+				} else {
+
+					/*
+					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+					if ( material.morphNormals ) {
+
+						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+					}
+					*/
+
+					object.__webglMorphTargetInfluences[ m ] = 0;
+
+				}
+
+				m ++;
+
+			}
+
+		}
+
+		// load updated influences uniform
+
+		if ( material.program.uniforms.morphTargetInfluences !== null ) {
+
+			_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
+
+		}
+
+	};
+
+	// Sorting
+
+	function painterSortStable ( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else {
+
+			return b.id - a.id;
+
+		}
+
+	};
+
+	function numericalSort ( a, b ) {
+
+		return b[ 0 ] - a[ 0 ];
+
+	};
+
+
+	// Rendering
+
+	this.render = function ( scene, camera, renderTarget, forceClear ) {
+
+		if ( camera instanceof THREE.Camera === false ) {
+
+			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
+
+		}
+
+		var i, il,
+
+		webglObject, object,
+		renderList,
+
+		lights = scene.__lights,
+		fog = scene.fog;
+
+		// reset caching for this frame
+
+		_currentMaterialId = -1;
+		_lightsNeedUpdate = true;
+
+		// update scene graph
+
+		if ( this.autoUpdateScene ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
+
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
+
+		// update WebGL objects
+
+		if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
+
+		// 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;
+
+		this.setRenderTarget( renderTarget );
+
+		if ( this.autoClear || forceClear ) {
+
+			this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
+
+		}
+
+		// set matrices for regular objects (frustum culled)
+
+		renderList = scene.__webglObjects;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			webglObject.render = false;
+
+			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;
+
+					if ( this.sortObjects === true ) {
+
+						if ( object.renderDepth !== null ) {
+
+							webglObject.z = object.renderDepth;
+
+						} else {
+
+							_vector3.getPositionFromMatrix( object.matrixWorld );
+							_vector3.applyProjection( _projScreenMatrix );
+
+							webglObject.z = _vector3.z;
+
+						}
+
+						webglObject.id = object.id;
+
+					}
+
+				}
+
+			}
+
+		}
+
+		if ( this.sortObjects ) {
+
+			renderList.sort( painterSortStable );
+
+		}
+
+		// set matrices for immediate objects
+
+		renderList = scene.__webglObjectsImmediate;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			if ( object.visible ) {
+
+				setupMatrices( object, camera );
+
+				unrollImmediateBufferMaterial( webglObject );
+
+			}
+
+		}
+
+		if ( scene.overrideMaterial ) {
+
+			var material = scene.overrideMaterial;
+
+			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 );
+
+			renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material );
+
+		} else {
+
+			var material = null;
+
+			// opaque pass (front-to-back order)
+
+			this.setBlending( THREE.NoBlending );
+
+			renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material );
+
+			// 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 );
+
+		}
+
+		// custom render plugins (post pass)
+
+		renderPlugins( this.renderPluginsPost, scene, camera );
+
+
+		// Generate mipmap if we're using any kind of mipmap filtering
+
+		if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
+
+			updateRenderTargetMipmap( renderTarget );
+
+		}
+
+		// Ensure depth buffer writing is enabled so it can be cleared on next render
+
+		this.setDepthTest( true );
+		this.setDepthWrite( true );
+
+		// _gl.finish();
+
+	};
+
+	function renderPlugins( plugins, scene, camera ) {
+
+		if ( ! plugins.length ) return;
+
+		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
+
+			// reset state for plugin (to start from clean slate)
+
+			_currentProgram = null;
+			_currentCamera = null;
+
+			_oldBlending = -1;
+			_oldDepthTest = -1;
+			_oldDepthWrite = -1;
+			_oldDoubleSided = -1;
+			_oldFlipSided = -1;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+
+			_lightsNeedUpdate = true;
+
+			plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
+
+			// reset state after plugin (anything could have changed)
+
+			_currentProgram = null;
+			_currentCamera = null;
+
+			_oldBlending = -1;
+			_oldDepthTest = -1;
+			_oldDepthWrite = -1;
+			_oldDoubleSided = -1;
+			_oldFlipSided = -1;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+
+			_lightsNeedUpdate = true;
+
+		}
+
+	};
+
+	function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
+
+		var webglObject, object, buffer, material, start, end, delta;
+
+		if ( reverse ) {
+
+			start = renderList.length - 1;
+			end = -1;
+			delta = -1;
+
+		} else {
+
+			start = 0;
+			end = renderList.length;
+			delta = 1;
+		}
+
+		for ( var i = start; i !== end; i += delta ) {
+
+			webglObject = renderList[ i ];
+
+			if ( webglObject.render ) {
+
+				object = webglObject.object;
+				buffer = webglObject.buffer;
+
+				if ( overrideMaterial ) {
+
+					material = overrideMaterial;
+
+				} else {
+
+					material = webglObject[ materialType ];
+
+					if ( ! material ) continue;
+
+					if ( useBlending ) _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 );
+
+				}
+
+				_this.setMaterialFaces( material );
+
+				if ( buffer instanceof THREE.BufferGeometry ) {
+
+					_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
+
+				} else {
+
+					_this.renderBuffer( camera, lights, fog, material, buffer, object );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	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 ) {
+
+				if ( overrideMaterial ) {
+
+					material = overrideMaterial;
+
+				} else {
+
+					material = webglObject[ materialType ];
+
+					if ( ! material ) continue;
+
+					if ( useBlending ) _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 );
+
+				}
+
+				_this.renderImmediateObject( camera, lights, fog, material, object );
+
+			}
+
+		}
+
+	};
+
+	this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
+
+		var program = setProgram( camera, lights, fog, material, object );
+
+		_currentGeometryGroupHash = -1;
+
+		_this.setMaterialFaces( material );
+
+		if ( object.immediateRenderCallback ) {
+
+			object.immediateRenderCallback( program, _gl, _frustum );
+
+		} else {
+
+			object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } );
+
+		}
+
+	};
+
+	function unrollImmediateBufferMaterial ( globject ) {
+
+		var object = globject.object,
+			material = object.material;
+
+		if ( material.transparent ) {
+
+			globject.transparent = material;
+			globject.opaque = null;
+
+		} else {
+
+			globject.opaque = material;
+			globject.transparent = null;
+
+		}
+
+	};
+
+	function unrollBufferMaterial ( globject ) {
+
+		var object = globject.object,
+			buffer = globject.buffer,
+			material, materialIndex, meshMaterial;
+
+		meshMaterial = object.material;
+
+		if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
+
+			materialIndex = buffer.materialIndex;
+
+			material = meshMaterial.materials[ materialIndex ];
+
+			if ( material.transparent ) {
+
+				globject.transparent = material;
+				globject.opaque = null;
+
+			} else {
+
+				globject.opaque = material;
+				globject.transparent = null;
+
+			}
+
+		} else {
+
+			material = meshMaterial;
+
+			if ( material ) {
+
+				if ( material.transparent ) {
+
+					globject.transparent = material;
+					globject.opaque = null;
+
+				} else {
+
+					globject.opaque = material;
+					globject.transparent = null;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	// Geometry splitting
+
+	function sortFacesByMaterial ( geometry, material ) {
+
+		var f, fl, face, materialIndex, vertices,
+			groupHash, hash_map = {};
+
+		var numMorphTargets = geometry.morphTargets.length;
+		var numMorphNormals = geometry.morphNormals.length;
+
+		var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+
+		geometry.geometryGroups = {};
+
+		for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+
+			face = geometry.faces[ f ];
+			materialIndex = usesFaceMaterial ? face.materialIndex : 0;
+
+			if ( hash_map[ materialIndex ] === undefined ) {
+
+				hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 };
+
+			}
+
+			groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+			if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+				geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+			}
+
+			vertices = face instanceof THREE.Face3 ? 3 : 4;
+
+			if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
+
+				hash_map[ materialIndex ].counter += 1;
+				groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+				if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+					geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+				}
+
+			}
+
+			if ( face instanceof THREE.Face3 ) {
+
+				geometry.geometryGroups[ groupHash ].faces3.push( f );
+
+			} else {
+
+				geometry.geometryGroups[ groupHash ].faces4.push( f );
+
+			}
+
+			geometry.geometryGroups[ groupHash ].vertices += vertices;
+
+		}
+
+		geometry.geometryGroupsList = [];
+
+		for ( var g in geometry.geometryGroups ) {
+
+			geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
+
+			geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
+
+		}
+
+	};
+
+	// Objects refresh
+
+	this.initWebGLObjects = function ( scene ) {
+
+		if ( !scene.__webglObjects ) {
+
+			scene.__webglObjects = [];
+			scene.__webglObjectsImmediate = [];
+			scene.__webglSprites = [];
+			scene.__webglFlares = [];
+
+		}
+
+		while ( scene.__objectsAdded.length ) {
+
+			addObject( scene.__objectsAdded[ 0 ], scene );
+			scene.__objectsAdded.splice( 0, 1 );
+
+		}
+
+		while ( scene.__objectsRemoved.length ) {
+
+			removeObject( scene.__objectsRemoved[ 0 ], scene );
+			scene.__objectsRemoved.splice( 0, 1 );
+
+		}
+
+		// update must be called after objects adding / removal
+
+		for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
+
+			updateObject( scene.__webglObjects[ o ].object );
+
+		}
+
+	};
+
+	// Objects adding
+
+	function addObject ( object, scene ) {
+
+		var g, geometry, material, geometryGroup;
+
+		if ( ! object.__webglInit ) {
+
+			object.__webglInit = true;
+
+			object._modelViewMatrix = new THREE.Matrix4();
+			object._normalMatrix = new THREE.Matrix3();
+
+			if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) {
+
+				object.geometry.__webglInit = true;
+				object.geometry.addEventListener( 'dispose', onGeometryDispose );
+
+			}
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+				material = object.material;
+
+				if ( geometry instanceof THREE.Geometry ) {
+
+					if ( geometry.geometryGroups === undefined ) {
+
+						sortFacesByMaterial( geometry, material );
+
+					}
+
+					// create separate VBOs per geometry chunk
+
+					for ( g in geometry.geometryGroups ) {
+
+						geometryGroup = geometry.geometryGroups[ g ];
+
+						// initialise VBO on the first access
+
+						if ( ! geometryGroup.__webglVertexBuffer ) {
+
+							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;
+
+						}
+
+					}
+
+				} else if ( geometry instanceof THREE.BufferGeometry ) {
+
+					initDirectBuffers( geometry );
+
+				}
+
+			} else if ( object instanceof THREE.Ribbon ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					createRibbonBuffers( geometry );
+					initRibbonBuffers( geometry, object );
+
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+					geometry.normalsNeedUpdate = true;
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					if ( geometry instanceof THREE.Geometry ) {
+
+            createLineBuffers( geometry );
+            initLineBuffers( geometry, object );
+
+            geometry.verticesNeedUpdate = true;
+            geometry.colorsNeedUpdate = true;
+            geometry.lineDistancesNeedUpdate = true;
+
+          } else if ( geometry instanceof THREE.BufferGeometry ) {
+
+						initDirectBuffers( geometry );
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.ParticleSystem ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					if ( geometry instanceof THREE.Geometry ) {
+
+						createParticleBuffers( geometry );
+						initParticleBuffers( geometry, object );
+
+						geometry.verticesNeedUpdate = true;
+						geometry.colorsNeedUpdate = true;
+
+					} else if ( geometry instanceof THREE.BufferGeometry ) {
+
+						initDirectBuffers( geometry );
+
+					}
+
+
+				}
+
+			}
+
+		}
+
+		if ( ! object.__webglActive ) {
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+
+				if ( geometry instanceof THREE.BufferGeometry ) {
+
+					addBuffer( scene.__webglObjects, geometry, object );
+
+				} else if ( geometry instanceof THREE.Geometry ) {
+
+					for ( g in geometry.geometryGroups ) {
+
+						geometryGroup = geometry.geometryGroups[ g ];
+
+						addBuffer( scene.__webglObjects, geometryGroup, object );
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Ribbon ||
+						object instanceof THREE.Line ||
+						object instanceof THREE.ParticleSystem ) {
+
+				geometry = object.geometry;
+				addBuffer( scene.__webglObjects, geometry, object );
+
+			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+				addBufferImmediate( scene.__webglObjectsImmediate, object );
+
+			} else if ( object instanceof THREE.Sprite ) {
+
+				scene.__webglSprites.push( object );
+
+			} else if ( object instanceof THREE.LensFlare ) {
+
+				scene.__webglFlares.push( object );
+
+			}
+
+			object.__webglActive = true;
+
+		}
+
+	};
+
+	function addBuffer ( objlist, buffer, object ) {
+
+		objlist.push(
+			{
+				buffer: buffer,
+				object: object,
+				opaque: null,
+				transparent: null
+			}
+		);
+
+	};
+
+	function addBufferImmediate ( objlist, object ) {
+
+		objlist.push(
+			{
+				object: object,
+				opaque: null,
+				transparent: null
+			}
+		);
+
+	};
+
+	// Objects updates
+
+	function updateObject ( object ) {
+
+		var geometry = object.geometry,
+			geometryGroup, customAttributesDirty, material;
+
+		if ( object instanceof THREE.Mesh ) {
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				if ( geometry.verticesNeedUpdate || geometry.elementsNeedUpdate ||
+					 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
+					 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate ) {
+
+					setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.elementsNeedUpdate = false;
+				geometry.uvsNeedUpdate = false;
+				geometry.normalsNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+				geometry.tangentsNeedUpdate = false;
+
+			} else {
+
+				// check all geometry groups
+
+				for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
+
+					geometryGroup = geometry.geometryGroupsList[ i ];
+
+					material = getBufferMaterial( object, geometryGroup );
+
+					if ( geometry.buffersNeedUpdate ) {
+
+						initMeshBuffers( geometryGroup, object );
+
+					}
+
+					customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+					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;
+
+				material.attributes && clearCustomAttributes( material );
+
+			}
+
+		} else if ( object instanceof THREE.Ribbon ) {
+
+			material = getBufferMaterial( object, geometry );
+
+			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.normalsNeedUpdate || customAttributesDirty ) {
+
+				setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW );
+
+			}
+
+			geometry.verticesNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
+			geometry.normalsNeedUpdate = false;
+
+			material.attributes && clearCustomAttributes( material );
+
+		} else if ( object instanceof THREE.Line ) {
+
+      if ( geometry instanceof THREE.BufferGeometry ) {
+
+				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) {
+
+					setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+
+			} else {
+
+        material = getBufferMaterial( object, geometry );
+
+        customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+        if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
+
+          setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
+
+        }
+
+        geometry.verticesNeedUpdate = false;
+        geometry.colorsNeedUpdate = false;
+        geometry.lineDistancesNeedUpdate = false;
+
+        material.attributes && clearCustomAttributes( material );
+
+      }
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) {
+
+					setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+
+			} else {
+
+				material = getBufferMaterial( object, geometry );
+
+				customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) {
+
+					setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+
+				material.attributes && clearCustomAttributes( material );
+
+			}
+
+		}
+
+	};
+
+	// Objects updates - custom attributes check
+
+	function areCustomAttributesDirty ( material ) {
+
+		for ( var a in material.attributes ) {
+
+			if ( material.attributes[ a ].needsUpdate ) return true;
+
+		}
+
+		return false;
+
+	};
+
+	function clearCustomAttributes ( material ) {
+
+		for ( var a in material.attributes ) {
+
+			material.attributes[ a ].needsUpdate = false;
+
+		}
+
+	};
+
+	// Objects removal
+
+	function removeObject ( object, scene ) {
+
+		if ( object instanceof THREE.Mesh  ||
+			 object instanceof THREE.ParticleSystem ||
+			 object instanceof THREE.Ribbon ||
+			 object instanceof THREE.Line ) {
+
+			removeInstances( scene.__webglObjects, object );
+
+		} else if ( object instanceof THREE.Sprite ) {
+
+			removeInstancesDirect( scene.__webglSprites, object );
+
+		} else if ( object instanceof THREE.LensFlare ) {
+
+			removeInstancesDirect( scene.__webglFlares, object );
+
+		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+			removeInstances( scene.__webglObjectsImmediate, object );
+
+		}
+
+		object.__webglActive = false;
+
+	};
+
+	function removeInstances ( objlist, object ) {
+
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+
+			if ( objlist[ o ].object === object ) {
+
+				objlist.splice( o, 1 );
+
+			}
+
+		}
+
+	};
+
+	function removeInstancesDirect ( objlist, object ) {
+
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+
+			if ( objlist[ o ] === object ) {
+
+				objlist.splice( o, 1 );
+
+			}
+
+		}
+
+	};
+
+	// Materials
+
+	this.initMaterial = function ( material, lights, fog, object ) {
+
+		material.addEventListener( 'dispose', onMaterialDispose );
+
+		var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
+
+		if ( material instanceof THREE.MeshDepthMaterial ) {
+
+			shaderID = 'depth';
+
+		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+			shaderID = 'normal';
+
+		} else if ( material instanceof THREE.MeshBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.MeshLambertMaterial ) {
+
+			shaderID = 'lambert';
+
+		} else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+			shaderID = 'phong';
+
+		} else if ( material instanceof THREE.LineBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+			shaderID = 'dashed';
+
+		} else if ( material instanceof THREE.ParticleBasicMaterial ) {
+
+			shaderID = 'particle_basic';
+
+		}
+
+		if ( shaderID ) {
+
+			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 );
+
+		maxShadows = allocateShadows( lights );
+
+		maxBones = allocateBones( object );
+
+		parameters = {
+
+			map: !!material.map,
+			envMap: !!material.envMap,
+			lightMap: !!material.lightMap,
+			bumpMap: !!material.bumpMap,
+			normalMap: !!material.normalMap,
+			specularMap: !!material.specularMap,
+
+			vertexColors: material.vertexColors,
+
+			fog: fog,
+			useFog: material.fog,
+			fogExp: fog instanceof THREE.FogExp2,
+
+			sizeAttenuation: material.sizeAttenuation,
+
+			skinning: material.skinning,
+			maxBones: maxBones,
+			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
+			boneTextureWidth: object && object.boneTextureWidth,
+			boneTextureHeight: object && object.boneTextureHeight,
+
+			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,
+			shadowMapType: this.shadowMapType,
+			shadowMapDebug: this.shadowMapDebug,
+			shadowMapCascade: this.shadowMapCascade,
+
+			alphaTest: material.alphaTest,
+			metal: material.metal,
+			perPixel: material.perPixel,
+			wrapAround: material.wrapAround,
+			doubleSided: material.side === THREE.DoubleSide,
+			flipSided: material.side === THREE.BackSide
+
+		};
+
+		material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters );
+
+		var attributes = material.program.attributes;
+
+		if ( material.morphTargets ) {
+
+			material.numSupportedMorphTargets = 0;
+
+			var id, base = "morphTarget";
+
+			for ( i = 0; i < this.maxMorphTargets; i ++ ) {
+
+				id = base + i;
+
+				if ( attributes[ id ] >= 0 ) {
+
+					material.numSupportedMorphTargets ++;
+
+				}
+
+			}
+
+		}
+
+		if ( material.morphNormals ) {
+
+			material.numSupportedMorphNormals = 0;
+
+			var id, base = "morphNormal";
+
+			for ( i = 0; i < this.maxMorphNormals; i ++ ) {
+
+				id = base + i;
+
+				if ( attributes[ id ] >= 0 ) {
+
+					material.numSupportedMorphNormals ++;
+
+				}
+
+			}
+
+		}
+
+		material.uniformsList = [];
+
+		for ( u in material.uniforms ) {
+
+			material.uniformsList.push( [ material.uniforms[ u ], u ] );
+
+		}
+
+	};
+
+	function setMaterialShaders( material, shaders ) {
+
+		material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
+		material.vertexShader = shaders.vertexShader;
+		material.fragmentShader = shaders.fragmentShader;
+
+	};
+
+	function setProgram( camera, lights, fog, material, object ) {
+
+		_usedTextureUnits = 0;
+
+		if ( material.needsUpdate ) {
+
+			if ( material.program ) deallocateMaterial( material );
+
+			_this.initMaterial( material, lights, fog, object );
+			material.needsUpdate = false;
+
+		}
+
+		if ( material.morphTargets ) {
+
+			if ( ! object.__webglMorphTargetInfluences ) {
+
+				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
+
+			}
+
+		}
+
+		var refreshMaterial = false;
+
+		var program = material.program,
+			p_uniforms = program.uniforms,
+			m_uniforms = material.uniforms;
+
+		if ( program !== _currentProgram ) {
+
+			_gl.useProgram( program );
+			_currentProgram = program;
+
+			refreshMaterial = true;
+
+		}
+
+		if ( material.id !== _currentMaterialId ) {
+
+			_currentMaterialId = material.id;
+			refreshMaterial = true;
+
+		}
+
+		if ( refreshMaterial || camera !== _currentCamera ) {
+
+			_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+			if ( camera !== _currentCamera ) _currentCamera = camera;
+
+		}
+
+		// 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 ( material.skinning ) {
+
+			if ( _supportsBoneTextures && object.useVertexTexture ) {
+
+				if ( p_uniforms.boneTexture !== null ) {
+
+					var textureUnit = getTextureUnit();
+
+					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
+					_this.setTexture( object.boneTexture, textureUnit );
+
+				}
+
+			} else {
+
+				if ( p_uniforms.boneGlobalMatrices !== null ) {
+
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
+
+				}
+
+			}
+
+		}
+
+		if ( refreshMaterial ) {
+
+			// refresh uniforms common to several materials
+
+			if ( fog && material.fog ) {
+
+				refreshUniformsFog( m_uniforms, fog );
+
+			}
+
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material.lights ) {
+
+				if ( _lightsNeedUpdate ) {
+
+					setupLights( program, lights );
+					_lightsNeedUpdate = false;
+
+				}
+
+				refreshUniformsLights( m_uniforms, _lights );
+
+			}
+
+			if ( material instanceof THREE.MeshBasicMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ) {
+
+				refreshUniformsCommon( m_uniforms, material );
+
+			}
+
+			// refresh single material specific uniforms
+
+			if ( material instanceof THREE.LineBasicMaterial ) {
+
+				refreshUniformsLine( m_uniforms, material );
+
+			} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+				refreshUniformsLine( m_uniforms, material );
+				refreshUniformsDash( m_uniforms, material );
+
+			} else if ( material instanceof THREE.ParticleBasicMaterial ) {
+
+				refreshUniformsParticle( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+				refreshUniformsPhong( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshLambertMaterial ) {
+
+				refreshUniformsLambert( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+				m_uniforms.mNear.value = camera.near;
+				m_uniforms.mFar.value = camera.far;
+				m_uniforms.opacity.value = material.opacity;
+
+			} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+				m_uniforms.opacity.value = material.opacity;
+
+			}
+
+			if ( object.receiveShadow && ! material._shadowPass ) {
+
+				refreshUniformsShadow( m_uniforms, lights );
+
+			}
+
+			// load common uniforms
+
+			loadUniformsGeneric( program, material.uniformsList );
+
+			// load material specific uniforms
+			// (shader material also gets them for the sake of genericity)
+
+			if ( material instanceof THREE.ShaderMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ||
+				 material.envMap ) {
+
+				if ( p_uniforms.cameraPosition !== null ) {
+
+					_vector3.getPositionFromMatrix( camera.matrixWorld );
+					_gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );
+
+				}
+
+			}
+
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.ShaderMaterial ||
+				 material.skinning ) {
+
+				if ( p_uniforms.viewMatrix !== null ) {
+
+					_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
+
+				}
+
+			}
+
+		}
+
+		loadUniformsMatrices( p_uniforms, object );
+
+		if ( p_uniforms.modelMatrix !== null ) {
+
+			_gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
+
+		}
+
+		return program;
+
+	};
+
+	// Uniforms (refresh uniforms objects)
+
+	function refreshUniformsCommon ( uniforms, material ) {
+
+		uniforms.opacity.value = material.opacity;
+
+		if ( _this.gammaInput ) {
+
+			uniforms.diffuse.value.copyGammaToLinear( material.color );
+
+		} else {
+
+			uniforms.diffuse.value = material.color;
+
+		}
+
+		uniforms.map.value = material.map;
+		uniforms.lightMap.value = material.lightMap;
+		uniforms.specularMap.value = material.specularMap;
+
+		if ( material.bumpMap ) {
+
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+
+		}
+
+		if ( material.normalMap ) {
+
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+
+		}
+
+		// uv repeat and offset setting priorities
+		//	1. color map
+		//	2. specular map
+		//	3. normal map
+		//	4. bump map
+
+		var uvScaleMap;
+
+		if ( material.map ) {
+
+			uvScaleMap = material.map;
+
+		} else if ( material.specularMap ) {
+
+			uvScaleMap = material.specularMap;
+
+		} else if ( material.normalMap ) {
+
+			uvScaleMap = material.normalMap;
+
+		} else if ( material.bumpMap ) {
+
+			uvScaleMap = material.bumpMap;
+
+		}
+
+		if ( uvScaleMap !== undefined ) {
+
+			var offset = uvScaleMap.offset;
+			var repeat = uvScaleMap.repeat;
+
+			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+		}
+
+		uniforms.envMap.value = material.envMap;
+		uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
+
+		if ( _this.gammaInput ) {
+
+			//uniforms.reflectivity.value = material.reflectivity * material.reflectivity;
+			uniforms.reflectivity.value = material.reflectivity;
+
+		} else {
+
+			uniforms.reflectivity.value = material.reflectivity;
+
+		}
+
+		uniforms.refractionRatio.value = material.refractionRatio;
+		uniforms.combine.value = material.combine;
+		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
+
+	};
+
+	function refreshUniformsLine ( uniforms, material ) {
+
+		uniforms.diffuse.value = material.color;
+		uniforms.opacity.value = material.opacity;
+
+	};
+
+	function refreshUniformsDash ( uniforms, material ) {
+
+		uniforms.dashSize.value = material.dashSize;
+		uniforms.totalSize.value = material.dashSize + material.gapSize;
+		uniforms.scale.value = material.scale;
+
+	};
+
+	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.
+
+		uniforms.map.value = material.map;
+
+	};
+
+	function refreshUniformsFog ( uniforms, fog ) {
+
+		uniforms.fogColor.value = fog.color;
+
+		if ( fog instanceof THREE.Fog ) {
+
+			uniforms.fogNear.value = fog.near;
+			uniforms.fogFar.value = fog.far;
+
+		} else if ( fog instanceof THREE.FogExp2 ) {
+
+			uniforms.fogDensity.value = fog.density;
+
+		}
+
+	};
+
+	function refreshUniformsPhong ( uniforms, material ) {
+
+		uniforms.shininess.value = material.shininess;
+
+		if ( _this.gammaInput ) {
+
+			uniforms.ambient.value.copyGammaToLinear( material.ambient );
+			uniforms.emissive.value.copyGammaToLinear( material.emissive );
+			uniforms.specular.value.copyGammaToLinear( material.specular );
+
+		} else {
+
+			uniforms.ambient.value = material.ambient;
+			uniforms.emissive.value = material.emissive;
+			uniforms.specular.value = material.specular;
+
+		}
+
+		if ( material.wrapAround ) {
+
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
+
+		}
+
+	};
+
+	function refreshUniformsLambert ( uniforms, material ) {
+
+		if ( _this.gammaInput ) {
+
+			uniforms.ambient.value.copyGammaToLinear( material.ambient );
+			uniforms.emissive.value.copyGammaToLinear( material.emissive );
+
+		} else {
+
+			uniforms.ambient.value = material.ambient;
+			uniforms.emissive.value = material.emissive;
+
+		}
+
+		if ( material.wrapAround ) {
+
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
+
+		}
+
+	};
+
+	function refreshUniformsLights ( uniforms, lights ) {
+
+		uniforms.ambientLightColor.value = lights.ambient;
+
+		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;
+
+		uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
+		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
+		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
+
+	};
+
+	function refreshUniformsShadow ( uniforms, lights ) {
+
+		if ( uniforms.shadowMatrix ) {
+
+			var j = 0;
+
+			for ( var i = 0, il = lights.length; i < il; i ++ ) {
+
+				var light = lights[ i ];
+
+				if ( ! light.castShadow ) continue;
+
+				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;
+
+					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
+					uniforms.shadowBias.value[ j ] = light.shadowBias;
+
+					j ++;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	// Uniforms (load to GPU)
+
+	function loadUniformsMatrices ( uniforms, object ) {
+
+		_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements );
+
+		if ( uniforms.normalMatrix ) {
+
+			_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements );
+
+		}
+
+	};
+
+	function getTextureUnit() {
+
+		var textureUnit = _usedTextureUnits;
+
+		if ( textureUnit >= _maxTextures ) {
+
+			console.warn( "WebGLRenderer: trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures );
+
+		}
+
+		_usedTextureUnits += 1;
+
+		return textureUnit;
+
+	};
+
+	function loadUniformsGeneric ( program, uniforms ) {
+
+		var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset;
+
+		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;
+
+			if ( type === "i" ) { // single integer
+
+				_gl.uniform1i( location, value );
+
+			} else if ( type === "f" ) { // single float
+
+				_gl.uniform1f( location, value );
+
+			} else if ( type === "v2" ) { // single THREE.Vector2
+
+				_gl.uniform2f( location, value.x, value.y );
+
+			} else if ( type === "v3" ) { // single THREE.Vector3
+
+				_gl.uniform3f( location, value.x, value.y, value.z );
+
+			} else if ( type === "v4" ) { // single THREE.Vector4
+
+				_gl.uniform4f( location, value.x, value.y, value.z, value.w );
+
+			} else if ( type === "c" ) { // single THREE.Color
+
+				_gl.uniform3f( location, value.r, value.g, value.b );
+
+			} else if ( type === "iv1" ) { // flat array of integers (JS or typed array)
+
+				_gl.uniform1iv( location, value );
+
+			} else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array)
+
+				_gl.uniform3iv( location, value );
+
+			} else if ( type === "fv1" ) { // flat array of floats (JS or typed array)
+
+				_gl.uniform1fv( location, value );
+
+			} else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array)
+
+				_gl.uniform3fv( location, value );
+
+			} else if ( type === "v2v" ) { // array of THREE.Vector2
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 2 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 2;
+
+					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset + 1 ] = value[ i ].y;
+
+				}
+
+				_gl.uniform2fv( location, uniform._array );
+
+			} else if ( type === "v3v" ) { // array of THREE.Vector3
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 3 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 3;
+
+					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset + 1 ] = value[ i ].y;
+					uniform._array[ offset + 2 ] = value[ i ].z;
+
+				}
+
+				_gl.uniform3fv( location, uniform._array );
+
+			} else if ( type === "v4v" ) { // array of THREE.Vector4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 4 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 4;
+
+					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;
+
+				}
+
+				_gl.uniform4fv( location, uniform._array );
+
+			} else if ( type === "m4") { // single THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 );
+
+				}
+
+				value.flattenToArray( uniform._array );
+				_gl.uniformMatrix4fv( location, false, uniform._array );
+
+			} else if ( type === "m4v" ) { // array of THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+
+				}
+
+				_gl.uniformMatrix4fv( location, false, uniform._array );
+
+			} else if ( type === "t" ) { // single THREE.Texture (2d or cube)
+
+				texture = value;
+				textureUnit = getTextureUnit();
+
+				_gl.uniform1i( location, textureUnit );
+
+				if ( !texture ) continue;
+
+				if ( texture.image instanceof Array && texture.image.length === 6 ) {
+
+					setCubeTexture( texture, textureUnit );
+
+				} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+					setCubeTextureDynamic( texture, textureUnit );
+
+				} else {
+
+					_this.setTexture( texture, textureUnit );
+
+				}
+
+			} else if ( type === "tv" ) { // array of THREE.Texture (2d)
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = [];
+
+				}
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					uniform._array[ i ] = getTextureUnit();
+
+				}
+
+				_gl.uniform1iv( location, uniform._array );
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					texture = uniform.value[ i ];
+					textureUnit = uniform._array[ i ];
+
+					if ( !texture ) continue;
+
+					_this.setTexture( texture, textureUnit );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function setupMatrices ( object, camera ) {
+
+		object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+
+		object._normalMatrix.getInverse( object._modelViewMatrix );
+		object._normalMatrix.transpose();
+
+	};
+
+	//
+
+	function setColorGamma( array, offset, color, intensitySq ) {
+
+		array[ offset ]     = color.r * color.r * intensitySq;
+		array[ offset + 1 ] = color.g * color.g * intensitySq;
+		array[ offset + 2 ] = color.b * color.b * intensitySq;
+
+	};
+
+	function setColorLinear( array, offset, color, intensity ) {
+
+		array[ offset ]     = color.r * intensity;
+		array[ offset + 1 ] = color.g * intensity;
+		array[ offset + 2 ] = color.b * intensity;
+
+	};
+
+	function setupLights ( program, lights ) {
+
+		var l, ll, light, n,
+		r = 0, g = 0, b = 0,
+		color, skyColor, groundColor,
+		intensity,  intensitySq,
+		position,
+		distance,
+
+		zlights = _lights,
+
+		dirColors = zlights.directional.colors,
+		dirPositions = zlights.directional.positions,
+
+		pointColors = zlights.point.colors,
+		pointPositions = zlights.point.positions,
+		pointDistances = zlights.point.distances,
+
+		spotColors = zlights.spot.colors,
+		spotPositions = zlights.spot.positions,
+		spotDistances = zlights.spot.distances,
+		spotDirections = zlights.spot.directions,
+		spotAnglesCos = zlights.spot.anglesCos,
+		spotExponents = zlights.spot.exponents,
+
+		hemiSkyColors = zlights.hemi.skyColors,
+		hemiGroundColors = zlights.hemi.groundColors,
+		hemiPositions = zlights.hemi.positions,
+
+		dirLength = 0,
+		pointLength = 0,
+		spotLength = 0,
+		hemiLength = 0,
+
+		dirCount = 0,
+		pointCount = 0,
+		spotCount = 0,
+		hemiCount = 0,
+
+		dirOffset = 0,
+		pointOffset = 0,
+		spotOffset = 0,
+		hemiOffset = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+			light = lights[ l ];
+
+			if ( light.onlyShadow ) continue;
+
+			color = light.color;
+			intensity = light.intensity;
+			distance = light.distance;
+
+			if ( light instanceof THREE.AmbientLight ) {
+
+				if ( ! light.visible ) continue;
+
+				if ( _this.gammaInput ) {
+
+					r += color.r * color.r;
+					g += color.g * color.g;
+					b += color.b * color.b;
+
+				} else {
+
+					r += color.r;
+					g += color.g;
+					b += color.b;
+
+				}
+
+			} else if ( light instanceof THREE.DirectionalLight ) {
+
+				dirCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				_direction.getPositionFromMatrix( light.matrixWorld );
+				_vector3.getPositionFromMatrix( light.target.matrixWorld );
+				_direction.sub( _vector3 );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+
+				dirOffset = dirLength * 3;
+
+				dirPositions[ dirOffset ]     = _direction.x;
+				dirPositions[ dirOffset + 1 ] = _direction.y;
+				dirPositions[ dirOffset + 2 ] = _direction.z;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( dirColors, dirOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( dirColors, dirOffset, color, intensity );
+
+				}
+
+				dirLength += 1;
+
+			} else if ( light instanceof THREE.PointLight ) {
+
+				pointCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				pointOffset = pointLength * 3;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( pointColors, pointOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( pointColors, pointOffset, color, intensity );
+
+				}
+
+				_vector3.getPositionFromMatrix( light.matrixWorld );
+
+				pointPositions[ pointOffset ]     = _vector3.x;
+				pointPositions[ pointOffset + 1 ] = _vector3.y;
+				pointPositions[ pointOffset + 2 ] = _vector3.z;
+
+				pointDistances[ pointLength ] = distance;
+
+				pointLength += 1;
+
+			} else if ( light instanceof THREE.SpotLight ) {
+
+				spotCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				spotOffset = spotLength * 3;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( spotColors, spotOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( spotColors, spotOffset, color, intensity );
+
+				}
+
+				_vector3.getPositionFromMatrix( light.matrixWorld );
+
+				spotPositions[ spotOffset ]     = _vector3.x;
+				spotPositions[ spotOffset + 1 ] = _vector3.y;
+				spotPositions[ spotOffset + 2 ] = _vector3.z;
+
+				spotDistances[ spotLength ] = distance;
+
+				_direction.copy( _vector3 );
+				_vector3.getPositionFromMatrix( light.target.matrixWorld );
+				_direction.sub( _vector3 );
+				_direction.normalize();
+
+				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;
+
+			} else if ( light instanceof THREE.HemisphereLight ) {
+
+				hemiCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				_direction.getPositionFromMatrix( light.matrixWorld );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				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;
+
+				skyColor = light.color;
+				groundColor = light.groundColor;
+
+				if ( _this.gammaInput ) {
+
+					intensitySq = intensity * intensity;
+
+					setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq );
+					setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq );
+
+				} else {
+
+					setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
+					setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
+
+				}
+
+				hemiLength += 1;
+
+			}
+
+		}
+
+		// 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;
+
+		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;
+
+	};
+
+	// GL state setting
+
+	this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
+
+		if ( cullFace === THREE.CullFaceNone ) {
+
+			_gl.disable( _gl.CULL_FACE );
+
+		} else {
+
+			if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
+
+				_gl.frontFace( _gl.CW );
+
+			} else {
+
+				_gl.frontFace( _gl.CCW );
+
+			}
+
+			if ( cullFace === THREE.CullFaceBack ) {
+
+				_gl.cullFace( _gl.BACK );
+
+			} else if ( cullFace === THREE.CullFaceFront ) {
+
+				_gl.cullFace( _gl.FRONT );
+
+			} else {
+
+				_gl.cullFace( _gl.FRONT_AND_BACK );
+
+			}
+
+			_gl.enable( _gl.CULL_FACE );
+
+		}
+
+	};
+
+	this.setMaterialFaces = function ( material ) {
+
+		var doubleSided = material.side === THREE.DoubleSide;
+		var flipSided = material.side === THREE.BackSide;
+
+		if ( _oldDoubleSided !== doubleSided ) {
+
+			if ( doubleSided ) {
+
+				_gl.disable( _gl.CULL_FACE );
+
+			} else {
+
+				_gl.enable( _gl.CULL_FACE );
+
+			}
+
+			_oldDoubleSided = doubleSided;
+
+		}
+
+		if ( _oldFlipSided !== flipSided ) {
+
+			if ( flipSided ) {
+
+				_gl.frontFace( _gl.CW );
+
+			} else {
+
+				_gl.frontFace( _gl.CCW );
+
+			}
+
+			_oldFlipSided = flipSided;
+
+		}
+
+	};
+
+	this.setDepthTest = function ( depthTest ) {
+
+		if ( _oldDepthTest !== depthTest ) {
+
+			if ( depthTest ) {
+
+				_gl.enable( _gl.DEPTH_TEST );
+
+			} else {
+
+				_gl.disable( _gl.DEPTH_TEST );
+
+			}
+
+			_oldDepthTest = depthTest;
+
+		}
+
+	};
+
+	this.setDepthWrite = function ( depthWrite ) {
+
+		if ( _oldDepthWrite !== depthWrite ) {
+
+			_gl.depthMask( depthWrite );
+			_oldDepthWrite = depthWrite;
+
+		}
+
+	};
+
+	function setLineWidth ( width ) {
+
+		if ( width !== _oldLineWidth ) {
+
+			_gl.lineWidth( width );
+
+			_oldLineWidth = width;
+
+		}
+
+	};
+
+	function setPolygonOffset ( polygonoffset, factor, units ) {
+
+		if ( _oldPolygonOffset !== polygonoffset ) {
+
+			if ( polygonoffset ) {
+
+				_gl.enable( _gl.POLYGON_OFFSET_FILL );
+
+			} else {
+
+				_gl.disable( _gl.POLYGON_OFFSET_FILL );
+
+			}
+
+			_oldPolygonOffset = polygonoffset;
+
+		}
+
+		if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) {
+
+			_gl.polygonOffset( factor, units );
+
+			_oldPolygonOffsetFactor = factor;
+			_oldPolygonOffsetUnits = units;
+
+		}
+
+	};
+
+	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
+
+		if ( blending !== _oldBlending ) {
+
+			if ( blending === THREE.NoBlending ) {
+
+				_gl.disable( _gl.BLEND );
+
+			} else if ( blending === THREE.AdditiveBlending ) {
+
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
+
+			} else if ( blending === THREE.SubtractiveBlending ) {
+
+				// TODO: Find blendFuncSeparate() combination
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
+
+			} else if ( blending === THREE.MultiplyBlending ) {
+
+				// TODO: Find blendFuncSeparate() combination
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
+
+			} else if ( blending === THREE.CustomBlending ) {
+
+				_gl.enable( _gl.BLEND );
+
+			} else {
+
+				_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 );
+
+			}
+
+			_oldBlending = blending;
+
+		}
+
+		if ( blending === THREE.CustomBlending ) {
+
+			if ( blendEquation !== _oldBlendEquation ) {
+
+				_gl.blendEquation( paramThreeToGL( blendEquation ) );
+
+				_oldBlendEquation = blendEquation;
+
+			}
+
+			if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) {
+
+				_gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) );
+
+				_oldBlendSrc = blendSrc;
+				_oldBlendDst = blendDst;
+
+			}
+
+		} else {
+
+			_oldBlendEquation = null;
+			_oldBlendSrc = null;
+			_oldBlendDst = null;
+
+		}
+
+	};
+
+	// Defines
+
+	function generateDefines ( defines ) {
+
+		var value, chunk, chunks = [];
+
+		for ( var d in defines ) {
+
+			value = defines[ d ];
+			if ( value === false ) continue;
+
+			chunk = "#define " + d + " " + value;
+			chunks.push( chunk );
+
+		}
+
+		return chunks.join( "\n" );
+
+	};
+
+	// Shaders
+
+	function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters ) {
+
+		var p, pl, d, program, code;
+		var chunks = [];
+
+		// Generate code
+
+		if ( shaderID ) {
+
+			chunks.push( shaderID );
+
+		} else {
+
+			chunks.push( fragmentShader );
+			chunks.push( vertexShader );
+
+		}
+
+		for ( d in defines ) {
+
+			chunks.push( d );
+			chunks.push( defines[ d ] );
+
+		}
+
+		for ( p in parameters ) {
+
+			chunks.push( p );
+			chunks.push( parameters[ p ] );
+
+		}
+
+		code = chunks.join();
+
+		// Check if code has been already compiled
+
+		for ( p = 0, pl = _programs.length; p < pl; p ++ ) {
+
+			var programInfo = _programs[ p ];
+
+			if ( programInfo.code === code ) {
+
+				//console.log( "Code already compiled." /*: \n\n" + code*/ );
+
+				programInfo.usedTimes ++;
+
+				return programInfo.program;
+
+			}
+
+		}
+
+		var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC";
+
+		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
+
+			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF";
+
+		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
+
+			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT";
+
+		}
+
+		//console.log( "building new program " );
+
+		//
+
+		var customDefines = generateDefines( defines );
+
+		//
+
+		program = _gl.createProgram();
+
+		var prefix_vertex = [
+
+			"precision " + _precision + " float;",
+
+			customDefines,
+
+			_supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
+
+			_this.gammaInput ? "#define GAMMA_INPUT" : "",
+			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
+			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+
+			"#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,
+
+			"#define MAX_BONES " + parameters.maxBones,
+
+			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" : "",
+
+			parameters.skinning ? "#define USE_SKINNING" : "",
+			parameters.useVertexTexture ? "#define BONE_TEXTURE" : "",
+			parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "",
+			parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "",
+
+			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" : "",
+
+			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
+			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
+			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
+			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+
+			parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
+
+			"uniform mat4 modelMatrix;",
+			"uniform mat4 modelViewMatrix;",
+			"uniform mat4 projectionMatrix;",
+			"uniform mat4 viewMatrix;",
+			"uniform mat3 normalMatrix;",
+			"uniform vec3 cameraPosition;",
+
+			"attribute vec3 position;",
+			"attribute vec3 normal;",
+			"attribute vec2 uv;",
+			"attribute vec2 uv2;",
+
+			"#ifdef USE_COLOR",
+
+				"attribute vec3 color;",
+
+			"#endif",
+
+			"#ifdef USE_MORPHTARGETS",
+
+				"attribute vec3 morphTarget0;",
+				"attribute vec3 morphTarget1;",
+				"attribute vec3 morphTarget2;",
+				"attribute vec3 morphTarget3;",
+
+				"#ifdef USE_MORPHNORMALS",
+
+					"attribute vec3 morphNormal0;",
+					"attribute vec3 morphNormal1;",
+					"attribute vec3 morphNormal2;",
+					"attribute vec3 morphNormal3;",
+
+				"#else",
+
+					"attribute vec3 morphTarget4;",
+					"attribute vec3 morphTarget5;",
+					"attribute vec3 morphTarget6;",
+					"attribute vec3 morphTarget7;",
+
+				"#endif",
+
+			"#endif",
+
+			"#ifdef USE_SKINNING",
+
+				"attribute vec4 skinIndex;",
+				"attribute vec4 skinWeight;",
+
+			"#endif",
+
+			""
+
+		].join("\n");
+
+		var prefix_fragment = [
+
+			"precision " + _precision + " float;",
+
+			( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "",
+
+			customDefines,
+
+			"#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,
+
+			parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
+
+			_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" : "",
+
+			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" : "",
+
+			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" : "",
+
+			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
+			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
+			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
+			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+
+			"uniform mat4 viewMatrix;",
+			"uniform vec3 cameraPosition;",
+			""
+
+		].join("\n");
+
+		var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
+		var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );
+
+		_gl.attachShader( program, glVertexShader );
+		_gl.attachShader( program, glFragmentShader );
+
+		_gl.linkProgram( program );
+
+		if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
+
+			console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
+
+		}
+
+		// clean up
+
+		_gl.deleteShader( glFragmentShader );
+		_gl.deleteShader( glVertexShader );
+
+		//console.log( prefix_fragment + fragmentShader );
+		//console.log( prefix_vertex + vertexShader );
+
+		program.uniforms = {};
+		program.attributes = {};
+
+		var identifiers, u, a, i;
+
+		// cache uniform locations
+
+		identifiers = [
+
+			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
+			'morphTargetInfluences'
+
+		];
+
+		if ( parameters.useVertexTexture ) {
+
+			identifiers.push( 'boneTexture' );
+
+		} else {
+
+			identifiers.push( 'boneGlobalMatrices' );
+
+		}
+
+		for ( u in uniforms ) {
+
+			identifiers.push( u );
+
+		}
+
+		cacheUniformLocations( program, identifiers );
+
+		// cache attributes locations
+
+		identifiers = [
+
+			"position", "normal", "uv", "uv2", "tangent", "color",
+			"skinIndex", "skinWeight", "lineDistance"
+
+		];
+
+		for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
+
+			identifiers.push( "morphTarget" + i );
+
+		}
+
+		for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
+
+			identifiers.push( "morphNormal" + i );
+
+		}
+
+		for ( a in attributes ) {
+
+			identifiers.push( a );
+
+		}
+
+		cacheAttributeLocations( program, identifiers );
+
+		program.id = _programs_counter ++;
+
+		_programs.push( { program: program, code: code, usedTimes: 1 } );
+
+		_this.info.memory.programs = _programs.length;
+
+		return program;
+
+	};
+
+	// Shader parameters cache
+
+	function cacheUniformLocations ( program, identifiers ) {
+
+		var i, l, id;
+
+		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+			id = identifiers[ i ];
+			program.uniforms[ id ] = _gl.getUniformLocation( program, id );
+
+		}
+
+	};
+
+	function cacheAttributeLocations ( program, identifiers ) {
+
+		var i, l, id;
+
+		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+			id = identifiers[ i ];
+			program.attributes[ id ] = _gl.getAttribLocation( program, id );
+
+		}
+
+	};
+
+	function addLineNumbers ( string ) {
+
+		var chunks = string.split( "\n" );
+
+		for ( var i = 0, il = chunks.length; i < il; i ++ ) {
+
+			// Chrome reports shader errors on lines
+			// starting counting from 1
+
+			chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ];
+
+		}
+
+		return chunks.join( "\n" );
+
+	};
+
+	function getShader ( type, string ) {
+
+		var shader;
+
+		if ( type === "fragment" ) {
+
+			shader = _gl.createShader( _gl.FRAGMENT_SHADER );
+
+		} else if ( type === "vertex" ) {
+
+			shader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		}
+
+		_gl.shaderSource( shader, string );
+		_gl.compileShader( shader );
+
+		if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
+
+			console.error( _gl.getShaderInfoLog( shader ) );
+			console.error( addLineNumbers( string ) );
+			return null;
+
+		}
+
+		return shader;
+
+	};
+
+	// Textures
+
+
+	function isPowerOfTwo ( value ) {
+
+		return ( value & ( value - 1 ) ) === 0;
+
+	};
+
+	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
+
+		if ( isImagePowerOfTwo ) {
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
+
+		} else {
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
+
+		}
+
+		if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) {
+
+			if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
+
+				_gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) );
+				texture.__oldAnisotropy = texture.anisotropy;
+
+			}
+
+		}
+
+	};
+
+	this.setTexture = function ( texture, slot ) {
+
+		if ( texture.needsUpdate ) {
+
+			if ( ! texture.__webglInit ) {
+
+				texture.__webglInit = true;
+
+				texture.addEventListener( 'dispose', onTextureDispose );
+
+				texture.__webglTexture = _gl.createTexture();
+
+				_this.info.memory.textures ++;
+
+			}
+
+			_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 );
+
+			var image = texture.image,
+			isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+			glFormat = paramThreeToGL( texture.format ),
+			glType = paramThreeToGL( texture.type );
+
+			setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
+
+			var mipmap, mipmaps = texture.mipmaps;
+
+			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
+
+				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+					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 );
+
+					}
+
+					texture.generateMipmaps = false;
+
+				} else {
+
+					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
+
+				}
+
+			} else if ( texture instanceof THREE.CompressedTexture ) {
+
+				// compressed textures can only use manually created mipmaps
+				// WebGL can't generate mipmaps for DDS textures
+
+				for( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+					mipmap = mipmaps[ i ];
+					_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, 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
+
+				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+					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 {
+
+					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
+
+				}
+
+			}
+
+			if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+			texture.needsUpdate = false;
+
+			if ( texture.onUpdate ) texture.onUpdate();
+
+		} else {
+
+			_gl.activeTexture( _gl.TEXTURE0 + slot );
+			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
+
+		}
+
+	};
+
+	function clampToMaxSize ( image, 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 maxDimension = Math.max( image.width, image.height );
+		var newWidth = Math.floor( image.width * maxSize / maxDimension );
+		var newHeight = Math.floor( image.height * maxSize / maxDimension );
+
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = newWidth;
+		canvas.height = newHeight;
+
+		var ctx = canvas.getContext( "2d" );
+		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight );
+
+		return canvas;
+
+	}
+
+	function setCubeTexture ( texture, slot ) {
+
+		if ( texture.image.length === 6 ) {
+
+			if ( texture.needsUpdate ) {
+
+				if ( ! texture.image.__webglTextureCube ) {
+
+					texture.image.__webglTextureCube = _gl.createTexture();
+
+					_this.info.memory.textures ++;
+
+				}
+
+				_gl.activeTexture( _gl.TEXTURE0 + slot );
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
+
+				_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+
+				var isCompressed = texture instanceof THREE.CompressedTexture;
+
+				var cubeImage = [];
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					if ( _this.autoScaleCubemaps && ! isCompressed ) {
+
+						cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
+
+					} else {
+
+						cubeImage[ i ] = texture.image[ i ];
+
+					}
+
+				}
+
+				var image = cubeImage[ 0 ],
+				isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+				glFormat = paramThreeToGL( texture.format ),
+				glType = paramThreeToGL( texture.type );
+
+				setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					if ( isCompressed ) {
+
+						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
+
+						for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+
+							mipmap = mipmaps[ j ];
+							_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+						}
+
+					} else {
+
+						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
+
+					}
+
+				}
+
+				if ( texture.generateMipmaps && isImagePowerOfTwo ) {
+
+					_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+				}
+
+				texture.needsUpdate = false;
+
+				if ( texture.onUpdate ) texture.onUpdate();
+
+			} else {
+
+				_gl.activeTexture( _gl.TEXTURE0 + slot );
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
+
+			}
+
+		}
+
+	};
+
+	function setCubeTextureDynamic ( texture, slot ) {
+
+		_gl.activeTexture( _gl.TEXTURE0 + slot );
+		_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture );
+
+	};
+
+	// Render targets
+
+	function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
+
+		_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+		_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 );
+
+	};
+
+	function setupRenderBuffer ( renderbuffer, renderTarget  ) {
+
+		_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
+
+		if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
+			_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 ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
+			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+		*/
+		} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
+			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+		} else {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
+
+		}
+
+	};
+
+	this.setRenderTarget = function ( renderTarget ) {
+
+		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
+
+		if ( renderTarget && ! renderTarget.__webglFramebuffer ) {
+
+			if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
+			if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
+
+			renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
+
+			renderTarget.__webglTexture = _gl.createTexture();
+
+			_this.info.memory.textures ++;
+
+			// Setup texture, create render and frame buffers
+
+			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ),
+				glFormat = paramThreeToGL( renderTarget.format ),
+				glType = paramThreeToGL( renderTarget.type );
+
+			if ( isCube ) {
+
+				renderTarget.__webglFramebuffer = [];
+				renderTarget.__webglRenderbuffer = [];
+
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
+				setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo );
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer();
+					renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
+
+					_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+					setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
+					setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget );
+
+				}
+
+				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+			} else {
+
+				renderTarget.__webglFramebuffer = _gl.createFramebuffer();
+
+				if ( renderTarget.shareDepthFrom ) {
+
+					renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;
+
+				} else {
+
+					renderTarget.__webglRenderbuffer = _gl.createRenderbuffer();
+
+				}
+
+				_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
+				setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo );
+
+				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+				setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
+
+				if ( renderTarget.shareDepthFrom ) {
+
+					if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+						_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
+
+					} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+						_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
+
+					}
+
+				} else {
+
+					setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget );
+
+				}
+
+				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+			}
+
+			// Release everything
+
+			if ( isCube ) {
+
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+			} else {
+
+				_gl.bindTexture( _gl.TEXTURE_2D, null );
+
+			}
+
+			_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
+			_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
+
+		}
+
+		var framebuffer, width, height, vx, vy;
+
+		if ( renderTarget ) {
+
+			if ( isCube ) {
+
+				framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ];
+
+			} else {
+
+				framebuffer = renderTarget.__webglFramebuffer;
+
+			}
+
+			width = renderTarget.width;
+			height = renderTarget.height;
+
+			vx = 0;
+			vy = 0;
+
+		} else {
+
+			framebuffer = null;
+
+			width = _viewportWidth;
+			height = _viewportHeight;
+
+			vx = _viewportX;
+			vy = _viewportY;
+
+		}
+
+		if ( framebuffer !== _currentFramebuffer ) {
+
+			_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+			_gl.viewport( vx, vy, width, height );
+
+			_currentFramebuffer = framebuffer;
+
+		}
+
+		_currentWidth = width;
+		_currentHeight = height;
+
+	};
+
+	function updateRenderTargetMipmap ( renderTarget ) {
+
+		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
+			_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+		} else {
+
+			_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
+			_gl.generateMipmap( _gl.TEXTURE_2D );
+			_gl.bindTexture( _gl.TEXTURE_2D, null );
+
+		}
+
+	};
+
+	// Fallback filters for non-power-of-2 textures
+
+	function filterFallback ( f ) {
+
+		if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
+
+			return _gl.NEAREST;
+
+		}
+
+		return _gl.LINEAR;
+
+	};
+
+	// Map three.js constants to WebGL constants
+
+	function paramThreeToGL ( p ) {
+
+		if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
+		if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
+		if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
+
+		if ( p === THREE.NearestFilter ) return _gl.NEAREST;
+		if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
+		if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
+
+		if ( p === THREE.LinearFilter ) return _gl.LINEAR;
+		if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
+		if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
+
+		if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
+		if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
+		if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
+		if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
+
+		if ( p === THREE.ByteType ) return _gl.BYTE;
+		if ( p === THREE.ShortType ) return _gl.SHORT;
+		if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
+		if ( p === THREE.IntType ) return _gl.INT;
+		if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
+		if ( p === THREE.FloatType ) return _gl.FLOAT;
+
+		if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
+		if ( p === THREE.RGBFormat ) return _gl.RGB;
+		if ( p === THREE.RGBAFormat ) return _gl.RGBA;
+		if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
+		if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
+
+		if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
+		if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
+		if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
+
+		if ( p === THREE.ZeroFactor ) return _gl.ZERO;
+		if ( p === THREE.OneFactor ) return _gl.ONE;
+		if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
+		if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
+		if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
+		if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
+		if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
+		if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
+
+		if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
+		if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
+		if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
+
+		if ( _glExtensionCompressedTextureS3TC !== undefined ) {
+
+			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;
+
+		}
+
+		return 0;
+
+	};
+
+	// Allocations
+
+	function allocateBones ( object ) {
+
+		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
+
+			return 1024;
+
+		} else {
+
+			// default for when object is not specified
+			// ( for example when prebuilding shader
+			//   to be used with multiple objects )
+			//
+			// 	- leave some extra space for other uniforms
+			//  - limit here is ANGLE's 254 max uniform vectors
+			//    (up to 54 should be safe)
+
+			var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS );
+			var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
+
+			var maxBones = nVertexMatrices;
+
+			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
+
+				maxBones = Math.min( object.bones.length, maxBones );
+
+				if ( maxBones < object.bones.length ) {
+
+					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
+
+				}
+
+			}
+
+			return maxBones;
+
+		}
+
+	};
+
+	function allocateLights ( lights ) {
+
+		var l, ll, light, dirLights, pointLights, spotLights, hemiLights;
+
+		dirLights = pointLights = spotLights = hemiLights = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+			light = lights[ l ];
+
+			if ( light.onlyShadow ) continue;
+
+			if ( light instanceof THREE.DirectionalLight ) dirLights ++;
+			if ( light instanceof THREE.PointLight ) pointLights ++;
+			if ( light instanceof THREE.SpotLight ) spotLights ++;
+			if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
+
+		}
+
+		return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights };
+
+	};
+
+	function allocateShadows ( lights ) {
+
+		var l, ll, light, maxShadows = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l++ ) {
+
+			light = lights[ l ];
+
+			if ( ! light.castShadow ) continue;
+
+			if ( light instanceof THREE.SpotLight ) maxShadows ++;
+			if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++;
+
+		}
+
+		return maxShadows;
+
+	};
+
+	// Initialization
+
+	function initGL () {
+
+		try {
+
+			if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { alpha: _alpha, premultipliedAlpha: _premultipliedAlpha, antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) {
+
+				throw 'Error creating WebGL context.';
+
+			}
+
+		} catch ( error ) {
+
+			console.error( error );
+
+		}
+
+		_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
+		_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() { 
+				
+				return {
+					"rangeMin"  : 1,
+					"rangeMax"  : 1,
+					"precision" : 1
+				};
+				
+			}
+		}
+
+	};
+
+	function setDefaultGLState () {
+
+		_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.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	// default plugins (order is important)
+
+	this.shadowMapPlugin = new THREE.ShadowMapPlugin();
+	this.addPrePlugin( this.shadowMapPlugin );
+
+	this.addPostPlugin( new THREE.SpritePlugin() );
+	this.addPostPlugin( new THREE.LensFlarePlugin() );
+
+};
+/**
+ * @author szimek / https://github.com/szimek/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.WebGLRenderTarget = function ( width, height, options ) {
+
+	THREE.EventDispatcher.call( this );
+
+	this.width = width;
+	this.height = height;
+
+	options = options || {};
+
+	this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping;
+	this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping;
+
+	this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter;
+	this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter;
+
+	this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1;
+
+	this.offset = new THREE.Vector2( 0, 0 );
+	this.repeat = new THREE.Vector2( 1, 1 );
+
+	this.format = options.format !== undefined ? options.format : THREE.RGBAFormat;
+	this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType;
+
+	this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+	this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+
+	this.generateMipmaps = true;
+
+	this.shareDepthFrom = null;
+
+};
+
+THREE.WebGLRenderTarget.prototype.clone = function() {
+
+	var tmp = new THREE.WebGLRenderTarget( this.width, this.height );
+
+	tmp.wrapS = this.wrapS;
+	tmp.wrapT = this.wrapT;
+
+	tmp.magFilter = this.magFilter;
+	tmp.minFilter = this.minFilter;
+
+	tmp.anisotropy = this.anisotropy;
+
+	tmp.offset.copy( this.offset );
+	tmp.repeat.copy( this.repeat );
+
+	tmp.format = this.format;
+	tmp.type = this.type;
+
+	tmp.depthBuffer = this.depthBuffer;
+	tmp.stencilBuffer = this.stencilBuffer;
+
+	tmp.generateMipmaps = this.generateMipmaps;
+
+	tmp.shareDepthFrom = this.shareDepthFrom;
+
+	return tmp;
+
+};
+
+THREE.WebGLRenderTarget.prototype.dispose = function () {
+
+	this.dispatchEvent( { type: 'dispose' } );
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com
+ */
+
+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 );
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableFace3 = function () {
+
+	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.color = null;
+	this.material = null;
+	this.uvs = [[]];
+
+	this.z = null;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableFace4 = function () {
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+	this.v3 = new THREE.RenderableVertex();
+	this.v4 = 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(), new THREE.Vector3() ];
+	this.vertexNormalsModelView = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+
+	this.color = null;
+	this.material = null;
+	this.uvs = [[]];
+
+	this.z = null;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableObject = function () {
+
+	this.object = null;
+	this.z = null;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableParticle = function () {
+
+	this.object = null;
+
+	this.x = null;
+	this.y = null;
+	this.z = null;
+
+	this.rotation = null;
+	this.scale = new THREE.Vector2();
+
+	this.material = null;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableLine = function () {
+
+	this.z = null;
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+
+	this.material = null;
+
+};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.GeometryUtils = {
+
+	// Merge two geometries or geometry and geometry from object (using object's transform)
+
+	merge: function ( geometry1, object2 /* mesh | geometry */ ) {
+
+		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 ];
+
+		if ( object2 instanceof THREE.Mesh ) {
+
+			object2.matrixAutoUpdate && object2.updateMatrix();
+
+			matrix = object2.matrix;
+
+			normalMatrix = new THREE.Matrix3();
+			normalMatrix.getInverse( matrix );
+			normalMatrix.transpose();
+
+		}
+
+		// vertices
+
+		for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
+
+			var vertex = vertices2[ i ];
+
+			var vertexCopy = vertex.clone();
+
+			if ( matrix ) vertexCopy.applyMatrix4( matrix );
+
+			vertices1.push( vertexCopy );
+
+		}
+
+		// faces
+
+		for ( i = 0, il = faces2.length; i < il; i ++ ) {
+
+			var face = faces2[ i ], faceCopy, normal, color,
+			faceVertexNormals = face.vertexNormals,
+			faceVertexColors = face.vertexColors;
+
+			if ( face instanceof THREE.Face3 ) {
+
+				faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset );
+
+			}
+
+			faceCopy.normal.copy( face.normal );
+
+			if ( normalMatrix ) {
+
+				faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
+
+			}
+
+			for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
+
+				normal = faceVertexNormals[ j ].clone();
+
+				if ( normalMatrix ) {
+
+					normal.applyMatrix3( normalMatrix ).normalize();
+
+				}
+
+				faceCopy.vertexNormals.push( normal );
+
+			}
+
+			faceCopy.color.copy( face.color );
+
+			for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
+
+				color = faceVertexColors[ j ];
+				faceCopy.vertexColors.push( color.clone() );
+
+			}
+
+			faceCopy.materialIndex = face.materialIndex;
+
+			faceCopy.centroid.copy( face.centroid );
+
+			if ( matrix ) {
+
+				faceCopy.centroid.applyMatrix4( matrix );
+
+			}
+
+			faces1.push( faceCopy );
+
+		}
+
+		// uvs
+
+		for ( i = 0, il = uvs2.length; i < il; i ++ ) {
+
+			var uv = uvs2[ i ], uvCopy = [];
+
+			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+
+			}
+
+			uvs1.push( uvCopy );
+
+		}
+
+	},
+
+	removeMaterials: function ( geometry, materialIndexArray ) {
+
+		var materialIndexMap = {};
+
+		for ( var i = 0, il = materialIndexArray.length; i < il; i ++ ) {
+
+			materialIndexMap[ materialIndexArray[i] ] = true;
+
+		}
+
+		var face, newFaces = [];
+
+		for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
+
+			face = geometry.faces[ i ];
+			if ( ! ( face.materialIndex in materialIndexMap ) ) newFaces.push( face );
+
+		}
+
+		geometry.faces = newFaces;
+
+	},
+
+	// Get random point in triangle (via barycentric coordinates)
+	// 	(uniform distribution)
+	// 	http://www.cgafaq.info/wiki/Random_Point_In_Triangle
+
+	randomPointInTriangle: function ( vectorA, vectorB, vectorC ) {
+
+		var a, b, c,
+			point = new THREE.Vector3(),
+			tmp = THREE.GeometryUtils.__v1;
+
+		a = THREE.GeometryUtils.random();
+		b = THREE.GeometryUtils.random();
+
+		if ( ( a + b ) > 1 ) {
+
+			a = 1 - a;
+			b = 1 - b;
+
+		}
+
+		c = 1 - a - b;
+
+		point.copy( vectorA );
+		point.multiplyScalar( a );
+
+		tmp.copy( vectorB );
+		tmp.multiplyScalar( b );
+
+		point.add( tmp );
+
+		tmp.copy( vectorC );
+		tmp.multiplyScalar( c );
+
+		point.add( tmp );
+
+		return point;
+
+	},
+
+	// Get random point in face (triangle / quad)
+	// (uniform distribution)
+
+	randomPointInFace: function ( face, geometry, useCachedAreas ) {
+
+		var vA, vB, vC, vD;
+
+		if ( face instanceof THREE.Face3 ) {
+
+			vA = geometry.vertices[ face.a ];
+			vB = geometry.vertices[ face.b ];
+			vC = geometry.vertices[ face.c ];
+
+			return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
+
+		} else if ( face instanceof THREE.Face4 ) {
+
+			vA = geometry.vertices[ face.a ];
+			vB = geometry.vertices[ face.b ];
+			vC = geometry.vertices[ face.c ];
+			vD = geometry.vertices[ face.d ];
+
+			var area1, area2;
+
+			if ( useCachedAreas ) {
+
+				if ( face._area1 && face._area2 ) {
+
+					area1 = face._area1;
+					area2 = face._area2;
+
+				} else {
+
+					area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD );
+					area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
+
+					face._area1 = area1;
+					face._area2 = area2;
+
+				}
+
+			} else {
+
+				area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ),
+				area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
+
+			}
+
+			var r = THREE.GeometryUtils.random() * ( area1 + area2 );
+
+			if ( r < area1 ) {
+
+				return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vD );
+
+			} else {
+
+				return THREE.GeometryUtils.randomPointInTriangle( vB, vC, vD );
+
+			}
+
+		}
+
+	},
+
+	// 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
+
+	randomPointsInGeometry: function ( geometry, n ) {
+
+		var face, i,
+			faces = geometry.faces,
+			vertices = geometry.vertices,
+			il = faces.length,
+			totalArea = 0,
+			cumulativeAreas = [],
+			vA, vB, vC, vD;
+
+		// precompute face areas
+
+		for ( i = 0; i < il; i ++ ) {
+
+			face = faces[ i ];
+
+			if ( face instanceof THREE.Face3 ) {
+
+				vA = vertices[ face.a ];
+				vB = vertices[ face.b ];
+				vC = vertices[ face.c ];
+
+				face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
+
+			} else if ( face instanceof THREE.Face4 ) {
+
+				vA = vertices[ face.a ];
+				vB = vertices[ face.b ];
+				vC = vertices[ face.c ];
+				vD = vertices[ face.d ];
+
+				face._area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD );
+				face._area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
+
+				face._area = face._area1 + face._area2;
+
+			}
+
+			totalArea += face._area;
+
+			cumulativeAreas[ i ] = totalArea;
+
+		}
+
+		// binary search cumulative areas array
+
+		function binarySearchIndices( value ) {
+
+			function binarySearch( start, end ) {
+
+				// return closest larger index
+				// if exact number is not found
+
+				if ( end < start )
+					return start;
+
+				var mid = start + Math.floor( ( end - start ) / 2 );
+
+				if ( cumulativeAreas[ mid ] > value ) {
+
+					return binarySearch( start, mid - 1 );
+
+				} else if ( cumulativeAreas[ mid ] < value ) {
+
+					return binarySearch( mid + 1, end );
+
+				} else {
+
+					return mid;
+
+				}
+
+			}
+
+			var result = binarySearch( 0, cumulativeAreas.length - 1 )
+			return result;
+
+		}
+
+		// pick random face weighted by face area
+
+		var r, index,
+			result = [];
+
+		var stats = {};
+
+		for ( i = 0; i < n; i ++ ) {
+
+			r = THREE.GeometryUtils.random() * totalArea;
+
+			index = binarySearchIndices( r );
+
+			result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
+
+			if ( ! stats[ index ] ) {
+
+				stats[ index ] = 1;
+
+			} else {
+
+				stats[ index ] += 1;
+
+			}
+
+		}
+
+		return result;
+
+	},
+
+	// Get triangle area (half of parallelogram)
+	//	http://mathworld.wolfram.com/TriangleArea.html
+
+	triangleArea: function ( vectorA, vectorB, vectorC ) {
+
+		var tmp1 = THREE.GeometryUtils.__v1,
+			tmp2 = THREE.GeometryUtils.__v2;
+
+		tmp1.subVectors( vectorB, vectorA );
+		tmp2.subVectors( vectorC, vectorA );
+		tmp1.cross( tmp2 );
+
+		return 0.5 * tmp1.length();
+
+	},
+
+	// Center geometry so that 0,0,0 is in center of bounding box
+
+	center: function ( geometry ) {
+
+		geometry.computeBoundingBox();
+
+		var bb = geometry.boundingBox;
+
+		var offset = new THREE.Vector3();
+
+		offset.addVectors( bb.min, bb.max );
+		offset.multiplyScalar( -0.5 );
+
+		geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) );
+		geometry.computeBoundingBox();
+
+		return offset;
+
+	},
+
+	// Normalize UVs to be from <0,1>
+	// (for now just the first set of UVs)
+
+	normalizeUVs: function ( geometry ) {
+
+		var uvSet = geometry.faceVertexUvs[ 0 ];
+
+		for ( var i = 0, il = uvSet.length; i < il; i ++ ) {
+
+			var uvs = uvSet[ i ];
+
+			for ( var j = 0, jl = uvs.length; j < jl; j ++ ) {
+
+				// texture repeat
+
+				if( uvs[ j ].x !== 1.0 ) uvs[ j ].x = uvs[ j ].x - Math.floor( uvs[ j ].x );
+				if( uvs[ j ].y !== 1.0 ) uvs[ j ].y = uvs[ j ].y - Math.floor( uvs[ j ].y );
+
+			}
+
+		}
+
+	},
+
+	triangulateQuads: function ( geometry ) {
+
+		var i, il, j, jl;
+
+		var faces = [];
+		var faceUvs = [];
+		var faceVertexUvs = [];
+
+		for ( i = 0, il = geometry.faceUvs.length; i < il; i ++ ) {
+
+			faceUvs[ i ] = [];
+
+		}
+
+		for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
+
+			faceVertexUvs[ i ] = [];
+
+		}
+
+		for ( i = 0, il = geometry.faces.length; i < il; i ++ ) {
+
+			var face = geometry.faces[ i ];
+
+			if ( face instanceof THREE.Face4 ) {
+
+				var a = face.a;
+				var b = face.b;
+				var c = face.c;
+				var d = face.d;
+
+				var triA = new THREE.Face3();
+				var triB = new THREE.Face3();
+
+				triA.color.copy( face.color );
+				triB.color.copy( face.color );
+
+				triA.materialIndex = face.materialIndex;
+				triB.materialIndex = face.materialIndex;
+
+				triA.a = a;
+				triA.b = b;
+				triA.c = d;
+
+				triB.a = b;
+				triB.b = c;
+				triB.c = d;
+
+				if ( face.vertexColors.length === 4 ) {
+
+					triA.vertexColors[ 0 ] = face.vertexColors[ 0 ].clone();
+					triA.vertexColors[ 1 ] = face.vertexColors[ 1 ].clone();
+					triA.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone();
+
+					triB.vertexColors[ 0 ] = face.vertexColors[ 1 ].clone();
+					triB.vertexColors[ 1 ] = face.vertexColors[ 2 ].clone();
+					triB.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone();
+
+				}
+
+				faces.push( triA, triB );
+
+				for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+
+					if ( geometry.faceVertexUvs[ j ].length ) {
+
+						var uvs = geometry.faceVertexUvs[ j ][ i ];
+
+						var uvA = uvs[ 0 ];
+						var uvB = uvs[ 1 ];
+						var uvC = uvs[ 2 ];
+						var uvD = uvs[ 3 ];
+
+						var uvsTriA = [ uvA.clone(), uvB.clone(), uvD.clone() ];
+						var uvsTriB = [ uvB.clone(), uvC.clone(), uvD.clone() ];
+
+						faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
+
+					}
+
+				}
+
+				for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) {
+
+					if ( geometry.faceUvs[ j ].length ) {
+
+						var faceUv = geometry.faceUvs[ j ][ i ];
+
+						faceUvs[ j ].push( faceUv, faceUv );
+
+					}
+
+				}
+
+			} else {
+
+				faces.push( face );
+
+				for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) {
+
+					faceUvs[ j ].push( geometry.faceUvs[ j ][ i ] );
+
+				}
+
+				for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+
+					faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
+
+				}
+
+			}
+
+		}
+
+		geometry.faces = faces;
+		geometry.faceUvs = faceUvs;
+		geometry.faceVertexUvs = faceVertexUvs;
+
+		geometry.computeCentroids();
+		geometry.computeFaceNormals();
+		geometry.computeVertexNormals();
+
+		if ( geometry.hasTangents ) geometry.computeTangents();
+
+	},
+
+	setMaterialIndex: function ( geometry, index, startFace, endFace ){
+
+		var faces = geometry.faces;
+		var start = startFace || 0;
+		var end = endFace || faces.length - 1;
+
+		for ( var i = start; i <= end; i ++ ) {
+
+			faces[i].materialIndex = index;
+
+		}
+
+    }
+
+};
+
+THREE.GeometryUtils.random = THREE.Math.random16;
+
+THREE.GeometryUtils.__v1 = new THREE.Vector3();
+THREE.GeometryUtils.__v2 = new THREE.Vector3();
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageUtils = {
+
+	crossOrigin: 'anonymous',
+
+	loadTexture: function ( url, mapping, onLoad, onError ) {
+
+		var image = new Image();
+		var texture = new THREE.Texture( image, mapping );
+
+		var loader = new THREE.ImageLoader();
+
+		loader.addEventListener( 'load', function ( event ) {
+
+			texture.image = event.content;
+			texture.needsUpdate = true;
+
+			if ( onLoad ) onLoad( texture );
+
+		} );
+
+		loader.addEventListener( 'error', function ( event ) {
+
+			if ( onError ) onError( event.message );
+
+		} );
+
+		loader.crossOrigin = this.crossOrigin;
+		loader.load( url, image );
+
+		texture.sourceFile = url;
+
+		return texture;
+
+	},
+
+	loadCompressedTexture: function ( url, mapping, onLoad, onError ) {
+
+		var texture = new THREE.CompressedTexture();
+		texture.mapping = mapping;
+
+		var request = new XMLHttpRequest();
+
+		request.onload = function () {
+
+			var buffer = request.response;
+			var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+			texture.format = dds.format;
+
+			texture.mipmaps = dds.mipmaps;
+			texture.image.width = dds.width;
+			texture.image.height = dds.height;
+
+			// gl.generateMipmap fails for compressed textures
+			// mipmaps must be embedded in the DDS file
+			// or texture filters must not use mipmapping
+
+			texture.generateMipmaps = false;
+
+			texture.needsUpdate = true;
+
+			if ( onLoad ) onLoad( texture );
+
+		}
+
+		request.onerror = onError;
+
+		request.open( 'GET', url, true );
+		request.responseType = "arraybuffer";
+		request.send( null );
+
+		return texture;
+
+	},
+
+	loadTextureCube: function ( array, mapping, onLoad, onError ) {
+
+		var images = [];
+		images.loadCount = 0;
+
+		var texture = new THREE.Texture();
+		texture.image = images;
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		// no flipping needed for cube textures
+
+		texture.flipY = false;
+
+		for ( var i = 0, il = array.length; i < il; ++ i ) {
+
+			var cubeImage = new Image();
+			images[ i ] = cubeImage;
+
+			cubeImage.onload = function () {
+
+				images.loadCount += 1;
+
+				if ( images.loadCount === 6 ) {
+
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			};
+
+			cubeImage.onerror = onError;
+
+			cubeImage.crossOrigin = this.crossOrigin;
+			cubeImage.src = array[ i ];
+
+		}
+
+		return texture;
+
+	},
+
+	loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) {
+
+		var images = [];
+		images.loadCount = 0;
+
+		var texture = new THREE.CompressedTexture();
+		texture.image = images;
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		// no flipping for cube textures
+		// (also flipping doesn't work for compressed textures )
+
+		texture.flipY = false;
+
+		// can't generate mipmaps for compressed textures
+		// mips must be embedded in DDS files
+
+		texture.generateMipmaps = false;
+
+		var generateCubeFaceCallback = function ( rq, img ) {
+
+			return function () {
+
+				var buffer = rq.response;
+				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+				img.format = dds.format;
+
+				img.mipmaps = dds.mipmaps;
+				img.width = dds.width;
+				img.height = dds.height;
+
+				images.loadCount += 1;
+
+				if ( images.loadCount === 6 ) {
+
+					texture.format = dds.format;
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			}
+
+		}
+
+		// compressed cubemap textures as 6 separate DDS files
+
+		if ( array instanceof Array ) {
+
+			for ( var i = 0, il = array.length; i < il; ++ i ) {
+
+				var cubeImage = {};
+				images[ i ] = cubeImage;
+
+				var request = new XMLHttpRequest();
+
+				request.onload = generateCubeFaceCallback( request, cubeImage );
+				request.onerror = onError;
+
+				var url = array[ i ];
+
+				request.open( 'GET', url, true );
+				request.responseType = "arraybuffer";
+				request.send( null );
+
+			}
+
+		// compressed cubemap texture stored in a single DDS file
+
+		} else {
+
+			var url = array;
+			var request = new XMLHttpRequest();
+
+			request.onload = function( ) {
+
+				var buffer = request.response;
+				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+				if ( dds.isCubemap ) {
+
+					var faces = dds.mipmaps.length / dds.mipmapCount;
+
+					for ( var f = 0; f < faces; f ++ ) {
+
+						images[ f ] = { mipmaps : [] };
+
+						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;
+
+						}
+
+					}
+
+					texture.format = dds.format;
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			}
+
+			request.onerror = onError;
+
+			request.open( 'GET', url, true );
+			request.responseType = "arraybuffer";
+			request.send( null );
+
+		}
+
+		return texture;
+
+	},
+
+	parseDDS: function ( buffer, loadMipmaps ) {
+
+		var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
+
+		// Adapted from @toji's DDS utils
+		//	https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
+
+		// All values and structures referenced from:
+		// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
+
+		var DDS_MAGIC = 0x20534444;
+
+		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;
+
+		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 ) {
+
+			return value.charCodeAt(0) +
+				(value.charCodeAt(1) << 8) +
+				(value.charCodeAt(2) << 16) +
+				(value.charCodeAt(3) << 24);
+
+		}
+
+		function int32ToFourCC( value ) {
+
+			return String.fromCharCode(
+				value & 0xff,
+				(value >> 8) & 0xff,
+				(value >> 16) & 0xff,
+				(value >> 24) & 0xff
+			);
+		}
+
+		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
+
+		// Offsets into the header array
+
+		var off_magic = 0;
+
+		var off_size = 1;
+		var off_flags = 2;
+		var off_height = 3;
+		var off_width = 4;
+
+		var off_mipmapCount = 7;
+
+		var off_pfFlags = 20;
+		var off_pfFourCC = 21;
+
+		var off_caps = 27;
+		var off_caps2 = 28;
+		var off_caps3 = 29;
+		var off_caps4 = 30;
+
+		// Parse header
+
+		var header = new Int32Array( buffer, 0, headerLengthInt );
+
+		if ( header[ off_magic ] !== DDS_MAGIC ) {
+
+			console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" );
+			return dds;
+
+		}
+
+		if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
+
+			console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" );
+			return dds;
+
+		}
+
+		var blockBytes;
+
+		var fourCC = header[ off_pfFourCC ];
+
+		switch ( fourCC ) {
+
+			case FOURCC_DXT1:
+
+				blockBytes = 8;
+				dds.format = THREE.RGB_S3TC_DXT1_Format;
+				break;
+
+			case FOURCC_DXT3:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT3_Format;
+				break;
+
+			case FOURCC_DXT5:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT5_Format;
+				break;
+
+			default:
+
+				console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) );
+				return dds;
+
+		}
+
+		dds.mipmapCount = 1;
+
+		if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
+
+			dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
+
+		}
+
+		//TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
+
+		dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
+
+		dds.width = header[ off_width ];
+		dds.height = header[ off_height ];
+
+		var dataOffset = header[ off_size ] + 4;
+
+		// Extract mipmaps buffers
+
+		var width = dds.width;
+		var height = dds.height;
+
+		var faces = dds.isCubemap ? 6 : 1;
+
+		for ( var face = 0; face < faces; face ++ ) {
+
+			for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+				var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
+				var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
+
+				var mipmap = { "data": byteArray, "width": width, "height": height };
+				dds.mipmaps.push( mipmap );
+
+				dataOffset += dataLength;
+
+				width = Math.max( width * 0.5, 1 );
+				height = Math.max( height * 0.5, 1 );
+
+			}
+
+			width = dds.width;
+			height = dds.height;
+
+		}
+
+		return dds;
+
+	},
+
+	getNormalMap: function ( image, depth ) {
+
+		// Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
+
+		var cross = function ( a, b ) {
+
+			return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
+
+		}
+
+		var subtract = function ( a, b ) {
+
+			return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
+
+		}
+
+		var normalize = function ( a ) {
+
+			var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
+			return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
+
+		}
+
+		depth = depth | 1;
+
+		var width = image.width;
+		var height = image.height;
+
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = width;
+		canvas.height = height;
+
+		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;
+
+		for ( var x = 0; x < width; x ++ ) {
+
+			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;
+
+				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 ] );
+
+				var normals = [];
+				var num_points = points.length;
+
+				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 ) ) );
+
+				}
+
+				var normal = [ 0, 0, 0 ];
+
+				for ( var i = 0; i < normals.length; i ++ ) {
+
+					normal[ 0 ] += normals[ i ][ 0 ];
+					normal[ 1 ] += normals[ i ][ 1 ];
+					normal[ 2 ] += normals[ i ][ 2 ];
+
+				}
+
+				normal[ 0 ] /= normals.length;
+				normal[ 1 ] /= normals.length;
+				normal[ 2 ] /= normals.length;
+
+				var idx = ( y * width + x ) * 4;
+
+				output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
+				output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
+				output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
+				output[ idx + 3 ] = 255;
+
+			}
+
+		}
+
+		context.putImageData( imageData, 0, 0 );
+
+		return canvas;
+
+	},
+
+	generateDataTexture: function ( width, height, color ) {
+
+		var size = width * height;
+		var data = new Uint8Array( 3 * size );
+
+		var r = Math.floor( color.r * 255 );
+		var g = Math.floor( color.g * 255 );
+		var b = Math.floor( color.b * 255 );
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			data[ i * 3 ] 	  = r;
+			data[ i * 3 + 1 ] = g;
+			data[ i * 3 + 2 ] = b;
+
+		}
+
+		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
+		texture.needsUpdate = true;
+
+		return texture;
+
+	}
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneUtils = {
+
+	createMultiMaterialObject: function ( geometry, materials ) {
+
+		var group = new THREE.Object3D();
+
+		for ( var i = 0, l = materials.length; i < l; i ++ ) {
+
+			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+
+		}
+
+		return group;
+
+	},
+
+	detach : function ( child, parent, scene ) {
+
+		child.applyMatrix( parent.matrixWorld );
+		parent.remove( child );
+		scene.add( child );
+
+	},
+
+	attach: function ( child, scene, parent ) {
+
+		var matrixWorldInverse = new THREE.Matrix4();
+		matrixWorldInverse.getInverse( parent.matrixWorld );
+		child.applyMatrix( matrixWorldInverse );
+
+		scene.remove( child );
+		parent.add( child );
+
+	}
+
+};
+/**
+ * @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/
+ *
+ */
+
+THREE.FontUtils = {
+
+	faces : {},
+
+	// Just for now. face[weight][style]
+
+	face : "helvetiker",
+	weight: "normal",
+	style : "normal",
+	size : 150,
+	divisions : 10,
+
+	getFace : function() {
+
+		return this.faces[ this.face ][ this.weight ][ this.style ];
+
+	},
+
+	loadFace : function( data ) {
+
+		var family = data.familyName.toLowerCase();
+
+		var ThreeFont = this;
+
+		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
+
+		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
+		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+
+		var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+
+		return data;
+
+	},
+
+	drawText : function( text ) {
+
+		var characterPts = [], allPts = [];
+
+		// RenderText
+
+		var i, p,
+			face = this.getFace(),
+			scale = this.size / face.resolution,
+			offset = 0,
+			chars = String( text ).split( '' ),
+			length = chars.length;
+
+		var fontPaths = [];
+
+		for ( i = 0; i < length; i ++ ) {
+
+			var path = new THREE.Path();
+
+			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
+			offset += ret.offset;
+
+			fontPaths.push( ret.path );
+
+		}
+
+		// get the width
+
+		var width = offset / 2;
+		//
+		// for ( p = 0; p < allPts.length; p++ ) {
+		//
+		// 	allPts[ p ].x -= width;
+		//
+		// }
+
+		//var extract = this.extractPoints( allPts, characterPts );
+		//extract.contour = allPts;
+
+		//extract.paths = fontPaths;
+		//extract.offset = width;
+
+		return { paths : fontPaths, offset : width };
+
+	},
+
+
+
+
+	extractGlyphPoints : function( c, face, scale, offset, path ) {
+
+		var pts = [];
+
+		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;
+
+		if ( glyph.o ) {
+
+			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+			length = outline.length;
+
+			scaleX = scale;
+			scaleY = scale;
+
+			for ( i = 0; i < length; ) {
+
+				action = outline[ i ++ ];
+
+				//console.log( action );
+
+				switch( action ) {
+
+				case 'm':
+
+					// Move To
+
+					x = outline[ i++ ] * scaleX + offset;
+					y = outline[ i++ ] * scaleY;
+
+					path.moveTo( x, y );
+					break;
+
+				case 'l':
+
+					// Line To
+
+					x = outline[ i++ ] * scaleX + offset;
+					y = outline[ i++ ] * scaleY;
+					path.lineTo(x,y);
+					break;
+
+				case 'q':
+
+					// QuadraticCurveTo
+
+					cpx  = outline[ i++ ] * scaleX + offset;
+					cpy  = outline[ i++ ] * scaleY;
+					cpx1 = outline[ i++ ] * scaleX + offset;
+					cpy1 = outline[ i++ ] * scaleY;
+
+					path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
+
+					laste = pts[ pts.length - 1 ];
+
+					if ( laste ) {
+
+						cpx0 = laste.x;
+						cpy0 = laste.y;
+
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+
+							var t = i2 / divisions;
+							var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+							var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+					  }
+
+				  }
+
+				  break;
+
+				case 'b':
+
+					// Cubic Bezier Curve
+
+					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;
+
+					path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
+
+					laste = pts[ pts.length - 1 ];
+
+					if ( laste ) {
+
+						cpx0 = laste.x;
+						cpy0 = laste.y;
+
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+
+							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 );
+
+						}
+
+					}
+
+					break;
+
+				}
+
+			}
+		}
+
+
+
+		return { offset: glyph.ha*scale, path:path};
+	}
+
+};
+
+
+THREE.FontUtils.generateShapes = function( text, parameters ) {
+
+	// Parameters 
+
+	parameters = parameters || {};
+
+	var size = parameters.size !== undefined ? parameters.size : 100;
+	var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4;
+
+	var font = parameters.font !== undefined ? parameters.font : "helvetiker";
+	var weight = parameters.weight !== undefined ? parameters.weight : "normal";
+	var style = parameters.style !== undefined ? parameters.style : "normal";
+
+	THREE.FontUtils.size = size;
+	THREE.FontUtils.divisions = curveSegments;
+
+	THREE.FontUtils.face = font;
+	THREE.FontUtils.weight = weight;
+	THREE.FontUtils.style = style;
+
+	// Get a Font data json object
+
+	var data = THREE.FontUtils.drawText( text );
+
+	var paths = data.paths;
+	var shapes = [];
+
+	for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+
+		Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+
+	}
+
+	return shapes;
+
+};
+
+
+/**
+ * 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
+ *
+ */
+
+
+( function( namespace ) {
+
+	var EPSILON = 0.0000000001;
+
+	// takes in an contour array and returns
+
+	var process = function( contour, indices ) {
+
+		var n = contour.length;
+
+		if ( n < 3 ) return null;
+
+		var result = [],
+			verts = [],
+			vertIndices = [];
+
+		/* we want a counter-clockwise polygon in verts */
+
+		var u, v, w;
+
+		if ( area( contour ) > 0.0 ) {
+
+			for ( v = 0; v < n; v++ ) verts[ v ] = v;
+
+		} else {
+
+			for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
+
+		}
+
+		var nv = n;
+
+		/*  remove nv - 2 vertices, creating 1 triangle every time */
+
+		var count = 2 * nv;   /* error detection */
+
+		for( v = nv - 1; nv > 2; ) {
+
+			/* if we loop, it is probably a non-simple polygon */
+
+			if ( ( count-- ) <= 0 ) {
+
+				//** Triangulate: ERROR - probable bad polygon!
+
+				//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!" );
+
+				if ( indices ) return vertIndices;
+				return result;
+
+			}
+
+			/* three consecutive vertices in current polygon, <u,v,w> */
+
+			u = v; 	 	if ( nv <= u ) u = 0;     /* previous */
+			v = u + 1;  if ( nv <= v ) v = 0;     /* new v    */
+			w = v + 1;  if ( nv <= w ) w = 0;     /* next     */
+
+			if ( snip( contour, u, v, w, nv, verts ) ) {
+
+				var a, b, c, s, t;
+
+				/* true names of the vertices */
+
+				a = verts[ u ];
+				b = verts[ v ];
+				c = verts[ w ];
+
+				/* output Triangle */
+
+				result.push( [ contour[ a ],
+					contour[ b ],
+					contour[ c ] ] );
+
+
+				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
+
+				/* remove v from the remaining polygon */
+
+				for( s = v, t = v + 1; t < nv; s++, t++ ) {
+
+					verts[ s ] = verts[ t ];
+
+				}
+
+				nv--;
+
+				/* reset error detection counter */
+
+				count = 2 * nv;
+
+			}
+
+		}
+
+		if ( indices ) return vertIndices;
+		return result;
+
+	};
+
+	// calculate area of the contour polygon
+
+	var area = function ( contour ) {
+
+		var n = contour.length;
+		var a = 0.0;
+
+		for( var p = n - 1, q = 0; q < n; p = q++ ) {
+
+			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
+
+		}
+
+		return a * 0.5;
+
+	};
+
+	var snip = function ( contour, u, v, w, n, verts ) {
+
+		var p;
+		var ax, ay, bx, by;
+		var cx, cy, px, py;
+
+		ax = contour[ verts[ u ] ].x;
+		ay = contour[ verts[ u ] ].y;
+
+		bx = contour[ verts[ v ] ].x;
+		by = contour[ verts[ v ] ].y;
+
+		cx = contour[ verts[ w ] ].x;
+		cy = contour[ verts[ w ] ].y;
+
+		if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
+
+		var aX, aY, bX, bY, cX, cY;
+		var apx, apy, bpx, bpy, cpx, cpy;
+		var cCROSSap, bCROSScp, aCROSSbp;
+
+		aX = cx - bx;  aY = cy - by;
+		bX = ax - cx;  bY = ay - cy;
+		cX = bx - ax;  cY = by - ay;
+
+		for ( p = 0; p < n; p++ ) {
+
+			if( (p === u) || (p === v) || (p === w) ) continue;
+
+			px = contour[ verts[ p ] ].x
+			py = contour[ verts[ p ] ].y
+
+			apx = px - ax;  apy = py - ay;
+			bpx = px - bx;  bpy = py - by;
+			cpx = px - cx;  cpy = py - cy;
+
+			// see if p is inside triangle abc
+
+			aCROSSbp = aX*bpy - aY*bpx;
+			cCROSSap = cX*apy - cY*apx;
+			bCROSScp = bX*cpy - bY*cpx;
+
+			if ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) ) return false;
+
+		}
+
+		return true;
+
+	};
+
+
+	namespace.Triangulate = process;
+	namespace.Triangulate.area = area;
+
+	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 };/**
+ * @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 file contains following classes:
+ *
+ * -- 2d classes --
+ * THREE.Curve
+ * 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 () {
+
+};
+
+// Virtual base class method to overwrite and implement in subclasses
+//	- t [0 .. 1]
+
+THREE.Curve.prototype.getPoint = function ( t ) {
+
+	console.log( "Warning, getPoint() not implemented!" );
+	return null;
+
+};
+
+// Get point at relative position in curve according to arc length
+// - u [0 .. 1]
+
+THREE.Curve.prototype.getPointAt = function ( u ) {
+
+	var t = this.getUtoTmapping( u );
+	return this.getPoint( t );
+
+};
+
+// Get sequence of points using getPoint( t )
+
+THREE.Curve.prototype.getPoints = function ( divisions ) {
+
+	if ( !divisions ) divisions = 5;
+
+	var d, pts = [];
+
+	for ( d = 0; d <= divisions; d ++ ) {
+
+		pts.push( this.getPoint( d / divisions ) );
+
+	}
+
+	return pts;
+
+};
+
+// Get sequence of points using getPointAt( u )
+
+THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
+
+	if ( !divisions ) divisions = 5;
+
+	var d, pts = [];
+
+	for ( d = 0; d <= divisions; d ++ ) {
+
+		pts.push( this.getPointAt( d / divisions ) );
+
+	}
+
+	return pts;
+
+};
+
+// Get total curve arc length
+
+THREE.Curve.prototype.getLength = function () {
+
+	var lengths = this.getLengths();
+	return lengths[ lengths.length - 1 ];
+
+};
+
+// Get list of cumulative segment lengths
+
+THREE.Curve.prototype.getLengths = function ( divisions ) {
+
+	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;
+
+	}
+
+	this.needsUpdate = false;
+
+	var cache = [];
+	var current, last = this.getPoint( 0 );
+	var p, sum = 0;
+
+	cache.push( 0 );
+
+	for ( p = 1; p <= divisions; p ++ ) {
+
+		current = this.getPoint ( p / divisions );
+		sum += current.distanceTo( last );
+		cache.push( sum );
+		last = current;
+
+	}
+
+	this.cacheArcLengths = cache;
+
+	return cache; // { sums: cache, sum:sum }; Sum is in the last element.
+
+};
+
+
+THREE.Curve.prototype.updateArcLengths = function() {
+	this.needsUpdate = true;
+	this.getLengths();
+};
+
+// 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;
+
+	var targetArcLength; // The targeted u distance value to get
+
+	if ( distance ) {
+
+		targetArcLength = distance;
+
+	} else {
+
+		targetArcLength = u * arcLengths[ il - 1 ];
+
+	}
+
+	//var time = Date.now();
+
+	// binary search for the index with largest value smaller than target u distance
+
+	var low = 0, high = il - 1, comparison;
+
+	while ( low <= high ) {
+
+		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
+
+		comparison = arcLengths[ i ] - targetArcLength;
+
+		if ( comparison < 0 ) {
+
+			low = i + 1;
+			continue;
+
+		} else if ( comparison > 0 ) {
+
+			high = i - 1;
+			continue;
+
+		} else {
+
+			high = i;
+			break;
+
+			// DONE
+
+		}
+
+	}
+
+	i = high;
+
+	//console.log('b' , i, low, high, Date.now()- time);
+
+	if ( arcLengths[ i ] == targetArcLength ) {
+
+		var t = i / ( il - 1 );
+		return t;
+
+	}
+
+	// we could get finer grain at lengths, or use simple interpolatation between two points
+
+	var lengthBefore = arcLengths[ i ];
+    var lengthAfter = arcLengths[ i + 1 ];
+
+    var segmentLength = lengthAfter - lengthBefore;
+
+    // determine where we are between the 'before' and 'after' points
+
+    var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+
+    // add that fractional amount to t
+
+    var t = ( i + segmentFraction ) / ( il -1 );
+
+	return t;
+
+};
+
+// 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
+
+THREE.Curve.prototype.getTangent = function( t ) {
+
+	var delta = 0.0001;
+	var t1 = t - delta;
+	var t2 = t + delta;
+
+	// Capping in case of danger
+
+	if ( t1 < 0 ) t1 = 0;
+	if ( t2 > 1 ) t2 = 1;
+
+	var pt1 = this.getPoint( t1 );
+	var pt2 = this.getPoint( t2 );
+
+	var vec = pt2.clone().sub(pt1);
+	return vec.normalize();
+
+};
+
+
+THREE.Curve.prototype.getTangentAt = function ( u ) {
+
+	var t = this.getUtoTmapping( u );
+	return this.getTangent( t );
+
+};
+
+/**************************************************************
+ *	Line
+ **************************************************************/
+
+THREE.LineCurve = function ( v1, v2 ) {
+
+	this.v1 = v1;
+	this.v2 = v2;
+
+};
+
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+
+THREE.LineCurve.prototype.getPoint = function ( t ) {
+
+	var point = this.v2.clone().sub(this.v1);
+	point.multiplyScalar( t ).add( this.v1 );
+
+	return point;
+
+};
+
+// Line curve is linear, so we can overwrite default getPointAt
+
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
+
+	return this.getPoint( u );
+
+};
+
+THREE.LineCurve.prototype.getTangent = function( t ) {
+
+	var tangent = this.v2.clone().sub(this.v1);
+
+	return tangent.normalize();
+
+};
+
+/**************************************************************
+ *	Quadratic Bezier curve
+ **************************************************************/
+
+
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
+
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
+
+};
+
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+
+
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
+
+	var tx, ty;
+
+	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 );
+
+};
+
+
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+
+	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 );
+
+	// returns unit vector
+
+	var tangent = new THREE.Vector2( tx, ty );
+	tangent.normalize();
+
+	return tangent;
+
+};
+
+
+/**************************************************************
+ *	Cubic Bezier curve
+ **************************************************************/
+
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
+
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
+	this.v3 = v3;
+
+};
+
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
+
+	var tx, ty;
+
+	tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
+	ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
+
+	return new THREE.Vector2( tx, ty );
+
+};
+
+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();
+
+	return tangent;
+
+};
+
+
+/**************************************************************
+ *	Spline curve
+ **************************************************************/
+
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
+
+	this.points = (points == undefined) ? [] : points;
+
+};
+
+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;
+
+	intPoint = Math.floor( point );
+	weight = point - intPoint;
+
+	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;
+
+	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;
+
+};
+
+/**************************************************************
+ *	Ellipse curve
+ **************************************************************/
+
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius,
+							aStartAngle, aEndAngle,
+							aClockwise ) {
+
+	this.aX = aX;
+	this.aY = aY;
+
+	this.xRadius = xRadius;
+	this.yRadius = yRadius;
+
+	this.aStartAngle = aStartAngle;
+	this.aEndAngle = aEndAngle;
+
+	this.aClockwise = aClockwise;
+
+};
+
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
+
+	var deltaAngle = this.aEndAngle - this.aStartAngle;
+
+	if ( !this.aClockwise ) {
+
+		t = 1 - t;
+
+	}
+
+	var angle = this.aStartAngle + t * deltaAngle;
+
+	var tx = this.aX + this.xRadius * Math.cos( angle );
+	var ty = this.aY + this.yRadius * Math.sin( angle );
+
+	return new THREE.Vector2( tx, ty );
+
+};
+
+/**************************************************************
+ *	Arc curve
+ **************************************************************/
+
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+
+	THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+};
+
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
+
+
+/**************************************************************
+ *	Utils
+ **************************************************************/
+
+THREE.Curve.Utils = {
+
+	tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
+
+		return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+
+	},
+
+	// Puay Bing, thanks for helping with this derivative!
+
+	tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
+
+		return -3 * p0 * (1 - t) * (1 - t)  +
+			3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
+			6 * t *  p2 * (1-t) - 3 * t * t * p2 +
+			3 * t * t * p3;
+	},
+
+
+	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
+
+		return h00 + h10 + h01 + h11;
+
+	},
+
+	// Catmull-Rom
+
+	interpolate: function( p0, p1, p2, p3, t ) {
+
+		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;
+
+	}
+
+};
+
+
+// TODO: Transformation for Curves?
+
+/**************************************************************
+ *	3D Curves
+ **************************************************************/
+
+// A Factory method for creating new curve subclasses
+
+THREE.Curve.create = function ( constructor, getPointFunc ) {
+
+	constructor.prototype = Object.create( THREE.Curve.prototype );
+	constructor.prototype.getPoint = getPointFunc;
+
+	return constructor;
+
+};
+
+
+/**************************************************************
+ *	Line3D
+ **************************************************************/
+
+THREE.LineCurve3 = THREE.Curve.create(
+
+	function ( v1, v2 ) {
+
+		this.v1 = v1;
+		this.v2 = v2;
+
+	},
+
+	function ( t ) {
+
+		var r = new THREE.Vector3();
+
+
+		r.subVectors( this.v2, this.v1 ); // diff
+		r.multiplyScalar( t );
+		r.add( this.v1 );
+
+		return r;
+
+	}
+
+);
+
+
+/**************************************************************
+ *	Quadratic Bezier 3D curve
+ **************************************************************/
+
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
+
+	function ( v0, v1, v2 ) {
+
+		this.v0 = v0;
+		this.v1 = v1;
+		this.v2 = v2;
+
+	},
+
+	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 );
+
+		return new THREE.Vector3( tx, ty, tz );
+
+	}
+
+);
+
+
+
+/**************************************************************
+ *	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;
+
+	},
+
+	function ( t ) {
+
+		var tx, ty, tz;
+
+		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 );
+
+		return new THREE.Vector3( tx, ty, tz );
+
+	}
+
+);
+
+
+
+/**************************************************************
+ *	Spline 3D curve
+ **************************************************************/
+
+
+THREE.SplineCurve3 = THREE.Curve.create(
+
+	function ( points /* array of Vector3 */) {
+
+		this.points = (points == undefined) ? [] : points;
+
+	},
+
+	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;
+
+		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] ];
+
+		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;
+
+	}
+
+);
+
+
+// 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;
+
+// 		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] ];
+
+// 	// 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;
+
+// }
+
+/**************************************************************
+ *	Closed Spline 3D curve
+ **************************************************************/
+
+
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
+
+	function ( points /* array of Vector3 */) {
+
+		this.points = (points == undefined) ? [] : points;
+
+	},
+
+    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
+
+        intPoint = Math.floor( point );
+        weight = point - intPoint;
+
+        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;
+
+        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 );
+
+        return v;
+
+    }
+
+);
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ **/
+
+/**************************************************************
+ *	Curved Path - a curve path is simply a array of connected
+ *  curves, but retains the api of a curve
+ **************************************************************/
+
+THREE.CurvePath = function () {
+
+	this.curves = [];
+	this.bends = [];
+	
+	this.autoClose = false; // Automatically closes the path
+};
+
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+
+THREE.CurvePath.prototype.add = function ( curve ) {
+
+	this.curves.push( curve );
+
+};
+
+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) );
+	}
+	
+};
+
+// To get accurate point with reference to
+// entire path distance at time t,
+// following has to be done:
+
+// 1. Length of each sub path have to be known
+// 2. Locate and identify type of curve
+// 3. Get t for the curve
+// 4. Return curve.getPointAt(t')
+
+THREE.CurvePath.prototype.getPoint = function( t ) {
+
+	var d = t * this.getLength();
+	var curveLengths = this.getCurveLengths();
+	var i = 0, diff, curve;
+
+	// To think about boundaries points.
+
+	while ( i < curveLengths.length ) {
+
+		if ( curveLengths[ i ] >= d ) {
+
+			diff = curveLengths[ i ] - d;
+			curve = this.curves[ i ];
+
+			var u = 1 - diff / curve.getLength();
+
+			return curve.getPointAt( u );
+
+			break;
+		}
+
+		i ++;
+
+	}
+
+	return null;
+
+	// loop where sum != 0, sum > d , sum+1 <d
+
+};
+
+/*
+THREE.CurvePath.prototype.getTangent = function( t ) {
+};*/
+
+
+// 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
+
+THREE.CurvePath.prototype.getLength = function() {
+
+	var lens = this.getCurveLengths();
+	return lens[ lens.length - 1 ];
+
+};
+
+// Compute lengths and cache them
+// We cannot overwrite getLengths() because UtoT mapping uses it.
+
+THREE.CurvePath.prototype.getCurveLengths = function() {
+
+	// We use cache values if curves and cache array are same length
+
+	if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
+
+		return this.cacheLengths;
+
+	};
+
+	// Get length of subsurve
+	// Push sums into cached array
+
+	var lengths = [], sums = 0;
+	var i, il = this.curves.length;
+
+	for ( i = 0; i < il; i ++ ) {
+
+		sums += this.curves[ i ].getLength();
+		lengths.push( sums );
+
+	}
+
+	this.cacheLengths = lengths;
+
+	return lengths;
+
+};
+
+
+
+// Returns min and max coordinates, as well as centroid
+
+THREE.CurvePath.prototype.getBoundingBox = function () {
+
+	var points = this.getPoints();
+
+	var maxX, maxY, maxZ;
+	var minX, minY, minZ;
+
+	maxX = maxY = Number.NEGATIVE_INFINITY;
+	minX = minY = Number.POSITIVE_INFINITY;
+
+	var p, i, il, sum;
+
+	var v3 = points[0] instanceof THREE.Vector3;
+
+	sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
+
+	for ( i = 0, il = points.length; i < il; i ++ ) {
+
+		p = points[ i ];
+
+		if ( p.x > maxX ) maxX = p.x;
+		else if ( p.x < minX ) minX = p.x;
+
+		if ( p.y > maxY ) maxY = p.y;
+		else if ( p.y < minY ) minY = p.y;
+
+		if ( v3 ) {
+
+			if ( p.z > maxZ ) maxZ = p.z;
+			else if ( p.z < minZ ) minZ = p.z;
+
+		}
+
+		sum.add( p );
+
+	}
+
+	var ret = {
+
+		minX: minX,
+		minY: minY,
+		maxX: maxX,
+		maxY: maxY,
+		centroid: sum.divideScalar( il )
+
+	};
+
+	if ( v3 ) {
+
+		ret.maxZ = maxZ;
+		ret.minZ = minZ;
+
+	}
+
+	return ret;
+
+};
+
+/**************************************************************
+ *	Create Geometries Helpers
+ **************************************************************/
+
+/// Generate geometry from path points (for Line or ParticleSystem objects)
+
+THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
+
+	var pts = this.getPoints( divisions, true );
+	return this.createGeometry( pts );
+
+};
+
+// Generate geometry from equidistance sampling along the path
+
+THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
+
+	var pts = this.getSpacedPoints( divisions, true );
+	return this.createGeometry( pts );
+
+};
+
+THREE.CurvePath.prototype.createGeometry = function( points ) {
+
+	var geometry = new THREE.Geometry();
+
+	for ( var i = 0; i < points.length; i ++ ) {
+
+		geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
+
+	}
+
+	return geometry;
+
+};
+
+
+/**************************************************************
+ *	Bend / Wrap Helper Methods
+ **************************************************************/
+
+// Wrap path / Bend modifiers?
+
+THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
+
+	this.bends.push( bendpath );
+
+};
+
+THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
+
+	var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
+	var i, il;
+
+	if ( !bends ) {
+
+		bends = this.bends;
+
+	}
+
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
+
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+
+	}
+
+	return oldPts;
+
+};
+
+THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
+
+	var oldPts = this.getSpacedPoints( segments );
+
+	var i, il;
+
+	if ( !bends ) {
+
+		bends = this.bends;
+
+	}
+
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
+
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+
+	}
+
+	return oldPts;
+
+};
+
+// 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();
+
+	var i, il, p, oldX, oldY, xNorm;
+
+	for ( i = 0, il = oldPts.length; i < il; i ++ ) {
+
+		p = oldPts[ i ];
+
+		oldX = p.x;
+		oldY = p.y;
+
+		xNorm = oldX / bounds.maxX;
+
+		// If using actual distance, for length > path, requires line extrusions
+		//xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
+
+		xNorm = path.getUtoTmapping( xNorm, oldX );
+
+		// check for out of bounds?
+
+		var pathPt = path.getPoint( xNorm );
+		var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY );
+
+		p.x = pathPt.x + normal.x;
+		p.y = pathPt.y + normal.y;
+
+	}
+
+	return oldPts;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Gyroscope = function () {
+
+	THREE.Object3D.call( this );
+
+};
+
+THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) {
+
+	this.matrixAutoUpdate && this.updateMatrix();
+
+	// update matrixWorld
+
+	if ( this.matrixWorldNeedsUpdate || force ) {
+
+		if ( this.parent ) {
+
+			this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+			this.matrixWorld.decompose( this.translationWorld, this.rotationWorld, this.scaleWorld );
+			this.matrix.decompose( this.translationObject, this.rotationObject, this.scaleObject );
+
+			this.matrixWorld.compose( this.translationWorld, this.rotationObject, this.scaleWorld );
+
+
+		} else {
+
+			this.matrixWorld.copy( this.matrix );
+
+		}
+
+
+		this.matrixWorldNeedsUpdate = false;
+
+		force = true;
+
+	}
+
+	// update children
+
+	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+		this.children[ i ].updateMatrixWorld( force );
+
+	}
+
+};
+
+THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3();
+THREE.Gyroscope.prototype.translationObject = new THREE.Vector3();
+THREE.Gyroscope.prototype.rotationWorld = new THREE.Quaternion();
+THREE.Gyroscope.prototype.rotationObject = new THREE.Quaternion();
+THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3();
+THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3();
+
+/**
+ * @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 = [];
+
+	if ( points ) {
+
+		this.fromPoints( points );
+
+	}
+
+};
+
+THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
+
+THREE.PathActions = {
+
+	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
+
+THREE.Path.prototype.fromPoints = function ( vectors ) {
+
+	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
+
+	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
+
+		this.lineTo( vectors[ v ].x, vectors[ v ].y );
+
+	};
+
+};
+
+// startPath() endPath()?
+
+THREE.Path.prototype.moveTo = function ( x, y ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
+
+};
+
+THREE.Path.prototype.lineTo = function ( x, y ) {
+
+	var args = Array.prototype.slice.call( arguments );
+
+	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 );
+
+	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 );
+
+	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.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
+												new THREE.Vector2( aCPx, aCPy ),
+												new THREE.Vector2( aX, aY ) );
+	this.curves.push( curve );
+
+	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
+
+};
+
+THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
+                                               aCP2x, aCP2y,
+                                               aX, aY ) {
+
+	var args = Array.prototype.slice.call( arguments );
+
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
+
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
+
+	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*/ ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
+
+	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 curve = new THREE.SplineCurve( npts );
+	this.curves.push( curve );
+
+	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
+
+};
+
+// FUTURE: Change the API or follow canvas API?
+
+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 );
+	
+ };
+
+ THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
+	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
+ };
+ 
+THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
+
+	var lastargs = this.actions[ this.actions.length - 1].args;
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
+
+	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
+		aStartAngle, aEndAngle, aClockwise );
+
+ };
+ 
+
+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 );
+
+	var lastPoint = curve.getPoint(aClockwise ? 1 : 0);
+	args.push(lastPoint.x);
+	args.push(lastPoint.y);
+
+	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
+
+ };
+
+THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
+
+	if ( ! divisions ) divisions = 40;
+
+	var points = [];
+
+	for ( var i = 0; i < divisions; i ++ ) {
+
+		points.push( this.getPoint( i / divisions ) );
+
+		//if( !this.getPoint( i / divisions ) ) throw "DIE";
+
+	}
+
+	// if ( closedPath ) {
+	//
+	// 	points.push( points[ 0 ] );
+	//
+	// }
+
+	return points;
+
+};
+
+/* Return an array of vectors based on contour of the path */
+
+THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
+
+	if (this.useSpacedPoints) {
+		console.log('tata');
+		return this.getSpacedPoints( divisions, closedPath );
+	}
+
+	divisions = divisions || 12;
+
+	var points = [];
+
+	var i, il, item, action, args;
+	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
+		laste, j,
+		t, tx, ty;
+
+	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+
+		item = this.actions[ i ];
+
+		action = item.action;
+		args = item.args;
+
+		switch( action ) {
+
+		case THREE.PathActions.MOVE_TO:
+
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+
+			break;
+
+		case THREE.PathActions.LINE_TO:
+
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+
+			break;
+
+		case THREE.PathActions.QUADRATIC_CURVE_TO:
+
+			cpx  = args[ 2 ];
+			cpy  = args[ 3 ];
+
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
+
+			if ( points.length > 0 ) {
+
+				laste = points[ points.length - 1 ];
+
+				cpx0 = laste.x;
+				cpy0 = laste.y;
+
+			} else {
+
+				laste = this.actions[ i - 1 ].args;
+
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
+
+			}
+
+			for ( j = 1; j <= divisions; j ++ ) {
+
+				t = j / divisions;
+
+				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+		  	}
+
+			break;
+
+		case THREE.PathActions.BEZIER_CURVE_TO:
+
+			cpx  = args[ 4 ];
+			cpy  = args[ 5 ];
+
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
+
+			cpx2 = args[ 2 ];
+			cpy2 = args[ 3 ];
+
+			if ( points.length > 0 ) {
+
+				laste = points[ points.length - 1 ];
+
+				cpx0 = laste.x;
+				cpy0 = laste.y;
+
+			} else {
+
+				laste = this.actions[ i - 1 ].args;
+
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
+
+			}
+
+
+			for ( j = 1; j <= divisions; j ++ ) {
+
+				t = j / divisions;
+
+				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
+				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+			}
+
+			break;
+
+		case THREE.PathActions.CSPLINE_THRU:
+
+			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 );
+
+			for ( j = 1; j <= n; j ++ ) {
+
+				points.push( spline.getPointAt( j / n ) ) ;
+
+			}
+
+			break;
+
+		case THREE.PathActions.ARC:
+
+			var aX = args[ 0 ], aY = args[ 1 ],
+				aRadius = args[ 2 ],
+				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
+				aClockwise = !!args[ 5 ];
+
+			var deltaAngle = aEndAngle - aStartAngle;
+			var angle;
+			var tdivisions = divisions * 2;
+
+			for ( j = 1; j <= tdivisions; j ++ ) {
+
+				t = j / tdivisions;
+
+				if ( ! aClockwise ) {
+
+					t = 1 - t;
+
+				}
+
+				angle = aStartAngle + t * deltaAngle;
+
+				tx = aX + aRadius * Math.cos( angle );
+				ty = aY + aRadius * Math.sin( angle );
+
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+			}
+
+			//console.log(points);
+
+		  break;
+		  
+		case THREE.PathActions.ELLIPSE:
+
+			var aX = args[ 0 ], aY = args[ 1 ],
+				xRadius = args[ 2 ],
+				yRadius = args[ 3 ],
+				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
+				aClockwise = !!args[ 6 ];
+
+
+			var deltaAngle = aEndAngle - aStartAngle;
+			var angle;
+			var tdivisions = divisions * 2;
+
+			for ( j = 1; j <= tdivisions; j ++ ) {
+
+				t = j / tdivisions;
+
+				if ( ! aClockwise ) {
+
+					t = 1 - t;
+
+				}
+
+				angle = aStartAngle + t * deltaAngle;
+
+				tx = aX + xRadius * Math.cos( angle );
+				ty = aY + yRadius * Math.sin( angle );
+
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+			}
+
+			//console.log(points);
+
+		  break;
+
+		} // end switch
+
+	}
+
+
+
+	// Normalize to remove the closing point by default.
+	var lastPoint = points[ points.length - 1];
+	var EPSILON = 0.0000000001;
+	if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
+             Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
+		points.splice( points.length - 1, 1);
+	if ( closedPath ) {
+
+		points.push( points[ 0 ] );
+
+	}
+
+	return points;
+
+};
+
+// Breaks path into shapes
+
+THREE.Path.prototype.toShapes = function() {
+
+	var i, il, item, action, args;
+
+	var subPaths = [], lastPath = new THREE.Path();
+
+	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+
+		item = this.actions[ i ];
+
+		args = item.args;
+		action = item.action;
+
+		if ( action == THREE.PathActions.MOVE_TO ) {
+
+			if ( lastPath.actions.length != 0 ) {
+
+				subPaths.push( lastPath );
+				lastPath = new THREE.Path();
+
+			}
+
+		}
+
+		lastPath[ action ].apply( lastPath, args );
+
+	}
+
+	if ( lastPath.actions.length != 0 ) {
+
+		subPaths.push( lastPath );
+
+	}
+
+	// console.log(subPaths);
+
+	if ( subPaths.length == 0 ) return [];
+
+	var tmpPath, tmpShape, shapes = [];
+
+	var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
+	// console.log("Holes first", holesFirst);
+
+	if ( subPaths.length == 1) {
+		tmpPath = subPaths[0];
+		tmpShape = new THREE.Shape();
+		tmpShape.actions = tmpPath.actions;
+		tmpShape.curves = tmpPath.curves;
+		shapes.push( tmpShape );
+		return shapes;
+	};
+
+	if ( holesFirst ) {
+
+		tmpShape = new THREE.Shape();
+
+		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+
+			tmpPath = subPaths[ i ];
+
+			if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) {
+
+				tmpShape.actions = tmpPath.actions;
+				tmpShape.curves = tmpPath.curves;
+
+				shapes.push( tmpShape );
+				tmpShape = new THREE.Shape();
+
+				//console.log('cw', i);
+
+			} else {
+
+				tmpShape.holes.push( tmpPath );
+
+				//console.log('ccw', i);
+
+			}
+
+		}
+
+	} else {
+
+		// Shapes first
+
+		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+
+			tmpPath = subPaths[ i ];
+
+			if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) {
+
+
+				if ( tmpShape ) shapes.push( tmpShape );
+
+				tmpShape = new THREE.Shape();
+				tmpShape.actions = tmpPath.actions;
+				tmpShape.curves = tmpPath.curves;
+
+			} else {
+
+				tmpShape.holes.push( tmpPath );
+
+			}
+
+		}
+
+		shapes.push( tmpShape );
+
+	}
+
+	//console.log("shape", shapes);
+
+	return shapes;
+
+};
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
+
+// STEP 1 Create a path.
+// STEP 2 Turn path into shape.
+// STEP 3 ExtrudeGeometry takes in Shape/Shapes
+// STEP 3a - Extract points from each shape, turn to vertices
+// STEP 3b - Triangulate each shape, add faces.
+
+THREE.Shape = function () {
+
+	THREE.Path.apply( this, arguments );
+	this.holes = [];
+
+};
+
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
+
+// Convenience method to return ExtrudeGeometry
+
+THREE.Shape.prototype.extrude = function ( options ) {
+
+	var extruded = new THREE.ExtrudeGeometry( this, options );
+	return extruded;
+
+};
+
+// Convenience method to return ShapeGeometry
+
+THREE.Shape.prototype.makeGeometry = function ( options ) {
+
+	var geometry = new THREE.ShapeGeometry( this, options );
+	return geometry;
+
+};
+
+// Get points of holes
+
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
+
+	var i, il = this.holes.length, holesPts = [];
+
+	for ( i = 0; i < il; i ++ ) {
+
+		holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
+
+	}
+
+	return holesPts;
+
+};
+
+// Get points of holes (spaced by regular distance)
+
+THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
+
+	var i, il = this.holes.length, holesPts = [];
+
+	for ( i = 0; i < il; i ++ ) {
+
+		holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
+
+	}
+
+	return holesPts;
+
+};
+
+
+// Get points of shape and holes (keypoints based on segments parameter)
+
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
+
+	return {
+
+		shape: this.getTransformedPoints( divisions ),
+		holes: this.getPointsHoles( divisions )
+
+	};
+
+};
+
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
+
+	if (this.useSpacedPoints) {
+		return this.extractAllSpacedPoints(divisions);
+	}
+
+	return this.extractAllPoints(divisions);
+
+};
+
+//
+// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
+//
+// 	return {
+//
+// 		shape: this.transform( bend, divisions ),
+// 		holes: this.getPointsHoles( divisions, bend )
+//
+// 	};
+//
+// };
+
+// Get points of shape and holes (spaced by regular distance)
+
+THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
+
+	return {
+
+		shape: this.getTransformedSpacedPoints( divisions ),
+		holes: this.getSpacedPointsHoles( divisions )
+
+	};
+
+};
+
+/**************************************************************
+ *	Utils
+ **************************************************************/
+
+THREE.Shape.Utils = {
+
+	/*
+		contour - array of vector2 for contour
+		holes   - array of array of vector2
+	*/
+
+	removeHoles: function ( contour, holes ) {
+
+		var shape = contour.concat(); // work on this shape
+		var allpoints = shape.concat();
+
+		/* For each isolated shape, find the closest points and break to the hole to allow triangulation */
+
+
+		var prevShapeVert, nextShapeVert,
+			prevHoleVert, nextHoleVert,
+			holeIndex, shapeIndex,
+			shapeId, shapeGroup,
+			h, h2,
+			hole, shortest, d,
+			p, pts1, pts2,
+			tmpShape1, tmpShape2,
+			tmpHole1, tmpHole2,
+			verts = [];
+
+		for ( h = 0; h < holes.length; h ++ ) {
+
+			hole = holes[ h ];
+
+			/*
+			shapeholes[ h ].concat(); // preserves original
+			holes.push( hole );
+			*/
+
+			Array.prototype.push.apply( allpoints, hole );
+
+			shortest = Number.POSITIVE_INFINITY;
+
+
+			// Find the shortest pair of pts between shape and hole
+
+			// 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.
+
+			for ( h2 = 0; h2 < hole.length; h2 ++ ) {
+
+				pts1 = hole[ h2 ];
+				var dist = [];
+
+				for ( p = 0; p < shape.length; p++ ) {
+
+					pts2 = shape[ p ];
+					d = pts1.distanceToSquared( pts2 );
+					dist.push( d );
+
+					if ( d < shortest ) {
+
+						shortest = d;
+						holeIndex = h2;
+						shapeIndex = p;
+
+					}
+
+				}
+
+			}
+
+			//console.log("shortest", shortest, dist);
+
+			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			var areaapts = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var areaa = THREE.FontUtils.Triangulate.area( areaapts );
+
+			var areabpts = [
+
+				hole[ holeIndex ],
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			var areab = THREE.FontUtils.Triangulate.area( areabpts );
+
+			var shapeOffset = 1;
+			var holeOffset = -1;
+
+			var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
+			shapeIndex += shapeOffset;
+			holeIndex += holeOffset;
+
+			if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
+			shapeIndex %= shape.length;
+
+			if ( holeIndex < 0 ) { holeIndex += hole.length;  }
+			holeIndex %= hole.length;
+
+			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			areaapts = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
+
+			areabpts = [
+
+				hole[ holeIndex ],
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
+			//console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ),  ( areaa2 + areab2 ));
+
+			if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
+
+				// In case areas are not correct.
+				//console.log("USE THIS");
+
+				shapeIndex = oldShapeIndex;
+				holeIndex = oldHoleIndex ;
+
+				if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
+				shapeIndex %= shape.length;
+
+				if ( holeIndex < 0 ) { holeIndex += hole.length;  }
+				holeIndex %= hole.length;
+
+				prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			} else {
+
+				//console.log("USE THAT ")
+
+			}
+
+			tmpShape1 = shape.slice( 0, shapeIndex );
+			tmpShape2 = shape.slice( shapeIndex );
+			tmpHole1 = hole.slice( holeIndex );
+			tmpHole2 = hole.slice( 0, holeIndex );
+
+			// Should check orders here again?
+
+			var trianglea = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var triangleb = [
+
+				hole[ holeIndex ] ,
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			verts.push( trianglea );
+			verts.push( triangleb );
+
+			shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+
+		}
+
+		return {
+
+			shape:shape, 		/* shape with no holes */
+			isolatedPts: verts, /* isolated faces */
+			allpoints: allpoints
+
+		}
+
+
+	},
+
+	triangulateShape: function ( contour, holes ) {
+
+		var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
+
+		var shape = shapeWithoutHoles.shape,
+			allpoints = shapeWithoutHoles.allpoints,
+			isolatedPts = shapeWithoutHoles.isolatedPts;
+
+		var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
+
+		// 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 );
+
+		var i, il, f, face,
+			key, index,
+			allPointsMap = {},
+			isolatedPointsMap = {};
+
+		// prepare all points map
+
+		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
+
+			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
+
+			if ( allPointsMap[ key ] !== undefined ) {
+
+				console.log( "Duplicate point", key );
+
+			}
+
+			allPointsMap[ key ] = i;
+
+		}
+
+		// check all face vertices against all points map
+
+		for ( i = 0, il = triangles.length; i < il; i ++ ) {
+
+			face = triangles[ i ];
+
+			for ( f = 0; f < 3; f ++ ) {
+
+				key = face[ f ].x + ":" + face[ f ].y;
+
+				index = allPointsMap[ key ];
+
+				if ( index !== undefined ) {
+
+					face[ f ] = index;
+
+				}
+
+			}
+
+		}
+
+		// check isolated points vertices against all points map
+
+		for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
+
+			face = isolatedPts[ i ];
+
+			for ( f = 0; f < 3; f ++ ) {
+
+				key = face[ f ].x + ":" + face[ f ].y;
+
+				index = allPointsMap[ key ];
+
+				if ( index !== undefined ) {
+
+					face[ f ] = index;
+
+				}
+
+			}
+
+		}
+
+		return triangles.concat( isolatedPts );
+
+	}, // end triangulate shapes
+
+	/*
+	triangulate2 : function( pts, holes ) {
+
+		// For use with Poly2Tri.js
+
+		var allpts = pts.concat();
+		var shape = [];
+		for (var p in pts) {
+			shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
+		}
+
+		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);
+		}
+
+		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;
+		};
+
+		// 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))
+					]);
+		}
+
+
+	//	console.log(facesPts);
+	//	console.log("triangles", triangles.length, triangles);
+
+		// Returns array of faces with 3 element each
+	return facesPts;
+	},
+*/
+
+	isClockWise: function ( pts ) {
+
+		return THREE.FontUtils.Triangulate.area( pts ) < 0;
+
+	},
+
+	// Bezier Curves formulas obtained from
+	// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+
+	// Quad Bezier Functions
+
+	b2p0: function ( t, p ) {
+
+		var k = 1 - t;
+		return k * k * p;
+
+	},
+
+	b2p1: function ( t, p ) {
+
+		return 2 * ( 1 - t ) * t * p;
+
+	},
+
+	b2p2: function ( t, p ) {
+
+		return t * t * p;
+
+	},
+
+	b2: function ( t, p0, p1, p2 ) {
+
+		return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
+
+	},
+
+	// Cubic Bezier Functions
+
+	b3p0: function ( t, p ) {
+
+		var k = 1 - t;
+		return k * k * k * p;
+
+	},
+
+	b3p1: function ( t, p ) {
+
+		var k = 1 - t;
+		return 3 * k * k * t * p;
+
+	},
+
+	b3p2: function ( t, p ) {
+
+		var k = 1 - t;
+		return 3 * k * t * t * p;
+
+	},
+
+	b3p3: function ( t, p ) {
+
+		return t * t * t * p;
+
+	},
+
+	b3: function ( t, p0, p1, p2, p3 ) {
+
+		return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) +  this.b3p3( t, p3 );
+
+	}
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.AnimationHandler = (function() {
+
+	var playing = [];
+	var library = {};
+	var that    = {};
+
+
+	//--- update ---
+
+	that.update = function( deltaTimeMS ) {
+
+		for( var i = 0; i < playing.length; i ++ )
+			playing[ i ].update( deltaTimeMS );
+
+	};
+
+
+	//--- add ---
+
+	that.addToUpdate = function( animation ) {
+
+		if ( playing.indexOf( animation ) === -1 )
+			playing.push( animation );
+
+	};
+
+
+	//--- remove ---
+
+	that.removeFromUpdate = function( animation ) {
+
+		var index = playing.indexOf( animation );
+
+		if( index !== -1 )
+			playing.splice( index, 1 );
+
+	};
+
+
+	//--- add ---
+
+	that.add = function( data ) {
+
+		if ( library[ data.name ] !== undefined )
+			console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." );
+
+		library[ data.name ] = data;
+		initData( data );
+
+	};
+
+
+	//--- get ---
+
+	that.get = function( name ) {
+
+		if ( typeof name === "string" ) {
+
+			if ( library[ name ] ) {
+
+				return library[ name ];
+
+			} else {
+
+				console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name );
+				return null;
+
+			}
+
+		} else {
+
+			// todo: add simple tween library
+
+		}
+
+	};
+
+	//--- parse ---
+
+	that.parse = function( root ) {
+
+		// setup hierarchy
+
+		var hierarchy = [];
+
+		if ( root instanceof THREE.SkinnedMesh ) {
+
+			for( var b = 0; b < root.bones.length; b++ ) {
+
+				hierarchy.push( root.bones[ b ] );
+
+			}
+
+		} else {
+
+			parseRecurseHierarchy( root, hierarchy );
+
+		}
+
+		return hierarchy;
+
+	};
+
+	var parseRecurseHierarchy = function( root, hierarchy ) {
+
+		hierarchy.push( root );
+
+		for( var c = 0; c < root.children.length; c++ )
+			parseRecurseHierarchy( root.children[ c ], hierarchy );
+
+	}
+
+
+	//--- init data ---
+
+	var initData = function( data ) {
+
+		if( data.initialized === true )
+			return;
+
+
+		// loop through all keys
+
+		for( var h = 0; h < data.hierarchy.length; h ++ ) {
+
+			for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+
+				// remove minus times
+
+				if( data.hierarchy[ h ].keys[ k ].time < 0 )
+					data.hierarchy[ h ].keys[ k ].time = 0;
+
+
+				// create quaternions
+
+				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( quat[0], quat[1], quat[2], quat[3] );
+
+				}
+
+			}
+
+
+			// prepare morph target keys
+
+			if( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
+
+				// get all used
+
+				var usedMorphTargets = {};
+
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+
+					for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+
+						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
+						usedMorphTargets[ morphTargetName ] = -1;
+
+					}
+
+				}
+
+				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
+
+
+				// set all used on all frames
+
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+
+					var influences = {};
+
+					for ( var morphTargetName in usedMorphTargets ) {
+
+						for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+
+							if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
+
+								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
+								break;
+
+							}
+
+						}
+
+						if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
+
+							influences[ morphTargetName ] = 0;
+
+						}
+
+					}
+
+					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
+
+				}
+
+			}
+
+
+			// remove all keys that are on the same time
+
+			for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
+
+				if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
+
+					data.hierarchy[ h ].keys.splice( k, 1 );
+					k --;
+
+				}
+
+			}
+
+
+			// set index
+
+			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+
+				data.hierarchy[ h ].keys[ k ].index = k;
+
+			}
+
+		}
+
+
+		// JIT
+
+		var lengthInFrames = parseInt( data.length * data.fps, 10 );
+
+		data.JIT = {};
+		data.JIT.hierarchy = [];
+
+		for( var h = 0; h < data.hierarchy.length; h ++ )
+			data.JIT.hierarchy.push( new Array( lengthInFrames ) );
+
+
+		// done
+
+		data.initialized = true;
+
+	};
+
+
+	// interpolation types
+
+	that.LINEAR = 0;
+	that.CATMULLROM = 1;
+	that.CATMULLROM_FORWARD = 2;
+
+	return that;
+
+}());
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+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;
+
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
+
+	this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
+
+	this.points = [];
+	this.target = new THREE.Vector3();
+
+};
+
+THREE.Animation.prototype.play = function ( loop, startTimeMS ) {
+
+	if ( this.isPlaying === false ) {
+
+		this.isPlaying = true;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+
+		// reset key cache
+
+		var h, hl = this.hierarchy.length,
+			object;
+
+		for ( h = 0; h < hl; h ++ ) {
+
+			object = this.hierarchy[ h ];
+
+			if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+				object.useQuaternion = true;
+
+			}
+
+			object.matrixAutoUpdate = true;
+
+			if ( object.animationCache === undefined ) {
+
+				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;
+
+			}
+
+			var prevKey = object.animationCache.prevKey;
+			var nextKey = object.animationCache.nextKey;
+
+			prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
+
+			nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
+			nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
+			nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+
+		}
+
+		this.update( 0 );
+
+	}
+
+	this.isPaused = false;
+
+	THREE.AnimationHandler.addToUpdate( this );
+
+};
+
+
+THREE.Animation.prototype.pause = function() {
+
+	if ( this.isPaused === true ) {
+
+		THREE.AnimationHandler.addToUpdate( this );
+
+	} else {
+
+		THREE.AnimationHandler.removeFromUpdate( this );
+
+	}
+
+	this.isPaused = !this.isPaused;
+
+};
+
+
+THREE.Animation.prototype.stop = function() {
+
+	this.isPlaying = false;
+	this.isPaused  = false;
+	THREE.AnimationHandler.removeFromUpdate( this );
+
+};
+
+
+THREE.Animation.prototype.update = function ( deltaTimeMS ) {
+
+	// early out
+
+	if ( this.isPlaying === false ) return;
+
+
+	// vars
+
+	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;
+
+
+	this.currentTime += deltaTimeMS * this.timeScale;
+
+	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 );
+
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+
+		object = this.hierarchy[ h ];
+		animationCache = object.animationCache;
+
+		// loop through pos/rot/scl
+
+		for ( var t = 0; t < 3; t ++ ) {
+
+			// get keys
+
+			type    = types[ t ];
+			prevKey = animationCache.prevKey[ type ];
+			nextKey = animationCache.nextKey[ type ];
+
+			// switch keys?
+
+			if ( nextKey.time <= unloopedCurrentTime ) {
+
+				// did we loop?
+
+				if ( currentTime < unloopedCurrentTime ) {
+
+					if ( this.loop ) {
+
+						prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+						nextKey = this.getNextKeyWith( type, h, 1 );
+
+						while( nextKey.time < currentTime ) {
+
+							prevKey = nextKey;
+							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+						}
+
+					} else {
+
+						this.stop();
+						return;
+
+					}
+
+				} else {
+
+					do {
+
+						prevKey = nextKey;
+						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+					} while( nextKey.time < currentTime )
+
+				}
+
+				animationCache.prevKey[ type ] = prevKey;
+				animationCache.nextKey[ type ] = nextKey;
+
+			}
+
+
+			object.matrixAutoUpdate = true;
+			object.matrixWorldNeedsUpdate = true;
+
+			scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+			prevXYZ = prevKey[ type ];
+			nextXYZ = nextKey[ type ];
+
+
+			// check scale error
+
+			if ( scale < 0 || scale > 1 ) {
+
+				console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
+				scale = scale < 0 ? 0 : 1;
+
+			}
+
+			// interpolate
+
+			if ( type === "pos" ) {
+
+				vector = object.position;
+
+				if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+
+					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;
+
+				} 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" ];
+
+					scale = scale * 0.33 + 0.33;
+
+					currentPoint = this.interpolateCatmullRom( this.points, scale );
+
+					vector.x = currentPoint[ 0 ];
+					vector.y = currentPoint[ 1 ];
+					vector.z = currentPoint[ 2 ];
+
+					if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+						forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
+
+						this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
+						this.target.sub( vector );
+						this.target.y = 0;
+						this.target.normalize();
+
+						angle = Math.atan2( this.target.x, this.target.z );
+						object.rotation.set( 0, angle, 0 );
+
+					}
+
+				}
+
+			} else if ( type === "rot" ) {
+
+				THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
+
+			} else if ( type === "scl" ) {
+
+				vector = object.scale;
+
+				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;
+
+			}
+
+		}
+
+	}
+
+};
+
+// Catmull-Rom spline
+
+THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) {
+
+	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 ] ];
+
+	w2 = weight * weight;
+	w3 = weight * w2;
+
+	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;
+
+};
+
+THREE.Animation.prototype.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
+
+	var v0 = ( p2 - p0 ) * 0.5,
+		v1 = ( p3 - p1 ) * 0.5;
+
+	return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+};
+
+
+
+// Get next key with
+
+THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+
+	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+		key = key < keys.length - 1 ? key : keys.length - 1;
+
+	} else {
+
+		key = key % keys.length;
+
+	}
+
+	for ( ; key < keys.length; key++ ) {
+
+		if ( keys[ key ][ type ] !== undefined ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return this.data.hierarchy[ h ].keys[ 0 ];
+
+};
+
+// Get previous key with
+
+THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+
+	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+		key = key > 0 ? key : 0;
+
+	} else {
+
+		key = key >= 0 ? key : key + keys.length;
+
+	}
+
+
+	for ( ; key >= 0; key -- ) {
+
+		if ( keys[ key ][ type ] !== undefined ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author khang duong
+ * @author erik kitson
+ */
+
+THREE.KeyFrameAnimation = function( 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;
+
+	// initialize to first keyframes
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		var keys = this.data.hierarchy[h].keys,
+			sids = this.data.hierarchy[h].sids,
+			obj = this.hierarchy[h];
+
+		if ( keys.length && sids ) {
+
+			for ( var s = 0; s < sids.length; s++ ) {
+
+				var sid = sids[ s ],
+					next = this.getNextKeyWith( sid, h, 0 );
+
+				if ( next ) {
+
+					next.apply( sid );
+
+				}
+
+			}
+
+			obj.matrixAutoUpdate = false;
+			this.data.hierarchy[h].node.updateMatrix();
+			obj.matrixWorldNeedsUpdate = true;
+
+		}
+
+	}
+
+};
+
+// Play
+
+THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
+
+	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;
+
+
+		// reset key cache
+
+		var h, hl = this.hierarchy.length,
+			object,
+			node;
+
+		for ( h = 0; h < hl; h++ ) {
+
+			object = this.hierarchy[ h ];
+			node = this.data.hierarchy[ h ];
+			object.useQuaternion = true;
+
+			if ( node.animationCache === undefined ) {
+
+				node.animationCache = {};
+				node.animationCache.prevKey = null;
+				node.animationCache.nextKey = null;
+				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+
+			}
+
+			var keys = this.data.hierarchy[h].keys;
+
+			if (keys.length) {
+
+				node.animationCache.prevKey = keys[ 0 ];
+				node.animationCache.nextKey = keys[ 1 ];
+
+				this.startTime = Math.min( keys[0].time, this.startTime );
+				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+
+			}
+
+		}
+
+		this.update( 0 );
+
+	}
+
+	this.isPaused = false;
+
+	THREE.AnimationHandler.addToUpdate( this );
+
+};
+
+
+
+// Pause
+
+THREE.KeyFrameAnimation.prototype.pause = function() {
+
+	if( this.isPaused ) {
+
+		THREE.AnimationHandler.addToUpdate( this );
+
+	} else {
+
+		THREE.AnimationHandler.removeFromUpdate( this );
+
+	}
+
+	this.isPaused = !this.isPaused;
+
+};
+
+
+// Stop
+
+THREE.KeyFrameAnimation.prototype.stop = function() {
+
+	this.isPlaying = false;
+	this.isPaused  = false;
+	THREE.AnimationHandler.removeFromUpdate( this );
+
+
+	// reset JIT matrix and remove cache
+
+	for ( var h = 0; h < this.data.hierarchy.length; h++ ) {
+        
+        var obj = this.hierarchy[ h ];
+		var node = this.data.hierarchy[ h ];
+
+		if ( node.animationCache !== undefined ) {
+
+			var original = node.animationCache.originalMatrix;
+
+			if( obj instanceof THREE.Bone ) {
+
+				original.copy( obj.skinMatrix );
+				obj.skinMatrix = original;
+
+			} else {
+
+				original.copy( obj.matrix );
+				obj.matrix = original;
+
+			}
+
+			delete node.animationCache;
+
+		}
+
+	}
+
+};
+
+
+// Update
+
+THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) {
+
+	// early out
+
+	if( !this.isPlaying ) return;
+
+
+	// vars
+
+	var prevKey, nextKey;
+	var object;
+	var node;
+	var frame;
+	var JIThierarchy = this.data.JIT.hierarchy;
+	var currentTime, unloopedCurrentTime;
+	var looped;
+
+
+	// update
+
+	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 ) {
+
+		currentTime = this.currentTime = this.startTimeMs + currentTime;
+
+	}
+
+	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+	looped 				= currentTime < unloopedCurrentTime;
+
+	if ( looped && !this.loop ) {
+
+		// Set the animation to the last keyframes and stop
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+			var keys = this.data.hierarchy[h].keys,
+				sids = this.data.hierarchy[h].sids,
+				end = keys.length-1,
+				obj = this.hierarchy[h];
+
+			if ( keys.length ) {
+
+				for ( var s = 0; s < sids.length; s++ ) {
+
+					var sid = sids[ s ],
+						prev = this.getPrevKeyWith( sid, h, end );
+
+					if ( prev ) {
+						prev.apply( sid );
+
+					}
+
+				}
+
+				this.data.hierarchy[h].node.updateMatrix();
+				obj.matrixWorldNeedsUpdate = true;
+
+			}
+
+		}
+
+		this.stop();
+		return;
+
+	}
+
+	// check pre-infinity
+	if ( currentTime < this.startTime ) {
+
+		return;
+
+	}
+
+	// update
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		object = this.hierarchy[ h ];
+		node = this.data.hierarchy[ h ];
+
+		var keys = node.keys,
+			animationCache = node.animationCache;
+
+		// use JIT?
+
+		if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
+
+			if( object instanceof THREE.Bone ) {
+
+				object.skinMatrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = false;
+
+			} else {
+
+				object.matrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = true;
+
+			}
+
+		// use interpolation
+
+		} else if ( keys.length ) {
+
+			// make sure so original matrix and not JIT matrix is set
+
+			if ( this.JITCompile && animationCache ) {
+
+				if( object instanceof THREE.Bone ) {
+
+					object.skinMatrix = animationCache.originalMatrix;
+
+				} else {
+
+					object.matrix = animationCache.originalMatrix;
+
+				}
+
+			}
+
+			prevKey = animationCache.prevKey;
+			nextKey = animationCache.nextKey;
+
+			if ( prevKey && nextKey ) {
+
+				// switch keys?
+
+				if ( nextKey.time <= unloopedCurrentTime ) {
+
+					// did we loop?
+
+					if ( looped && this.loop ) {
+
+						prevKey = keys[ 0 ];
+						nextKey = keys[ 1 ];
+
+						while ( nextKey.time < currentTime ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					} else if ( !looped ) {
+
+						var lastIndex = keys.length - 1;
+
+						while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					}
+
+					animationCache.prevKey = prevKey;
+					animationCache.nextKey = nextKey;
+
+				}
+                if(nextKey.time >= currentTime)
+                    prevKey.interpolate( nextKey, currentTime );
+                else
+                    prevKey.interpolate( nextKey, nextKey.time);
+
+			}
+
+			this.data.hierarchy[h].node.updateMatrix();
+			object.matrixWorldNeedsUpdate = true;
+
+		}
+
+	}
+
+	// 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();
+
+				} else {
+
+					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
+
+				}
+
+			}
+
+		}
+
+	}
+
+};
+
+// Get next key with
+
+THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key % keys.length;
+
+	for ( ; key < keys.length; key++ ) {
+
+		if ( keys[ key ].hasTarget( sid ) ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return keys[ 0 ];
+
+};
+
+// Get previous key with
+
+THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key >= 0 ? key : key + keys.length;
+
+	for ( ; key >= 0; key-- ) {
+
+		if ( keys[ key ].hasTarget( sid ) ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return keys[ keys.length - 1 ];
+
+};
+/**
+ * Camera for rendering cube maps
+ *	- renders scene into axis-aligned cube
+ *
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CubeCamera = function ( near, far, cubeResolution ) {
+
+	THREE.Object3D.call( this );
+
+	var fov = 90, aspect = 1;
+
+	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 );
+
+	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 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 );
+
+	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 );
+
+	this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
+
+	this.updateCubeMap = function ( renderer, scene ) {
+
+		var renderTarget = this.renderTarget;
+		var generateMipmaps = renderTarget.generateMipmaps;
+
+		renderTarget.generateMipmaps = false;
+
+		renderTarget.activeCubeFace = 0;
+		renderer.render( scene, cameraPX, renderTarget );
+
+		renderTarget.activeCubeFace = 1;
+		renderer.render( scene, cameraNX, renderTarget );
+
+		renderTarget.activeCubeFace = 2;
+		renderer.render( scene, cameraPY, renderTarget );
+
+		renderTarget.activeCubeFace = 3;
+		renderer.render( scene, cameraNY, renderTarget );
+
+		renderTarget.activeCubeFace = 4;
+		renderer.render( scene, cameraPZ, renderTarget );
+
+		renderTarget.generateMipmaps = generateMipmaps;
+
+		renderTarget.activeCubeFace = 5;
+		renderer.render( scene, cameraNZ, renderTarget );
+
+	};
+
+};
+
+THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
+/*
+ *	@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
+ *
+ */
+
+
+THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) {
+
+	THREE.Camera.call( this );
+
+	this.fov = fov;
+
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+	// We could also handle the projectionMatrix internally, but just wanted to test nested camera objects
+
+	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;
+
+	this.toPerspective();
+
+	var aspect = width/height;
+
+};
+
+THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype );
+
+THREE.CombinedCamera.prototype.toPerspective = function () {
+
+	// Switches to the Perspective Camera
+
+	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 = true;
+	this.inOrthographicMode = false;
+
+};
+
+THREE.CombinedCamera.prototype.toOrthographic = function () {
+
+	// Switches to the Orthographic camera estimating viewport from Perspective
+
+	var fov = this.fov;
+	var aspect = this.cameraP.aspect;
+	var near = this.cameraP.near;
+	var far = this.cameraP.far;
+
+	// The size that we set is the mid plane of the viewing frustum
+
+	var hyperfocus = ( near + far ) / 2;
+
+	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;
+
+	this.cameraO.left = -halfWidth;
+	this.cameraO.right = halfWidth;
+	this.cameraO.top = halfHeight;
+	this.cameraO.bottom = -halfHeight;
+
+	// this.cameraO.left = -farHalfWidth;
+	// this.cameraO.right = farHalfWidth;
+	// this.cameraO.top = farHalfHeight;
+	// this.cameraO.bottom = -farHalfHeight;
+
+	// 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();
+
+	this.near = this.cameraO.near;
+	this.far = this.cameraO.far;
+	this.projectionMatrix = this.cameraO.projectionMatrix;
+
+	this.inPerspectiveMode = false;
+	this.inOrthographicMode = true;
+
+};
+
+
+THREE.CombinedCamera.prototype.setSize = function( width, height ) {
+
+	this.cameraP.aspect = width / height;
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+};
+
+
+THREE.CombinedCamera.prototype.setFov = function( fov ) {
+
+	this.fov = fov;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+// For mantaining similar API with PerspectiveCamera
+
+THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toPerspective();
+		this.toOrthographic();
+
+	}
+
+};
+
+/*
+* 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 ) {
+
+	if ( frameHeight === undefined ) frameHeight = 24;
+
+	var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+
+	this.setFov( fov );
+
+	return fov;
+};
+
+
+THREE.CombinedCamera.prototype.setZoom = function( zoom ) {
+
+	this.zoom = zoom;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+THREE.CombinedCamera.prototype.toFrontView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+
+	// should we be modifing the matrix instead?
+
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBackView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toLeftView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = - Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toRightView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toTopView = function() {
+
+	this.rotation.x = - Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBottomView = function() {
+
+	this.rotation.x = Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- 3d asterisk shape (for line pieces THREE.Line)
+ */
+
+THREE.AsteriskGeometry = function ( innerRadius, outerRadius ) {
+
+	THREE.Geometry.call( this );
+
+	var sd = innerRadius;
+	var ed = outerRadius;
+
+	var sd2 = 0.707 * sd;
+	var ed2 = 0.707 * ed;
+
+	var rays = [ [ sd, 0, 0 ], [ ed, 0, 0 ], [ -sd, 0, 0 ], [ -ed, 0, 0 ],
+				 [ 0, sd, 0 ], [ 0, ed, 0 ], [ 0, -sd, 0 ], [ 0, -ed, 0 ],
+				 [ 0, 0, sd ], [ 0, 0, ed ], [ 0, 0, -sd ], [ 0, 0, -ed ],
+				 [ sd2, sd2, 0 ], [ ed2, ed2, 0 ], [ -sd2, -sd2, 0 ], [ -ed2, -ed2, 0 ],
+				 [ sd2, -sd2, 0 ], [ ed2, -ed2, 0 ], [ -sd2, sd2, 0 ], [ -ed2, ed2, 0 ],
+				 [ sd2, 0, sd2 ], [ ed2, 0, ed2 ], [ -sd2, 0, -sd2 ], [ -ed2, 0, -ed2 ],
+				 [ sd2, 0, -sd2 ], [ ed2, 0, -ed2 ], [ -sd2, 0, sd2 ], [ -ed2, 0, ed2 ],
+				 [ 0, sd2, sd2 ], [ 0, ed2, ed2 ], [ 0, -sd2, -sd2 ], [ 0, -ed2, -ed2 ],
+				 [ 0, sd2, -sd2 ], [ 0, ed2, -ed2 ], [ 0, -sd2, sd2 ], [ 0, -ed2, ed2 ]
+	];
+
+	for ( var i = 0, il = rays.length; i < il; i ++ ) {
+
+		var x = rays[ i ][ 0 ];
+		var y = rays[ i ][ 1 ];
+		var z = rays[ i ][ 2 ];
+
+		this.vertices.push( new THREE.Vector3( x, y, z ) );
+
+	}
+
+};
+
+THREE.AsteriskGeometry.prototype = Object.create( THREE.Geometry.prototype );/**
+ * @author hughes
+ */
+
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+    THREE.Geometry.call( this );
+
+    radius = radius || 50;
+
+    thetaStart = thetaStart !== undefined ? thetaStart : 0;
+    thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+    segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+
+    var i, uvs = [],
+    center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
+
+    this.vertices.push(center);
+    uvs.push( centerUV );
+
+    for ( i = 0; i <= segments; i ++ ) {
+
+        var vertex = new THREE.Vector3();
+
+        vertex.x = radius * Math.cos( thetaStart + i / segments * thetaLength );
+        vertex.y = radius * Math.sin( thetaStart + i / segments * thetaLength );
+
+        this.vertices.push( vertex );
+        uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) );
+
+    }
+
+    var n = new THREE.Vector3( 0, 0, -1 );
+
+    for ( i = 1; i <= segments; i ++ ) {
+
+        var v1 = i;
+        var v2 = i + 1 ;
+        var v3 = 0;
+
+        this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
+        this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] );
+
+    }
+
+    this.computeCentroids();
+    this.computeFaceNormals();
+
+    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+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 );
+
+	var scope = this;
+
+	this.width = width;
+	this.height = height;
+	this.depth = depth;
+
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+	this.depthSegments = depthSegments || 1;
+
+	var width_half = this.width / 2;
+	var height_half = this.height / 2;
+	var depth_half = this.depth / 2;
+
+	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
+
+	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+
+		var w, ix, iy,
+		gridX = scope.widthSegments,
+		gridY = scope.heightSegments,
+		width_half = width / 2,
+		height_half = height / 2,
+		offset = scope.vertices.length;
+
+		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+
+			w = 'z';
+
+		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+
+			w = 'y';
+			gridY = scope.depthSegments;
+
+		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+
+			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 ( ix = 0; ix < gridX1; 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;
+
+				scope.vertices.push( vector );
+
+			}
+
+		}
+
+		for ( iy = 0; iy < gridY; iy++ ) {
+
+			for ( ix = 0; ix < gridX; ix++ ) {
+
+				var a = ix + gridX1 * iy;
+				var b = ix + gridX1 * ( iy + 1 );
+				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+				var d = ( ix + 1 ) + gridX1 * iy;
+
+				var face = new THREE.Face4( a + offset, b + offset, c + offset, d + offset );
+				face.normal.copy( normal );
+				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
+				face.materialIndex = materialIndex;
+
+				scope.faces.push( face );
+				scope.faceVertexUvs[ 0 ].push( [
+							new THREE.Vector2( ix / gridX, 1 - iy / gridY ),
+							new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ),
+							new THREE.Vector2( ( ix + 1 ) / gridX, 1- ( iy + 1 ) / gridY ),
+							new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY )
+						] );
+
+			}
+
+		}
+
+	}
+
+	this.computeCentroids();
+	this.mergeVertices();
+
+};
+
+THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded ) {
+
+	THREE.Geometry.call( this );
+
+	radiusTop = radiusTop !== undefined ? radiusTop : 20;
+	radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+	height = height !== undefined ? height : 100;
+
+	var heightHalf = height / 2;
+	var segmentsX = radiusSegments || 8;
+	var segmentsY = heightSegments || 1;
+
+	var x, y, vertices = [], uvs = [];
+
+	for ( y = 0; y <= segmentsY; y ++ ) {
+
+		var verticesRow = [];
+		var uvsRow = [];
+
+		var v = y / segmentsY;
+		var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
+
+		for ( x = 0; x <= segmentsX; x ++ ) {
+
+			var u = x / segmentsX;
+
+			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 );
+
+			this.vertices.push( vertex );
+
+			verticesRow.push( this.vertices.length - 1 );
+			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+
+		}
+
+		vertices.push( verticesRow );
+		uvs.push( uvsRow );
+
+	}
+
+	var tanTheta = ( radiusBottom - radiusTop ) / height;
+	var na, nb;
+
+	for ( x = 0; x < segmentsX; x ++ ) {
+
+		if ( radiusTop !== 0 ) {
+
+			na = this.vertices[ vertices[ 0 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
+
+		} else {
+
+			na = this.vertices[ vertices[ 1 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
+
+		}
+
+		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 < segmentsY; y ++ ) {
+
+			var v1 = vertices[ y ][ x ];
+			var v2 = vertices[ y + 1 ][ x ];
+			var v3 = vertices[ y + 1 ][ x + 1 ];
+			var v4 = vertices[ y ][ x + 1 ];
+
+			var n1 = na.clone();
+			var n2 = na.clone();
+			var n3 = nb.clone();
+			var n4 = nb.clone();
+
+			var uv1 = uvs[ y ][ x ].clone();
+			var uv2 = uvs[ y + 1 ][ x ].clone();
+			var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
+			var uv4 = uvs[ y ][ x + 1 ].clone();
+
+			this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] );
+
+		}
+
+	}
+
+	// top cap
+
+	if ( !openEnded && radiusTop > 0 ) {
+
+		this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
+
+		for ( x = 0; x < segmentsX; x ++ ) {
+
+			var v1 = vertices[ 0 ][ x ];
+			var v2 = vertices[ 0 ][ x + 1 ];
+			var v3 = this.vertices.length - 1;
+
+			var n1 = new THREE.Vector3( 0, 1, 0 );
+			var n2 = new THREE.Vector3( 0, 1, 0 );
+			var n3 = new THREE.Vector3( 0, 1, 0 );
+
+			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 ] );
+
+		}
+
+	}
+
+	// bottom cap
+
+	if ( !openEnded && radiusBottom > 0 ) {
+
+		this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+
+		for ( x = 0; x < segmentsX; x ++ ) {
+
+			var v1 = vertices[ y ][ x + 1 ];
+			var v2 = vertices[ y ][ x ];
+			var v3 = this.vertices.length - 1;
+
+			var n1 = new THREE.Vector3( 0, - 1, 0 );
+			var n2 = new THREE.Vector3( 0, - 1, 0 );
+			var n3 = new THREE.Vector3( 0, - 1, 0 );
+
+			var uv1 = uvs[ y ][ x + 1 ].clone();
+			var uv2 = uvs[ y ][ x ].clone();
+			var uv3 = new THREE.Vector2( uv2.u, 1 );
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+		}
+
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+}
+
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Creates extruded geometry from a path shape.
+ *
+ * parameters = {
+ *
+ *  size: <float>, // size of the text
+ *  height: <float>, // thickness to extrude text
+ *  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>, // Amount
+ *
+ *  bevelEnabled: <bool>, // turn on bevel
+ *  bevelThickness: <float>, // how deep into text bevel goes
+ *  bevelSize: <float>, // how far from text 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 ) {
+
+	if ( typeof( shapes ) === "undefined" ) {
+		shapes = [];
+		return;
+	}
+
+	THREE.Geometry.call( this );
+
+	shapes = shapes instanceof Array ? shapes : [ shapes ];
+
+	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+
+	this.addShapeList( shapes, options );
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+	// can't really use automatic vertex normals
+	// as then front and back sides get smoothed too
+	// should do separate smoothing just for sides
+
+	//this.computeVertexNormals();
+
+	//console.log( "took", ( Date.now() - startTime ) );
+
+};
+
+THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+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 );
+	}
+};
+
+THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
+
+	var amount = options.amount !== undefined ? options.amount : 100;
+
+	var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
+	var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
+	var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
+
+	var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
+
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+
+	var steps = options.steps !== undefined ? options.steps : 1;
+
+	var extrudePath = options.extrudePath;
+	var extrudePts, extrudeByPath = false;
+
+	var material = options.material;
+	var extrudeMaterial = options.extrudeMaterial;
+
+	// 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();
+
+
+
+	var splineTube, binormal, normal, position2;
+	if ( extrudePath ) {
+
+		extrudePts = extrudePath.getSpacedPoints( steps );
+
+		extrudeByPath = true;
+		bevelEnabled = false; // bevels not supported for path extrusion
+
+		// SETUP TNB variables
+
+		// Reuse TNB from TubeGeomtry for now.
+		// TODO1 - have a .isClosed in spline?
+
+		splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
+
+		// 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
+
+	if ( ! bevelEnabled ) {
+
+		bevelSegments = 0;
+		bevelThickness = 0;
+		bevelSize = 0;
+
+	}
+
+	// Variables initalization
+
+	var ahole, h, hl; // looping of holes
+	var scope = this;
+	var bevelPoints = [];
+
+	var shapesOffset = this.vertices.length;
+
+	var shapePoints = shape.extractPoints( curveSegments );
+
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
+
+	var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
+
+	if ( reverse ) {
+
+		vertices = vertices.reverse();
+
+		// Maybe we should also check if holes are in the opposite direction, just to be safe ...
+
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+			ahole = holes[ h ];
+
+			if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
+
+				holes[ h ] = ahole.reverse();
+
+			}
+
+		}
+
+		reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
+
+	}
+
+
+	var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
+
+	/* Vertices */
+
+	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 ];
+
+		vertices = vertices.concat( ahole );
+
+	}
+
+
+	function scalePt2 ( pt, vec, size ) {
+
+		if ( !vec ) console.log( "die" );
+
+		return vec.clone().multiplyScalar( size ).add( pt );
+
+	}
+
+	var b, bs, t, z,
+		vert, vlen = vertices.length,
+		face, flen = faces.length,
+		cont, clen = contour.length;
+
+
+	// Find directions for point movement
+
+	var RAD_TO_DEGREES = 180 / Math.PI;
+
+
+	function getBevelVec( pt_i, pt_j, pt_k ) {
+
+		// Algorithm 2
+
+		return getBevelVec2( pt_i, pt_j, pt_k );
+
+	}
+
+	function getBevelVec1( pt_i, pt_j, pt_k ) {
+
+		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 );
+
+		if ( anglea > angleb ) {
+
+			angleb += Math.PI * 2;
+
+		}
+
+		var anglec = ( anglea + angleb ) / 2;
+
+
+		//console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
+
+		var x = - Math.cos( anglec );
+		var y = - Math.sin( anglec );
+
+		var vec = new THREE.Vector2( x, y ); //.normalize();
+
+		return vec;
+
+	}
+
+	function getBevelVec2( pt_i, pt_j, pt_k ) {
+
+		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;
+
+		// 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
+
+		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 );
+
+		// get unit vectors
+
+		v = a.normalize();
+		w = b.normalize();
+
+		// normals from pt i
+
+		v_hat.set( -v.y, v.x );
+		w_hat.set( w.y, -w.x );
+
+		// pts from i
+
+		p.copy( pt_i ).add( v_hat );
+		q.copy( pt_i ).add( w_hat );
+
+		if ( p.equals( q ) ) {
+
+			//console.log("Warning: lines are straight");
+			return w_hat.clone();
+
+		}
+
+		// Points from j, k. helps prevents points cross overover most of the time
+
+		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 );
+
+		// We should not reach these conditions
+
+		if ( v_dot_w_hat === 0 ) {
+
+			console.log( "Either infinite or no solutions!" );
+
+			if ( q_sub_p_dot_w_hat === 0 ) {
+
+				console.log( "Its finite solutions." );
+
+			} else {
+
+				console.log( "Too bad, no solutions." );
+
+			}
+
+		}
+
+		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 );
+
+		}
+
+		intersection = v.multiplyScalar( s ).add( p );
+
+		return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
+
+	}
+
+	var contourMovements = [];
+
+	for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+
+		if ( j === il ) j = 0;
+		if ( k === il ) k = 0;
+
+		//  (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 ];
+
+		contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
+
+	}
+
+	var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
+
+	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+		ahole = holes[ h ];
+
+		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 ] );
+
+		}
+
+		holesMovements.push( oneHoleMovements );
+		verticesMovements = verticesMovements.concat( oneHoleMovements );
+
+	}
+
+
+	// 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 );
+			//vert = scalePt( contour[ i ], contourCentroid, bs, false );
+			v( vert.x, vert.y,  - z );
+
+		}
+
+		// expand holes
+
+		for ( h = 0, hl = holes.length; h < hl; h++ ) {
+
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
+
+			for ( i = 0, il = ahole.length; i < il; i++ ) {
+
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+				//vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
+
+				v( vert.x, vert.y,  -z );
+
+			}
+
+		}
+
+	}
+
+	bs = bevelSize;
+
+	// Back facing vertices
+
+	for ( i = 0; i < vlen; i ++ ) {
+
+		vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+
+		if ( !extrudeByPath ) {
+
+			v( vert.x, vert.y, 0 );
+
+		} else {
+
+			// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
+
+			normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
+			binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
+
+			position2.copy( extrudePts[0] ).add(normal).add(binormal);
+
+			v( position2.x, position2.y, position2.z );
+
+		}
+
+	}
+
+	// Add stepped vertices...
+	// Including front facing vertices
+
+	var s;
+
+	for ( s = 1; s <= steps; s ++ ) {
+
+		for ( i = 0; i < vlen; i ++ ) {
+
+			vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+
+			if ( !extrudeByPath ) {
+
+				v( vert.x, vert.y, amount / steps * s );
+
+			} else {
+
+				// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
+
+				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 );
+
+			}
+
+		}
+
+	}
+
+
+	// 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 ) ;
+
+		// 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 );
+
+		}
+
+		// expand holes
+
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
+
+			for ( i = 0, il = ahole.length; i < il; i ++ ) {
+
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+
+				if ( !extrudeByPath ) {
+
+					v( vert.x, vert.y,  amount + z );
+
+				} else {
+
+					v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
+
+				}
+
+			}
+
+		}
+
+	}
+
+	/* Faces */
+
+	// Top and bottom faces
+
+	buildLidFaces();
+
+	// Sides faces
+
+	buildSideFaces();
+
+
+	/////  Internal functions
+
+	function buildLidFaces() {
+
+		if ( bevelEnabled ) {
+
+			var layer = 0 ; // steps + 1
+			var offset = vlen * layer;
+
+			// Bottom faces
+
+			for ( i = 0; i < flen; i ++ ) {
+
+				face = faces[ i ];
+				f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
+
+			}
+
+			layer = steps + bevelSegments * 2;
+			offset = vlen * layer;
+
+			// Top faces
+
+			for ( i = 0; i < flen; i ++ ) {
+
+				face = faces[ i ];
+				f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
+
+			}
+
+		} else {
+
+			// Bottom faces
+
+			for ( i = 0; i < flen; i++ ) {
+
+				face = faces[ i ];
+				f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
+
+			}
+
+			// Top faces
+
+			for ( i = 0; i < flen; i ++ ) {
+
+				face = faces[ i ];
+				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
+
+			}
+		}
+
+	}
+
+	// Create faces for the z-sides of the shape
+
+	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;
+
+		}
+
+	}
+
+	function sidewalls( contour, layeroffset ) {
+
+		var j, k;
+		i = contour.length;
+
+		while ( --i >= 0 ) {
+
+			j = i;
+			k = i - 1;
+			if ( k < 0 ) k = contour.length - 1;
+
+			//console.log('b', i,j, i-1, k,vertices.length);
+
+			var s = 0, sl = steps  + bevelSegments * 2;
+
+			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 );
+
+			}
+		}
+
+	}
+
+
+	function v( x, y, z ) {
+
+		scope.vertices.push( new THREE.Vector3( x, y, z ) );
+
+	}
+
+	function f3( a, b, c, isBottom ) {
+
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
+
+		// normal, color, material
+		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+
+		var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c );
+
+ 		scope.faceVertexUvs[ 0 ].push( uvs );
+
+	}
+
+	function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
+
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
+		d += shapesOffset;
+
+ 		scope.faces.push( new THREE.Face4( a, b, c, d, null, null, extrudeMaterial ) );
+
+ 		var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d,
+ 		                                    stepIndex, stepsLength, contourIndex1, contourIndex2 );
+ 		scope.faceVertexUvs[ 0 ].push( uvs );
+
+	}
+
+};
+
+THREE.ExtrudeGeometry.WorldUVGenerator = {
+
+	generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
+		var ax = geometry.vertices[ indexA ].x,
+			ay = geometry.vertices[ indexA ].y,
+
+			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 )
+		];
+
+	},
+
+	generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
+
+		return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
+
+	},
+
+	generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
+	                              indexA, indexB, indexC, indexD, stepIndex, stepsLength,
+	                              contourIndex1, contourIndex2 ) {
+
+		var ax = geometry.vertices[ indexA ].x,
+			ay = geometry.vertices[ indexA ].y,
+			az = geometry.vertices[ indexA ].z,
+
+			bx = geometry.vertices[ indexB ].x,
+			by = geometry.vertices[ indexB ].y,
+			bz = geometry.vertices[ indexB ].z,
+
+			cx = geometry.vertices[ indexC ].x,
+			cy = geometry.vertices[ indexC ].y,
+			cz = geometry.vertices[ indexC ].z,
+
+			dx = geometry.vertices[ indexD ].x,
+			dy = geometry.vertices[ indexD ].y,
+			dz = geometry.vertices[ indexD ].z;
+
+		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 )
+			];
+		}
+	}
+};
+
+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();
+/**
+ * @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.ShapeGeometry = function ( shapes, options ) {
+
+	THREE.Geometry.call( this );
+
+	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+
+	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+
+	this.addShapeList( shapes, options );
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+};
+
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * Add an array of shapes to THREE.ShapeGeometry.
+ */
+THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
+
+	for ( var i = 0, l = shapes.length; i < l; i++ ) {
+
+		this.addShape( shapes[ i ], options );
+
+	}
+
+	return this;
+
+};
+
+/**
+ * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
+ */
+THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
+
+	if ( options === undefined ) options = {};
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+
+	var material = options.material;
+	var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
+
+	var shapebb = this.shapebb;
+
+	//
+
+	var i, l, hole, s;
+
+	var shapesOffset = this.vertices.length;
+	var shapePoints = shape.extractPoints( curveSegments );
+
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
+
+	var reverse = !THREE.Shape.Utils.isClockWise( vertices );
+
+	if ( reverse ) {
+
+		vertices = vertices.reverse();
+
+		// 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++ ) {
+
+			hole = holes[ i ];
+
+			if ( THREE.Shape.Utils.isClockWise( hole ) ) {
+
+				holes[ i ] = hole.reverse();
+
+			}
+
+		}
+
+		reverse = false;
+
+	}
+
+	var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
+
+	// Vertices
+
+	var contour = vertices;
+
+	for ( i = 0, l = holes.length; i < l; i++ ) {
+
+		hole = holes[ i ];
+		vertices = vertices.concat( hole );
+
+	}
+
+	//
+
+	var vert, vlen = vertices.length;
+	var face, flen = faces.length;
+	var cont, clen = contour.length;
+
+	for ( i = 0; i < vlen; i++ ) {
+
+		vert = vertices[ i ];
+
+		this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
+
+	}
+
+	for ( i = 0; i < flen; i++ ) {
+
+		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 ) );
+
+	}
+
+};
+/**
+ * @author astrodud / http://astrodud.isgreat.org/
+ * @author zz85 / https://github.com/zz85
+ * @author bhouston / http://exocortex.com
+ */
+
+// points - to create a closed torus, one must use a set of points 
+//    like so: [ a, b, c, d, a ], see first is the same as last.
+// segments - the number of circumference segments to create
+// phiStart - the starting radian
+// phiLength - the radian (0 to 2*PI) range of the lathed section
+//    2*pi is a closed lathe, less than 2PI is a portion.
+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 ++ ) {
+
+		var phi = phiStart + i * inverseSegments * phiLength;
+
+		var c = Math.cos( phi ),
+			s = Math.sin( phi );
+
+		for ( var j = 0, jl = points.length; j < jl; j ++ ) {
+
+			var pt = points[ j ];
+
+			var vertex = new THREE.Vector3();
+
+			vertex.x = c * pt.x - s * pt.y;
+			vertex.y = s * pt.x + c * pt.y;
+			vertex.z = pt.z;
+
+			this.vertices.push( vertex );
+
+		}
+
+	}
+
+	var np = points.length;
+
+	for ( var i = 0, il = segments; i < il; i ++ ) {
+
+		for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
+
+			var base = j + np * i;
+			var a = base;
+			var b = base + np;
+			var c = base + 1 + np;
+			var d = base + 1;
+
+			this.faces.push( new THREE.Face4( a, b, c, d ) );
+
+			var u0 = i * inverseSegments;
+			var v0 = j * inversePointLength;
+			var u1 = u0 + inverseSegments;
+			var v1 = v0 + inversePointLength;
+
+			this.faceVertexUvs[ 0 ].push( [
+
+				new THREE.Vector2( u0, v0 ), 
+				new THREE.Vector2( u1, v0 ),
+				new THREE.Vector2( u1, v1 ),
+				new THREE.Vector2( u0, v1 )
+
+			] );
+
+		}
+
+	}
+
+	this.mergeVertices();
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.LatheGeometry.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/Plane.as
+ */
+
+THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
+
+	THREE.Geometry.call( this );
+
+	this.width = width;
+	this.height = height;
+
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+
+	var ix, iz;
+	var width_half = width / 2;
+	var height_half = height / 2;
+
+	var gridX = this.widthSegments;
+	var gridZ = this.heightSegments;
+
+	var gridX1 = gridX + 1;
+	var gridZ1 = gridZ + 1;
+
+	var segment_width = this.width / gridX;
+	var segment_height = this.height / gridZ;
+
+	var normal = new THREE.Vector3( 0, 0, 1 );
+
+	for ( iz = 0; iz < gridZ1; iz ++ ) {
+
+		for ( ix = 0; ix < gridX1; ix ++ ) {
+
+			var x = ix * segment_width - width_half;
+			var y = iz * segment_height - height_half;
+
+			this.vertices.push( new THREE.Vector3( x, - y, 0 ) );
+
+		}
+
+	}
+
+	for ( iz = 0; iz < gridZ; iz ++ ) {
+
+		for ( ix = 0; ix < gridX; ix ++ ) {
+
+			var a = ix + gridX1 * iz;
+			var b = ix + gridX1 * ( iz + 1 );
+			var c = ( ix + 1 ) + gridX1 * ( iz + 1 );
+			var d = ( ix + 1 ) + gridX1 * iz;
+
+			var face = new THREE.Face4( a, b, c, d );
+			face.normal.copy( normal );
+			face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
+
+			this.faces.push( face );
+			this.faceVertexUvs[ 0 ].push( [
+				new THREE.Vector2( ix / gridX, 1 - iz / gridZ ),
+				new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ ),
+				new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ),
+				new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ )
+			] );
+
+		}
+
+	}
+
+	this.computeCentroids();
+
+};
+
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+
+	THREE.Geometry.call( this );
+
+	this.radius = radius || 50;
+
+	this.widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
+	this.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 = [];
+
+	for ( y = 0; y <= this.heightSegments; y ++ ) {
+
+		var verticesRow = [];
+		var uvsRow = [];
+
+		for ( x = 0; x <= this.widthSegments; x ++ ) {
+
+			var u = x / this.widthSegments;
+			var v = y / this.heightSegments;
+
+			var vertex = new THREE.Vector3();
+			vertex.x = - this.radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+			vertex.y = this.radius * Math.cos( thetaStart + v * thetaLength );
+			vertex.z = this.radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+
+			this.vertices.push( vertex );
+
+			verticesRow.push( this.vertices.length - 1 );
+			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+
+		}
+
+		vertices.push( verticesRow );
+		uvs.push( uvsRow );
+
+	}
+
+	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 ) {
+
+				this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+			} else {
+
+				this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] );
+
+			}
+
+		}
+
+	}
+
+	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 ) {
+
+	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;
+
+	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
+
+};
+
+THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
+/**
+ * @author oosmoxiecode
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
+ */
+
+THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
+
+	THREE.Geometry.call( this );
+
+	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 = [];
+
+	for ( var j = 0; j <= this.radialSegments; j ++ ) {
+
+		for ( var i = 0; i <= this.tubularSegments; i ++ ) {
+
+			var u = i / this.tubularSegments * this.arc;
+			var v = j / this.radialSegments * Math.PI * 2;
+
+			center.x = this.radius * Math.cos( u );
+			center.y = this.radius * Math.sin( u );
+
+			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 );
+
+			this.vertices.push( vertex );
+
+			uvs.push( new THREE.Vector2( i / this.tubularSegments, j / this.radialSegments ) );
+			normals.push( vertex.clone().sub( center ).normalize() );
+
+		}
+	}
+
+
+	for ( var j = 1; j <= this.radialSegments; j ++ ) {
+
+		for ( var i = 1; i <= this.tubularSegments; i ++ ) {
+
+			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;
+
+			var face = new THREE.Face4( a, b, c, d, [ normals[ a ], normals[ b ], normals[ c ], normals[ d ] ] );
+			face.normal.add( normals[ a ] );
+			face.normal.add( normals[ b ] );
+			face.normal.add( normals[ c ] );
+			face.normal.add( normals[ d ] );
+			face.normal.normalize();
+
+			this.faces.push( face );
+
+			this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
+		}
+
+	}
+
+	this.computeCentroids();
+
+};
+
+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 ) {
+
+		this.grid[ i ] = new Array( this.tubularSegments );
+
+		for ( var j = 0; j < this.tubularSegments; ++ j ) {
+
+			var u = i / this.radialSegments * 2 * this.p * Math.PI;
+			var v = j / this.tubularSegments * 2 * Math.PI;
+			var p1 = getPos( u, v, this.q, this.p, this.radius, this.heightScale );
+			var p2 = getPos( u + 0.01, v, this.q, this.p, this.radius, this.heightScale );
+			var cx, cy;
+
+			tang.subVectors( p2, p1 );
+			n.addVectors( p2, p1 );
+
+			bitan.crossVectors( tang, n );
+			n.crossVectors( bitan, tang );
+			bitan.normalize();
+			n.normalize();
+
+			cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			cy = this.tube * Math.sin( v );
+
+			p1.x += cx * n.x + cy * bitan.x;
+			p1.y += cx * n.y + cy * bitan.y;
+			p1.z += cx * n.z + cy * bitan.z;
+
+			this.grid[ i ][ j ] = vert( p1.x, p1.y, p1.z );
+
+		}
+
+	}
+
+	for ( var i = 0; i < this.radialSegments; ++ i ) {
+
+		for ( var j = 0; j < this.tubularSegments; ++ j ) {
+
+			var ip = ( i + 1 ) % this.radialSegments;
+			var jp = ( j + 1 ) % this.tubularSegments;
+
+			var a = this.grid[ i ][ j ];
+			var b = this.grid[ ip ][ j ];
+			var c = this.grid[ ip ][ jp ];
+			var d = this.grid[ i ][ jp ];
+
+			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.Face4( a, b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva,uvb,uvc, uvd ] );
+
+		}
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+	function vert( x, y, z ) {
+
+		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
+
+	}
+
+	function getPos( u, v, in_q, in_p, radius, heightScale ) {
+
+		var cu = Math.cos( u );
+		var cv = Math.cos( v );
+		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 );
+/**
+ * @author WestLangley / https://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author miningold / https://github.com/miningold
+ *
+ * Modified from the TorusKnotGeometry by @oosmoxiecode
+ *
+ * Creates a tube which extrudes along a 3d spline
+ *
+ * Uses parallel transport frames as described in
+ * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
+ */
+
+THREE.TubeGeometry = function( path, segments, radius, radiusSegments, closed, debug ) {
+
+	THREE.Geometry.call( this );
+
+	this.path = path;
+	this.segments = segments || 64;
+	this.radius = radius || 1;
+	this.radiusSegments = radiusSegments || 8;
+	this.closed = closed || false;
+
+	if ( debug ) this.debug = new THREE.Object3D();
+
+	this.grid = [];
+
+	var scope = this,
+
+		tangent,
+		normal,
+		binormal,
+
+		numpoints = this.segments + 1,
+
+		x, y, z,
+		tx, ty, tz,
+		u, v,
+
+		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( 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;
+
+	function vert( x, y, z ) {
+
+		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
+
+	}
+
+
+	// consruct the grid
+
+	for ( i = 0; i < numpoints; i++ ) {
+
+		this.grid[ i ] = [];
+
+		u = i / ( numpoints - 1 );
+
+		pos = path.getPointAt( u );
+
+		tangent = tangents[ i ];
+		normal = normals[ i ];
+		binormal = binormals[ i ];
+
+		if ( this.debug ) {
+
+			this.debug.add( new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff ) );
+			this.debug.add( new THREE.ArrowHelper(normal, pos, radius, 0xff0000 ) );
+			this.debug.add( new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00 ) );
+
+		}
+
+		for ( j = 0; j < this.radiusSegments; j++ ) {
+
+			v = j / this.radiusSegments * 2 * Math.PI;
+
+			cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			cy = this.radius * Math.sin( v );
+
+			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.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
+
+		}
+	}
+
+
+	// construct the mesh
+
+	for ( i = 0; i < this.segments; i++ ) {
+
+		for ( j = 0; j < this.radiusSegments; j++ ) {
+
+			ip = ( this.closed ) ? (i + 1) % this.segments : i + 1;
+			jp = (j + 1) % this.radiusSegments;
+
+			a = this.grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
+			b = this.grid[ ip ][ j ];
+			c = this.grid[ ip ][ jp ];
+			d = this.grid[ i ][ jp ];
+
+			uva = new THREE.Vector2( i / this.segments, j / this.radiusSegments );
+			uvb = new THREE.Vector2( ( i + 1 ) / this.segments, j / this.radiusSegments );
+			uvc = new THREE.Vector2( ( i + 1 ) / this.segments, ( j + 1 ) / this.radiusSegments );
+			uvd = new THREE.Vector2( i / this.segments, ( j + 1 ) / this.radiusSegments );
+
+			this.faces.push( new THREE.Face4( a, b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc, uvd ] );
+
+		}
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+
+// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
+THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) {
+
+	var	tangent = new THREE.Vector3(),
+		normal = new THREE.Vector3(),
+		binormal = new THREE.Vector3(),
+
+		tangents = [],
+		normals = [],
+		binormals = [],
+
+		vec = new THREE.Vector3(),
+		mat = new THREE.Matrix4(),
+
+		numpoints = segments + 1,
+		theta,
+		epsilon = 0.0001,
+		smallest,
+
+		tx, ty, tz,
+		i, u, v;
+
+
+	// expose internals
+	this.tangents = tangents;
+	this.normals = normals;
+	this.binormals = binormals;
+
+	// compute the tangent vectors for each segment on the path
+
+	for ( i = 0; i < numpoints; i++ ) {
+
+		u = i / ( numpoints - 1 );
+
+		tangents[ i ] = path.getTangentAt( u );
+		tangents[ i ].normalize();
+
+	}
+
+	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();
+	}
+
+	function initialNormal2() {
+
+		// This uses the Frenet-Serret formula for deriving binormal
+		var t2 = path.getTangentAt( epsilon );
+
+		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();
+
+	}
+
+	function initialNormal3() {
+		// select an initial normal vector perpenicular to the first tangent vector,
+		// and in the direction of the smallest tangent xyz component
+
+		normals[ 0 ] = new THREE.Vector3();
+		binormals[ 0 ] = new THREE.Vector3();
+		smallest = Number.MAX_VALUE;
+		tx = Math.abs( tangents[ 0 ].x );
+		ty = Math.abs( tangents[ 0 ].y );
+		tz = Math.abs( tangents[ 0 ].z );
+
+		if ( tx <= smallest ) {
+			smallest = tx;
+			normal.set( 1, 0, 0 );
+		}
+
+		if ( ty <= smallest ) {
+			smallest = ty;
+			normal.set( 0, 1, 0 );
+		}
+
+		if ( tz <= smallest ) {
+			normal.set( 0, 0, 1 );
+		}
+
+		vec.crossVectors( tangents[ 0 ], normal ).normalize();
+
+		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
+
+	for ( i = 1; i < numpoints; i++ ) {
+
+		normals[ i ] = normals[ i-1 ].clone();
+
+		binormals[ i ] = binormals[ i-1 ].clone();
+
+		vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
+
+		if ( vec.length() > epsilon ) {
+
+			vec.normalize();
+
+			theta = Math.acos( tangents[ i-1 ].dot( tangents[ i ] ) );
+
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
+
+		}
+
+		binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+
+	}
+
+
+	// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
+
+	if ( closed ) {
+
+		theta = Math.acos( normals[ 0 ].dot( normals[ numpoints-1 ] ) );
+		theta /= ( numpoints - 1 );
+
+		if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
+
+			theta = -theta;
+
+		}
+
+		for ( i = 1; i < numpoints; i++ ) {
+
+			// twist a little...
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
+			binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+
+		}
+
+	}
+};
+/**
+ * @author clockworkgeek / https://github.com/clockworkgeek
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) {
+
+	THREE.Geometry.call( this );
+
+	radius = radius || 1;
+	detail = detail || 0;
+
+	var that = this;
+
+	for ( var i = 0, l = vertices.length; i < l; i ++ ) {
+
+		prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) );
+
+	}
+
+	var midpoints = [], p = this.vertices;
+
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+		make( p[ faces[ i ][ 0 ] ], p[ faces[ i ][ 1 ] ], p[ faces[ i ][ 2 ] ], detail );
+
+	}
+
+	this.mergeVertices();
+
+	// Apply radius
+
+	for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
+
+		this.vertices[ i ].multiplyScalar( radius );
+
+	}
+
+
+	// Project vector onto sphere's surface
+
+	function prepare( vector ) {
+
+		var vertex = vector.normalize().clone();
+		vertex.index = that.vertices.push( vertex ) - 1;
+
+		// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
+
+		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 );
+
+		return vertex;
+
+	}
+
+
+	// Approximate a curved face with recursively sub-divided triangles.
+
+	function make( v1, v2, v3, detail ) {
+
+		if ( detail < 1 ) {
+
+			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 );
+			face.normal = face.centroid.clone().normalize();
+			that.faces.push( face );
+
+			var azi = azimuth( face.centroid );
+			that.faceVertexUvs[ 0 ].push( [
+				correctUV( v1.uv, v1, azi ),
+				correctUV( v2.uv, v2, azi ),
+				correctUV( v3.uv, v3, azi )
+			] );
+
+		} else {
+
+			detail -= 1;
+
+			// split triangle into 4 smaller triangles
+
+			make( v1, midpoint( v1, v2 ), midpoint( v1, v3 ), detail ); // top quadrant
+			make( midpoint( v1, v2 ), v2, midpoint( v2, v3 ), detail ); // left quadrant
+			make( midpoint( v1, v3 ), midpoint( v2, v3 ), v3, detail ); // right quadrant
+			make( midpoint( v1, v2 ), midpoint( v2, v3 ), midpoint( v1, v3 ), detail ); // center quadrant
+
+		}
+
+	}
+
+	function midpoint( v1, v2 ) {
+
+		if ( !midpoints[ v1.index ] ) midpoints[ v1.index ] = [];
+		if ( !midpoints[ v2.index ] ) midpoints[ v2.index ] = [];
+
+		var mid = midpoints[ v1.index ][ v2.index ];
+
+		if ( mid === undefined ) {
+
+			// generate mean point and project to surface with prepare()
+
+			midpoints[ v1.index ][ v2.index ] = midpoints[ v2.index ][ v1.index ] = mid = prepare(
+				new THREE.Vector3().addVectors( v1, v2 ).divideScalar( 2 )
+			);
+		}
+
+		return mid;
+
+	}
+
+
+	// Angle around the Y axis, counter-clockwise when looking from above.
+
+	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;
+
+	}
+
+	this.computeCentroids();
+
+    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.IcosahedronGeometry = function ( radius, detail ) {
+
+	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+
+	var vertices = [
+		[ -1,  t,  0 ], [  1, t, 0 ], [ -1, -t,  0 ], [  1, -t,  0 ],
+		[  0, -1,  t ], [  0, 1, t ], [  0, -1, -t ], [  0,  1, -t ],
+		[  t,  0, -1 ], [  t, 0, 1 ], [ -t,  0, -1 ], [ -t,  0,  1 ]
+	];
+
+	var 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 ]
+	];
+
+	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+
+};
+
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.OctahedronGeometry = function ( radius, detail ) {
+
+	var vertices = [
+		[ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ]
+	];
+
+	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 ]
+	];
+
+	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+};
+
+THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @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 ]
+	];
+
+	var faces = [
+		[ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ]
+	];
+
+	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+
+};
+
+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, useTris );
+ *
+ */
+
+THREE.ParametricGeometry = function ( func, slices, stacks, useTris ) {
+
+	THREE.Geometry.call( this );
+
+	var verts = this.vertices;
+	var faces = this.faces;
+	var uvs = this.faceVertexUvs[ 0 ];
+
+	useTris = (useTris === undefined) ? false : useTris;
+
+	var i, il, j, p;
+	var u, v;
+
+	var stackCount = stacks + 1;
+	var sliceCount = slices + 1;
+
+	for ( i = 0; i <= stacks; i ++ ) {
+
+		v = i / stacks;
+
+		for ( j = 0; j <= slices; j ++ ) {
+
+			u = j / slices;
+
+			p = func( u, v );
+			verts.push( p );
+
+		}
+	}
+
+	var a, b, c, d;
+	var uva, uvb, uvc, uvd;
+
+	for ( i = 0; i < stacks; i ++ ) {
+
+		for ( j = 0; j < slices; j ++ ) {
+
+			a = i * sliceCount + j;
+			b = i * sliceCount + j + 1;
+			c = (i + 1) * sliceCount + j;
+			d = (i + 1) * sliceCount + j + 1;
+
+			uva = new THREE.Vector2( j / slices, i / stacks );
+			uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
+			uvc = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
+			uvd = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
+
+			if ( useTris ) {
+
+				faces.push( new THREE.Face3( a, b, c ) );
+				faces.push( new THREE.Face3( b, d, c ) );
+
+				uvs.push( [ uva, uvb, uvc ] );
+				uvs.push( [ uvb, uvd, uvc ] );
+
+			} else {
+
+				faces.push( new THREE.Face4( a, b, d, c ) );
+				uvs.push( [ uva, uvb, uvd, uvc ] );
+
+			}
+
+		}
+
+	}
+
+	// console.log(this);
+
+	// magic bullet
+	// var diff = this.mergeVertices();
+	// console.log('removed ', diff, ' vertices by merging');
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author qiao / https://github.com/qiao
+ * @fileoverview This is a convex hull generator using the incremental method. 
+ * The complexity is O(n^2) where n is the number of vertices.
+ * O(nlogn) algorithms do exist, but they are much more complicated.
+ *
+ * Benchmark: 
+ *
+ *  Platform: CPU: P7350 @2.00GHz Engine: V8
+ *
+ *  Num Vertices	Time(ms)
+ *
+ *     10           1
+ *     20           3
+ *     30           19
+ *     40           48
+ *     50           107
+ */
+
+THREE.ConvexGeometry = function( vertices ) {
+
+	THREE.Geometry.call( this );
+
+	var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ]; 
+
+	for ( var i = 3; i < vertices.length; i++ ) {
+
+		addPoint( i );
+
+	}
+
+
+	function addPoint( vertexId ) {
+
+		var vertex = vertices[ vertexId ].clone();
+
+		var mag = vertex.length();
+		vertex.x += mag * randomOffset();
+		vertex.y += mag * randomOffset();
+		vertex.z += mag * randomOffset();
+
+		var hole = [];
+
+		for ( var f = 0; f < faces.length; ) {
+
+			var face = faces[ f ];
+
+			// for each face, if the vertex can see it,
+			// then we try to add the face's edges into the hole.
+			if ( visible( face, vertex ) ) {
+
+				for ( var e = 0; e < 3; e++ ) {
+
+					var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ];
+					var boundary = true;
+
+					// remove duplicated edges.
+					for ( var h = 0; h < hole.length; h++ ) {
+
+						if ( equalEdge( hole[ h ], edge ) ) {
+
+							hole[ h ] = hole[ hole.length - 1 ];
+							hole.pop();
+							boundary = false;
+							break;
+
+						}
+
+					}
+
+					if ( boundary ) {
+
+						hole.push( edge );
+
+					}
+
+				}
+
+				// remove faces[ f ]
+				faces[ f ] = faces[ faces.length - 1 ];
+				faces.pop();
+
+			} else { // not visible
+
+				f++;
+
+			}
+		}
+
+		// construct the new faces formed by the edges of the hole and the vertex
+		for ( var h = 0; h < hole.length; h++ ) {
+
+			faces.push( [ 
+				hole[ h ][ 0 ],
+				hole[ h ][ 1 ],
+				vertexId
+			] );
+
+		}
+	}
+
+	/**
+	 * Whether the face is visible from the vertex
+	 */
+	function visible( face, vertex ) {
+
+		var va = vertices[ face[ 0 ] ];
+		var vb = vertices[ face[ 1 ] ];
+		var vc = vertices[ face[ 2 ] ];
+
+		var n = normal( va, vb, vc );
+
+		// distance from face to origin
+		var dist = n.dot( va );
+
+		return n.dot( vertex ) >= dist; 
+
+	}
+
+	/**
+	 * Face normal
+	 */
+	function normal( va, vb, vc ) {
+
+		var cb = new THREE.Vector3();
+		var ab = new THREE.Vector3();
+
+		cb.subVectors( vc, vb );
+		ab.subVectors( va, vb );
+		cb.cross( ab );
+
+		cb.normalize();
+
+		return cb;
+
+	}
+
+	/**
+	 * Detect whether two edges are equal.
+	 * Note that when constructing the convex hull, two same edges can only
+	 * be of the negative direction.
+	 */
+	function equalEdge( ea, eb ) {
+
+		return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ]; 
+
+	}
+
+	/**
+	 * Create a random offset between -1e-6 and 1e-6.
+	 */
+	function randomOffset() {
+
+		return ( Math.random() - 0.5 ) * 2 * 1e-6;
+
+	}
+
+
+	/**
+	 * XXX: Not sure if this is the correct approach. Need someone to review.
+	 */
+	function vertexUv( vertex ) {
+
+		var mag = vertex.length();
+		return new THREE.Vector2( vertex.x / mag, vertex.y / mag );
+
+	}
+
+	// Push vertices into `this.vertices`, skipping those inside the hull
+	var id = 0;
+	var newId = new Array( vertices.length ); // map from old vertex id to new id
+
+	for ( var i = 0; i < faces.length; i++ ) {
+
+		 var face = faces[ i ];
+
+		 for ( var j = 0; j < 3; j++ ) {
+
+				if ( newId[ face[ j ] ] === undefined ) {
+
+						newId[ face[ j ] ] = id++;
+						this.vertices.push( vertices[ face[ j ] ] );
+
+				}
+
+				face[ j ] = newId[ face[ j ] ];
+
+		 }
+
+	}
+
+	// Convert faces into instances of THREE.Face3
+	for ( var i = 0; i < faces.length; i++ ) {
+
+		this.faces.push( new THREE.Face3( 
+				faces[ i ][ 0 ],
+				faces[ i ][ 1 ],
+				faces[ i ][ 2 ]
+		) );
+
+	}
+
+	// Compute UVs
+	for ( var i = 0; i < this.faces.length; i++ ) {
+
+		var face = this.faces[ i ];
+
+		this.faceVertexUvs[ 0 ].push( [
+			vertexUv( this.vertices[ face.a ] ),
+			vertexUv( this.vertices[ face.b ] ),
+			vertexUv( this.vertices[ face.c ])
+		] );
+
+	}
+
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );
+/**
+ * @author sroucheray / http://sroucheray.org/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AxisHelper = function ( size ) {
+
+	var geometry = new THREE.Geometry();
+
+	geometry.vertices.push(
+		new THREE.Vector3(), new THREE.Vector3( size || 1, 0, 0 ),
+		new THREE.Vector3(), new THREE.Vector3( 0, size || 1, 0 ),
+		new THREE.Vector3(), new THREE.Vector3( 0, 0, size || 1 )
+	);
+
+	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 )
+	);
+
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+
+};
+
+THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author bhouston / https://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 ) {
+
+	THREE.Object3D.call( this );
+
+	if ( length === undefined ) length = 20;
+	if ( hex === undefined ) hex = 0xffff00;
+
+	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.add( this.line );
+
+	var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 );
+
+	this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) );
+	this.cone.position.set( 0, 1, 0 );
+	this.add( this.cone );
+
+	if ( origin instanceof THREE.Vector3 ) this.position = origin;
+
+	this.setDirection( dir );
+	this.setLength( length );
+
+};
+
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.ArrowHelper.prototype.setDirection = function ( dir ) {
+
+    var d = THREE.ArrowHelper.__v1.copy( dir ).normalize();
+
+    if ( d.y > 0.999 ) {
+
+        this.rotation.set( 0, 0, 0 );
+ 
+    } else if ( d.y < - 0.999 ) {
+
+        this.rotation.set( Math.PI, 0, 0 );
+
+    } else {
+
+	    var axis = THREE.ArrowHelper.__v2.set( d.z, 0, - d.x ).normalize();
+	    var radians = Math.acos( d.y );
+	    var quaternion = THREE.ArrowHelper.__q1.setFromAxisAngle( axis, radians );
+
+	    this.rotation.setEulerFromQuaternion( quaternion, this.eulerOrder );
+
+	}
+
+};
+
+THREE.ArrowHelper.prototype.setLength = function ( length ) {
+
+	this.scale.set( length, length, length );
+
+};
+
+THREE.ArrowHelper.prototype.setColor = function ( hex ) {
+
+	this.line.material.color.setHex( hex );
+	this.cone.material.color.setHex( hex );
+
+};
+
+THREE.ArrowHelper.__v1 = new THREE.Vector3();
+THREE.ArrowHelper.__v2 = new THREE.Vector3();
+THREE.ArrowHelper.__q1 = new THREE.Quaternion();
+/**
+ * @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
+ */
+
+THREE.CameraHelper = function ( camera ) {
+
+	THREE.Line.call( this );
+
+	var scope = this;
+
+	this.geometry = new THREE.Geometry();
+	this.material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
+	this.type = THREE.LinePieces;
+
+	this.matrixWorld = camera.matrixWorld;
+	this.matrixAutoUpdate = false;
+
+	this.pointMap = {};
+
+	// colors
+
+	var hexFrustum = 0xffaa00;
+	var hexCone = 0xff0000;
+	var hexUp = 0x00aaff;
+	var hexTarget = 0xffffff;
+	var hexCross = 0x333333;
+
+	// near
+
+	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 );
+
+	// sides
+
+	addLine( "n1", "f1", hexFrustum );
+	addLine( "n2", "f2", hexFrustum );
+	addLine( "n3", "f3", hexFrustum );
+	addLine( "n4", "f4", hexFrustum );
+
+	// cone
+
+	addLine( "p", "n1", hexCone );
+	addLine( "p", "n2", hexCone );
+	addLine( "p", "n3", hexCone );
+	addLine( "p", "n4", hexCone );
+
+	// up
+
+	addLine( "u1", "u2", hexUp );
+	addLine( "u2", "u3", hexUp );
+	addLine( "u3", "u1", hexUp );
+
+	// target
+
+	addLine( "c", "t", hexTarget );
+	addLine( "p", "c", hexCross );
+
+	// cross
+
+	addLine( "cn1", "cn2", hexCross );
+	addLine( "cn3", "cn4", hexCross );
+
+	addLine( "cf1", "cf2", hexCross );
+	addLine( "cf3", "cf4", hexCross );
+
+	this.camera = camera;
+
+	function addLine( a, b, hex ) {
+
+		addPoint( a, hex );
+		addPoint( b, hex );
+
+	}
+
+	function addPoint( id, hex ) {
+
+		scope.geometry.vertices.push( new THREE.Vector3() );
+		scope.geometry.colors.push( new THREE.Color( hex ) );
+
+		if ( scope.pointMap[ id ] === undefined ) scope.pointMap[ id ] = [];
+
+		scope.pointMap[ id ].push( scope.geometry.vertices.length - 1 );
+
+	}
+
+	this.update( camera );
+
+};
+
+THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
+
+THREE.CameraHelper.prototype.update = function () {
+
+	var scope = this;
+
+	var w = 1, h = 1;
+
+	// we need just camera projection matrix
+	// world matrix must be identity
+
+	THREE.CameraHelper.__c.projectionMatrix.copy( this.camera.projectionMatrix );
+
+	// center / target
+
+	setPoint( "c", 0, 0, -1 );
+	setPoint( "t", 0, 0,  1 );
+
+	// near
+
+	setPoint( "n1", -w, -h, -1 );
+	setPoint( "n2",  w, -h, -1 );
+	setPoint( "n3", -w,  h, -1 );
+	setPoint( "n4",  w,  h, -1 );
+
+	// far
+
+	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 );
+
+	setPoint( "cn1", -w,  0, -1 );
+	setPoint( "cn2",  w,  0, -1 );
+	setPoint( "cn3",  0, -h, -1 );
+	setPoint( "cn4",  0,  h, -1 );
+
+	function setPoint( point, x, y, z ) {
+
+		THREE.CameraHelper.__v.set( x, y, z );
+		THREE.CameraHelper.__projector.unprojectVector( THREE.CameraHelper.__v, THREE.CameraHelper.__c );
+
+		var points = scope.pointMap[ point ];
+
+		if ( points !== undefined ) {
+
+			for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+				scope.geometry.vertices[ points[ i ] ].copy( THREE.CameraHelper.__v );
+
+			}
+
+		}
+
+	}
+
+	this.geometry.verticesNeedUpdate = true;
+
+};
+
+THREE.CameraHelper.__projector = new THREE.Projector();
+THREE.CameraHelper.__v = new THREE.Vector3();
+THREE.CameraHelper.__c = new THREE.Camera();
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- shows directional light color, intensity, position, orientation and target
+ */
+
+THREE.DirectionalLightHelper = function ( light, sphereSize ) {
+
+	THREE.Object3D.call( this );
+
+	this.light = light;
+
+	// position
+
+	this.position = light.position;
+
+	// direction
+
+	this.direction = new THREE.Vector3();
+	this.direction.subVectors( light.target.position, light.position );
+
+	// color
+
+	var intensity = THREE.Math.clamp( light.intensity, 0, 1 );
+
+	this.color = light.color.clone();
+	this.color.multiplyScalar( intensity );
+
+	var hexColor = this.color.getHex();
+
+	// light helper
+
+	var bulbGeometry = new THREE.SphereGeometry( sphereSize, 16, 8 );
+	var raysGeometry = new THREE.AsteriskGeometry( sphereSize * 1.25, sphereSize * 2.25 );
+
+	var bulbMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false } );
+	var raysMaterial = new THREE.LineBasicMaterial( { color: hexColor, fog: false } );
+
+	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+
+	this.lightRays = new THREE.Line( raysGeometry, raysMaterial, THREE.LinePieces );
+
+	this.add( this.lightSphere );
+	this.add( this.lightRays );
+
+	this.lightSphere.properties.isGizmo = true;
+	this.lightSphere.properties.gizmoSubject = light;
+	this.lightSphere.properties.gizmoRoot = this;
+
+	// light target helper
+
+	this.targetSphere = null;
+
+	if ( light.target.properties.targetInverse !== undefined ) {
+
+		var targetGeo = new THREE.SphereGeometry( sphereSize, 8, 4 );
+		var targetMaterial = new THREE.MeshBasicMaterial( { color: hexColor, wireframe: true, fog: false } );
+
+		this.targetSphere = new THREE.Mesh( targetGeo, targetMaterial );
+		this.targetSphere.position = light.target.position;
+
+		this.targetSphere.properties.isGizmo = true;
+		this.targetSphere.properties.gizmoSubject = light.target;
+		this.targetSphere.properties.gizmoRoot = this.targetSphere;
+
+		var lineMaterial = new THREE.LineDashedMaterial( { color: hexColor, dashSize: 4, gapSize: 4, opacity: 0.75, transparent: true, fog: false } );
+		var lineGeometry = new THREE.Geometry();
+		lineGeometry.vertices.push( this.position.clone() );
+		lineGeometry.vertices.push( this.targetSphere.position.clone() );
+		lineGeometry.computeLineDistances();
+
+		this.targetLine = new THREE.Line( lineGeometry, lineMaterial );
+		this.targetLine.properties.isGizmo = true;
+
+	}
+
+	//
+
+	this.properties.isGizmo = true;
+
+}
+
+THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.DirectionalLightHelper.prototype.update = function () {
+
+	// update arrow orientation
+	// pointing from light to target
+
+	this.direction.subVectors( this.light.target.position, this.light.position );
+
+	// update arrow, spheres, rays and line colors to light color * light intensity
+
+	var intensity = THREE.Math.clamp( this.light.intensity, 0, 1 );
+
+	this.color.copy( this.light.color );
+	this.color.multiplyScalar( intensity );
+
+	this.lightSphere.material.color.copy( this.color );
+	this.lightRays.material.color.copy( this.color );
+
+	// Only update targetSphere and targetLine if available
+	if ( this.targetSphere !== null ) {
+
+		this.targetSphere.material.color.copy( this.color );
+		this.targetLine.material.color.copy( this.color );
+
+		// update target line vertices
+
+		this.targetLine.geometry.vertices[ 0 ].copy( this.light.position );
+		this.targetLine.geometry.vertices[ 1 ].copy( this.light.target.position );
+
+		this.targetLine.geometry.computeLineDistances();
+		this.targetLine.geometry.verticesNeedUpdate = true;
+
+	}
+
+}
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- shows hemisphere light intensity, sky and ground colors and directions
+ */
+
+THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
+
+	THREE.Object3D.call( this );
+
+	this.light = light;
+
+	// position
+
+	this.position = light.position;
+
+	//
+
+	var intensity = THREE.Math.clamp( light.intensity, 0, 1 );
+
+	// sky color
+
+	this.color = light.color.clone();
+	this.color.multiplyScalar( intensity );
+
+	var hexColor = this.color.getHex();
+
+	// ground color
+
+	this.groundColor = light.groundColor.clone();
+	this.groundColor.multiplyScalar( intensity );
+
+	var hexColorGround = this.groundColor.getHex();
+
+	// double colored light bulb
+
+	var bulbGeometry = new THREE.SphereGeometry( sphereSize, 16, 8, 0, Math.PI * 2, 0, Math.PI * 0.5 );
+	var bulbGroundGeometry = new THREE.SphereGeometry( sphereSize, 16, 8, 0, Math.PI * 2, Math.PI * 0.5, Math.PI );
+
+	var bulbSkyMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false } );
+	var bulbGroundMaterial = new THREE.MeshBasicMaterial( { color: hexColorGround, fog: false } );
+
+	for ( var i = 0, il = bulbGeometry.faces.length; i < il; i ++ ) {
+
+		bulbGeometry.faces[ i ].materialIndex = 0;
+
+	}
+
+	for ( var i = 0, il = bulbGroundGeometry.faces.length; i < il; i ++ ) {
+
+		bulbGroundGeometry.faces[ i ].materialIndex = 1;
+
+	}
+
+	THREE.GeometryUtils.merge( bulbGeometry, bulbGroundGeometry );
+
+	this.lightSphere = new THREE.Mesh( bulbGeometry, new THREE.MeshFaceMaterial( [ bulbSkyMaterial, bulbGroundMaterial ] ) );
+
+	// arrows for sky and ground light directions
+
+	this.lightArrow = new THREE.ArrowHelper( new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, ( sphereSize + arrowLength ) * 1.1, 0 ), arrowLength, hexColor );
+	this.lightArrow.rotation.x = Math.PI;
+
+	this.lightArrowGround = new THREE.ArrowHelper( new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, ( sphereSize + arrowLength ) * -1.1, 0 ), arrowLength, hexColorGround );
+
+	var joint = new THREE.Object3D();
+	joint.rotation.x = -Math.PI * 0.5;
+
+	joint.add( this.lightSphere );
+	joint.add( this.lightArrow );
+	joint.add( this.lightArrowGround );
+
+	this.add( joint );
+
+	//
+
+	this.lightSphere.properties.isGizmo = true;
+	this.lightSphere.properties.gizmoSubject = light;
+	this.lightSphere.properties.gizmoRoot = this;
+
+	//
+
+	this.properties.isGizmo = true;
+
+	//
+
+	this.target = new THREE.Vector3();
+	this.lookAt( this.target );
+
+}
+
+THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.HemisphereLightHelper.prototype.update = function () {
+
+	// update sphere sky and ground colors to light color * light intensity
+
+	var intensity = THREE.Math.clamp( this.light.intensity, 0, 1 );
+
+	this.color.copy( this.light.color );
+	this.color.multiplyScalar( intensity );
+
+	this.groundColor.copy( this.light.groundColor );
+	this.groundColor.multiplyScalar( intensity );
+
+	this.lightSphere.material.materials[ 0 ].color.copy( this.color );
+	this.lightSphere.material.materials[ 1 ].color.copy( this.groundColor );
+
+	this.lightArrow.setColor( this.color.getHex() );
+	this.lightArrowGround.setColor( this.groundColor.getHex() );
+
+	this.lookAt( this.target );
+
+}
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- shows point light color, intensity, position and distance
+ */
+
+THREE.PointLightHelper = function ( light, sphereSize ) {
+
+	THREE.Object3D.call( this );
+
+	this.light = light;
+
+	// position
+
+	this.position = light.position;
+
+	// color
+
+	var intensity = THREE.Math.clamp( light.intensity, 0, 1 );
+
+	this.color = light.color.clone();
+	this.color.multiplyScalar( intensity );
+
+	var hexColor = this.color.getHex();
+
+	// light helper
+
+	var bulbGeometry = new THREE.SphereGeometry( sphereSize, 16, 8 );
+	var raysGeometry = new THREE.AsteriskGeometry( sphereSize * 1.25, sphereSize * 2.25 );
+	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
+
+	var bulbMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false } );
+	var raysMaterial = new THREE.LineBasicMaterial( { color: hexColor, fog: false } );
+	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
+
+	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+	this.lightRays = new THREE.Line( raysGeometry, raysMaterial, THREE.LinePieces );
+	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
+
+	var d = light.distance;
+
+	if ( d === 0.0 ) {
+
+		this.lightDistance.visible = false;
+
+	} else {
+
+		this.lightDistance.scale.set( d, d, d );
+
+	}
+
+	this.add( this.lightSphere );
+	this.add( this.lightRays );
+	this.add( this.lightDistance );
+
+	//
+
+	this.lightSphere.properties.isGizmo = true;
+	this.lightSphere.properties.gizmoSubject = light;
+	this.lightSphere.properties.gizmoRoot = this;
+
+	//
+
+	this.properties.isGizmo = true;
+
+}
+
+THREE.PointLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.PointLightHelper.prototype.update = function () {
+
+	// update sphere and rays colors to light color * light intensity
+
+	var intensity = THREE.Math.clamp( this.light.intensity, 0, 1 );
+
+	this.color.copy( this.light.color );
+	this.color.multiplyScalar( intensity );
+
+	this.lightSphere.material.color.copy( this.color );
+	this.lightRays.material.color.copy( this.color );
+	this.lightDistance.material.color.copy( this.color );
+
+	//
+
+	var d = this.light.distance;
+
+	if ( d === 0.0 ) {
+
+		this.lightDistance.visible = false;
+
+	} else {
+
+		this.lightDistance.visible = true;
+		this.lightDistance.scale.set( d, d, d );
+
+	}
+
+}
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- shows spot light color, intensity, position, orientation, light cone and target
+ */
+
+THREE.SpotLightHelper = function ( light, sphereSize ) {
+
+	THREE.Object3D.call( this );
+
+	this.light = light;
+
+	// position
+
+	this.position = light.position;
+
+	// direction
+
+	this.direction = new THREE.Vector3();
+	this.direction.subVectors( light.target.position, light.position );
+
+	// color
+
+	var intensity = THREE.Math.clamp( light.intensity, 0, 1 );
+
+	this.color = light.color.clone();
+	this.color.multiplyScalar( intensity );
+
+	var hexColor = this.color.getHex();
+
+	// light helper
+
+	var bulbGeometry = new THREE.SphereGeometry( sphereSize, 16, 8 );
+	var raysGeometry = new THREE.AsteriskGeometry( sphereSize * 1.25, sphereSize * 2.25 );
+	var coneGeometry = new THREE.CylinderGeometry( 0.0001, 1, 1, 8, 1, true );
+
+	var coneMatrix = new THREE.Matrix4();
+	coneMatrix.rotateX( -Math.PI/2 );
+	coneMatrix.translate( new THREE.Vector3( 0, -0.5, 0 ) );
+	coneGeometry.applyMatrix( coneMatrix );
+
+	var bulbMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false } );
+	var raysMaterial = new THREE.LineBasicMaterial( { color: hexColor, fog: false } );
+	var coneMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.3, transparent: true } );
+
+	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+	this.lightCone = new THREE.Mesh( coneGeometry, coneMaterial );
+
+	var coneLength = light.distance ? light.distance : 10000;
+	var coneWidth = coneLength * Math.tan( light.angle * 0.5 ) * 2;
+	this.lightCone.scale.set( coneWidth, coneWidth, coneLength );
+
+	this.lightRays = new THREE.Line( raysGeometry, raysMaterial, THREE.LinePieces );
+
+	this.gyroscope = new THREE.Gyroscope();
+
+	this.gyroscope.add( this.lightSphere );
+	this.gyroscope.add( this.lightRays );
+
+	this.add( this.gyroscope );
+	this.add( this.lightCone );
+
+	this.lookAt( light.target.position );
+
+	this.lightSphere.properties.isGizmo = true;
+	this.lightSphere.properties.gizmoSubject = light;
+	this.lightSphere.properties.gizmoRoot = this;
+
+	// light target helper
+
+	this.targetSphere = null;
+
+	if ( light.target.properties.targetInverse !== undefined ) {
+
+		var targetGeo = new THREE.SphereGeometry( sphereSize, 8, 4 );
+		var targetMaterial = new THREE.MeshBasicMaterial( { color: hexColor, wireframe: true, fog: false } );
+
+		this.targetSphere = new THREE.Mesh( targetGeo, targetMaterial );
+		this.targetSphere.position = light.target.position;
+
+		this.targetSphere.properties.isGizmo = true;
+		this.targetSphere.properties.gizmoSubject = light.target;
+		this.targetSphere.properties.gizmoRoot = this.targetSphere;
+
+		var lineMaterial = new THREE.LineDashedMaterial( { color: hexColor, dashSize: 4, gapSize: 4, opacity: 0.75, transparent: true, fog: false } );
+		var lineGeometry = new THREE.Geometry();
+		lineGeometry.vertices.push( this.position.clone() );
+		lineGeometry.vertices.push( this.targetSphere.position.clone() );
+		lineGeometry.computeLineDistances();
+
+		this.targetLine = new THREE.Line( lineGeometry, lineMaterial );
+		this.targetLine.properties.isGizmo = true;
+
+	}
+
+	//
+
+	this.properties.isGizmo = true;
+
+}
+
+THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.SpotLightHelper.prototype.update = function () {
+
+	// update arrow orientation
+	// pointing from light to target
+
+	this.direction.subVectors( this.light.target.position, this.light.position );
+
+	// update light cone orientation and size
+
+	this.lookAt( this.light.target.position );
+
+	var coneLength = this.light.distance ? this.light.distance : 10000;
+	var coneWidth = coneLength * Math.tan( this.light.angle * 0.5 ) * 2;
+	this.lightCone.scale.set( coneWidth, coneWidth, coneLength );
+
+	// update arrow, spheres, rays and line colors to light color * light intensity
+
+	var intensity = THREE.Math.clamp( this.light.intensity, 0, 1 );
+
+	this.color.copy( this.light.color );
+	this.color.multiplyScalar( intensity );
+
+	this.lightSphere.material.color.copy( this.color );
+	this.lightRays.material.color.copy( this.color );
+	this.lightCone.material.color.copy( this.color );
+
+	// Only update targetSphere and targetLine if available
+	if ( this.targetSphere !== null ) {
+
+		this.targetSphere.material.color.copy( this.color );
+		this.targetLine.material.color.copy( this.color );
+
+		// update target line vertices
+
+		this.targetLine.geometry.vertices[ 0 ].copy( this.light.position );
+		this.targetLine.geometry.vertices[ 1 ].copy( this.light.target.position );
+
+		this.targetLine.geometry.computeLineDistances();
+		this.targetLine.geometry.verticesNeedUpdate = true;
+
+	}
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ImmediateRenderObject = function () {
+
+	THREE.Object3D.call( this );
+
+	this.render = function ( renderCallback ) { };
+
+};
+
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+
+	THREE.Object3D.call( this );
+
+	this.lensFlares = [];
+
+	this.positionScreen = new THREE.Vector3();
+	this.customUpdateCallback = undefined;
+
+	if( texture !== undefined ) {
+
+		this.add( texture, size, distance, blending, color );
+
+	}
+
+};
+
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+
+
+/*
+ * Add: adds another flare
+ */
+
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+
+	if( size === undefined ) size = -1;
+	if( distance === undefined ) distance = 0;
+	if( opacity === undefined ) opacity = 1;
+	if( color === undefined ) color = new THREE.Color( 0xffffff );
+	if( blending === undefined ) blending = THREE.NormalBlending;
+
+	distance = Math.min( distance, Math.max( 0, distance ) );
+
+	this.lensFlares.push( { texture: texture, 			// THREE.Texture
+		                    size: size, 				// size in pixels (-1 = use texture.width)
+		                    distance: distance, 		// distance (0-1) from light source (0=at light source)
+		                    x: 0, y: 0, z: 0,			// screen position (-1 => 1) z = 0 is ontop z = 1 is back
+		                    scale: 1, 					// scale
+		                    rotation: 1, 				// rotation
+		                    opacity: opacity,			// opacity
+							color: color,				// color
+		                    blending: blending } );		// blending
+
+};
+
+
+/*
+ * Update lens flares update positions on all flares based on the screen position
+ * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
+ */
+
+THREE.LensFlare.prototype.updateLensFlares = function () {
+
+	var f, fl = this.lensFlares.length;
+	var flare;
+	var vecX = -this.positionScreen.x * 2;
+	var vecY = -this.positionScreen.y * 2;
+
+	for( f = 0; f < fl; f ++ ) {
+
+		flare = this.lensFlares[ f ];
+
+		flare.x = this.positionScreen.x + vecX * flare.distance;
+		flare.y = this.positionScreen.y + vecY * flare.distance;
+
+		flare.wantedRotation = flare.x * Math.PI * 0.25;
+		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+
+	}
+
+};
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MorphBlendMesh = function( geometry, material ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	this.animationsMap = {};
+	this.animationsList = [];
+
+	// prepare default animation
+	// (all frames played together in 1 second)
+
+	var numFrames = this.geometry.morphTargets.length;
+
+	var name = "__default";
+
+	var startFrame = 0;
+	var endFrame = numFrames - 1;
+
+	var fps = numFrames / 1;
+
+	this.createAnimation( name, startFrame, endFrame, fps );
+	this.setAnimationWeight( name, 1 );
+
+};
+
+THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
+
+THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
+
+	var animation = {
+
+		startFrame: start,
+		endFrame: end,
+
+		length: end - start + 1,
+
+		fps: fps,
+		duration: ( end - start ) / fps,
+
+		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 ) {
+
+	var pattern = /([a-z]+)(\d+)/;
+
+	var firstAnimation, frameRanges = {};
+
+	var geometry = this.geometry;
+
+	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+
+		var morph = geometry.morphTargets[ i ];
+		var chunks = morph.name.match( pattern );
+
+		if ( chunks && chunks.length > 1 ) {
+
+			var name = chunks[ 1 ];
+			var num = chunks[ 2 ];
+
+			if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity };
+
+			var range = frameRanges[ name ];
+
+			if ( i < range.start ) range.start = i;
+			if ( i > range.end ) range.end = i;
+
+			if ( ! firstAnimation ) firstAnimation = name;
+
+		}
+
+	}
+
+	for ( var name in frameRanges ) {
+
+		var range = frameRanges[ name ];
+		this.createAnimation( name, range.start, range.end, fps );
+
+	}
+
+	this.firstAnimation = firstAnimation;
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.direction = 1;
+		animation.directionBackwards = false;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.direction = -1;
+		animation.directionBackwards = true;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.fps = fps;
+		animation.duration = ( animation.end - animation.start ) / animation.fps;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.duration = duration;
+		animation.fps = ( animation.end - animation.start ) / animation.duration;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.weight = weight;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.time = time;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
+
+	var time = 0;
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		time = animation.time;
+
+	}
+
+	return time;
+
+};
+
+THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
+
+	var duration = -1;
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		duration = animation.duration;
+
+	}
+
+	return duration;
+
+};
+
+THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.time = 0;
+		animation.active = true;
+
+	} else {
+
+		console.warn( "animation[" + name + "] undefined" );
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
+
+	var animation = this.animationsMap[ name ];
+
+	if ( animation ) {
+
+		animation.active = false;
+
+	}
+
+};
+
+THREE.MorphBlendMesh.prototype.update = function ( delta ) {
+
+	for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
+
+		var animation = this.animationsList[ i ];
+
+		if ( ! animation.active ) continue;
+
+		var frameTime = animation.duration / animation.length;
+
+		animation.time += animation.direction * delta;
+
+		if ( animation.mirroredLoop ) {
+
+			if ( animation.time > animation.duration || animation.time < 0 ) {
+
+				animation.direction *= -1;
+
+				if ( animation.time > animation.duration ) {
+
+					animation.time = animation.duration;
+					animation.directionBackwards = true;
+
+				}
+
+				if ( animation.time < 0 ) {
+
+					animation.time = 0;
+					animation.directionBackwards = false;
+
+				}
+
+			}
+
+		} else {
+
+			animation.time = animation.time % animation.duration;
+
+			if ( animation.time < 0 ) animation.time += animation.duration;
+
+		}
+
+		var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
+		var weight = animation.weight;
+
+		if ( keyframe !== animation.currentFrame ) {
+
+			this.morphTargetInfluences[ animation.lastFrame ] = 0;
+			this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
+
+			this.morphTargetInfluences[ keyframe ] = 0;
+
+			animation.lastFrame = animation.currentFrame;
+			animation.currentFrame = keyframe;
+
+		}
+
+		var mix = ( animation.time % frameTime ) / frameTime;
+
+		if ( animation.directionBackwards ) mix = 1 - mix;
+
+		this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
+		this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
+
+	}
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlarePlugin = function () {
+
+	var _gl, _renderer, _precision, _lensFlare = {};
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		_precision = renderer.getPrecision();
+
+		_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.
+
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = -1;
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 0;
+
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
+
+		_lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1;
+		_lensFlare.vertices[ i++ ] = 0;  _lensFlare.vertices[ i++ ] = 1;
+
+		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
+
+		_lensFlare.vertexBuffer     = _gl.createBuffer();
+		_lensFlare.elementBuffer    = _gl.createBuffer();
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW );
+
+		// textures
+
+		_lensFlare.tempTexture      = _gl.createTexture();
+		_lensFlare.occlusionTexture = _gl.createTexture();
+
+		_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 );
+
+		if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
+
+			_lensFlare.hasVertexTexture = false;
+			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision );
+
+		} else {
+
+			_lensFlare.hasVertexTexture = true;
+			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision );
+
+		}
+
+		_lensFlare.attributes = {};
+		_lensFlare.uniforms = {};
+
+		_lensFlare.attributes.vertex       = _gl.getAttribLocation ( _lensFlare.program, "position" );
+		_lensFlare.attributes.uv           = _gl.getAttribLocation ( _lensFlare.program, "uv" );
+
+		_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" );
+
+	};
+
+
+	/*
+	 * 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.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+		var flares = scene.__webglFlares,
+			nFlares = flares.length;
+
+		if ( ! nFlares ) return;
+
+		var tempPosition = new THREE.Vector3();
+
+		var invAspect = viewportHeight / viewportWidth,
+			halfViewportWidth = viewportWidth * 0.5,
+			halfViewportHeight = viewportHeight * 0.5;
+
+		var size = 16 / viewportHeight,
+			scale = new THREE.Vector2( size * invAspect, size );
+
+		var screenPosition = new THREE.Vector3( 1, 1, 0 ),
+			screenPositionPixels = new THREE.Vector2( 1, 1 );
+
+		var uniforms = _lensFlare.uniforms,
+			attributes = _lensFlare.attributes;
+
+		// set _lensFlare program and reset blending
+
+		_gl.useProgram( _lensFlare.program );
+
+		_gl.enableVertexAttribArray( _lensFlare.attributes.vertex );
+		_gl.enableVertexAttribArray( _lensFlare.attributes.uv );
+
+		// 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 );
+
+		_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 );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
+
+		_gl.disable( _gl.CULL_FACE );
+		_gl.depthMask( false );
+
+		var i, j, jl, flare, sprite;
+
+		for ( i = 0; i < nFlares; i ++ ) {
+
+			size = 16 / viewportHeight;
+			scale.set( size * invAspect, size );
+
+			// calc object screen position
+
+			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 );
+
+			// setup arrays for gl programs
+
+			screenPosition.copy( tempPosition )
+
+			screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
+			screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
+
+			// screen cull
+
+			if ( _lensFlare.hasVertexTexture || (
+				screenPositionPixels.x > 0 &&
+				screenPositionPixels.x < viewportWidth &&
+				screenPositionPixels.y > 0 &&
+				screenPositionPixels.y < viewportHeight ) ) {
+
+				// save current RGB to temp texture
+
+				_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 );
+
+
+				// 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 );
+
+				_gl.disable( _gl.BLEND );
+				_gl.enable( _gl.DEPTH_TEST );
+
+				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+
+				// copy result to occlusionMap
+
+				_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 );
+
+
+				// restore graphics
+
+				_gl.uniform1i( uniforms.renderType, 1 );
+				_gl.disable( _gl.DEPTH_TEST );
+
+				_gl.activeTexture( _gl.TEXTURE1 );
+				_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
+				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+
+				// update object positions
+
+				flare.positionScreen.copy( screenPosition )
+
+				if ( flare.customUpdateCallback ) {
+
+					flare.customUpdateCallback( flare );
+
+				} else {
+
+					flare.updateLensFlares();
+
+				}
+
+				// render flares
+
+				_gl.uniform1i( uniforms.renderType, 2 );
+				_gl.enable( _gl.BLEND );
+
+				for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+
+					sprite = flare.lensFlares[ j ];
+
+					if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
+
+						screenPosition.x = sprite.x;
+						screenPosition.y = sprite.y;
+						screenPosition.z = sprite.z;
+
+						size = sprite.size * sprite.scale / viewportHeight;
+
+						scale.x = size * invAspect;
+						scale.y = size;
+
+						_gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+						_gl.uniform2f( uniforms.scale, scale.x, scale.y );
+						_gl.uniform1f( uniforms.rotation, sprite.rotation );
+
+						_gl.uniform1f( uniforms.opacity, sprite.opacity );
+						_gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
+
+						_renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
+						_renderer.setTexture( sprite.texture, 1 );
+
+						_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+					}
+
+				}
+
+			}
+
+		}
+
+		// restore gl
+
+		_gl.enable( _gl.CULL_FACE );
+		_gl.enable( _gl.DEPTH_TEST );
+		_gl.depthMask( true );
+
+	};
+
+	function createProgram ( shader, precision ) {
+
+		var program = _gl.createProgram();
+
+		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
+		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		var prefix = "precision " + precision + " float;\n";
+
+		_gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+		_gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+
+		_gl.compileShader( fragmentShader );
+		_gl.compileShader( vertexShader );
+
+		_gl.attachShader( program, fragmentShader );
+		_gl.attachShader( program, vertexShader );
+
+		_gl.linkProgram( program );
+
+		return program;
+
+	};
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ShadowMapPlugin = function () {
+
+	var _gl,
+	_renderer,
+	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4(),
+
+	_min = new THREE.Vector3(),
+	_max = new THREE.Vector3(),
+
+	_matrixPosition = new THREE.Vector3();
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+		_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 } );
+
+		_depthMaterial._shadowPass = true;
+		_depthMaterialMorph._shadowPass = true;
+		_depthMaterialSkin._shadowPass = true;
+		_depthMaterialMorphSkin._shadowPass = true;
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;
+
+		this.update( scene, camera );
+
+	};
+
+	this.update = function ( scene, camera ) {
+
+		var i, il, j, jl, n,
+
+		shadowMap, shadowMatrix, shadowCamera,
+		program, buffer, material,
+		webglObject, object, light,
+		renderList,
+
+		lights = [],
+		k = 0,
+
+		fog = null;
+
+		// set GL state for depth map
+
+		_gl.clearColor( 1, 1, 1, 1 );
+		_gl.disable( _gl.BLEND );
+
+		_gl.enable( _gl.CULL_FACE );
+		_gl.frontFace( _gl.CCW );
+
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+
+			_gl.cullFace( _gl.FRONT );
+
+		} else {
+
+			_gl.cullFace( _gl.BACK );
+
+		}
+
+		_renderer.setDepthTest( true );
+
+		// preprocess lights
+		// 	- skip lights that are not casting shadows
+		//	- create virtual lights for cascaded shadow maps
+
+		for ( i = 0, il = scene.__lights.length; i < il; i ++ ) {
+
+			light = scene.__lights[ i ];
+
+			if ( ! light.castShadow ) continue;
+
+			if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
+
+				for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
+
+					var virtualLight;
+
+					if ( ! light.shadowCascadeArray[ n ] ) {
+
+						virtualLight = createVirtualLight( light, n );
+						virtualLight.originalCamera = camera;
+
+						var gyro = new THREE.Gyroscope();
+						gyro.position = light.shadowCascadeOffset;
+
+						gyro.add( virtualLight );
+						gyro.add( virtualLight.target );
+
+						camera.add( gyro );
+
+						light.shadowCascadeArray[ n ] = virtualLight;
+
+						console.log( "Created virtualLight", virtualLight );
+
+					} else {
+
+						virtualLight = light.shadowCascadeArray[ n ];
+
+					}
+
+					updateVirtualLight( light, n );
+
+					lights[ k ] = virtualLight;
+					k ++;
+
+				}
+
+			} else {
+
+				lights[ k ] = light;
+				k ++;
+
+			}
+
+		}
+
+		// render depth map
+
+		for ( i = 0, il = lights.length; i < il; i ++ ) {
+
+			light = lights[ i ];
+
+			if ( ! light.shadowMap ) {
+
+				var shadowFilter = THREE.LinearFilter;
+
+				if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
+
+					shadowFilter = THREE.NearestFilter;
+
+				}
+
+				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
+
+				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
+				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
+
+				light.shadowMatrix = new THREE.Matrix4();
+
+			}
+
+			if ( ! light.shadowCamera ) {
+
+				if ( light instanceof THREE.SpotLight ) {
+
+					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
+
+				} else if ( light instanceof THREE.DirectionalLight ) {
+
+					light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
+
+				} else {
+
+					console.error( "Unsupported light type for shadow" );
+					continue;
+
+				}
+
+				scene.add( light.shadowCamera );
+
+				if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();
+
+			}
+
+			if ( light.shadowCameraVisible && ! light.cameraHelper ) {
+
+				light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
+				light.shadowCamera.add( light.cameraHelper );
+
+			}
+
+			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
+
+				updateShadowCamera( camera, light );
+
+			}
+
+			shadowMap = light.shadowMap;
+			shadowMatrix = light.shadowMatrix;
+			shadowCamera = light.shadowCamera;
+
+			shadowCamera.position.getPositionFromMatrix( light.matrixWorld );
+			_matrixPosition.getPositionFromMatrix( light.target.matrixWorld );
+			shadowCamera.lookAt( _matrixPosition );
+			shadowCamera.updateMatrixWorld();
+
+			shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
+
+			if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
+			if ( light.shadowCameraVisible ) light.cameraHelper.update();
+
+			// compute shadow matrix
+
+			shadowMatrix.set( 0.5, 0.0, 0.0, 0.5,
+							  0.0, 0.5, 0.0, 0.5,
+							  0.0, 0.0, 0.5, 0.5,
+							  0.0, 0.0, 0.0, 1.0 );
+
+			shadowMatrix.multiply( shadowCamera.projectionMatrix );
+			shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+
+			// update camera matrices and frustum
+
+			_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+			_frustum.setFromMatrix( _projScreenMatrix );
+
+			// render shadow map
+
+			_renderer.setRenderTarget( shadowMap );
+			_renderer.clear();
+
+			// set object matrices & frustum culling
+
+			renderList = scene.__webglObjects;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+				object = webglObject.object;
+
+				webglObject.render = false;
+
+				if ( object.visible && object.castShadow ) {
+
+					if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+						object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+
+						webglObject.render = true;
+
+					}
+
+				}
+
+			}
+
+			// render regular objects
+
+			var objectMaterial, useMorphing, useSkinning;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+
+				if ( webglObject.render ) {
+
+					object = webglObject.object;
+					buffer = webglObject.buffer;
+
+					// culling is overriden globally for all objects
+					// while rendering depth map
+
+					// 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 );
+
+					useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
+					useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+
+					if ( object.customDepthMaterial ) {
+
+						material = object.customDepthMaterial;
+
+					} else if ( useSkinning ) {
+
+						material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+
+					} else if ( useMorphing ) {
+
+						material = _depthMaterialMorph;
+
+					} else {
+
+						material = _depthMaterial;
+
+					}
+
+					if ( buffer instanceof THREE.BufferGeometry ) {
+
+						_renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object );
+
+					} else {
+
+						_renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object );
+
+					}
+
+				}
+
+			}
+
+			// set matrices and render immediate objects
+
+			renderList = scene.__webglObjectsImmediate;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+				object = webglObject.object;
+
+				if ( object.visible && object.castShadow ) {
+
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+
+					_renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object );
+
+				}
+
+			}
+
+		}
+
+		// restore GL state
+
+		var clearColor = _renderer.getClearColor(),
+		clearAlpha = _renderer.getClearAlpha();
+
+		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
+		_gl.enable( _gl.BLEND );
+
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+
+			_gl.cullFace( _gl.BACK );
+
+		}
+
+	};
+
+	function createVirtualLight( light, cascade ) {
+
+		var virtualLight = new THREE.DirectionalLight();
+
+		virtualLight.isVirtual = true;
+
+		virtualLight.onlyShadow = true;
+		virtualLight.castShadow = true;
+
+		virtualLight.shadowCameraNear = light.shadowCameraNear;
+		virtualLight.shadowCameraFar = light.shadowCameraFar;
+
+		virtualLight.shadowCameraLeft = light.shadowCameraLeft;
+		virtualLight.shadowCameraRight = light.shadowCameraRight;
+		virtualLight.shadowCameraBottom = light.shadowCameraBottom;
+		virtualLight.shadowCameraTop = light.shadowCameraTop;
+
+		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
+
+		virtualLight.shadowDarkness = light.shadowDarkness;
+
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+		virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
+		virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
+
+		virtualLight.pointsWorld = [];
+		virtualLight.pointsFrustum = [];
+
+		var pointsWorld = virtualLight.pointsWorld,
+			pointsFrustum = virtualLight.pointsFrustum;
+
+		for ( var i = 0; i < 8; i ++ ) {
+
+			pointsWorld[ i ] = new THREE.Vector3();
+			pointsFrustum[ i ] = new THREE.Vector3();
+
+		}
+
+		var nearZ = light.shadowCascadeNearZ[ cascade ];
+		var farZ = light.shadowCascadeFarZ[ cascade ];
+
+		pointsFrustum[ 0 ].set( -1, -1, nearZ );
+		pointsFrustum[ 1 ].set(  1, -1, nearZ );
+		pointsFrustum[ 2 ].set( -1,  1, nearZ );
+		pointsFrustum[ 3 ].set(  1,  1, nearZ );
+
+		pointsFrustum[ 4 ].set( -1, -1, farZ );
+		pointsFrustum[ 5 ].set(  1, -1, farZ );
+		pointsFrustum[ 6 ].set( -1,  1, farZ );
+		pointsFrustum[ 7 ].set(  1,  1, farZ );
+
+		return virtualLight;
+
+	}
+
+	// Synchronize virtual light with the original light
+
+	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;
+
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+
+		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
+
+	function updateShadowCamera( camera, light ) {
+
+		var shadowCamera = light.shadowCamera,
+			pointsFrustum = light.pointsFrustum,
+			pointsWorld = light.pointsWorld;
+
+		_min.set( Infinity, Infinity, Infinity );
+		_max.set( -Infinity, -Infinity, -Infinity );
+
+		for ( var i = 0; i < 8; i ++ ) {
+
+			var p = pointsWorld[ i ];
+
+			p.copy( pointsFrustum[ i ] );
+			THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera );
+
+			p.applyMatrix4( shadowCamera.matrixWorldInverse );
+
+			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;
+
+			if ( p.z < _min.z ) _min.z = p.z;
+			if ( p.z > _max.z ) _max.z = p.z;
+
+		}
+
+		shadowCamera.left = _min.x;
+		shadowCamera.right = _max.x;
+		shadowCamera.top = _max.y;
+		shadowCamera.bottom = _min.y;
+
+		// can't really fit near/far
+		//shadowCamera.near = _min.z;
+		//shadowCamera.far = _max.z;
+
+		shadowCamera.updateProjectionMatrix();
+
+	}
+
+	// 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;
+
+	};
+
+};
+
+THREE.ShadowMapPlugin.__projector = new THREE.Projector();
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpritePlugin = function () {
+
+	var _gl, _renderer, _precision, _sprite = {};
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		_precision = renderer.getPrecision();
+
+		_sprite.vertices = new Float32Array( 8 + 8 );
+		_sprite.faces    = new Uint16Array( 6 );
+
+		var i = 0;
+
+		_sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1;	// vertex 0
+		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 0;	// uv 0
+
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = -1;	// vertex 1
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 0;	// uv 1
+
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;	// vertex 2
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;	// uv 2
+
+		_sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1;	// vertex 3
+		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 1;	// uv 3
+
+		i = 0;
+
+		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2;
+		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3;
+
+		_sprite.vertexBuffer  = _gl.createBuffer();
+		_sprite.elementBuffer = _gl.createBuffer();
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW );
+
+		_sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ], _precision );
+
+		_sprite.attributes = {};
+		_sprite.uniforms = {};
+
+		_sprite.attributes.position           = _gl.getAttribLocation ( _sprite.program, "position" );
+		_sprite.attributes.uv                 = _gl.getAttribLocation ( _sprite.program, "uv" );
+
+		_sprite.uniforms.uvOffset             = _gl.getUniformLocation( _sprite.program, "uvOffset" );
+		_sprite.uniforms.uvScale              = _gl.getUniformLocation( _sprite.program, "uvScale" );
+
+		_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.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" );
+
+		_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" );
+
+		_sprite.uniforms.alphaTest 		  	  = _gl.getUniformLocation( _sprite.program, "alphaTest" );
+
+	};
+
+	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+		var sprites = scene.__webglSprites,
+			nSprites = sprites.length;
+
+		if ( ! nSprites ) return;
+
+		var attributes = _sprite.attributes,
+			uniforms = _sprite.uniforms;
+
+		var invAspect = viewportHeight / viewportWidth;
+
+		var halfViewportWidth = viewportWidth * 0.5,
+			halfViewportHeight = viewportHeight * 0.5;
+
+		// setup gl
+
+		_gl.useProgram( _sprite.program );
+
+		_gl.enableVertexAttribArray( attributes.position );
+		_gl.enableVertexAttribArray( attributes.uv );
+
+		_gl.disable( _gl.CULL_FACE );
+		_gl.enable( _gl.BLEND );
+
+		_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 );
+
+		_gl.activeTexture( _gl.TEXTURE0 );
+		_gl.uniform1i( uniforms.map, 0 );
+
+		var oldFogType = 0;
+		var sceneFogType = 0;
+		var fog = scene.fog;
+
+		if ( fog ) {
+
+			_gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
+
+			if ( fog instanceof THREE.Fog ) {
+
+				_gl.uniform1f( uniforms.fogNear, fog.near );
+				_gl.uniform1f( uniforms.fogFar, fog.far );
+
+				_gl.uniform1i( uniforms.fogType, 1 );
+				oldFogType = 1;
+				sceneFogType = 1;
+
+			} else if ( fog instanceof THREE.FogExp2 ) {
+
+				_gl.uniform1f( uniforms.fogDensity, fog.density );
+
+				_gl.uniform1i( uniforms.fogType, 2 );
+				oldFogType = 2;
+				sceneFogType = 2;
+
+			}
+
+		} else {
+
+			_gl.uniform1i( uniforms.fogType, 0 );
+			oldFogType = 0;
+			sceneFogType = 0;
+
+		}
+
+
+		// update positions and sort
+
+		var i, sprite, material, screenPosition, size, fogType, scale = [];
+
+		for( i = 0; i < nSprites; i ++ ) {
+
+			sprite = sprites[ i ];
+			material = sprite.material;
+
+			if ( ! sprite.visible || material.opacity === 0 ) continue;
+
+			if ( ! material.useScreenCoordinates ) {
+
+				sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+				sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
+
+			} else {
+
+				sprite.z = - sprite.position.z;
+
+			}
+
+		}
+
+		sprites.sort( painterSortStable );
+
+		// render all sprites
+
+		for( i = 0; i < nSprites; i ++ ) {
+
+			sprite = sprites[ i ];
+			material = sprite.material;
+
+			if ( ! sprite.visible || material.opacity === 0 ) continue;
+
+			if ( material.map && material.map.image && material.map.image.width ) {
+
+				_gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+
+				if ( material.useScreenCoordinates === true ) {
+
+					_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 ) )
+					);
+
+					scale[ 0 ] = _renderer.devicePixelRatio;
+					scale[ 1 ] = _renderer.devicePixelRatio;
+
+				} else {
+
+					_gl.uniform1i( uniforms.useScreenCoordinates, 0 );
+					_gl.uniform1i( uniforms.sizeAttenuation, material.sizeAttenuation ? 1 : 0 );
+					_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
+
+					scale[ 0 ] = 1;
+					scale[ 1 ] = 1;
+
+				}
+
+				if ( scene.fog && material.fog ) {
+
+					fogType = sceneFogType;
+
+				} else {
+
+					fogType = 0;
+
+				}
+
+				if ( oldFogType !== fogType ) {
+
+					_gl.uniform1i( uniforms.fogType, fogType );
+					oldFogType = fogType;
+
+				}
+
+				size = 1 / ( material.scaleByViewport ? viewportHeight : 1 );
+
+				scale[ 0 ] *= size * invAspect * sprite.scale.x
+				scale[ 1 ] *= size * sprite.scale.y;
+
+				_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 );
+
+				_gl.uniform1f( uniforms.rotation, sprite.rotation );
+				_gl.uniform2fv( uniforms.scale, scale );
+
+				_renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+				_renderer.setDepthTest( material.depthTest );
+				_renderer.setDepthWrite( material.depthWrite );
+				_renderer.setTexture( material.map, 0 );
+
+				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+			}
+
+		}
+
+		// restore gl
+
+		_gl.enable( _gl.CULL_FACE );
+
+	};
+
+	function createProgram ( shader, precision ) {
+
+		var program = _gl.createProgram();
+
+		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
+		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		var prefix = "precision " + precision + " float;\n";
+
+		_gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+		_gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+
+		_gl.compileShader( fragmentShader );
+		_gl.compileShader( vertexShader );
+
+		_gl.attachShader( program, fragmentShader );
+		_gl.attachShader( program, vertexShader );
+
+		_gl.linkProgram( program );
+
+		return program;
+
+	};
+
+	function painterSortStable ( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else {
+
+			return b.id - a.id;
+
+		}
+
+	};
+
+};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DepthPassPlugin = function () {
+
+	this.enabled = false;
+	this.renderTarget = null;
+
+	var _gl,
+	_renderer,
+	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4();
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+		_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 } );
+
+		_depthMaterial._shadowPass = true;
+		_depthMaterialMorph._shadowPass = true;
+		_depthMaterialSkin._shadowPass = true;
+		_depthMaterialMorphSkin._shadowPass = true;
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( ! this.enabled ) return;
+
+		this.update( scene, camera );
+
+	};
+
+	this.update = function ( scene, camera ) {
+
+		var i, il, j, jl, n,
+
+		program, buffer, material,
+		webglObject, object, light,
+		renderList,
+
+		fog = null;
+
+		// set GL state for depth map
+
+		_gl.clearColor( 1, 1, 1, 1 );
+		_gl.disable( _gl.BLEND );
+
+		_renderer.setDepthTest( true );
+
+		// update scene
+
+		if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
+
+		// render depth map
+
+		_renderer.setRenderTarget( this.renderTarget );
+		_renderer.clear();
+
+		// set object matrices & frustum culling
+
+		renderList = scene.__webglObjects;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+			object = webglObject.object;
+
+			webglObject.render = false;
+
+			if ( object.visible ) {
+
+				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+					object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+
+					webglObject.render = true;
+
+				}
+
+			}
+
+		}
+
+		// render regular objects
+
+		var objectMaterial, useMorphing, useSkinning;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+
+			if ( webglObject.render ) {
+
+				object = webglObject.object;
+				buffer = webglObject.buffer;
+
+				// todo: create proper depth material for particles
+
+				if ( object instanceof THREE.ParticleSystem && !object.customDepthMaterial ) continue;
+
+				objectMaterial = getObjectMaterial( object );
+
+				if ( objectMaterial ) _renderer.setMaterialFaces( object.material );
+
+				useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
+				useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+
+				if ( object.customDepthMaterial ) {
+
+					material = object.customDepthMaterial;
+
+				} else if ( useSkinning ) {
+
+					material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+
+				} else if ( useMorphing ) {
+
+					material = _depthMaterialMorph;
+
+				} else {
+
+					material = _depthMaterial;
+
+				}
+
+				if ( buffer instanceof THREE.BufferGeometry ) {
+
+					_renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object );
+
+				} else {
+
+					_renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object );
+
+				}
+
+			}
+
+		}
+
+		// set matrices and render immediate objects
+
+		renderList = scene.__webglObjectsImmediate;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+			object = webglObject.object;
+
+			if ( object.visible ) {
+
+				object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+
+				_renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object );
+
+			}
+
+		}
+
+		// restore GL state
+
+		var clearColor = _renderer.getClearColor(),
+		clearAlpha = _renderer.getClearAlpha();
+
+		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
+		_gl.enable( _gl.BLEND );
+
+	};
+
+	// For the moment just ignore objects that have multiple materials with different animation methods
+	// Only the first material will be taken into account for deciding which depth material to use
+
+	function getObjectMaterial( object ) {
+
+		return object.material instanceof THREE.MeshFaceMaterial
+			? object.material.materials[ 0 ]
+			: object.material;
+
+	};
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ *
+ */
+
+THREE.ShaderFlares = {
+
+	'lensFlareVertexTexture': {
+
+		vertexShader: [
+
+			"uniform lowp int renderType;",
+
+			"uniform vec3 screenPosition;",
+			"uniform vec2 scale;",
+			"uniform float rotation;",
+
+			"uniform sampler2D occlusionMap;",
+
+			"attribute vec2 position;",
+			"attribute vec2 uv;",
+
+			"varying vec2 vUV;",
+			"varying float vVisibility;",
+
+			"void main() {",
+
+				"vUV = uv;",
+
+				"vec2 pos = position;",
+
+				"if( renderType == 2 ) {",
+
+					"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.5, 0.1 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.9, 0.1 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.9, 0.9 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.1, 0.9 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.1, 0.5 ) ) +",
+									  "texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
+
+					"vVisibility = (       visibility.r / 9.0 ) *",
+								  "( 1.0 - visibility.g / 9.0 ) *",
+								  "(       visibility.b / 9.0 ) *",
+								  "( 1.0 - visibility.a / 9.0 );",
+
+					"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+					"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+
+				"}",
+
+				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform lowp int renderType;",
+
+			"uniform sampler2D map;",
+			"uniform float opacity;",
+			"uniform vec3 color;",
+
+			"varying vec2 vUV;",
+			"varying float vVisibility;",
+
+			"void main() {",
+
+				// pink square
+
+				"if( renderType == 0 ) {",
+
+					"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
+
+				// restore
+
+				"} else if( renderType == 1 ) {",
+
+					"gl_FragColor = texture2D( map, vUV );",
+
+				// flare
+
+				"} else {",
+
+					"vec4 texture = texture2D( map, vUV );",
+					"texture.a *= opacity * vVisibility;",
+					"gl_FragColor = texture;",
+					"gl_FragColor.rgb *= color;",
+
+				"}",
+
+			"}"
+		].join( "\n" )
+
+	},
+
+
+	'lensFlare': {
+
+		vertexShader: [
+
+			"uniform lowp int renderType;",
+
+			"uniform vec3 screenPosition;",
+			"uniform vec2 scale;",
+			"uniform float rotation;",
+
+			"attribute vec2 position;",
+			"attribute vec2 uv;",
+
+			"varying vec2 vUV;",
+
+			"void main() {",
+
+				"vUV = uv;",
+
+				"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;",
+
+				"}",
+
+				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"precision mediump float;",
+
+			"uniform lowp int renderType;",
+
+			"uniform sampler2D map;",
+			"uniform sampler2D occlusionMap;",
+			"uniform float opacity;",
+			"uniform vec3 color;",
+
+			"varying vec2 vUV;",
+
+			"void main() {",
+
+				// pink square
+
+				"if( renderType == 0 ) {",
+
+					"gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
+
+				// restore
+
+				"} else if( renderType == 1 ) {",
+
+					"gl_FragColor = texture2D( map, vUV );",
+
+				// flare
+
+				"} else {",
+
+					"float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a +",
+									   "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a +",
+									   "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a +",
+									   "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;",
+
+				"}",
+
+			"}"
+
+		].join( "\n" )
+
+	}
+
+};
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ */
+
+THREE.ShaderSprite = {
+
+	'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;",
+
+			"attribute vec2 position;",
+			"attribute vec2 uv;",
+
+			"varying vec2 vUV;",
+
+			"void main() {",
+
+				"vUV = uvOffset + uv * uvScale;",
+
+				"vec2 alignedPosition = position + alignment;",
+
+				"vec2 rotatedPosition;",
+				"rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;",
+				"rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;",
+
+				"vec4 finalPosition;",
+
+				"if( useScreenCoordinates != 0 ) {",
+
+					"finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );",
+
+				"} else {",
+
+					"finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );",
+					"finalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );",
+
+				"}",
+
+				"gl_Position = finalPosition;",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"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;",
+
+			"varying vec2 vUV;",
+
+			"void main() {",
+
+				"vec4 texture = texture2D( map, vUV );",
+
+				"if ( texture.a < alphaTest ) discard;",
+
+				"gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );",
+
+				"if ( fogType > 0 ) {",
+
+					"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+					"float fogFactor = 0.0;",
+
+					"if ( fogType == 1 ) {",
+
+						"fogFactor = smoothstep( fogNear, fogFar, depth );",
+
+					"} else {",
+
+						"const float LOG2 = 1.442695;",
+						"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
+						"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
+
+					"}",
+
+					"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
+
+				"}",
+
+			"}"
+
+		].join( "\n" )
+
+	}
+
+};
\ No newline at end of file
diff --git a/DUBREUIL/lib/three.min.js b/DUBREUIL/lib/three.min.js
new file mode 100644
index 0000000..c504d19
--- /dev/null
+++ b/DUBREUIL/lib/three.min.js
@@ -0,0 +1,706 @@
+// three.js - http://github.com/mrdoob/three.js
+'use strict';var THREE=THREE||{REVISION:"56"};self.console=self.console||{info:function(){},log:function(){},debug:function(){},warn:function(){},error:function(){}};self.Int32Array=self.Int32Array||Array;self.Float32Array=self.Float32Array||Array;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&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];void 0===window.requestAnimationFrame&&(window.requestAnimationFrame=function(b){var c=Date.now(),f=Math.max(0,16-(c-a)),g=window.setTimeout(function(){b(c+f)},f);a=c+f;return g});window.cancelAnimationFrame=window.cancelAnimationFrame||
+function(a){window.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};
+THREE.extend(THREE.Color.prototype,{r:1,g:1,b:1,set:function(a){switch(typeof a){case "number":this.setHex(a);break;case "string":this.setStyle(a)}},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},setHSV:function(a,b,c){console.log("DEPRECATED: Color's .setHSV() will be removed. Use .setHSL( h, s, l ) instead.");return this.setHSL(a,b*c/(1>(a=(2-b)*c)?a:2-a),a/2)},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=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),g,h=(f+e)/2;if(f===e)f=g=0;else{var i=e-f,f=0.5>=h?i/(e+f):i/(2-e-f);switch(e){case b:g=(c-d)/i+(c<d?6:0);break;case c:g=(d-b)/i+2;break;case d:g=(b-c)/i+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},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.extend(THREE.Quaternion.prototype,{set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=a.w;return this},setFromEuler:function(a,b){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);void 0===b||"XYZ"===b?(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"===b?(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"===b?(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"===b?(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"===b?(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"===b&&(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);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);return this},
+setFromRotationMatrix:function(a){var b=a.elements,c=b[0],a=b[4],d=b[8],e=b[1],f=b[5],g=b[9],h=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-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=(i-g)/c,this.x=0.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=0.25*c,this.z=(g+i)/c):(c=2*Math.sqrt(1+b-c-f),this.w=(e-a)/c,this.x=(d+h)/c,this.y=(g+i)/c,this.z=0.25*c);return this},inverse:function(){this.conjugate().normalize();
+return this},conjugate:function(){this.x*=-1;this.y*=-1;this.z*=-1;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,g=b.x,h=b.y,i=b.z,k=b.w;this.x=c*k+f*g+d*i-e*h;this.y=d*k+f*h+e*g-c*i;this.z=e*k+f*i+c*h-d*g;this.w=f*k-c*g-d*h-e*i;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,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),i=Math.sqrt(1-g*g);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;g=Math.sin((1-b)*h)/i;h=Math.sin(b*h)/i;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;return this},equals:function(a){return a.x===this.x&&a.y===
+this.y&&a.z===this.z&&a.w===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.extend(THREE.Vector2.prototype,{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?(this.x/=a,this.y/=a):this.set(0,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},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};
+THREE.extend(THREE.Vector3.prototype,{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,g=a.z,a=a.w,h=a*b+f*d-g*c,i=a*c+g*b-e*d,k=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=h*a+b*-e+i*-g-k*-f;this.y=i*a+b*-f+k*-e-h*-g;this.z=k*a+b*-g+h*-f-i*-e;return this},applyEuler:function(){var a=new THREE.Quaternion;return function(b,c){var d=a.setFromEuler(b,c);this.applyQuaternion(d);return this}}(),applyAxisAngle:function(){var a=
+new THREE.Quaternion;return function(b,c){var d=a.setFromAxisAngle(b,c);this.applyQuaternion(d);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?(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){this.x=a.y*b.z-a.z*b.y;this.y=a.z*b.x-a.x*b.z;this.z=a.x*b.y-a.y*b.x;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)}}(),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},getPositionFromMatrix:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];
+return this},setEulerFromRotationMatrix:function(a,b){function c(a){return Math.min(Math.max(a,-1),1)}var d=a.elements,e=d[0],f=d[4],g=d[8],h=d[1],i=d[5],k=d[9],l=d[2],m=d[6],d=d[10];void 0===b||"XYZ"===b?(this.y=Math.asin(c(g)),0.99999>Math.abs(g)?(this.x=Math.atan2(-k,d),this.z=Math.atan2(-f,e)):(this.x=Math.atan2(m,i),this.z=0)):"YXZ"===b?(this.x=Math.asin(-c(k)),0.99999>Math.abs(k)?(this.y=Math.atan2(g,d),this.z=Math.atan2(h,i)):(this.y=Math.atan2(-l,e),this.z=0)):"ZXY"===b?(this.x=Math.asin(c(m)),
+0.99999>Math.abs(m)?(this.y=Math.atan2(-l,d),this.z=Math.atan2(-f,i)):(this.y=0,this.z=Math.atan2(h,e))):"ZYX"===b?(this.y=Math.asin(-c(l)),0.99999>Math.abs(l)?(this.x=Math.atan2(m,d),this.z=Math.atan2(h,e)):(this.x=0,this.z=Math.atan2(-f,i))):"YZX"===b?(this.z=Math.asin(c(h)),0.99999>Math.abs(h)?(this.x=Math.atan2(-k,i),this.y=Math.atan2(-l,e)):(this.x=0,this.y=Math.atan2(g,d))):"XZY"===b&&(this.z=Math.asin(-c(f)),0.99999>Math.abs(f)?(this.x=Math.atan2(m,i),this.y=Math.atan2(g,e)):(this.x=Math.atan2(-k,
+d),this.y=0));return this},setEulerFromQuaternion:function(a,b){function c(a){return Math.min(Math.max(a,-1),1)}var d=a.x*a.x,e=a.y*a.y,f=a.z*a.z,g=a.w*a.w;void 0===b||"XYZ"===b?(this.x=Math.atan2(2*(a.x*a.w-a.y*a.z),g-d-e+f),this.y=Math.asin(c(2*(a.x*a.z+a.y*a.w))),this.z=Math.atan2(2*(a.z*a.w-a.x*a.y),g+d-e-f)):"YXZ"===b?(this.x=Math.asin(c(2*(a.x*a.w-a.y*a.z))),this.y=Math.atan2(2*(a.x*a.z+a.y*a.w),g-d-e+f),this.z=Math.atan2(2*(a.x*a.y+a.z*a.w),g-d+e-f)):"ZXY"===b?(this.x=Math.asin(c(2*(a.x*a.w+
+a.y*a.z))),this.y=Math.atan2(2*(a.y*a.w-a.z*a.x),g-d-e+f),this.z=Math.atan2(2*(a.z*a.w-a.x*a.y),g-d+e-f)):"ZYX"===b?(this.x=Math.atan2(2*(a.x*a.w+a.z*a.y),g-d-e+f),this.y=Math.asin(c(2*(a.y*a.w-a.x*a.z))),this.z=Math.atan2(2*(a.x*a.y+a.z*a.w),g+d-e-f)):"YZX"===b?(this.x=Math.atan2(2*(a.x*a.w-a.z*a.y),g-d+e-f),this.y=Math.atan2(2*(a.y*a.w-a.x*a.z),g+d-e-f),this.z=Math.asin(c(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-d+e-f),this.y=Math.atan2(2*(a.x*a.z+a.y*a.w),g+d-
+e-f),this.z=Math.asin(c(2*(a.z*a.w-a.x*a.y))));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},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},toArray:function(){return[this.x,this.y,this.z]},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.extend(THREE.Vector4.prototype,{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?(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],g=a[1],h=a[5],i=a[9];c=a[2];b=a[6];var k=a[10];if(0.01>Math.abs(d-g)&&0.01>Math.abs(f-c)&&0.01>Math.abs(i-b)){if(0.1>Math.abs(d+g)&&0.1>Math.abs(f+c)&&0.1>Math.abs(i+b)&&0.1>Math.abs(e+h+k-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;h=(h+1)/2;k=(k+1)/2;d=(d+g)/4;f=(f+c)/4;i=(i+b)/4;e>h&&e>k?0.01>e?(b=0,d=c=0.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):h>k?0.01>h?(b=0.707106781,c=0,d=0.707106781):(c=Math.sqrt(h),
+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)+(g-d)*(g-d));0.001>Math.abs(a)&&(a=1);this.x=(b-i)/a;this.y=(f-c)/a;this.z=(g-d)/a;this.w=Math.acos((e+h+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},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.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
+THREE.extend(THREE.Line3.prototype,{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,
+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.extend(THREE.Box2.prototype,{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.extend(THREE.Box3.prototype,{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),b.z<this.min.z?this.min.z=b.z:b.z>this.max.z&&(this.max.z=b.z)}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}}(),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,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,g,h,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,g||0,h||0,void 0!==i?i:1)};
+THREE.extend(THREE.Matrix3.prototype,{set:function(a,b,c,d,e,f,g,h,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]=g;k[5]=h;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],g=a[5],h=a[6],i=a[7],a=a[8];return b*f*a-b*g*i-c*e*a+c*g*h+d*e*i-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");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,g,h,i,k,l,m,n,s,r,p){var q=this.elements=new Float32Array(16);q[0]=void 0!==a?a:1;q[4]=b||0;q[8]=c||0;q[12]=d||0;q[1]=e||0;q[5]=void 0!==f?f:1;q[9]=g||0;q[13]=h||0;q[2]=i||0;q[6]=k||0;q[10]=void 0!==l?l:1;q[14]=m||0;q[3]=n||0;q[7]=s||0;q[11]=r||0;q[15]=void 0!==p?p:1};
+THREE.extend(THREE.Matrix4.prototype,{set:function(a,b,c,d,e,f,g,h,i,k,l,m,n,s,r,p){var q=this.elements;q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=h;q[2]=i;q[6]=k;q[10]=l;q[14]=m;q[3]=n;q[7]=s;q[11]=r;q[15]=p;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){a=a.elements;this.set(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]);return this},setRotationFromEuler:function(a,b){var c=this.elements,
+d=a.x,e=a.y,f=a.z,g=Math.cos(d),d=Math.sin(d),h=Math.cos(e),e=Math.sin(e),i=Math.cos(f),f=Math.sin(f);if(void 0===b||"XYZ"===b){var k=g*i,l=g*f,m=d*i,n=d*f;c[0]=h*i;c[4]=-h*f;c[8]=e;c[1]=l+m*e;c[5]=k-n*e;c[9]=-d*h;c[2]=n-k*e;c[6]=m+l*e;c[10]=g*h}else"YXZ"===b?(k=h*i,l=h*f,m=e*i,n=e*f,c[0]=k+n*d,c[4]=m*d-l,c[8]=g*e,c[1]=g*f,c[5]=g*i,c[9]=-d,c[2]=l*d-m,c[6]=n+k*d,c[10]=g*h):"ZXY"===b?(k=h*i,l=h*f,m=e*i,n=e*f,c[0]=k-n*d,c[4]=-g*f,c[8]=m+l*d,c[1]=l+m*d,c[5]=g*i,c[9]=n-k*d,c[2]=-g*e,c[6]=d,c[10]=g*h):
+"ZYX"===b?(k=g*i,l=g*f,m=d*i,n=d*f,c[0]=h*i,c[4]=m*e-l,c[8]=k*e+n,c[1]=h*f,c[5]=n*e+k,c[9]=l*e-m,c[2]=-e,c[6]=d*h,c[10]=g*h):"YZX"===b?(k=g*h,l=g*e,m=d*h,n=d*e,c[0]=h*i,c[4]=n-k*f,c[8]=m*f+l,c[1]=f,c[5]=g*i,c[9]=-d*i,c[2]=-e*i,c[6]=l*f+m,c[10]=k-n*f):"XZY"===b&&(k=g*h,l=g*e,m=d*h,n=d*e,c[0]=h*i,c[4]=-f,c[8]=e*i,c[1]=k*f+n,c[5]=g*i,c[9]=l*f-m,c[2]=m*f-l,c[6]=d*i,c[10]=n*f+k);return this},setRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,g=c+c,h=d+d,i=e+e,a=c*g,k=c*h,
+c=c*i,l=d*h,d=d*i,e=e*i,g=f*g,h=f*h,f=f*i;b[0]=1-(l+e);b[4]=k-f;b[8]=c+h;b[1]=k+f;b[5]=1-(a+e);b[9]=d-g;b[2]=c-h;b[6]=d+g;b[10]=1-(a+l);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?(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],g=c[4],h=c[8],i=c[12],k=c[1],l=c[5],m=c[9],n=c[13],s=c[2],r=c[6],p=c[10],q=c[14],y=c[3],v=c[7],z=c[11],c=c[15],t=d[0],A=d[4],I=d[8],C=d[12],x=d[1],G=d[5],J=d[9],
+E=d[13],H=d[2],B=d[6],W=d[10],F=d[14],K=d[3],L=d[7],U=d[11],d=d[15];e[0]=f*t+g*x+h*H+i*K;e[4]=f*A+g*G+h*B+i*L;e[8]=f*I+g*J+h*W+i*U;e[12]=f*C+g*E+h*F+i*d;e[1]=k*t+l*x+m*H+n*K;e[5]=k*A+l*G+m*B+n*L;e[9]=k*I+l*J+m*W+n*U;e[13]=k*C+l*E+m*F+n*d;e[2]=s*t+r*x+p*H+q*K;e[6]=s*A+r*G+p*B+q*L;e[10]=s*I+r*J+p*W+q*U;e[14]=s*C+r*E+p*F+q*d;e[3]=y*t+v*x+z*H+c*K;e[7]=y*A+v*G+z*B+c*L;e[11]=y*I+v*J+z*W+c*U;e[15]=y*C+v*E+z*F+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){var b=this.elements,c=a.x,d=a.y,e=a.z;a.x=c*b[0]+d*b[4]+e*b[8];a.y=
+c*b[1]+d*b[5]+e*b[9];a.z=c*b[2]+d*b[6]+e*b[10];a.normalize();return a},crossVector:function(a){var b=this.elements,c=new THREE.Vector4;c.x=b[0]*a.x+b[4]*a.y+b[8]*a.z+b[12]*a.w;c.y=b[1]*a.x+b[5]*a.y+b[9]*a.z+b[13]*a.w;c.z=b[2]*a.x+b[6]*a.y+b[10]*a.z+b[14]*a.w;c.w=a.w?b[3]*a.x+b[7]*a.y+b[11]*a.z+b[15]*a.w:1;return c},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],i=a[13],k=a[2],l=a[6],m=a[10],n=a[14];return a[3]*(+e*h*l-d*i*l-e*g*m+c*i*m+d*g*n-c*h*n)+a[7]*
+(+b*h*n-b*i*m+e*f*m-d*f*n+d*i*k-e*h*k)+a[11]*(+b*i*l-b*g*n-e*f*l+c*f*n+e*g*k-c*i*k)+a[15]*(-d*g*k-b*h*l+b*g*m+d*f*l-c*f*m+c*h*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],g=d[8],h=d[12],i=d[1],k=d[5],l=d[9],m=d[13],n=d[2],s=d[6],r=d[10],p=d[14],q=d[3],y=d[7],v=d[11],z=d[15];c[0]=l*p*y-m*r*y+m*s*v-k*p*v-l*s*z+k*r*z;c[4]=h*r*y-g*p*y-h*s*v+f*p*v+g*s*z-f*r*z;c[8]=g*m*y-h*l*y+h*k*v-f*m*v-g*k*z+f*l*z;c[12]=h*l*s-g*m*s-h*k*r+f*m*r+g*k*p-f*l*p;c[1]=m*r*q-l*p*q-
+m*n*v+i*p*v+l*n*z-i*r*z;c[5]=g*p*q-h*r*q+h*n*v-e*p*v-g*n*z+e*r*z;c[9]=h*l*q-g*m*q-h*i*v+e*m*v+g*i*z-e*l*z;c[13]=g*m*n-h*l*n+h*i*r-e*m*r-g*i*p+e*l*p;c[2]=k*p*q-m*s*q+m*n*y-i*p*y-k*n*z+i*s*z;c[6]=h*s*q-f*p*q-h*n*y+e*p*y+f*n*z-e*s*z;c[10]=f*m*q-h*k*q+h*i*y-e*m*y-f*i*z+e*k*z;c[14]=h*k*n-f*m*n-h*i*s+e*m*s+f*i*p-e*k*p;c[3]=l*s*q-k*r*q-l*n*y+i*r*y+k*n*v-i*s*v;c[7]=f*r*q-g*s*q+g*n*y-e*r*y-f*n*v+e*s*v;c[11]=g*k*q-f*l*q-g*i*y+e*l*y+f*i*v-e*k*v;c[15]=f*l*n-g*k*n+g*i*s-e*l*s-f*i*r+e*k*r;c=d[0]*c[0]+d[1]*c[4]+
+d[2]*c[8]+d[3]*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},compose:function(){var a=new THREE.Matrix4,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements;a.identity();a.setRotationFromQuaternion(d);b.makeScale(e.x,e.y,e.z);this.multiplyMatrices(a,b);f[12]=c.x;f[13]=c.y;f[14]=c.z;return this}}(),decompose:function(){var a=
+new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Matrix4;return function(e,f,g){var h=this.elements;a.set(h[0],h[1],h[2]);b.set(h[4],h[5],h[6]);c.set(h[8],h[9],h[10]);e=e instanceof THREE.Vector3?e:new THREE.Vector3;f=f instanceof THREE.Quaternion?f:new THREE.Quaternion;g=g instanceof THREE.Vector3?g:new THREE.Vector3;g.x=a.length();g.y=b.length();g.z=c.length();e.x=h[12];e.y=h[13];e.z=h[14];d.copy(this);d.elements[0]/=g.x;d.elements[1]/=g.x;d.elements[2]/=g.x;d.elements[4]/=
+g.y;d.elements[5]/=g.y;d.elements[6]/=g.y;d.elements[8]/=g.z;d.elements[9]/=g.z;d.elements[10]/=g.z;f.setFromRotationMatrix(d);return[e,f,g]}}(),extractPosition: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}}(),translate:function(a){var b=this.elements,c=a.x,d=a.y,a=a.z;b[12]=b[0]*c+b[4]*d+b[8]*a+b[12];b[13]=b[1]*c+b[5]*d+b[9]*a+b[13];b[14]=b[2]*c+b[6]*d+b[10]*a+b[14];b[15]=b[3]*c+b[7]*d+b[11]*a+b[15];return this},rotateX:function(a){var b=this.elements,c=b[4],d=b[5],e=b[6],f=b[7],g=b[8],h=b[9],i=b[10],k=b[11],l=Math.cos(a),a=Math.sin(a);b[4]=l*c+a*g;b[5]=l*d+a*h;b[6]=l*e+a*i;b[7]=l*f+a*k;b[8]=l*g-a*c;b[9]=l*h-a*d;b[10]=
+l*i-a*e;b[11]=l*k-a*f;return this},rotateY:function(a){var b=this.elements,c=b[0],d=b[1],e=b[2],f=b[3],g=b[8],h=b[9],i=b[10],k=b[11],l=Math.cos(a),a=Math.sin(a);b[0]=l*c-a*g;b[1]=l*d-a*h;b[2]=l*e-a*i;b[3]=l*f-a*k;b[8]=l*g+a*c;b[9]=l*h+a*d;b[10]=l*i+a*e;b[11]=l*k+a*f;return this},rotateZ:function(a){var b=this.elements,c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],k=b[7],l=Math.cos(a),a=Math.sin(a);b[0]=l*c+a*g;b[1]=l*d+a*h;b[2]=l*e+a*i;b[3]=l*f+a*k;b[4]=l*g-a*c;b[5]=l*h-a*d;b[6]=l*i-a*e;b[7]=l*
+k-a*f;return this},rotateByAxis:function(a,b){var c=this.elements;if(1===a.x&&0===a.y&&0===a.z)return this.rotateX(b);if(0===a.x&&1===a.y&&0===a.z)return this.rotateY(b);if(0===a.x&&0===a.y&&1===a.z)return this.rotateZ(b);var d=a.x,e=a.y,f=a.z,g=Math.sqrt(d*d+e*e+f*f),d=d/g,e=e/g,f=f/g,g=d*d,h=e*e,i=f*f,k=Math.cos(b),l=Math.sin(b),m=1-k,n=d*e*m,s=d*f*m,m=e*f*m,d=d*l,r=e*l,l=f*l,f=g+(1-g)*k,g=n+l,e=s-r,n=n-l,h=h+(1-h)*k,l=m+d,s=s+r,m=m-d,i=i+(1-i)*k,k=c[0],d=c[1],r=c[2],p=c[3],q=c[4],y=c[5],v=c[6],
+z=c[7],t=c[8],A=c[9],I=c[10],C=c[11];c[0]=f*k+g*q+e*t;c[1]=f*d+g*y+e*A;c[2]=f*r+g*v+e*I;c[3]=f*p+g*z+e*C;c[4]=n*k+h*q+l*t;c[5]=n*d+h*y+l*A;c[6]=n*r+h*v+l*I;c[7]=n*p+h*z+l*C;c[8]=s*k+m*q+i*t;c[9]=s*d+m*y+i*A;c[10]=s*r+m*v+i*I;c[11]=s*p+m*z+i*C;return this},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,i=e*f,k=e*g;this.set(i*f+c,i*g-d*h,i*h+d*g,0,i*g+d*h,k*g+c,k*h-d*f,0,i*h-d*g,k*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},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){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 g=this.elements,h=b-a,i=c-d,k=f-e;g[0]=2/h;g[4]=0;g[8]=0;g[12]=-((b+a)/h);g[1]=0;g[5]=2/i;g[9]=0;g[13]=-((c+d)/i);g[2]=0;g[6]=0;g[10]=-2/k;g[14]=-((f+e)/k);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},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};
+THREE.extend(THREE.Ray.prototype,{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 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);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},isIntersectionPlane:function(a){return 0!=a.normal.dot(this.direction)||0==a.distanceToPoint(this.origin)?!0:!1},distanceToPlane:function(a){var b=
+a.normal.dot(this.direction);if(0==b){if(0==a.distanceToPoint(this.origin))return 0}else return-(this.origin.dot(a.normal)+a.constant)/b},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return void 0===c?void 0:this.at(c,b)},applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);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.extend(THREE.Sphere.prototype,{set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromCenterAndPoints:function(a,b){for(var c=0,d=0,e=b.length;d<e;d++)var f=a.distanceToSquared(b[d]),c=Math.max(c,f);this.center=a;this.radius=Math.sqrt(c);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.extend(THREE.Frustum.prototype,{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],d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],i=c[6],k=c[7],l=c[8],m=c[9],n=c[10],s=c[11],r=c[12],p=c[13],q=c[14],c=c[15];b[0].setComponents(f-a,k-g,s-l,c-r).normalize();b[1].setComponents(f+
+a,k+g,s+l,c+r).normalize();b[2].setComponents(f+d,k+h,s+m,c+p).normalize();b[3].setComponents(f-d,k-h,s-m,c-p).normalize();b[4].setComponents(f-e,k-i,s-n,c-q).normalize();b[5].setComponents(f+e,k+i,s+n,c+q).normalize();return this},intersectsObject:function(){var a=new THREE.Vector3;return function(b){var c=b.matrixWorld,d=this.planes,b=-b.geometry.boundingSphere.radius*c.getMaxScaleOnAxis();a.getPositionFromMatrix(c);for(c=0;6>c;c++)if(d[c].distanceToPoint(a)<b)return!1;return!0}}(),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},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.extend(THREE.Plane.prototype,{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).getInverse(c).transpose(),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={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,g){a=0.5*(c-a);d=0.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,i,k,l,m,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;k=this.points[c[0]];l=this.points[c[1]];m=this.points[c[2]];n=this.points[c[3]];h=g*g;i=g*h;d.x=b(k.x,l.x,m.x,n.x,g,h,i);d.y=b(k.y,l.y,m.y,n.y,g,h,i);d.z=b(k.z,l.z,m.z,n.z,g,h,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,g=new THREE.Vector3,h=[],i=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),i+=g.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(h[b]=i,e=b);h[h.length]=i;return{chunks:h,total:i}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,g,h=[],i=new THREE.Vector3,k=this.getLength();h.push(i.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=k.chunks[b]-k.chunks[b-1];g=Math.ceil(a*c/k.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<g-1;c++)d=e+c*(1/g)*(f-e),d=this.getPoint(d),
+h.push(i.copy(d).clone());h.push(i.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);var d=a.dot(a),e=a.dot(b),f=a.dot(c),i=b.dot(b),g=b.dot(c),k=d*i-e*e,h=h||new THREE.Vector3;if(0==k)return h.set(-2,-1,-1);k=1/k;i=(i*f-e*g)*k;d=(d*g-e*f)*k;return h.set(1-i-d,d,i)}}();
+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.extend(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||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};
+THREE.extend(THREE.Clock.prototype,{start:function(){this.oldTime=this.startTime=void 0!==window.performance&&void 0!==window.performance.now?window.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!==window.performance&&void 0!==window.performance.now?window.performance.now():Date.now(),
+a=0.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}});THREE.EventDispatcher=function(){var a={};this.addEventListener=function(b,c){void 0===a[b]&&(a[b]=[]);-1===a[b].indexOf(c)&&a[b].push(c)};this.removeEventListener=function(b,c){var d=a[b].indexOf(c);-1!==d&&a[b].splice(d,1)};this.dispatchEvent=function(b){var c=a[b.type];if(void 0!==c){b.target=this;for(var d=0,e=c.length;d<e;d++)c[d].call(this,b)}}};(function(a){a.Raycaster=function(b,c,d,e){this.ray=new a.Ray(b,c);0<this.ray.direction.lengthSq()&&this.ray.direction.normalize();this.near=d||0;this.far=e||Infinity};var b=new a.Sphere,c=new a.Ray,d=new a.Plane,e=new a.Vector3,f=new a.Vector3,g=new a.Matrix4,h=function(a,b){return a.distance-b.distance},i=function(h,i,k){if(h instanceof a.Particle){f.getPositionFromMatrix(h.matrixWorld);i=i.ray.distanceToPoint(f);if(i>h.scale.x)return k;k.push({distance:i,point:h.position,face:null,object:h})}else if(h instanceof
+a.Mesh){f.getPositionFromMatrix(h.matrixWorld);b.set(f,h.geometry.boundingSphere.radius*h.matrixWorld.getMaxScaleOnAxis());if(!i.ray.isIntersectionSphere(b))return k;var s=h.geometry,r=s.vertices,p=h.material instanceof a.MeshFaceMaterial,q=!0===p?h.material.materials:null,y=h.material.side,v,z,t,A=i.precision;h.matrixRotationWorld.extractRotation(h.matrixWorld);g.getInverse(h.matrixWorld);c.copy(i.ray).applyMatrix4(g);for(var I=0,C=s.faces.length;I<C;I++){var x=s.faces[I],y=!0===p?q[x.materialIndex]:
+h.material;if(void 0!==y){d.setFromNormalAndCoplanarPoint(x.normal,r[x.a]);var G=c.distanceToPlane(d);if(!(Math.abs(G)<A)&&!(0>G)){y=y.side;if(y!==a.DoubleSide&&(v=c.direction.dot(d.normal),!(y===a.FrontSide?0>v:0<v)))continue;if(!(G<i.near||G>i.far)){e=c.at(G,e);if(x instanceof a.Face3){if(y=r[x.a],v=r[x.b],z=r[x.c],!a.Triangle.containsPoint(e,y,v,z))continue}else if(x instanceof a.Face4){if(y=r[x.a],v=r[x.b],z=r[x.c],t=r[x.d],!a.Triangle.containsPoint(e,y,v,t)&&!a.Triangle.containsPoint(e,v,z,t))continue}else throw Error("face type not supported");
+k.push({distance:G,point:i.ray.at(G),face:x,faceIndex:I,object:h})}}}}}},k=function(a,b,c){for(var a=a.getDescendants(),d=0,e=a.length;d<e;d++)i(a[d],b,c)};a.Raycaster.prototype.precision=1E-4;a.Raycaster.prototype.set=function(a,b){this.ray.set(a,b);0<this.ray.direction.length()&&this.ray.direction.normalize()};a.Raycaster.prototype.intersectObject=function(a,b){var c=[];!0===b&&k(a,this,c);i(a,this,c);c.sort(h);return c};a.Raycaster.prototype.intersectObjects=function(a,b){for(var c=[],d=0,e=a.length;d<
+e;d++)i(a[d],this,c),!0===b&&k(a[d],this,c);c.sort(h);return c}})(THREE);THREE.Object3D=function(){this.id=THREE.Object3DIdCount++;this.name="";this.properties={};this.parent=void 0;this.children=[];this.up=new THREE.Vector3(0,1,0);this.position=new THREE.Vector3;this.rotation=new THREE.Vector3;this.eulerOrder=THREE.Object3D.defaultEulerOrder;this.scale=new THREE.Vector3(1,1,1);this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;this.matrixRotationWorld=new THREE.Matrix4;this.matrixWorldNeedsUpdate=this.matrixAutoUpdate=
+!0;this.quaternion=new THREE.Quaternion;this.useQuaternion=!1;this.visible=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this._vector=new THREE.Vector3};
+THREE.Object3D.prototype={constructor:THREE.Object3D,applyMatrix:function(a){this.matrix.multiplyMatrices(a,this.matrix);this.scale.getScaleFromMatrix(this.matrix);a=(new THREE.Matrix4).extractRotation(this.matrix);this.rotation.setEulerFromRotationMatrix(a,this.eulerOrder);this.position.getPositionFromMatrix(this.matrix)},translate:function(a,b){this.matrix.rotateAxis(b);this.position.add(b.multiplyScalar(a))},translateX:function(a){this.translate(a,this._vector.set(1,0,0))},translateY:function(a){this.translate(a,
+this._vector.set(0,1,0))},translateZ:function(a){this.translate(a,this._vector.set(0,0,1))},localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(a){return a.applyMatrix4(THREE.Object3D.__m1.getInverse(this.matrixWorld))},lookAt:function(a){this.matrix.lookAt(a,this.position,this.up);this.rotationAutoUpdate&&(!1===this.useQuaternion?this.rotation.setEulerFromRotationMatrix(this.matrix,this.eulerOrder):this.quaternion.copy(this.matrix.decompose()[1]))},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;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;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)},getChildByName: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.getChildByName(a,b),void 0!==e))return e}},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.setPosition(this.position);
+!1===this.useQuaternion?this.matrix.setRotationFromEuler(this.rotation,this.eulerOrder):this.matrix.setRotationFromQuaternion(this.quaternion);(1!==this.scale.x||1!==this.scale.y||1!==this.scale.z)&&this.matrix.scale(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){void 0===a&&(a=new THREE.Object3D);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.rotation instanceof THREE.Vector3&&a.rotation.copy(this.rotation);a.eulerOrder=this.eulerOrder;a.scale.copy(this.scale);a.renderDepth=this.renderDepth;a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);a.matrixWorld.copy(this.matrixWorld);a.matrixRotationWorld.copy(this.matrixRotationWorld);
+a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.quaternion.copy(this.quaternion);a.useQuaternion=this.useQuaternion;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;for(var b=0;b<this.children.length;b++)a.add(this.children[b].clone());return a}};THREE.Object3D.__m1=new THREE.Matrix4;THREE.Object3D.defaultEulerOrder="XYZ";THREE.Object3DIdCount=0;THREE.Projector=function(){function a(){if(f===h){var a=new THREE.RenderableObject;g.push(a);h++;f++;return a}return g[f++]}function b(){if(k===m){var a=new THREE.RenderableVertex;l.push(a);m++;k++;return a}return l[k++]}function c(a,b){return b.z-a.z}function d(a,b){var c=0,d=1,e=a.z+a.w,f=b.z+b.w,g=-a.z+a.w,h=-b.z+b.w;if(0<=e&&0<=f&&0<=g&&0<=h)return!0;if(0>e&&0>f||0>g&&0>h)return!1;0>e?c=Math.max(c,e/(e-f)):0>f&&(d=Math.min(d,e/(e-f)));0>g?c=Math.max(c,g/(g-h)):0>h&&(d=Math.min(d,g/(g-h)));if(d<
+c)return!1;a.lerp(b,c);b.lerp(a,1-d);return!0}var e,f,g=[],h=0,i,k,l=[],m=0,n,s,r=[],p=0,q,y=[],v=0,z,t,A=[],I=0,C,x,G=[],J=0,E={objects:[],sprites:[],lights:[],elements:[]},H=new THREE.Vector3,B=new THREE.Vector4,W=new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)),F=new THREE.Box3,K=Array(3),L=Array(4),U=new THREE.Matrix4,fa=new THREE.Matrix4,Ca,$a=new THREE.Matrix4,M=new THREE.Matrix3,ca=new THREE.Matrix3,qa=new THREE.Vector3,ha=new THREE.Frustum,ra=new THREE.Vector4,N=new THREE.Vector4;
+this.projectVector=function(a,b){b.matrixWorldInverse.getInverse(b.matrixWorld);fa.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);return a.applyProjection(fa)};this.unprojectVector=function(a,b){b.projectionMatrixInverse.getInverse(b.projectionMatrix);fa.multiplyMatrices(b.matrixWorld,b.projectionMatrixInverse);return a.applyProjection(fa)};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)};this.projectScene=function(g,h,m,Pa){var ta=!1,ka,aa,pa,Y,da,la,Z,oa,gb,nb,ia,Wa,ab;x=t=q=s=0;E.elements.length=0;g.updateMatrixWorld();void 0===h.parent&&h.updateMatrixWorld();U.copy(h.matrixWorldInverse.getInverse(h.matrixWorld));fa.multiplyMatrices(h.projectionMatrix,U);ca.getInverse(U);ca.transpose();ha.setFromMatrix(fa);f=0;E.objects.length=0;E.sprites.length=0;E.lights.length=0;var Fa=function(b){for(var c=0,d=b.children.length;c<d;c++){var f=b.children[c];if(!1!==f.visible){if(f instanceof
+THREE.Light)E.lights.push(f);else if(f instanceof THREE.Mesh||f instanceof THREE.Line){if(!1===f.frustumCulled||!0===ha.intersectsObject(f))e=a(),e.object=f,null!==f.renderDepth?e.z=f.renderDepth:(H.getPositionFromMatrix(f.matrixWorld),H.applyProjection(fa),e.z=H.z),E.objects.push(e)}else f instanceof THREE.Sprite||f instanceof THREE.Particle?(e=a(),e.object=f,null!==f.renderDepth?e.z=f.renderDepth:(H.getPositionFromMatrix(f.matrixWorld),H.applyProjection(fa),e.z=H.z),E.sprites.push(e)):(e=a(),e.object=
+f,null!==f.renderDepth?e.z=f.renderDepth:(H.getPositionFromMatrix(f.matrixWorld),H.applyProjection(fa),e.z=H.z),E.objects.push(e));Fa(f)}}};Fa(g);!0===m&&E.objects.sort(c);g=0;for(m=E.objects.length;g<m;g++)if(oa=E.objects[g].object,Ca=oa.matrixWorld,k=0,oa instanceof THREE.Mesh){gb=oa.geometry;pa=gb.vertices;nb=gb.faces;gb=gb.faceVertexUvs;M.getInverse(Ca);M.transpose();Wa=oa.material instanceof THREE.MeshFaceMaterial;ab=!0===Wa?oa.material:null;ka=0;for(aa=pa.length;ka<aa;ka++)i=b(),i.positionWorld.copy(pa[ka]).applyMatrix4(Ca),
+i.positionScreen.copy(i.positionWorld).applyMatrix4(fa),i.positionScreen.x/=i.positionScreen.w,i.positionScreen.y/=i.positionScreen.w,i.positionScreen.z/=i.positionScreen.w,i.visible=!(-1>i.positionScreen.x||1<i.positionScreen.x||-1>i.positionScreen.y||1<i.positionScreen.y||-1>i.positionScreen.z||1<i.positionScreen.z);pa=0;for(ka=nb.length;pa<ka;pa++){aa=nb[pa];var Xa=!0===Wa?ab.materials[aa.materialIndex]:oa.material;if(void 0!==Xa){la=Xa.side;if(aa instanceof THREE.Face3)if(Y=l[aa.a],da=l[aa.b],
+Z=l[aa.c],K[0]=Y.positionScreen,K[1]=da.positionScreen,K[2]=Z.positionScreen,!0===Y.visible||!0===da.visible||!0===Z.visible||W.isIntersectionBox(F.setFromPoints(K)))if(ta=0>(Z.positionScreen.x-Y.positionScreen.x)*(da.positionScreen.y-Y.positionScreen.y)-(Z.positionScreen.y-Y.positionScreen.y)*(da.positionScreen.x-Y.positionScreen.x),la===THREE.DoubleSide||ta===(la===THREE.FrontSide))s===p?(ia=new THREE.RenderableFace3,r.push(ia),p++,s++,n=ia):n=r[s++],n.v1.copy(Y),n.v2.copy(da),n.v3.copy(Z);else continue;
+else continue;else if(aa instanceof THREE.Face4)if(Y=l[aa.a],da=l[aa.b],Z=l[aa.c],ia=l[aa.d],L[0]=Y.positionScreen,L[1]=da.positionScreen,L[2]=Z.positionScreen,L[3]=ia.positionScreen,!0===Y.visible||!0===da.visible||!0===Z.visible||!0===ia.visible||W.isIntersectionBox(F.setFromPoints(L)))if(ta=0>(ia.positionScreen.x-Y.positionScreen.x)*(da.positionScreen.y-Y.positionScreen.y)-(ia.positionScreen.y-Y.positionScreen.y)*(da.positionScreen.x-Y.positionScreen.x)||0>(da.positionScreen.x-Z.positionScreen.x)*
+(ia.positionScreen.y-Z.positionScreen.y)-(da.positionScreen.y-Z.positionScreen.y)*(ia.positionScreen.x-Z.positionScreen.x),la===THREE.DoubleSide||ta===(la===THREE.FrontSide)){if(q===v){var ub=new THREE.RenderableFace4;y.push(ub);v++;q++;n=ub}else n=y[q++];n.v1.copy(Y);n.v2.copy(da);n.v3.copy(Z);n.v4.copy(ia)}else continue;else continue;n.normalModel.copy(aa.normal);!1===ta&&(la===THREE.BackSide||la===THREE.DoubleSide)&&n.normalModel.negate();n.normalModel.applyMatrix3(M).normalize();n.normalModelView.copy(n.normalModel).applyMatrix3(ca);
+n.centroidModel.copy(aa.centroid).applyMatrix4(Ca);Z=aa.vertexNormals;Y=0;for(da=Z.length;Y<da;Y++)ia=n.vertexNormalsModel[Y],ia.copy(Z[Y]),!1===ta&&(la===THREE.BackSide||la===THREE.DoubleSide)&&ia.negate(),ia.applyMatrix3(M).normalize(),n.vertexNormalsModelView[Y].copy(ia).applyMatrix3(ca);n.vertexNormalsLength=Z.length;Y=0;for(da=gb.length;Y<da;Y++)if(ia=gb[Y][pa],void 0!==ia){la=0;for(Z=ia.length;la<Z;la++)n.uvs[Y][la]=ia[la]}n.color=aa.color;n.material=Xa;qa.copy(n.centroidModel).applyProjection(fa);
+n.z=qa.z;E.elements.push(n)}}}else if(oa instanceof THREE.Line){$a.multiplyMatrices(fa,Ca);pa=oa.geometry.vertices;Y=b();Y.positionScreen.copy(pa[0]).applyMatrix4($a);nb=oa.type===THREE.LinePieces?2:1;ka=1;for(aa=pa.length;ka<aa;ka++)Y=b(),Y.positionScreen.copy(pa[ka]).applyMatrix4($a),0<(ka+1)%nb||(da=l[k-2],ra.copy(Y.positionScreen),N.copy(da.positionScreen),!0===d(ra,N)&&(ra.multiplyScalar(1/ra.w),N.multiplyScalar(1/N.w),t===I?(gb=new THREE.RenderableLine,A.push(gb),I++,t++,z=gb):z=A[t++],z.v1.positionScreen.copy(ra),
+z.v2.positionScreen.copy(N),z.z=Math.max(ra.z,N.z),z.material=oa.material,E.elements.push(z)))}g=0;for(m=E.sprites.length;g<m;g++)oa=E.sprites[g].object,Ca=oa.matrixWorld,oa instanceof THREE.Particle&&(B.set(Ca.elements[12],Ca.elements[13],Ca.elements[14],1),B.applyMatrix4(fa),B.z/=B.w,0<B.z&&1>B.z&&(x===J?(ta=new THREE.RenderableParticle,G.push(ta),J++,x++,C=ta):C=G[x++],C.object=oa,C.x=B.x/B.w,C.y=B.y/B.w,C.z=B.z,C.rotation=oa.rotation.z,C.scale.x=oa.scale.x*Math.abs(C.x-(B.x+h.projectionMatrix.elements[0])/
+(B.w+h.projectionMatrix.elements[12])),C.scale.y=oa.scale.y*Math.abs(C.y-(B.y+h.projectionMatrix.elements[5])/(B.w+h.projectionMatrix.elements[13])),C.material=oa.material,E.elements.push(C)));!0===Pa&&E.elements.sort(c);return E}};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,g){this.a=a;this.b=b;this.c=c;this.d=d;this.normal=e instanceof THREE.Vector3?e:new THREE.Vector3;this.vertexNormals=e instanceof Array?e:[];this.color=f instanceof THREE.Color?f:new THREE.Color;this.vertexColors=f instanceof Array?f:[];this.vertexTangents=[];this.materialIndex=void 0!==g?g:0;this.centroid=new THREE.Vector3};
+THREE.Face4.prototype={constructor:THREE.Face4,clone:function(){var a=new THREE.Face4(this.a,this.b,this.c,this.d);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.Geometry=function(){THREE.EventDispatcher.call(this);this.id=THREE.GeometryIdCount++;this.name="";this.vertices=[];this.colors=[];this.normals=[];this.faces=[];this.faceUvs=[[]];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).getInverse(a).transpose(),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,g=e.vertexNormals.length;f<g;f++)e.vertexNormals[f].applyMatrix3(b).normalize();e.centroid.applyMatrix4(a)}},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 instanceof THREE.Face3?(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)):c instanceof THREE.Face4&&(c.centroid.add(this.vertices[c.a]),c.centroid.add(this.vertices[c.b]),c.centroid.add(this.vertices[c.c]),c.centroid.add(this.vertices[c.d]),c.centroid.divideScalar(4))},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,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 instanceof THREE.Face3?d.vertexNormals=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3]:d instanceof THREE.Face4&&
+(d.vertexNormals=[new THREE.Vector3,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,g,h,i=new THREE.Vector3,k=new THREE.Vector3,l=new THREE.Vector3,m=new THREE.Vector3,n=new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d instanceof THREE.Face3?(a=this.vertices[d.a],f=this.vertices[d.b],g=this.vertices[d.c],i.subVectors(g,f),k.subVectors(a,f),i.cross(k),e[d.a].add(i),e[d.b].add(i),
+e[d.c].add(i)):d instanceof THREE.Face4&&(a=this.vertices[d.a],f=this.vertices[d.b],g=this.vertices[d.c],h=this.vertices[d.d],l.subVectors(h,f),k.subVectors(a,f),l.cross(k),e[d.a].add(l),e[d.b].add(l),e[d.d].add(l),m.subVectors(h,g),n.subVectors(f,g),m.cross(n),e[d.b].add(m),e[d.c].add(m),e[d.d].add(m))}else{b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d instanceof THREE.Face3?(e[d.a].add(d.normal),e[d.b].add(d.normal),e[d.c].add(d.normal)):d instanceof THREE.Face4&&(e[d.a].add(d.normal),e[d.b].add(d.normal),
+e[d.c].add(d.normal),e[d.d].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 instanceof THREE.Face3?(d.vertexNormals[0].copy(e[d.a]),d.vertexNormals[1].copy(e[d.b]),d.vertexNormals[2].copy(e[d.c])):d instanceof THREE.Face4&&(d.vertexNormals[0].copy(e[d.a]),d.vertexNormals[1].copy(e[d.b]),d.vertexNormals[2].copy(e[d.c]),d.vertexNormals[3].copy(e[d.d]))},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=[];var g=this.morphNormals[a].faceNormals,h=this.morphNormals[a].vertexNormals,i,k;c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],i=new THREE.Vector3,k=e instanceof THREE.Face3?{a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3}:{a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3,d:new THREE.Vector3},g.push(i),h.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],i=g.faceNormals[c],k=g.vertexNormals[c],i.copy(e.normal),e instanceof THREE.Face3?(k.a.copy(e.vertexNormals[0]),k.b.copy(e.vertexNormals[1]),k.c.copy(e.vertexNormals[2])):(k.a.copy(e.vertexNormals[0]),k.b.copy(e.vertexNormals[1]),k.c.copy(e.vertexNormals[2]),k.d.copy(e.vertexNormals[3]))}c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],e.normal=e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){function a(a,b,c,
+d,e,f,x){h=a.vertices[b];i=a.vertices[c];k=a.vertices[d];l=g[e];m=g[f];n=g[x];s=i.x-h.x;r=k.x-h.x;p=i.y-h.y;q=k.y-h.y;y=i.z-h.z;v=k.z-h.z;z=m.x-l.x;t=n.x-l.x;A=m.y-l.y;I=n.y-l.y;C=1/(z*I-t*A);E.set((I*s-A*r)*C,(I*p-A*q)*C,(I*y-A*v)*C);H.set((z*r-t*s)*C,(z*q-t*p)*C,(z*v-t*y)*C);G[b].add(E);G[c].add(E);G[d].add(E);J[b].add(H);J[c].add(H);J[d].add(H)}var b,c,d,e,f,g,h,i,k,l,m,n,s,r,p,q,y,v,z,t,A,I,C,x,G=[],J=[],E=new THREE.Vector3,H=new THREE.Vector3,B=new THREE.Vector3,W=new THREE.Vector3,F=new THREE.Vector3;
+b=0;for(c=this.vertices.length;b<c;b++)G[b]=new THREE.Vector3,J[b]=new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)f=this.faces[b],g=this.faceVertexUvs[0][b],f instanceof THREE.Face3?a(this,f.a,f.b,f.c,0,1,2):f instanceof THREE.Face4&&(a(this,f.a,f.b,f.d,0,1,3),a(this,f.b,f.c,f.d,1,2,3));var K=["a","b","c","d"];b=0;for(c=this.faces.length;b<c;b++){f=this.faces[b];for(d=0;d<f.vertexNormals.length;d++)F.copy(f.vertexNormals[d]),e=f[K[d]],x=G[e],B.copy(x),B.sub(F.multiplyScalar(F.dot(x))).normalize(),
+W.crossVectors(f.vertexNormals[d],x),e=W.dot(J[e]),e=0>e?-1:1,f.vertexTangents[d]=new THREE.Vector4(B.x,B.y,B.z,e)}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.setFromCenterAndPoints(this.boundingSphere.center,this.vertices)},mergeVertices:function(){var a={},b=[],c=[],d,e=Math.pow(10,4),f,g,h,i,k;this.__tmpVertices=void 0;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)].join("_"),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];e=[];f=0;for(g=this.faces.length;f<g;f++)if(a=this.faces[f],a instanceof THREE.Face3){a.a=c[a.a];a.b=c[a.b];a.c=c[a.c];
+h=[a.a,a.b,a.c];d=-1;for(i=0;3>i;i++)if(h[i]==h[(i+1)%3]){e.push(f);break}}else if(a instanceof THREE.Face4){a.a=c[a.a];a.b=c[a.b];a.c=c[a.c];a.d=c[a.d];h=[a.a,a.b,a.c,a.d];d=-1;for(i=0;4>i;i++)h[i]==h[(i+1)%4]&&(0<=d&&e.push(f),d=i);if(0<=d){h.splice(d,1);var l=new THREE.Face3(h[0],h[1],h[2],a.normal,a.color,a.materialIndex);h=0;for(i=this.faceVertexUvs.length;h<i;h++)(k=this.faceVertexUvs[h][f])&&k.splice(d,1);a.vertexNormals&&0<a.vertexNormals.length&&(l.vertexNormals=a.vertexNormals,l.vertexNormals.splice(d,
+1));a.vertexColors&&0<a.vertexColors.length&&(l.vertexColors=a.vertexColors,l.vertexColors.splice(d,1));this.faces[f]=l}}for(f=e.length-1;0<=f;f--){this.faces.splice(f,1);h=0;for(i=this.faceVertexUvs.length;h<i;h++)this.faceVertexUvs[h].splice(f,1)}c=this.vertices.length-b.length;this.vertices=b;return c},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=[],g=0,h=e.length;g<h;g++)f.push(new THREE.Vector2(e[g].x,e[g].y));a.faceVertexUvs[0].push(f)}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.GeometryIdCount=0;THREE.BufferGeometry=function(){THREE.EventDispatcher.call(this);this.id=THREE.GeometryIdCount++;this.attributes={};this.dynamic=!1;this.offsets=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.morphTargets=[]};
+THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,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&&(b=new THREE.Matrix3,b.getInverse(a).transpose(),b.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,g=a.length;f<g;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(){null===this.boundingSphere&&(this.boundingSphere=
+new THREE.Sphere);var a=this.attributes.position.array;if(a){for(var b,c=0,d,e,f=0,g=a.length;f<g;f+=3)b=a[f],d=a[f+1],e=a[f+2],b=b*b+d*d+e*e,b>c&&(c=b);this.boundingSphere.radius=Math.sqrt(c)}},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),numItems: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,g,h,i,k,l,m,n=new THREE.Vector3,s=new THREE.Vector3,r=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3;if(this.attributes.index){var y=this.attributes.index.array,v=this.offsets;c=0;for(d=v.length;c<d;++c){b=v[c].start;g=v[c].count;var z=v[c].index;a=b;for(b+=g;a<b;a+=3)g=z+y[a],h=z+y[a+1],i=z+y[a+2],k=e[3*g],l=e[3*g+1],m=e[3*g+2],n.set(k,l,m),k=e[3*h],l=e[3*h+1],m=e[3*h+2],s.set(k,l,m),k=e[3*i],l=e[3*i+1],m=e[3*i+2],r.set(k,
+l,m),p.subVectors(r,s),q.subVectors(n,s),p.cross(q),f[3*g]+=p.x,f[3*g+1]+=p.y,f[3*g+2]+=p.z,f[3*h]+=p.x,f[3*h+1]+=p.y,f[3*h+2]+=p.z,f[3*i]+=p.x,f[3*i+1]+=p.y,f[3*i+2]+=p.z}}else{a=0;for(b=e.length;a<b;a+=9)k=e[a],l=e[a+1],m=e[a+2],n.set(k,l,m),k=e[a+3],l=e[a+4],m=e[a+5],s.set(k,l,m),k=e[a+6],l=e[a+7],m=e[a+8],r.set(k,l,m),p.subVectors(r,s),q.subVectors(n,s),p.cross(q),f[a]=p.x,f[a+1]=p.y,f[a+2]=p.z,f[a+3]=p.x,f[a+4]=p.y,f[a+5]=p.z,f[a+6]=p.x,f[a+7]=p.y,f[a+8]=p.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){Ca.x=d[3*a];Ca.y=d[3*a+1];Ca.z=d[3*a+2];$a.copy(Ca);ca=i[a];U.copy(ca);U.sub(Ca.multiplyScalar(Ca.dot(ca))).normalize();fa.crossVectors($a,ca);qa=fa.dot(k[a]);M=0>qa?-1:1;h[4*a]=U.x;h[4*a+1]=U.y;h[4*a+2]=U.z;h[4*a+3]=M}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;if(void 0===this.attributes.tangent){var g=4*f;this.attributes.tangent={itemSize:4,array:new Float32Array(g),numItems:g}}for(var h=this.attributes.tangent.array,i=[],k=[],g=0;g<f;g++)i[g]=
+new THREE.Vector3,k[g]=new THREE.Vector3;var l,m,n,s,r,p,q,y,v,z,t,A,I,C,x,f=new THREE.Vector3,g=new THREE.Vector3,G,J,E,H,B,W,F,K=this.offsets;E=0;for(H=K.length;E<H;++E){J=K[E].start;B=K[E].count;var L=K[E].index;G=J;for(J+=B;G<J;G+=3)B=L+b[G],W=L+b[G+1],F=L+b[G+2],l=c[3*B],m=c[3*B+1],n=c[3*B+2],s=c[3*W],r=c[3*W+1],p=c[3*W+2],q=c[3*F],y=c[3*F+1],v=c[3*F+2],z=e[2*B],t=e[2*B+1],A=e[2*W],I=e[2*W+1],C=e[2*F],x=e[2*F+1],s-=l,l=q-l,r-=m,m=y-m,p-=n,n=v-n,A-=z,z=C-z,I-=t,t=x-t,x=1/(A*t-z*I),f.set((t*s-
+I*l)*x,(t*r-I*m)*x,(t*p-I*n)*x),g.set((A*l-z*s)*x,(A*m-z*r)*x,(A*n-z*p)*x),i[B].add(f),i[W].add(f),i[F].add(f),k[B].add(g),k[W].add(g),k[F].add(g)}var U=new THREE.Vector3,fa=new THREE.Vector3,Ca=new THREE.Vector3,$a=new THREE.Vector3,M,ca,qa;E=0;for(H=K.length;E<H;++E){J=K[E].start;B=K[E].count;L=K[E].index;G=J;for(J+=B;G<J;G+=3)B=L+b[G],W=L+b[G+1],F=L+b[G+2],a(B),a(W),a(F)}this.tangentsNeedUpdate=this.hasTangents=!0}},dispose:function(){this.dispatchEvent({type:"dispose"})}};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(a){this.matrix.lookAt(this.position,a,this.up);!0===this.rotationAutoUpdate&&(!1===this.useQuaternion?this.rotation.setEulerFromRotationMatrix(this.matrix,this.eulerOrder):this.quaternion.copy(this.matrix.decompose()[1]))};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.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.Light=function(a){THREE.Object3D.call(this);this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);THREE.AmbientLight=function(a){THREE.Light.call(this,a)};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);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=new THREE.Vector3(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.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.groundColor=new THREE.Color(b);this.position=new THREE.Vector3(0,100,0);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);THREE.PointLight=function(a,b,c){THREE.Light.call(this,a);this.position=new THREE.Vector3(0,0,0);this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);THREE.SpotLight=function(a,b,c,d,e){THREE.Light.call(this,a);this.position=new THREE.Vector3(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/2;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.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,h,i,k,q){var y=/\.dds$/i.test(f),v=b+"/"+f;if(y){var z=THREE.ImageUtils.loadCompressedTexture(v);a[e]=z}else z=document.createElement("canvas"),a[e]=new THREE.Texture(z);a[e].sourceFile=f;h&&(a[e].repeat.set(h[0],h[1]),1!==h[0]&&(a[e].wrapS=THREE.RepeatWrapping),1!==h[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]]));q&&(a[e].anisotropy=q);if(!y){var t=a[e],a=new Image;a.onload=function(){if(!c(this.width)||!c(this.height)){var a=d(this.width),b=d(this.height);t.image.width=a;t.image.height=b;t.image.getContext("2d").drawImage(this,0,0,a,b)}else t.image=this;t.needsUpdate=!0};a.crossOrigin=g.crossOrigin;a.src=v}}function f(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var g=this,h="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?h="MeshPhongMaterial":"basic"===k&&(h="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?(h=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(h.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),h=new THREE.ShaderMaterial({fragmentShader:h.fragmentShader,vertexShader:h.vertexShader,uniforms:k,lights:!0,fog:!0}),i.transparent&&(h.transparent=!0)):h=new THREE[h](i);void 0!==a.DbgName&&(h.name=a.DbgName);return h}};THREE.ImageLoader=function(){THREE.EventDispatcher.call(this);this.crossOrigin=null};THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b){var c=this;void 0===b&&(b=new Image);b.addEventListener("load",function(){c.dispatchEvent({type:"load",content:b})},!1);b.addEventListener("error",function(){c.dispatchEvent({type:"error",message:"Couldn't load URL ["+a+"]"})},!1);c.crossOrigin&&(b.crossOrigin=c.crossOrigin);b.src=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,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);a.createModel(h,c,d)}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===g&&(g=f.getResponseHeader("Content-Length")),
+e({total:g,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&(g=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
+THREE.JSONLoader.prototype.createModel=function(a,b,c){var d=new THREE.Geometry,e=void 0!==a.scale?1/a.scale:1,f,g,h,i,k,l,m,n,s,r,p,q,y,v,z,t=a.faces;r=a.vertices;var A=a.normals,I=a.colors,C=0;for(f=0;f<a.uvs.length;f++)a.uvs[f].length&&C++;for(f=0;f<C;f++)d.faceUvs[f]=[],d.faceVertexUvs[f]=[];i=0;for(k=r.length;i<k;)l=new THREE.Vector3,l.x=r[i++]*e,l.y=r[i++]*e,l.z=r[i++]*e,d.vertices.push(l);i=0;for(k=t.length;i<k;){r=t[i++];l=r&1;h=r&2;f=r&4;g=r&8;n=r&16;m=r&32;p=r&64;r&=128;l?(q=new THREE.Face4,
+q.a=t[i++],q.b=t[i++],q.c=t[i++],q.d=t[i++],l=4):(q=new THREE.Face3,q.a=t[i++],q.b=t[i++],q.c=t[i++],l=3);h&&(h=t[i++],q.materialIndex=h);h=d.faces.length;if(f)for(f=0;f<C;f++)y=a.uvs[f],s=t[i++],z=y[2*s],s=y[2*s+1],d.faceUvs[f][h]=new THREE.Vector2(z,s);if(g)for(f=0;f<C;f++){y=a.uvs[f];v=[];for(g=0;g<l;g++)s=t[i++],z=y[2*s],s=y[2*s+1],v[g]=new THREE.Vector2(z,s);d.faceVertexUvs[f][h]=v}n&&(n=3*t[i++],g=new THREE.Vector3,g.x=A[n++],g.y=A[n++],g.z=A[n],q.normal=g);if(m)for(f=0;f<l;f++)n=3*t[i++],g=
+new THREE.Vector3,g.x=A[n++],g.y=A[n++],g.z=A[n],q.vertexNormals.push(g);p&&(m=t[i++],m=new THREE.Color(I[m]),q.color=m);if(r)for(f=0;f<l;f++)m=t[i++],m=new THREE.Color(I[m]),q.vertexColors.push(m);d.faces.push(q)}if(a.skinWeights){i=0;for(k=a.skinWeights.length;i<k;i+=2)t=a.skinWeights[i],A=a.skinWeights[i+1],d.skinWeights.push(new THREE.Vector4(t,A,0,0))}if(a.skinIndices){i=0;for(k=a.skinIndices.length;i<k;i+=2)t=a.skinIndices[i],A=a.skinIndices[i+1],d.skinIndices.push(new THREE.Vector4(t,A,0,0))}d.bones=
+a.bones;d.animation=a.animation;if(void 0!==a.morphTargets){i=0;for(k=a.morphTargets.length;i<k;i++){d.morphTargets[i]={};d.morphTargets[i].name=a.morphTargets[i].name;d.morphTargets[i].vertices=[];I=d.morphTargets[i].vertices;C=a.morphTargets[i].vertices;t=0;for(A=C.length;t<A;t+=3)r=new THREE.Vector3,r.x=C[t]*e,r.y=C[t+1]*e,r.z=C[t+2]*e,I.push(r)}}if(void 0!==a.morphColors){i=0;for(k=a.morphColors.length;i<k;i++){d.morphColors[i]={};d.morphColors[i].name=a.morphColors[i].name;d.morphColors[i].colors=
+[];A=d.morphColors[i].colors;I=a.morphColors[i].colors;e=0;for(t=I.length;e<t;e+=3)C=new THREE.Color(16755200),C.setRGB(I[e],I[e+1],I[e+2]),A.push(C)}}d.computeCentroids();d.computeFaceNormals();a=this.initMaterials(a.materials,c);this.needsTangents(a)&&d.computeTangents();b(d,a)};THREE.LoadingMonitor=function(){THREE.EventDispatcher.call(this);var a=this,b=0,c=0,d=function(){b++;a.dispatchEvent({type:"progress",loaded:b,total:c});b===c&&a.dispatchEvent({type:"load"})};this.add=function(a){c++;a.addEventListener("load",d,!1)}};THREE.SceneLoader=function(){this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){};this.callbackSync=function(){};this.callbackProgress=function(){};this.geometryHandlerMap={};this.hierarchyHandlerMap={};this.addGeometryHandler("ascii",THREE.JSONLoader)};THREE.SceneLoader.prototype.constructor=THREE.SceneLoader;
+THREE.SceneLoader.prototype.load=function(a,b){var c=this,d=new XMLHttpRequest;d.onreadystatechange=function(){if(4===d.readyState)if(200===d.status||0===d.status){var e=JSON.parse(d.responseText);c.parse(e,b,a)}else console.error("THREE.SceneLoader: Couldn't load ["+a+"] ["+d.status+"]")};d.open("GET",a,!0);d.send(null)};THREE.SceneLoader.prototype.addGeometryHandler=function(a,b){this.geometryHandlerMap[a]={loaderClass:b}};
+THREE.SceneLoader.prototype.addHierarchyHandler=function(a,b){this.hierarchyHandlerMap[a]={loaderClass:b}};
+THREE.SceneLoader.prototype.parse=function(a,b,c){function d(a,b){return"relativeToHTML"==b?a:m+"/"+a}function e(){f(x.scene,J.objects)}function f(a,b){var c,e,g,i,k,m,p;for(p in b)if(void 0===x.objects[p]){var q=b[p],t=null;if(q.type&&q.type in l.hierarchyHandlerMap){if(void 0===q.loading){e={type:1,url:1,material:1,position:1,rotation:1,scale:1,visible:1,children:1,properties:1,skin:1,morph:1,mirroredLoop:1,duration:1};g={};for(var B in q)B in e||(g[B]=q[B]);s=x.materials[q.material];q.loading=
+!0;e=l.hierarchyHandlerMap[q.type].loaderObject;e.options?e.load(d(q.url,J.urlBaseType),h(p,a,s,q)):e.load(d(q.url,J.urlBaseType),h(p,a,s,q),g)}}else if(void 0!==q.geometry){if(n=x.geometries[q.geometry]){t=!1;s=x.materials[q.material];t=s instanceof THREE.ShaderMaterial;g=q.position;i=q.rotation;k=q.scale;c=q.matrix;m=q.quaternion;q.material||(s=new THREE.MeshFaceMaterial(x.face_materials[q.geometry]));s instanceof THREE.MeshFaceMaterial&&0===s.materials.length&&(s=new THREE.MeshFaceMaterial(x.face_materials[q.geometry]));
+if(s instanceof THREE.MeshFaceMaterial)for(e=0;e<s.materials.length;e++)t=t||s.materials[e]instanceof THREE.ShaderMaterial;t&&n.computeTangents();q.skin?t=new THREE.SkinnedMesh(n,s):q.morph?(t=new THREE.MorphAnimMesh(n,s),void 0!==q.duration&&(t.duration=q.duration),void 0!==q.time&&(t.time=q.time),void 0!==q.mirroredLoop&&(t.mirroredLoop=q.mirroredLoop),s.morphNormals&&n.computeMorphNormals()):t=new THREE.Mesh(n,s);t.name=p;c?(t.matrixAutoUpdate=!1,t.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])):(t.position.set(g[0],g[1],g[2]),m?(t.quaternion.set(m[0],m[1],m[2],m[3]),t.useQuaternion=!0):t.rotation.set(i[0],i[1],i[2]),t.scale.set(k[0],k[1],k[2]));t.visible=q.visible;t.castShadow=q.castShadow;t.receiveShadow=q.receiveShadow;a.add(t);x.objects[p]=t}}else"DirectionalLight"===q.type||"PointLight"===q.type||"AmbientLight"===q.type?(v=void 0!==q.color?q.color:16777215,z=void 0!==q.intensity?q.intensity:1,"DirectionalLight"===q.type?(g=q.direction,
+y=new THREE.DirectionalLight(v,z),y.position.set(g[0],g[1],g[2]),q.target&&(G.push({object:y,targetName:q.target}),y.target=null)):"PointLight"===q.type?(g=q.position,e=q.distance,y=new THREE.PointLight(v,z,e),y.position.set(g[0],g[1],g[2])):"AmbientLight"===q.type&&(y=new THREE.AmbientLight(v)),a.add(y),y.name=p,x.lights[p]=y,x.objects[p]=y):"PerspectiveCamera"===q.type||"OrthographicCamera"===q.type?("PerspectiveCamera"===q.type?r=new THREE.PerspectiveCamera(q.fov,q.aspect,q.near,q.far):"OrthographicCamera"===
+q.type&&(r=new THREE.OrthographicCamera(q.left,q.right,q.top,q.bottom,q.near,q.far)),g=q.position,r.position.set(g[0],g[1],g[2]),a.add(r),r.name=p,x.cameras[p]=r,x.objects[p]=r):(g=q.position,i=q.rotation,k=q.scale,m=q.quaternion,t=new THREE.Object3D,t.name=p,t.position.set(g[0],g[1],g[2]),m?(t.quaternion.set(m[0],m[1],m[2],m[3]),t.useQuaternion=!0):t.rotation.set(i[0],i[1],i[2]),t.scale.set(k[0],k[1],k[2]),t.visible=void 0!==q.visible?q.visible:!1,a.add(t),x.objects[p]=t,x.empties[p]=t);if(t){if(void 0!==
+q.properties)for(var C in q.properties)t.properties[C]=q.properties[C];if(void 0!==q.groups)for(e=0;e<q.groups.length;e++)g=q.groups[e],void 0===x.groups[g]&&(x.groups[g]=[]),x.groups[g].push(p);void 0!==q.children&&f(t,q.children)}}}function g(a){return function(b,c){x.geometries[a]=b;x.face_materials[a]=c;e();t-=1;l.onLoadComplete();k()}}function h(a,b,c,d){return function(f){var f=f.content?f.content:f.dae?f.scene:f,g=d.position,h=d.rotation,i=d.quaternion,n=d.scale;f.position.set(g[0],g[1],g[2]);
+i?(f.quaternion.set(i[0],i[1],i[2],i[3]),f.useQuaternion=!0):f.rotation.set(h[0],h[1],h[2]);f.scale.set(n[0],n[1],n[2]);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;x.objects[a]=f;e();t-=1;l.onLoadComplete();k()}}function i(a){return function(b,c){x.geometries[a]=b;x.face_materials[a]=c}}function k(){l.callbackProgress({totalModels:I,totalTextures:C,loadedModels:I-t,loadedTextures:C-A},x);l.onLoadProgress();if(0===
+t&&0===A){for(var a=0;a<G.length;a++){var c=G[a],d=x.objects[c.targetName];d?c.object.target=d:(c.object.target=new THREE.Object3D,x.scene.add(c.object.target));c.object.target.properties.targetInverse=c.object}b(x)}}var l=this,m=THREE.Loader.prototype.extractUrlBase(c),n,s,r,p,q,y,v,z,t,A,I,C,x,G=[],J=a,E;for(E in this.geometryHandlerMap)a=this.geometryHandlerMap[E].loaderClass,this.geometryHandlerMap[E].loaderObject=new a;for(E in this.hierarchyHandlerMap)a=this.hierarchyHandlerMap[E].loaderClass,
+this.hierarchyHandlerMap[E].loaderObject=new a;A=t=0;x={scene:new THREE.Scene,geometries:{},face_materials:{},materials:{},textures:{},objects:{},cameras:{},lights:{},fogs:{},empties:{},groups:{}};if(J.transform&&(E=J.transform.position,a=J.transform.rotation,c=J.transform.scale,E&&x.scene.position.set(E[0],E[1],E[2]),a&&x.scene.rotation.set(a[0],a[1],a[2]),c&&x.scene.scale.set(c[0],c[1],c[2]),E||a||c))x.scene.updateMatrix(),x.scene.updateMatrixWorld();E=function(a){return function(){A-=a;k();l.onLoadComplete()}};
+for(var H in J.fogs)a=J.fogs[H],"linear"===a.type?p=new THREE.Fog(0,a.near,a.far):"exp2"===a.type&&(p=new THREE.FogExp2(0,a.density)),a=a.color,p.color.setRGB(a[0],a[1],a[2]),x.fogs[H]=p;for(var B in J.geometries)p=J.geometries[B],p.type in this.geometryHandlerMap&&(t+=1,l.onLoadStart());for(var W in J.objects)p=J.objects[W],p.type&&p.type in this.hierarchyHandlerMap&&(t+=1,l.onLoadStart());I=t;for(B in J.geometries)if(p=J.geometries[B],"cube"===p.type)n=new THREE.CubeGeometry(p.width,p.height,p.depth,
+p.widthSegments,p.heightSegments,p.depthSegments),x.geometries[B]=n;else if("plane"===p.type)n=new THREE.PlaneGeometry(p.width,p.height,p.widthSegments,p.heightSegments),x.geometries[B]=n;else if("sphere"===p.type)n=new THREE.SphereGeometry(p.radius,p.widthSegments,p.heightSegments),x.geometries[B]=n;else if("cylinder"===p.type)n=new THREE.CylinderGeometry(p.topRad,p.botRad,p.height,p.radSegs,p.heightSegs),x.geometries[B]=n;else if("torus"===p.type)n=new THREE.TorusGeometry(p.radius,p.tube,p.segmentsR,
+p.segmentsT),x.geometries[B]=n;else if("icosahedron"===p.type)n=new THREE.IcosahedronGeometry(p.radius,p.subdivisions),x.geometries[B]=n;else if(p.type in this.geometryHandlerMap){W={};for(q in p)"type"!==q&&"url"!==q&&(W[q]=p[q]);this.geometryHandlerMap[p.type].loaderObject.load(d(p.url,J.urlBaseType),g(B),W)}else"embedded"===p.type&&(W=J.embeds[p.id],W.metadata=J.metadata,W&&this.geometryHandlerMap.ascii.loaderObject.createModel(W,i(B),""));for(var F in J.textures)if(B=J.textures[F],B.url instanceof
+Array){A+=B.url.length;for(q=0;q<B.url.length;q++)l.onLoadStart()}else A+=1,l.onLoadStart();C=A;for(F in J.textures){B=J.textures[F];void 0!==B.mapping&&void 0!==THREE[B.mapping]&&(B.mapping=new THREE[B.mapping]);if(B.url instanceof Array){W=B.url.length;p=[];for(q=0;q<W;q++)p[q]=d(B.url[q],J.urlBaseType);q=(q=/\.dds$/i.test(p[0]))?THREE.ImageUtils.loadCompressedTextureCube(p,B.mapping,E(W)):THREE.ImageUtils.loadTextureCube(p,B.mapping,E(W))}else q=/\.dds$/i.test(B.url),W=d(B.url,J.urlBaseType),p=
+E(1),q=q?THREE.ImageUtils.loadCompressedTexture(W,B.mapping,p):THREE.ImageUtils.loadTexture(W,B.mapping,p),void 0!==THREE[B.minFilter]&&(q.minFilter=THREE[B.minFilter]),void 0!==THREE[B.magFilter]&&(q.magFilter=THREE[B.magFilter]),B.anisotropy&&(q.anisotropy=B.anisotropy),B.repeat&&(q.repeat.set(B.repeat[0],B.repeat[1]),1!==B.repeat[0]&&(q.wrapS=THREE.RepeatWrapping),1!==B.repeat[1]&&(q.wrapT=THREE.RepeatWrapping)),B.offset&&q.offset.set(B.offset[0],B.offset[1]),B.wrap&&(W={repeat:THREE.RepeatWrapping,
+mirror:THREE.MirroredRepeatWrapping},void 0!==W[B.wrap[0]]&&(q.wrapS=W[B.wrap[0]]),void 0!==W[B.wrap[1]]&&(q.wrapT=W[B.wrap[1]]));x.textures[F]=q}var K,L;for(K in J.materials){F=J.materials[K];for(L in F.parameters)"envMap"===L||"map"===L||"lightMap"===L||"bumpMap"===L?F.parameters[L]=x.textures[F.parameters[L]]:"shading"===L?F.parameters[L]="flat"===F.parameters[L]?THREE.FlatShading:THREE.SmoothShading:"side"===L?F.parameters[L]="double"==F.parameters[L]?THREE.DoubleSide:"back"==F.parameters[L]?
+THREE.BackSide:THREE.FrontSide:"blending"===L?F.parameters[L]=F.parameters[L]in THREE?THREE[F.parameters[L]]:THREE.NormalBlending:"combine"===L?F.parameters[L]=F.parameters[L]in THREE?THREE[F.parameters[L]]:THREE.MultiplyOperation:"vertexColors"===L?"face"==F.parameters[L]?F.parameters[L]=THREE.FaceColors:F.parameters[L]&&(F.parameters[L]=THREE.VertexColors):"wrapRGB"===L&&(E=F.parameters[L],F.parameters[L]=new THREE.Vector3(E[0],E[1],E[2]));void 0!==F.parameters.opacity&&1>F.parameters.opacity&&
+(F.parameters.transparent=!0);F.parameters.normalMap?(E=THREE.ShaderLib.normalmap,B=THREE.UniformsUtils.clone(E.uniforms),q=F.parameters.color,W=F.parameters.specular,p=F.parameters.ambient,H=F.parameters.shininess,B.tNormal.value=x.textures[F.parameters.normalMap],F.parameters.normalScale&&B.uNormalScale.value.set(F.parameters.normalScale[0],F.parameters.normalScale[1]),F.parameters.map&&(B.tDiffuse.value=F.parameters.map,B.enableDiffuse.value=!0),F.parameters.envMap&&(B.tCube.value=F.parameters.envMap,
+B.enableReflection.value=!0,B.uReflectivity.value=F.parameters.reflectivity),F.parameters.lightMap&&(B.tAO.value=F.parameters.lightMap,B.enableAO.value=!0),F.parameters.specularMap&&(B.tSpecular.value=x.textures[F.parameters.specularMap],B.enableSpecular.value=!0),F.parameters.displacementMap&&(B.tDisplacement.value=x.textures[F.parameters.displacementMap],B.enableDisplacement.value=!0,B.uDisplacementBias.value=F.parameters.displacementBias,B.uDisplacementScale.value=F.parameters.displacementScale),
+B.uDiffuseColor.value.setHex(q),B.uSpecularColor.value.setHex(W),B.uAmbientColor.value.setHex(p),B.uShininess.value=H,F.parameters.opacity&&(B.uOpacity.value=F.parameters.opacity),s=new THREE.ShaderMaterial({fragmentShader:E.fragmentShader,vertexShader:E.vertexShader,uniforms:B,lights:!0,fog:!0})):s=new THREE[F.type](F.parameters);x.materials[K]=s}for(K in J.materials)if(F=J.materials[K],F.parameters.materials){L=[];for(q=0;q<F.parameters.materials.length;q++)L.push(x.materials[F.parameters.materials[q]]);
+x.materials[K].materials=L}e();x.cameras&&J.defaults.camera&&(x.currentCamera=x.cameras[J.defaults.camera]);x.fogs&&J.defaults.fog&&(x.scene.fog=x.fogs[J.defaults.fog]);l.callbackSync(x);k()};THREE.TextureLoader=function(){THREE.EventDispatcher.call(this);this.crossOrigin=null};THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a){var b=this,c=new Image;c.addEventListener("load",function(){var a=new THREE.Texture(c);a.needsUpdate=!0;b.dispatchEvent({type:"load",content:a})},!1);c.addEventListener("error",function(){b.dispatchEvent({type:"error",message:"Couldn't load URL ["+a+"]"})},!1);b.crossOrigin&&(c.crossOrigin=b.crossOrigin);c.src=a}};THREE.Material=function(){THREE.EventDispatcher.call(this);this.id=THREE.MaterialIdCount++;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.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=0;this.overdraw=!1;this.needsUpdate=this.visible=!0};
+THREE.Material.prototype.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&&c instanceof THREE.Color?d.copy(c):d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]=c}}};
+THREE.Material.prototype.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};THREE.Material.prototype.dispose=function(){this.dispatchEvent({type:"dispose"})};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.LineBasicMaterial;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.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(){return new THREE.MeshFaceMaterial(this.materials.slice(0))};THREE.ParticleBasicMaterial=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.ParticleBasicMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.ParticleBasicMaterial.prototype.clone=function(){var a=new THREE.ParticleBasicMaterial;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.ParticleCanvasMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.program=function(){};this.setValues(a)};THREE.ParticleCanvasMaterial.prototype=Object.create(THREE.Material.prototype);THREE.ParticleCanvasMaterial.prototype.clone=function(){var a=new THREE.ParticleCanvasMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.program=this.program;return a};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.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);
+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.scaleByViewport=!this.sizeAttenuation;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);void 0===a.scaleByViewport&&(this.scaleByViewport=!this.sizeAttenuation)};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.scaleByViewport=this.scaleByViewport;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(1,-1);
+THREE.SpriteAlignment.topCenter=new THREE.Vector2(0,-1);THREE.SpriteAlignment.topRight=new THREE.Vector2(-1,-1);THREE.SpriteAlignment.centerLeft=new THREE.Vector2(1,0);THREE.SpriteAlignment.center=new THREE.Vector2(0,0);THREE.SpriteAlignment.centerRight=new THREE.Vector2(-1,0);THREE.SpriteAlignment.bottomLeft=new THREE.Vector2(1,1);THREE.SpriteAlignment.bottomCenter=new THREE.Vector2(0,1);THREE.SpriteAlignment.bottomRight=new THREE.Vector2(-1,1);THREE.Texture=function(a,b,c,d,e,f,g,h,i){THREE.EventDispatcher.call(this);this.id=THREE.TextureIdCount++;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!==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.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.TextureIdCount=0;THREE.CompressedTexture=function(a,b,c,d,e,f,g,h,i,k,l){THREE.Texture.call(this,null,f,g,h,i,k,d,e,l);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,g,h,i,k,l){THREE.Texture.call(this,null,f,g,h,i,k,d,e,l);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.Particle=function(a){THREE.Object3D.call(this);this.material=a};THREE.Particle.prototype=Object.create(THREE.Object3D.prototype);THREE.Particle.prototype.clone=function(a){void 0===a&&(a=new THREE.Particle(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){THREE.Object3D.call(this);this.geometry=a;this.material=void 0!==b?b:new THREE.ParticleBasicMaterial({color:16777215*Math.random()});this.sortParticles=!1;this.geometry&&null===this.geometry.boundingSphere&&this.geometry.computeBoundingSphere();this.frustumCulled=!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=a;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.type=void 0!==c?c:THREE.LineStrip;this.geometry&&(this.geometry.boundingSphere||this.geometry.computeBoundingSphere())};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=a;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random(),wireframe:!0});void 0!==this.geometry&&(null===this.geometry.boundingSphere&&this.geometry.computeBoundingSphere(),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]),b.useQuaternion=!0,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(a){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||a)this.parent?this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=!1;for(var a=0,b=this.children.length;a<b;a++){var c=this.children[a];c instanceof THREE.Bone?c.update(this.identityMatrix,!1):c.updateMatrixWorld(!0)}if(void 0==this.boneInverses){this.boneInverses=[];a=0;for(b=this.bones.length;a<
+b;a++)c=new THREE.Matrix4,c.getInverse(this.bones[a].skinMatrix),this.boneInverses.push(c)}a=0;for(b=this.bones.length;a<b;a++)THREE.SkinnedMesh.offsetMatrix.multiplyMatrices(this.bones[a].skinMatrix,this.boneInverses[a]),THREE.SkinnedMesh.offsetMatrix.flattenToArrayOffset(this.boneMatrices,16*a);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)};
+THREE.SkinnedMesh.prototype.pose=function(){this.updateMatrixWorld(!0);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.SkinnedMesh.offsetMatrix=new THREE.Matrix4;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 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=1E3*((c.end-c.start)/b),this.time=0):console.warn("animation["+a+"] undefined")};
+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.Ribbon=function(a,b){THREE.Object3D.call(this);this.geometry=a;this.material=b};THREE.Ribbon.prototype=Object.create(THREE.Object3D.prototype);THREE.Ribbon.prototype.clone=function(a){void 0===a&&(a=new THREE.Ribbon(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.LODs=[]};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.LODs.length&&!(b<this.LODs[c].visibleAtDistance);c++);this.LODs.splice(c,0,{visibleAtDistance:b,object3D:a});this.add(a)};
+THREE.LOD.prototype.update=function(a){if(1<this.LODs.length){a.matrixWorldInverse.getInverse(a.matrixWorld);a=a.matrixWorldInverse;a=-(a.elements[2]*this.matrixWorld.elements[12]+a.elements[6]*this.matrixWorld.elements[13]+a.elements[10]*this.matrixWorld.elements[14]+a.elements[14]);this.LODs[0].object3D.visible=!0;for(var b=1;b<this.LODs.length;b++)if(a>=this.LODs[b].visibleAtDistance)this.LODs[b-1].object3D.visible=!1,this.LODs[b].object3D.visible=!0;else break;for(;b<this.LODs.length;b++)this.LODs[b].object3D.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.rotation3d=this.rotation;this.rotation=0};THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.updateMatrix=function(){this.matrix.setPosition(this.position);this.rotation3d.set(0,0,this.rotation);this.matrix.setRotationFromEuler(this.rotation3d);(1!==this.scale.x||1!==this.scale.y)&&this.matrix.scale(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.Scene=function(){THREE.Object3D.call(this);this.overrideMaterial=this.fog=null;this.matrixAutoUpdate=!1;this.__objects=[];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)&&-1===this.__objects.indexOf(a)){this.__objects.push(a);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)}else a instanceof THREE.Camera||(b=this.__objects.indexOf(a),-1!==b&&(this.__objects.splice(b,1),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.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){C!==a&&(C=t.globalAlpha=a)}function c(a){x!==a&&(a===THREE.NormalBlending?t.globalCompositeOperation="source-over":a===THREE.AdditiveBlending?t.globalCompositeOperation="lighter":a===THREE.SubtractiveBlending&&(t.globalCompositeOperation="darker"),x=a)}function d(a){E!==a&&(E=t.lineWidth=a)}function e(a){H!==a&&(H=t.lineCap=a)}function f(a){B!==a&&(B=t.lineJoin=a)}function g(a){G!==a&&(G=t.strokeStyle=a)}function h(a){J!==a&&(J=t.fillStyle=a)}function i(a,
+b){if(W!==a||F!==b)t.setLineDash([a,b]),W=a,F=b}console.log("THREE.CanvasRenderer",THREE.REVISION);var k=THREE.Math.smoothstep,a=a||{},l=this,m,n,s,r=new THREE.Projector,p=void 0!==a.canvas?a.canvas:document.createElement("canvas"),q,y,v,z,t=p.getContext("2d"),A=new THREE.Color(0),I=0,C=1,x=0,G=null,J=null,E=null,H=null,B=null,W=null,F=0,K,L,U,fa,Ca=new THREE.RenderableVertex,$a=new THREE.RenderableVertex,M,ca,qa,ha,ra,N,Ma,Na,mb,Pa,ta,ka,aa=new THREE.Color,pa=new THREE.Color,Y=new THREE.Color,da=
+new THREE.Color,la=new THREE.Color,Z=new THREE.Color,oa=new THREE.Color,gb=new THREE.Color,nb={},ia={},Wa,ab,Fa,Xa,ub,Ib,Jb,fc,Ab,mc,pb=new THREE.Box2,Ka=new THREE.Box2,Va=new THREE.Box2,gc=!1,vb=new THREE.Color,Qa=new THREE.Color,La=new THREE.Color,bb=new THREE.Vector3,xb,j,yb,Ra,cb,Sa,zb=16;xb=document.createElement("canvas");xb.width=xb.height=2;j=xb.getContext("2d");j.fillStyle="rgba(0,0,0,1)";j.fillRect(0,0,2,2);yb=j.getImageData(0,0,2,2);Ra=yb.data;cb=document.createElement("canvas");cb.width=
+cb.height=zb;Sa=cb.getContext("2d");Sa.translate(-zb/2,-zb/2);Sa.scale(zb,zb);zb--;void 0===t.setLineDash&&(t.setLineDash=void 0!==t.mozDash?function(a){t.mozDash=null!==a[0]?a:null}:function(){});this.domElement=p;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==window.devicePixelRatio?window.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){q=a*this.devicePixelRatio;y=b*this.devicePixelRatio;v=Math.floor(q/2);z=Math.floor(y/2);p.width=q;p.height=y;p.style.width=a+"px";p.style.height=b+"px";pb.set(new THREE.Vector2(-v,-z),new THREE.Vector2(v,z));Ka.set(new THREE.Vector2(-v,-z),new THREE.Vector2(v,z));C=1;x=0;B=H=E=J=G=null};this.setClearColor=function(a,b){A.copy(a);I=void 0!==b?b:1;Ka.set(new THREE.Vector2(-v,-z),new THREE.Vector2(v,z))};this.setClearColorHex=function(a,b){A.setHex(a);I=void 0!==b?b:1;Ka.set(new THREE.Vector2(-v,
+-z),new THREE.Vector2(v,z))};this.getMaxAnisotropy=function(){return 0};this.clear=function(){t.setTransform(1,0,0,-1,v,z);!1===Ka.empty()&&(Ka.intersect(pb),Ka.expandByScalar(2),1>I&&t.clearRect(Ka.min.x|0,Ka.min.y|0,Ka.max.x-Ka.min.x|0,Ka.max.y-Ka.min.y|0),0<I&&(c(THREE.NormalBlending),b(1),h("rgba("+Math.floor(255*A.r)+","+Math.floor(255*A.g)+","+Math.floor(255*A.b)+","+I+")"),t.fillRect(Ka.min.x|0,Ka.min.y|0,Ka.max.x-Ka.min.x|0,Ka.max.y-Ka.min.y|0)),Ka.makeEmpty())};this.render=function(a,p){function q(a,
+b,c){for(var d=0,e=s.length;d<e;d++){var f=s[d];gb.copy(f.color);if(f instanceof THREE.DirectionalLight){var g=bb.getPositionFromMatrix(f.matrixWorld).normalize(),j=b.dot(g);0>=j||(j*=f.intensity,c.add(gb.multiplyScalar(j)))}else f instanceof THREE.PointLight&&(g=bb.getPositionFromMatrix(f.matrixWorld),j=b.dot(bb.subVectors(g,a).normalize()),0>=j||(j*=0==f.distance?1:1-Math.min(a.distanceTo(g)/f.distance,1),0!=j&&(j*=f.intensity,c.add(gb.multiplyScalar(j)))))}}function x(a,d,e,f,g,j,h,i){l.info.render.vertices+=
+3;l.info.render.faces++;b(i.opacity);c(i.blending);M=a.positionScreen.x;ca=a.positionScreen.y;qa=d.positionScreen.x;ha=d.positionScreen.y;ra=e.positionScreen.x;N=e.positionScreen.y;y(M,ca,qa,ha,ra,N);(i instanceof THREE.MeshLambertMaterial||i instanceof THREE.MeshPhongMaterial)&&null===i.map?(Z.copy(i.color),oa.copy(i.emissive),i.vertexColors===THREE.FaceColors&&Z.multiply(h.color),!0===gc?!1===i.wireframe&&i.shading==THREE.SmoothShading&&3==h.vertexNormalsLength?(pa.copy(vb),Y.copy(vb),da.copy(vb),
+q(h.v1.positionWorld,h.vertexNormalsModel[0],pa),q(h.v2.positionWorld,h.vertexNormalsModel[1],Y),q(h.v3.positionWorld,h.vertexNormalsModel[2],da),pa.multiply(Z).add(oa),Y.multiply(Z).add(oa),da.multiply(Z).add(oa),la.addColors(Y,da).multiplyScalar(0.5),Fa=E(pa,Y,da,la),G(M,ca,qa,ha,ra,N,0,0,1,0,0,1,Fa)):(aa.copy(vb),q(h.centroidModel,h.normalModel,aa),aa.multiply(Z).add(oa),!0===i.wireframe?C(aa,i.wireframeLinewidth,i.wireframeLinecap,i.wireframeLinejoin):A(aa)):!0===i.wireframe?C(i.color,i.wireframeLinewidth,
+i.wireframeLinecap,i.wireframeLinejoin):A(i.color)):i instanceof THREE.MeshBasicMaterial||i instanceof THREE.MeshLambertMaterial||i instanceof THREE.MeshPhongMaterial?null!==i.map?i.map.mapping instanceof THREE.UVMapping&&(Xa=h.uvs[0],F(M,ca,qa,ha,ra,N,Xa[f].x,Xa[f].y,Xa[g].x,Xa[g].y,Xa[j].x,Xa[j].y,i.map)):null!==i.envMap?i.envMap.mapping instanceof THREE.SphericalReflectionMapping&&(bb.copy(h.vertexNormalsModelView[f]),ub=0.5*bb.x+0.5,Ib=0.5*bb.y+0.5,bb.copy(h.vertexNormalsModelView[g]),Jb=0.5*
+bb.x+0.5,fc=0.5*bb.y+0.5,bb.copy(h.vertexNormalsModelView[j]),Ab=0.5*bb.x+0.5,mc=0.5*bb.y+0.5,F(M,ca,qa,ha,ra,N,ub,Ib,Jb,fc,Ab,mc,i.envMap)):(aa.copy(i.color),i.vertexColors===THREE.FaceColors&&aa.multiply(h.color),!0===i.wireframe?C(aa,i.wireframeLinewidth,i.wireframeLinecap,i.wireframeLinejoin):A(aa)):i instanceof THREE.MeshDepthMaterial?(Wa=p.near,ab=p.far,pa.r=pa.g=pa.b=1-k(a.positionScreen.z*a.positionScreen.w,Wa,ab),Y.r=Y.g=Y.b=1-k(d.positionScreen.z*d.positionScreen.w,Wa,ab),da.r=da.g=da.b=
+1-k(e.positionScreen.z*e.positionScreen.w,Wa,ab),la.addColors(Y,da).multiplyScalar(0.5),Fa=E(pa,Y,da,la),G(M,ca,qa,ha,ra,N,0,0,1,0,0,1,Fa)):i instanceof THREE.MeshNormalMaterial&&(i.shading==THREE.FlatShading?(a=h.normalModelView,aa.setRGB(a.x,a.y,a.z).multiplyScalar(0.5).addScalar(0.5),!0===i.wireframe?C(aa,i.wireframeLinewidth,i.wireframeLinecap,i.wireframeLinejoin):A(aa)):i.shading==THREE.SmoothShading&&(a=h.vertexNormalsModelView[f],pa.setRGB(a.x,a.y,a.z).multiplyScalar(0.5).addScalar(0.5),a=
+h.vertexNormalsModelView[g],Y.setRGB(a.x,a.y,a.z).multiplyScalar(0.5).addScalar(0.5),a=h.vertexNormalsModelView[j],da.setRGB(a.x,a.y,a.z).multiplyScalar(0.5).addScalar(0.5),la.addColors(Y,da).multiplyScalar(0.5),Fa=E(pa,Y,da,la),G(M,ca,qa,ha,ra,N,0,0,1,0,0,1,Fa)))}function y(a,b,c,d,e,f){t.beginPath();t.moveTo(a,b);t.lineTo(c,d);t.lineTo(e,f);t.closePath()}function B(a,b,c,d,e,f,g,j){t.beginPath();t.moveTo(a,b);t.lineTo(c,d);t.lineTo(e,f);t.lineTo(g,j);t.closePath()}function C(a,b,c,j){d(b);e(c);
+f(j);g(a.getStyle());t.stroke();Va.expandByScalar(2*b)}function A(a){h(a.getStyle());t.fill()}function F(a,b,c,d,e,f,g,j,i,wa,k,l,n){if(!(n instanceof THREE.DataTexture||void 0===n.image||0==n.image.width)){if(!0===n.needsUpdate){var m=n.wrapS==THREE.RepeatWrapping,hb=n.wrapT==THREE.RepeatWrapping;nb[n.id]=t.createPattern(n.image,!0===m&&!0===hb?"repeat":!0===m&&!1===hb?"repeat-x":!1===m&&!0===hb?"repeat-y":"no-repeat");n.needsUpdate=!1}void 0===nb[n.id]?h("rgba(0,0,0,1)"):h(nb[n.id]);var m=n.offset.x/
+n.repeat.x,hb=n.offset.y/n.repeat.y,p=n.image.width*n.repeat.x,q=n.image.height*n.repeat.y,g=(g+m)*p,j=(1-j+hb)*q,c=c-a,d=d-b,e=e-a,f=f-b,i=(i+m)*p-g,wa=(1-wa+hb)*q-j,k=(k+m)*p-g,l=(1-l+hb)*q-j,m=i*l-k*wa;0===m?(void 0===ia[n.id]&&(b=document.createElement("canvas"),b.width=n.image.width,b.height=n.image.height,b=b.getContext("2d"),b.drawImage(n.image,0,0),ia[n.id]=b.getImageData(0,0,n.image.width,n.image.height).data),b=ia[n.id],g=4*(Math.floor(g)+Math.floor(j)*n.image.width),aa.setRGB(b[g]/255,
+b[g+1]/255,b[g+2]/255),A(aa)):(m=1/m,n=(l*c-wa*e)*m,wa=(l*d-wa*f)*m,c=(i*e-k*c)*m,d=(i*f-k*d)*m,a=a-n*g-c*j,g=b-wa*g-d*j,t.save(),t.transform(n,wa,c,d,a,g),t.fill(),t.restore())}}function G(a,b,c,d,e,f,g,j,i,h,wa,k,n){var l,m;l=n.width-1;m=n.height-1;g*=l;j*=m;c-=a;d-=b;e-=a;f-=b;i=i*l-g;h=h*m-j;wa=wa*l-g;k=k*m-j;m=1/(i*k-wa*h);l=(k*c-h*e)*m;h=(k*d-h*f)*m;c=(i*e-wa*c)*m;d=(i*f-wa*d)*m;a=a-l*g-c*j;b=b-h*g-d*j;t.save();t.transform(l,h,c,d,a,b);t.clip();t.drawImage(n,0,0);t.restore()}function E(a,b,
+c,d){Ra[0]=255*a.r|0;Ra[1]=255*a.g|0;Ra[2]=255*a.b|0;Ra[4]=255*b.r|0;Ra[5]=255*b.g|0;Ra[6]=255*b.b|0;Ra[8]=255*c.r|0;Ra[9]=255*c.g|0;Ra[10]=255*c.b|0;Ra[12]=255*d.r|0;Ra[13]=255*d.g|0;Ra[14]=255*d.b|0;j.putImageData(yb,0,0);Sa.drawImage(xb,0,0);return cb}function I(a,b){var c=b.x-a.x,d=b.y-a.y,e=c*c+d*d;0!==e&&(e=1/Math.sqrt(e),c*=e,d*=e,b.x+=c,b.y+=d,a.x-=c,a.y-=d)}if(!1===p instanceof THREE.Camera)console.error("THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.");else{!0===
+this.autoClear&&this.clear();t.setTransform(1,0,0,-1,v,z);l.info.render.vertices=0;l.info.render.faces=0;m=r.projectScene(a,p,this.sortObjects,this.sortElements);n=m.elements;s=m.lights;gc=0<s.length;if(!0===gc){vb.setRGB(0,0,0);Qa.setRGB(0,0,0);La.setRGB(0,0,0);for(var J=0,W=s.length;J<W;J++){var P=s[J],X=P.color;P instanceof THREE.AmbientLight?vb.add(X):P instanceof THREE.DirectionalLight?Qa.add(X):P instanceof THREE.PointLight&&La.add(X)}}J=0;for(W=n.length;J<W;J++){var H=n[J],P=H.material;if(!(void 0===
+P||!1===P.visible)){Va.makeEmpty();if(H instanceof THREE.RenderableParticle){K=H;K.x*=v;K.y*=z;var X=K,wa=H;b(P.opacity);c(P.blending);var Bb=void 0,hb=void 0,Cb=void 0,Db=void 0,md=H=void 0,nd=void 0;P instanceof THREE.ParticleBasicMaterial?null===P.map?(Cb=wa.object.scale.x,Db=wa.object.scale.y,Cb*=wa.scale.x*v,Db*=wa.scale.y*z,Va.min.set(X.x-Cb,X.y-Db),Va.max.set(X.x+Cb,X.y+Db),!1!==pb.isIntersectionBox(Va)&&(h(P.color.getStyle()),t.save(),t.translate(X.x,X.y),t.rotate(-wa.rotation),t.scale(Cb,
+Db),t.fillRect(-1,-1,2,2),t.restore())):(H=P.map.image,md=H.width>>1,nd=H.height>>1,Cb=wa.scale.x*v,Db=wa.scale.y*z,Bb=Cb*md,hb=Db*nd,Va.min.set(X.x-Bb,X.y-hb),Va.max.set(X.x+Bb,X.y+hb),!1!==pb.isIntersectionBox(Va)&&(t.save(),t.translate(X.x,X.y),t.rotate(-wa.rotation),t.scale(Cb,-Db),t.translate(-md,-nd),t.drawImage(H,0,0),t.restore())):P instanceof THREE.ParticleCanvasMaterial&&(Bb=wa.scale.x*v,hb=wa.scale.y*z,Va.min.set(X.x-Bb,X.y-hb),Va.max.set(X.x+Bb,X.y+hb),!1!==pb.isIntersectionBox(Va)&&(g(P.color.getStyle()),
+h(P.color.getStyle()),t.save(),t.translate(X.x,X.y),t.rotate(-wa.rotation),t.scale(Bb,hb),P.program(t),t.restore()))}else if(H instanceof THREE.RenderableLine)K=H.v1,L=H.v2,K.positionScreen.x*=v,K.positionScreen.y*=z,L.positionScreen.x*=v,L.positionScreen.y*=z,Va.setFromPoints([K.positionScreen,L.positionScreen]),!0===pb.isIntersectionBox(Va)&&(X=K,wa=L,b(P.opacity),c(P.blending),t.beginPath(),t.moveTo(X.positionScreen.x,X.positionScreen.y),t.lineTo(wa.positionScreen.x,wa.positionScreen.y),P instanceof
+THREE.LineBasicMaterial?(d(P.linewidth),e(P.linecap),f(P.linejoin),g(P.color.getStyle()),i(null,null),t.stroke(),Va.expandByScalar(2*P.linewidth)):P instanceof THREE.LineDashedMaterial&&(d(P.linewidth),e(P.linecap),f(P.linejoin),g(P.color.getStyle()),i(P.dashSize,P.gapSize),t.stroke(),Va.expandByScalar(2*P.linewidth)));else if(H instanceof THREE.RenderableFace3){K=H.v1;L=H.v2;U=H.v3;if(-1>K.positionScreen.z||1<K.positionScreen.z)continue;if(-1>L.positionScreen.z||1<L.positionScreen.z)continue;if(-1>
+U.positionScreen.z||1<U.positionScreen.z)continue;K.positionScreen.x*=v;K.positionScreen.y*=z;L.positionScreen.x*=v;L.positionScreen.y*=z;U.positionScreen.x*=v;U.positionScreen.y*=z;!0===P.overdraw&&(I(K.positionScreen,L.positionScreen),I(L.positionScreen,U.positionScreen),I(U.positionScreen,K.positionScreen));Va.setFromPoints([K.positionScreen,L.positionScreen,U.positionScreen]);x(K,L,U,0,1,2,H,P)}else if(H instanceof THREE.RenderableFace4){K=H.v1;L=H.v2;U=H.v3;fa=H.v4;if(-1>K.positionScreen.z||
+1<K.positionScreen.z)continue;if(-1>L.positionScreen.z||1<L.positionScreen.z)continue;if(-1>U.positionScreen.z||1<U.positionScreen.z)continue;if(-1>fa.positionScreen.z||1<fa.positionScreen.z)continue;K.positionScreen.x*=v;K.positionScreen.y*=z;L.positionScreen.x*=v;L.positionScreen.y*=z;U.positionScreen.x*=v;U.positionScreen.y*=z;fa.positionScreen.x*=v;fa.positionScreen.y*=z;Ca.positionScreen.copy(L.positionScreen);$a.positionScreen.copy(fa.positionScreen);!0===P.overdraw&&(I(K.positionScreen,L.positionScreen),
+I(L.positionScreen,fa.positionScreen),I(fa.positionScreen,K.positionScreen),I(U.positionScreen,Ca.positionScreen),I(U.positionScreen,$a.positionScreen));Va.setFromPoints([K.positionScreen,L.positionScreen,U.positionScreen,fa.positionScreen]);X=K;wa=L;Bb=U;hb=fa;Cb=Ca;Db=$a;l.info.render.vertices+=4;l.info.render.faces++;b(P.opacity);c(P.blending);void 0!==P.map&&null!==P.map||void 0!==P.envMap&&null!==P.envMap?(x(X,wa,hb,0,1,3,H,P),x(Cb,Bb,Db,1,2,3,H,P)):(M=X.positionScreen.x,ca=X.positionScreen.y,
+qa=wa.positionScreen.x,ha=wa.positionScreen.y,ra=Bb.positionScreen.x,N=Bb.positionScreen.y,Ma=hb.positionScreen.x,Na=hb.positionScreen.y,mb=Cb.positionScreen.x,Pa=Cb.positionScreen.y,ta=Db.positionScreen.x,ka=Db.positionScreen.y,P instanceof THREE.MeshLambertMaterial||P instanceof THREE.MeshPhongMaterial?(Z.copy(P.color),oa.copy(P.emissive),P.vertexColors===THREE.FaceColors&&Z.multiply(H.color),!0===gc?!1===P.wireframe&&P.shading==THREE.SmoothShading&&4==H.vertexNormalsLength?(pa.copy(vb),Y.copy(vb),
+da.copy(vb),la.copy(vb),q(H.v1.positionWorld,H.vertexNormalsModel[0],pa),q(H.v2.positionWorld,H.vertexNormalsModel[1],Y),q(H.v4.positionWorld,H.vertexNormalsModel[3],da),q(H.v3.positionWorld,H.vertexNormalsModel[2],la),pa.multiply(Z).add(oa),Y.multiply(Z).add(oa),da.multiply(Z).add(oa),la.multiply(Z).add(oa),Fa=E(pa,Y,da,la),y(M,ca,qa,ha,Ma,Na),G(M,ca,qa,ha,Ma,Na,0,0,1,0,0,1,Fa),y(mb,Pa,ra,N,ta,ka),G(mb,Pa,ra,N,ta,ka,1,0,1,1,0,1,Fa)):(aa.copy(vb),q(H.centroidModel,H.normalModel,aa),aa.multiply(Z).add(oa),
+B(M,ca,qa,ha,ra,N,Ma,Na),!0===P.wireframe?C(aa,P.wireframeLinewidth,P.wireframeLinecap,P.wireframeLinejoin):A(aa)):(aa.addColors(Z,oa),B(M,ca,qa,ha,ra,N,Ma,Na),!0===P.wireframe?C(aa,P.wireframeLinewidth,P.wireframeLinecap,P.wireframeLinejoin):A(aa))):P instanceof THREE.MeshBasicMaterial?(aa.copy(P.color),P.vertexColors===THREE.FaceColors&&aa.multiply(H.color),B(M,ca,qa,ha,ra,N,Ma,Na),!0===P.wireframe?C(aa,P.wireframeLinewidth,P.wireframeLinecap,P.wireframeLinejoin):A(aa)):P instanceof THREE.MeshNormalMaterial?
+(X=void 0,P.shading==THREE.FlatShading?(X=H.normalModelView,aa.setRGB(X.x,X.y,X.z).multiplyScalar(0.5).addScalar(0.5),B(M,ca,qa,ha,ra,N,Ma,Na),!0===P.wireframe?C(aa,P.wireframeLinewidth,P.wireframeLinecap,P.wireframeLinejoin):A(aa)):P.shading==THREE.SmoothShading&&(X=H.vertexNormalsModelView[0],pa.setRGB(X.x,X.y,X.z).multiplyScalar(0.5).addScalar(0.5),X=H.vertexNormalsModelView[1],Y.setRGB(X.x,X.y,X.z).multiplyScalar(0.5).addScalar(0.5),X=H.vertexNormalsModelView[3],da.setRGB(X.x,X.y,X.z).multiplyScalar(0.5).addScalar(0.5),
+X=H.vertexNormalsModelView[2],la.setRGB(X.x,X.y,X.z).multiplyScalar(0.5).addScalar(0.5),Fa=E(pa,Y,da,la),y(M,ca,qa,ha,Ma,Na),G(M,ca,qa,ha,Ma,Na,0,0,1,0,0,1,Fa),y(mb,Pa,ra,N,ta,ka),G(mb,Pa,ra,N,ta,ka,1,0,1,1,0,1,Fa))):P instanceof THREE.MeshDepthMaterial&&(Wa=p.near,ab=p.far,pa.r=pa.g=pa.b=1-k(X.positionScreen.z*X.positionScreen.w,Wa,ab),Y.r=Y.g=Y.b=1-k(wa.positionScreen.z*wa.positionScreen.w,Wa,ab),da.r=da.g=da.b=1-k(hb.positionScreen.z*hb.positionScreen.w,Wa,ab),la.r=la.g=la.b=1-k(Bb.positionScreen.z*
+Bb.positionScreen.w,Wa,ab),Fa=E(pa,Y,da,la),y(M,ca,qa,ha,Ma,Na),G(M,ca,qa,ha,Ma,Na,0,0,1,0,0,1,Fa),y(mb,Pa,ra,N,ta,ka),G(mb,Pa,ra,N,ta,ka,1,0,1,1,0,1,Fa)))}Ka.union(Va)}}t.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( -viewPosition, 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, opacity );\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;\nmat4 getBoneMatrix( const in float i ) {\nfloat j = i * 4.0;\nfloat x = mod( j, N_BONE_PIXEL_X );\nfloat y = floor( j / N_BONE_PIXEL_X );\nconst float dx = 1.0 / N_BONE_PIXEL_X;\nconst float dy = 1.0 / N_BONE_PIXEL_Y;\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 ) ) );\nif ( depthKernel[0][0] < shadowCoord.z ) shadowKernel[0][0] = 0.25;\nelse shadowKernel[0][0] = 0.0;\ndepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( depthKernel[0][1] < shadowCoord.z ) shadowKernel[0][1] = 0.25;\nelse shadowKernel[0][1] = 0.0;\ndepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( depthKernel[0][2] < shadowCoord.z ) shadowKernel[0][2] = 0.25;\nelse shadowKernel[0][2] = 0.0;\ndepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( depthKernel[1][0] < shadowCoord.z ) shadowKernel[1][0] = 0.25;\nelse shadowKernel[1][0] = 0.0;\ndepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( depthKernel[1][1] < shadowCoord.z ) shadowKernel[1][1] = 0.25;\nelse shadowKernel[1][1] = 0.0;\ndepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( depthKernel[1][2] < shadowCoord.z ) shadowKernel[1][2] = 0.25;\nelse shadowKernel[1][2] = 0.0;\ndepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( depthKernel[2][0] < shadowCoord.z ) shadowKernel[2][0] = 0.25;\nelse shadowKernel[2][0] = 0.0;\ndepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( depthKernel[2][1] < shadowCoord.z ) shadowKernel[2][1] = 0.25;\nelse shadowKernel[2][1] = 0.0;\ndepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( depthKernel[2][2] < shadowCoord.z ) shadowKernel[2][2] = 0.25;\nelse shadowKernel[2][2] = 0.0;\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;\nvoid main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvNormal = normalize( normalMatrix * normal );\ngl_Position = projectionMatrix * mvPosition;\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){if(a.__webglCustomAttributesList)for(var b in a.__webglCustomAttributesList)j.deleteBuffer(a.__webglCustomAttributesList[b].buffer)}function c(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 g=1;"v2"===f.type?g=2:"v3"===f.type?g=3:"v4"===f.type?
+g=4:"c"===f.type&&(g=3);f.size=g;f.array=new Float32Array(c*g);f.buffer=j.createBuffer();f.buffer.belongsToAttribute=e;f.needsUpdate=!0}a.__webglCustomAttributesList.push(f)}}}function d(a,b){var c=b.geometry,d=a.faces3,h=a.faces4,i=3*d.length+4*h.length,k=1*d.length+2*h.length,h=3*d.length+4*h.length,d=e(b,a),n=g(d),l=f(d),m=d.vertexColors?d.vertexColors:!1;a.__vertexArray=new Float32Array(3*i);l&&(a.__normalArray=new Float32Array(3*i));c.hasTangents&&(a.__tangentArray=new Float32Array(4*i));m&&
+(a.__colorArray=new Float32Array(3*i));if(n){if(0<c.faceUvs.length||0<c.faceVertexUvs.length)a.__uvArray=new Float32Array(2*i);if(1<c.faceUvs.length||1<c.faceVertexUvs.length)a.__uv2Array=new Float32Array(2*i)}b.geometry.skinWeights.length&&b.geometry.skinIndices.length&&(a.__skinIndexArray=new Float32Array(4*i),a.__skinWeightArray=new Float32Array(4*i));a.__faceArray=new Uint16Array(3*k);a.__lineArray=new Uint16Array(2*h);if(a.numMorphTargets){a.__morphTargetsArrays=[];c=0;for(n=a.numMorphTargets;c<
+n;c++)a.__morphTargetsArrays.push(new Float32Array(3*i))}if(a.numMorphNormals){a.__morphNormalsArrays=[];c=0;for(n=a.numMorphNormals;c<n;c++)a.__morphNormalsArrays.push(new Float32Array(3*i))}a.__webglFaceCount=3*k;a.__webglLineCount=2*h;if(d.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var p in d.attributes){var k=d.attributes[p],c={},q;for(q in k)c[q]=k[q];if(!c.__webglInitialized||c.createUniqueBuffers)c.__webglInitialized=!0,h=1,"v2"===c.type?h=2:
+"v3"===c.type?h=3:"v4"===c.type?h=4:"c"===c.type&&(h=3),c.size=h,c.array=new Float32Array(i*h),c.buffer=j.createBuffer(),c.buffer.belongsToAttribute=p,k.needsUpdate=!0,c.__original=k;a.__webglCustomAttributesList.push(c)}}a.__inittedArrays=!0}function e(a,b){return a.material instanceof THREE.MeshFaceMaterial?a.material.materials[b.materialIndex]:a.material}function f(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 g(a){return a.map||a.lightMap||a.bumpMap||a.normalMap||a.specularMap||a instanceof THREE.ShaderMaterial?!0:!1}function h(a){var b,c,d;for(b in a.attributes)d="index"===b?j.ELEMENT_ARRAY_BUFFER:j.ARRAY_BUFFER,c=a.attributes[b],c.buffer=j.createBuffer(),j.bindBuffer(d,c.buffer),j.bufferData(d,c.array,j.STATIC_DRAW)}function i(a,b,c){var d=a.attributes,e=d.index,f=d.position,g=d.normal,h=d.uv,i=d.color,d=d.tangent;a.elementsNeedUpdate&&
+void 0!==e&&(j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,e.buffer),j.bufferData(j.ELEMENT_ARRAY_BUFFER,e.array,b));a.verticesNeedUpdate&&void 0!==f&&(j.bindBuffer(j.ARRAY_BUFFER,f.buffer),j.bufferData(j.ARRAY_BUFFER,f.array,b));a.normalsNeedUpdate&&void 0!==g&&(j.bindBuffer(j.ARRAY_BUFFER,g.buffer),j.bufferData(j.ARRAY_BUFFER,g.array,b));a.uvsNeedUpdate&&void 0!==h&&(j.bindBuffer(j.ARRAY_BUFFER,h.buffer),j.bufferData(j.ARRAY_BUFFER,h.array,b));a.colorsNeedUpdate&&void 0!==i&&(j.bindBuffer(j.ARRAY_BUFFER,
+i.buffer),j.bufferData(j.ARRAY_BUFFER,i.array,b));a.tangentsNeedUpdate&&void 0!==d&&(j.bindBuffer(j.ARRAY_BUFFER,d.buffer),j.bufferData(j.ARRAY_BUFFER,d.array,b));if(c)for(var k in a.attributes)delete a.attributes[k].array}function k(a){Ka[a]||(j.enableVertexAttribArray(a),Ka[a]=!0)}function l(){for(var a in Ka)Ka[a]&&(j.disableVertexAttribArray(a),Ka[a]=!1)}function m(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}function n(a,b){return b[0]-a[0]}function s(a,b,c){if(a.length)for(var d=0,e=a.length;d<e;d++)aa=
+mb=null,ta=ka=la=da=Wa=ia=Z=-1,bb=!0,a[d].render(b,c,mc,pb),aa=mb=null,ta=ka=la=da=Wa=ia=Z=-1,bb=!0}function r(a,b,c,d,e,f,g,j){var h,i,k,n;b?(i=a.length-1,n=b=-1):(i=0,b=a.length,n=1);for(var l=i;l!==b;l+=n)if(h=a[l],h.render){i=h.object;k=h.buffer;if(j)h=j;else{h=h[c];if(!h)continue;g&&N.setBlending(h.blending,h.blendEquation,h.blendSrc,h.blendDst);N.setDepthTest(h.depthTest);N.setDepthWrite(h.depthWrite);E(h.polygonOffset,h.polygonOffsetFactor,h.polygonOffsetUnits)}N.setMaterialFaces(h);k instanceof
+THREE.BufferGeometry?N.renderBufferDirect(d,e,f,h,k,i):N.renderBuffer(d,e,f,h,k,i)}}function p(a,b,c,d,e,f,g){for(var h,j,i=0,k=a.length;i<k;i++)if(h=a[i],j=h.object,j.visible){if(g)h=g;else{h=h[b];if(!h)continue;f&&N.setBlending(h.blending,h.blendEquation,h.blendSrc,h.blendDst);N.setDepthTest(h.depthTest);N.setDepthWrite(h.depthWrite);E(h.polygonOffset,h.polygonOffsetFactor,h.polygonOffsetUnits)}N.renderImmediateObject(c,d,e,h,j)}}function q(a,b,c){a.push({buffer:b,object:c,opaque:null,transparent:null})}
+function y(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;return!1}function v(a){for(var b in a.attributes)a.attributes[b].needsUpdate=!1}function z(a,b){for(var c=a.length-1;0<=c;c--)a[c].object===b&&a.splice(c,1)}function t(a,b){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1)}function A(a,b,c,d,e){Y=0;d.needsUpdate&&(d.program&&Pc(d),N.initMaterial(d,b,c,e),d.needsUpdate=!1);d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(N.maxMorphTargets));
+var f=!1,g=d.program,h=g.uniforms,i=d.uniforms;g!==mb&&(j.useProgram(g),mb=g,f=!0);d.id!==ta&&(ta=d.id,f=!0);if(f||a!==aa)j.uniformMatrix4fv(h.projectionMatrix,!1,a.projectionMatrix.elements),a!==aa&&(aa=a);if(d.skinning)if(tc&&e.useVertexTexture){if(null!==h.boneTexture){var k=I();j.uniform1i(h.boneTexture,k);N.setTexture(e.boneTexture,k)}}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(bb){for(var n,l=k=0,m=0,p,q,s,r=xb,t=r.directional.colors,v=r.directional.positions,y=r.point.colors,z=r.point.positions,B=r.point.distances,C=r.spot.colors,A=r.spot.positions,F=r.spot.distances,E=r.spot.directions,J=r.spot.anglesCos,K=r.spot.exponents,H=r.hemi.skyColors,M=r.hemi.groundColors,P=r.hemi.positions,
+X=0,da=0,ka=0,fa=0,ca=0,pa=0,Ma=0,ha=0,O=n=0,c=s=O=0,f=b.length;c<f;c++)n=b[c],n.onlyShadow||(p=n.color,q=n.intensity,s=n.distance,n instanceof THREE.AmbientLight?n.visible&&(N.gammaInput?(k+=p.r*p.r,l+=p.g*p.g,m+=p.b*p.b):(k+=p.r,l+=p.g,m+=p.b)):n instanceof THREE.DirectionalLight?(ca+=1,n.visible&&(La.getPositionFromMatrix(n.matrixWorld),Qa.getPositionFromMatrix(n.target.matrixWorld),La.sub(Qa),La.normalize(),0===La.x&&0===La.y&&0===La.z||(n=3*X,v[n]=La.x,v[n+1]=La.y,v[n+2]=La.z,N.gammaInput?x(t,
+n,p,q*q):G(t,n,p,q),X+=1))):n instanceof THREE.PointLight?(pa+=1,n.visible&&(O=3*da,N.gammaInput?x(y,O,p,q*q):G(y,O,p,q),Qa.getPositionFromMatrix(n.matrixWorld),z[O]=Qa.x,z[O+1]=Qa.y,z[O+2]=Qa.z,B[da]=s,da+=1)):n instanceof THREE.SpotLight?(Ma+=1,n.visible&&(O=3*ka,N.gammaInput?x(C,O,p,q*q):G(C,O,p,q),Qa.getPositionFromMatrix(n.matrixWorld),A[O]=Qa.x,A[O+1]=Qa.y,A[O+2]=Qa.z,F[ka]=s,La.copy(Qa),Qa.getPositionFromMatrix(n.target.matrixWorld),La.sub(Qa),La.normalize(),E[O]=La.x,E[O+1]=La.y,E[O+2]=La.z,
+J[ka]=Math.cos(n.angle),K[ka]=n.exponent,ka+=1)):n instanceof THREE.HemisphereLight&&(ha+=1,n.visible&&(La.getPositionFromMatrix(n.matrixWorld),La.normalize(),0===La.x&&0===La.y&&0===La.z||(s=3*fa,P[s]=La.x,P[s+1]=La.y,P[s+2]=La.z,p=n.color,n=n.groundColor,N.gammaInput?(q*=q,x(H,s,p,q),x(M,s,n,q)):(G(H,s,p,q),G(M,s,n,q)),fa+=1))));c=3*X;for(f=Math.max(t.length,3*ca);c<f;c++)t[c]=0;c=3*da;for(f=Math.max(y.length,3*pa);c<f;c++)y[c]=0;c=3*ka;for(f=Math.max(C.length,3*Ma);c<f;c++)C[c]=0;c=3*fa;for(f=
+Math.max(H.length,3*ha);c<f;c++)H[c]=0;c=3*fa;for(f=Math.max(M.length,3*ha);c<f;c++)M[c]=0;r.directional.length=X;r.point.length=da;r.spot.length=ka;r.hemi.length=fa;r.ambient[0]=k;r.ambient[1]=l;r.ambient[2]=m;bb=!1}c=xb;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;N.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 Z;d.map?Z=d.map:d.specularMap?Z=d.specularMap:d.normalMap?Z=d.normalMap:d.bumpMap&&(Z=d.bumpMap);void 0!==Z&&(c=Z.offset,Z=Z.repeat,i.offsetRepeat.value.set(c.x,c.y,Z.x,Z.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.ParticleBasicMaterial?(i.psColor.value=d.color,i.opacity.value=d.opacity,i.size.value=d.size,i.scale.value=U.height/2,i.map.value=d.map):d instanceof THREE.MeshPhongMaterial?(i.shininess.value=d.shininess,N.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?(N.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=Z=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[Z]=k.shadowMap,i.shadowMapSize.value[Z]=k.shadowMapSize,i.shadowMatrix.value[Z]=k.shadowMatrix,i.shadowDarkness.value[Z]=k.shadowDarkness,i.shadowBias.value[Z]=k.shadowBias,Z++}b=d.uniformsList;i=0;for(Z=b.length;i<Z;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(m=k.length;l<m;l++)r=2*l,c._array[r]=k[l].x,c._array[r+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(m=k.length;l<m;l++)r=3*l,c._array[r]=k[l].x,c._array[r+1]=k[l].y,c._array[r+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(m=k.length;l<m;l++)r=4*l,c._array[r]=k[l].x,c._array[r+1]=k[l].y,c._array[r+2]=k[l].z,c._array[r+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(m=k.length;l<m;l++)k[l].flattenToArrayOffset(c._array,16*l);j.uniformMatrix4fv(f,!1,c._array)}else if("t"===l){if(r=k,k=I(),j.uniform1i(f,k),r)if(r.image instanceof Array&&6===r.image.length){if(c=r,f=k,6===c.image.length)if(c.needsUpdate){c.image.__webglTextureCube||(c.image.__webglTextureCube=j.createTexture(),N.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++)N.autoScaleCubemaps&&!f?(m=k,r=l,t=c.image[l],y=gd,t.width<=y&&t.height<=y||(z=Math.max(t.width,t.height),v=Math.floor(t.width*y/z),y=Math.floor(t.height*y/z),z=document.createElement("canvas"),z.width=v,z.height=y,z.getContext("2d").drawImage(t,0,0,t.width,t.height,0,0,v,y),t=z),m[r]=t):k[l]=c.image[l];l=k[0];m=0===(l.width&l.width-1)&&0===(l.height&l.height-1);r=L(c.format);
+t=L(c.type);W(j.TEXTURE_CUBE_MAP,c,m);for(l=0;6>l;l++)if(f){y=k[l].mipmaps;z=0;for(B=y.length;z<B;z++)v=y[z],j.compressedTexImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,z,r,v.width,v.height,0,v.data)}else j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,0,r,r,t,k[l]);c.generateMipmaps&&m&&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 r instanceof THREE.WebGLRenderTargetCube?
+(c=r,j.activeTexture(j.TEXTURE0+k),j.bindTexture(j.TEXTURE_CUBE_MAP,c.__webglTexture)):N.setTexture(r,k)}else if("tv"===l){void 0===c._array&&(c._array=[]);l=0;for(m=c.value.length;l<m;l++)c._array[l]=I();j.uniform1iv(f,c._array);l=0;for(m=c.value.length;l<m;l++)r=c.value[l],k=c._array[l],r&&N.setTexture(r,k)}if((d instanceof THREE.ShaderMaterial||d instanceof THREE.MeshPhongMaterial||d.envMap)&&null!==h.cameraPosition)Qa.getPositionFromMatrix(a.matrixWorld),j.uniform3f(h.cameraPosition,Qa.x,Qa.y,
+Qa.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 I(){var a=Y;a>=Mc&&console.warn("WebGLRenderer: trying to use "+
+a+" texture units while this GPU supports only "+Mc);Y+=1;return a}function C(a,b){a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,a.matrixWorld);a._normalMatrix.getInverse(a._modelViewMatrix);a._normalMatrix.transpose()}function x(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 G(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function J(a){a!==ub&&(j.lineWidth(a),ub=a)}function E(a,b,c){ab!==a&&(a?j.enable(j.POLYGON_OFFSET_FILL):j.disable(j.POLYGON_OFFSET_FILL),ab=a);if(a&&
+(Fa!==b||Xa!==c))j.polygonOffset(b,c),Fa=b,Xa=c}function H(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 B(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(H(b)),null):c}function W(a,b,c){c?(j.texParameteri(a,j.TEXTURE_WRAP_S,L(b.wrapS)),j.texParameteri(a,
+j.TEXTURE_WRAP_T,L(b.wrapT)),j.texParameteri(a,j.TEXTURE_MAG_FILTER,L(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,L(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,K(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,K(b.minFilter)));if(cb&&b.type!==THREE.FloatType&&(1<b.anisotropy||b.__oldAnisotropy))j.texParameterf(a,cb.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,Cc)),b.__oldAnisotropy=
+b.anisotropy}function F(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 K(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?j.NEAREST:j.LINEAR}function L(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!==Sa){if(a===THREE.RGB_S3TC_DXT1_Format)return Sa.COMPRESSED_RGB_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT1_Format)return Sa.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return Sa.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return Sa.COMPRESSED_RGBA_S3TC_DXT5_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);var a=a||{},U=void 0!==a.canvas?a.canvas:document.createElement("canvas"),
+fa=void 0!==a.precision?a.precision:"highp",Ca=void 0!==a.alpha?a.alpha:!0,$a=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,M=void 0!==a.antialias?a.antialias:!1,ca=void 0!==a.stencil?a.stencil:!0,qa=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,ha=void 0!==a.clearColor?new THREE.Color(a.clearColor):new THREE.Color(0),ra=void 0!==a.clearAlpha?a.clearAlpha:0;this.domElement=U;this.context=null;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==window.devicePixelRatio?
+window.devicePixelRatio:1;this.autoUpdateScene=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 N=this,Ma=[],Na=0,mb=null,Pa=null,ta=-1,ka=null,aa=null,pa=0,Y=0,da=-1,la=-1,Z=-1,oa=-1,gb=-1,nb=-1,ia=-1,Wa=-1,ab=null,Fa=null,Xa=null,ub=null,Ib=0,Jb=0,fc=0,Ab=0,mc=0,pb=0,Ka={},Va=new THREE.Frustum,gc=new THREE.Matrix4,vb=new THREE.Matrix4,Qa=new THREE.Vector3,La=new THREE.Vector3,bb=!0,xb={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,yb,Ra,cb,Sa;try{if(!(j=U.getContext("experimental-webgl",{alpha:Ca,premultipliedAlpha:$a,antialias:M,stencil:ca,preserveDrawingBuffer:qa})))throw"Error creating WebGL context.";}catch(zb){console.error(zb)}yb=j.getExtension("OES_texture_float");Ra=j.getExtension("OES_standard_derivatives");cb=j.getExtension("EXT_texture_filter_anisotropic")||
+j.getExtension("MOZ_EXT_texture_filter_anisotropic")||j.getExtension("WEBKIT_EXT_texture_filter_anisotropic");Sa=j.getExtension("WEBGL_compressed_texture_s3tc")||j.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||j.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");yb||console.log("THREE.WebGLRenderer: Float textures not supported.");Ra||console.log("THREE.WebGLRenderer: Standard derivatives not supported.");cb||console.log("THREE.WebGLRenderer: Anisotropic texture filtering not supported.");
+Sa||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.clearColor(ha.r,ha.g,ha.b,ra);this.context=j;
+var Mc=j.getParameter(j.MAX_TEXTURE_IMAGE_UNITS),fd=j.getParameter(j.MAX_VERTEX_TEXTURE_IMAGE_UNITS);j.getParameter(j.MAX_TEXTURE_SIZE);var gd=j.getParameter(j.MAX_CUBE_MAP_TEXTURE_SIZE),Cc=cb?j.getParameter(cb.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0,sc=0<fd,tc=sc&&yb;Sa&&j.getParameter(j.COMPRESSED_TEXTURE_FORMATS);var jd=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.HIGH_FLOAT),kd=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.MEDIUM_FLOAT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.LOW_FLOAT);var ld=
+j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.HIGH_FLOAT),id=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 hd=0<jd.precision&&0<ld.precision,Nc=0<kd.precision&&0<id.precision;"highp"===fa&&!hd&&(Nc?(fa="mediump",console.warn("WebGLRenderer: highp not supported, using mediump")):(fa="lowp",console.warn("WebGLRenderer: highp and mediump not supported, using lowp")));"mediump"===fa&&!Nc&&(fa="lowp",console.warn("WebGLRenderer: mediump not supported, using lowp"));this.getContext=function(){return j};this.supportsVertexTextures=function(){return sc};this.supportsFloatTextures=function(){return yb};
+this.supportsStandardDerivatives=function(){return Ra};this.supportsCompressedTextureS3TC=function(){return Sa};this.getMaxAnisotropy=function(){return Cc};this.getPrecision=function(){return fa};this.setSize=function(a,b){U.width=a*this.devicePixelRatio;U.height=b*this.devicePixelRatio;U.style.width=a+"px";U.style.height=b+"px";this.setViewport(0,0,U.width,U.height)};this.setViewport=function(a,b,c,d){Ib=void 0!==a?a:0;Jb=void 0!==b?b:0;fc=void 0!==c?c:U.width;Ab=void 0!==d?d:U.height;j.viewport(Ib,
+Jb,fc,Ab)};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.setClearColorHex=function(a,b){ha.setHex(a);ra=b;j.clearColor(ha.r,ha.g,ha.b,ra)};this.setClearColor=function(a,b){ha.copy(a);ra=b;j.clearColor(ha.r,ha.g,ha.b,ra)};this.getClearColor=function(){return ha};this.getClearAlpha=function(){return ra};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){mb=null;ta=ka=Wa=ia=Z=-1;bb=!0;la=da=-1;this.shadowMapPlugin.update(a,b)};var wd=function(a){a=a.target;a.removeEventListener("dispose",wd);a.__webglInit=void 0;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.geometryGroups)for(var c in a.geometryGroups){var d=a.geometryGroups[c];if(void 0!==d.numMorphTargets)for(var e=0,f=d.numMorphTargets;e<f;e++)j.deleteBuffer(d.__webglMorphTargetsBuffers[e]);if(void 0!==d.numMorphNormals){e=0;for(f=d.numMorphNormals;e<f;e++)j.deleteBuffer(d.__webglMorphNormalsBuffers[e])}b(d)}b(a);
+N.info.memory.geometries--},Oc=function(a){a=a.target;a.removeEventListener("dispose",Oc);a.image&&a.image.__webglTextureCube?j.deleteTexture(a.image.__webglTextureCube):a.__webglInit&&(a.__webglInit=!1,j.deleteTexture(a.__webglTexture));N.info.memory.textures--},P=function(a){a=a.target;a.removeEventListener("dispose",P);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);N.info.memory.textures--},X=function(a){a=a.target;a.removeEventListener("dispose",X);Pc(a)},Pc=function(a){var b=a.program;if(void 0!==b){a.program=void 0;var c,d,e=!1,a=0;for(c=Ma.length;a<c;a++)if(d=Ma[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=Ma.length;a<c;a++)d=Ma[a],d.program!==b&&e.push(d);Ma=e;j.deleteProgram(b);N.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,g,i,h,k,l,n,m,p,q=3*a.count;for(p=0;p<q;p+=9)m=a.normalArray,d=m[p],e=m[p+1],f=m[p+2],g=m[p+3],h=m[p+4],l=m[p+5],i=m[p+6],k=m[p+7],n=m[p+8],d=(d+g+i)/3,e=(e+h+k)/3,f=(f+l+n)/3,m[p]=d,m[p+1]=e,m[p+2]=f,m[p+3]=d,m[p+4]=e,m[p+5]=f,m[p+6]=d,m[p+7]=e,m[p+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)if(c=A(a,b,c,d,f),a=c.attributes,b=!1,c=16777215*e.id+2*c.id+(d.wireframe?1:0),c!==ka&&(ka=c,b=!0),b&&l(),f instanceof THREE.Mesh)if(d=e.attributes.index){f=e.offsets;1<f.length&&(b=!0);for(var c=0,g=f.length;c<g;c++){var i=f[c].index;if(b){var h=e.attributes.position,n=h.itemSize;j.bindBuffer(j.ARRAY_BUFFER,h.buffer);k(a.position);j.vertexAttribPointer(a.position,n,j.FLOAT,!1,0,4*i*n);n=e.attributes.normal;if(0<=
+a.normal&&n){var m=n.itemSize;j.bindBuffer(j.ARRAY_BUFFER,n.buffer);k(a.normal);j.vertexAttribPointer(a.normal,m,j.FLOAT,!1,0,4*i*m)}n=e.attributes.uv;0<=a.uv&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.uv),j.vertexAttribPointer(a.uv,m,j.FLOAT,!1,0,4*i*m));n=e.attributes.color;0<=a.color&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.color),j.vertexAttribPointer(a.color,m,j.FLOAT,!1,0,4*i*m));n=e.attributes.tangent;0<=a.tangent&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,
+n.buffer),k(a.tangent),j.vertexAttribPointer(a.tangent,m,j.FLOAT,!1,0,4*i*m));j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,d.buffer)}j.drawElements(j.TRIANGLES,f[c].count,j.UNSIGNED_SHORT,2*f[c].start);N.info.render.calls++;N.info.render.vertices+=f[c].count;N.info.render.faces+=f[c].count/3}}else b&&(h=e.attributes.position,n=h.itemSize,j.bindBuffer(j.ARRAY_BUFFER,h.buffer),k(a.position),j.vertexAttribPointer(a.position,n,j.FLOAT,!1,0,0),n=e.attributes.normal,0<=a.normal&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,
+n.buffer),k(a.normal),j.vertexAttribPointer(a.normal,m,j.FLOAT,!1,0,0)),n=e.attributes.uv,0<=a.uv&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.uv),j.vertexAttribPointer(a.uv,m,j.FLOAT,!1,0,0)),n=e.attributes.color,0<=a.color&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.color),j.vertexAttribPointer(a.color,m,j.FLOAT,!1,0,0)),n=e.attributes.tangent,0<=a.tangent&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.tangent),j.vertexAttribPointer(a.tangent,m,j.FLOAT,
+!1,0,0))),j.drawArrays(j.TRIANGLES,0,h.numItems/3),N.info.render.calls++,N.info.render.vertices+=h.numItems/3,N.info.render.faces+=h.numItems/3/3;else f instanceof THREE.ParticleSystem?b&&(h=e.attributes.position,n=h.itemSize,j.bindBuffer(j.ARRAY_BUFFER,h.buffer),k(a.position),j.vertexAttribPointer(a.position,n,j.FLOAT,!1,0,0),n=e.attributes.color,0<=a.color&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.color),j.vertexAttribPointer(a.color,m,j.FLOAT,!1,0,0)),j.drawArrays(j.POINTS,0,
+h.numItems/3),N.info.render.calls++,N.info.render.points+=h.numItems/3):f instanceof THREE.Line&&b&&(h=e.attributes.position,n=h.itemSize,j.bindBuffer(j.ARRAY_BUFFER,h.buffer),k(a.position),j.vertexAttribPointer(a.position,n,j.FLOAT,!1,0,0),n=e.attributes.color,0<=a.color&&n&&(m=n.itemSize,j.bindBuffer(j.ARRAY_BUFFER,n.buffer),k(a.color),j.vertexAttribPointer(a.color,m,j.FLOAT,!1,0,0)),J(d.linewidth),j.drawArrays(j.LINE_STRIP,0,h.numItems/3),N.info.render.calls++,N.info.render.points+=h.numItems)};
+this.renderBuffer=function(a,b,c,d,e,f){if(!1!==d.visible){var g,i,c=A(a,b,c,d,f),a=c.attributes,b=!1,c=16777215*e.id+2*c.id+(d.wireframe?1:0);c!==ka&&(ka=c,b=!0);b&&l();if(!d.morphTargets&&0<=a.position)b&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglVertexBuffer),k(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]),k(c.position),
+j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0)):0<=c.position&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglVertexBuffer),k(c.position),j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0));if(f.morphTargetForcedOrder.length){var h=0;i=f.morphTargetForcedOrder;for(g=f.morphTargetInfluences;h<d.numSupportedMorphTargets&&h<i.length;)0<=c["morphTarget"+h]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[i[h]]),k(c["morphTarget"+h]),j.vertexAttribPointer(c["morphTarget"+h],3,j.FLOAT,!1,0,0)),0<=
+c["morphNormal"+h]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[i[h]]),k(c["morphNormal"+h]),j.vertexAttribPointer(c["morphNormal"+h],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[h]=g[i[h]],h++}else{i=[];g=f.morphTargetInfluences;var m,p=g.length;for(m=0;m<p;m++)h=g[m],0<h&&i.push([h,m]);i.length>d.numSupportedMorphTargets?(i.sort(n),i.length=d.numSupportedMorphTargets):i.length>d.numSupportedMorphNormals?i.sort(n):0===i.length&&i.push([0,0]);for(h=0;h<d.numSupportedMorphTargets;)i[h]?
+(m=i[h][1],0<=c["morphTarget"+h]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[m]),k(c["morphTarget"+h]),j.vertexAttribPointer(c["morphTarget"+h],3,j.FLOAT,!1,0,0)),0<=c["morphNormal"+h]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[m]),k(c["morphNormal"+h]),j.vertexAttribPointer(c["morphNormal"+h],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[h]=g[m]):f.__webglMorphTargetInfluences[h]=0,h++}null!==d.program.uniforms.morphTargetInfluences&&j.uniform1fv(d.program.uniforms.morphTargetInfluences,
+f.__webglMorphTargetInfluences)}if(b){if(e.__webglCustomAttributesList){g=0;for(i=e.__webglCustomAttributesList.length;g<i;g++)c=e.__webglCustomAttributesList[g],0<=a[c.buffer.belongsToAttribute]&&(j.bindBuffer(j.ARRAY_BUFFER,c.buffer),k(a[c.buffer.belongsToAttribute]),j.vertexAttribPointer(a[c.buffer.belongsToAttribute],c.size,j.FLOAT,!1,0,0))}0<=a.color&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglColorBuffer),k(a.color),j.vertexAttribPointer(a.color,3,j.FLOAT,!1,0,0));0<=a.normal&&(j.bindBuffer(j.ARRAY_BUFFER,
+e.__webglNormalBuffer),k(a.normal),j.vertexAttribPointer(a.normal,3,j.FLOAT,!1,0,0));0<=a.tangent&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglTangentBuffer),k(a.tangent),j.vertexAttribPointer(a.tangent,4,j.FLOAT,!1,0,0));0<=a.uv&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUVBuffer),k(a.uv),j.vertexAttribPointer(a.uv,2,j.FLOAT,!1,0,0));0<=a.uv2&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUV2Buffer),k(a.uv2),j.vertexAttribPointer(a.uv2,2,j.FLOAT,!1,0,0));d.skinning&&(0<=a.skinIndex&&0<=a.skinWeight)&&(j.bindBuffer(j.ARRAY_BUFFER,
+e.__webglSkinIndicesBuffer),k(a.skinIndex),j.vertexAttribPointer(a.skinIndex,4,j.FLOAT,!1,0,0),j.bindBuffer(j.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),k(a.skinWeight),j.vertexAttribPointer(a.skinWeight,4,j.FLOAT,!1,0,0));0<=a.lineDistance&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglLineDistanceBuffer),k(a.lineDistance),j.vertexAttribPointer(a.lineDistance,1,j.FLOAT,!1,0,0))}f instanceof THREE.Mesh?(d.wireframe?(J(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)),N.info.render.calls++,N.info.render.vertices+=e.__webglFaceCount,N.info.render.faces+=e.__webglFaceCount/3):f instanceof THREE.Line?(f=f.type===THREE.LineStrip?j.LINE_STRIP:j.LINES,J(d.linewidth),j.drawArrays(f,0,e.__webglLineCount),N.info.render.calls++):f instanceof THREE.ParticleSystem?(j.drawArrays(j.POINTS,0,e.__webglParticleCount),
+N.info.render.calls++,N.info.render.points+=e.__webglParticleCount):f instanceof THREE.Ribbon&&(j.drawArrays(j.TRIANGLE_STRIP,0,e.__webglVertexCount),N.info.render.calls++)}};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,g,i,h=a.__lights,k=a.fog;ta=-1;bb=!0;this.autoUpdateScene&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();b.matrixWorldInverse.getInverse(b.matrixWorld);
+gc.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);Va.setFromMatrix(gc);this.autoUpdateObjects&&this.initWebGLObjects(a);s(this.renderPluginsPre,a,b);N.info.render.calls=0;N.info.render.vertices=0;N.info.render.faces=0;N.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil);i=a.__webglObjects;d=0;for(e=i.length;d<e;d++)if(f=i[d],g=f.object,f.render=!1,g.visible&&(!(g instanceof THREE.Mesh||g instanceof
+THREE.ParticleSystem)||!g.frustumCulled||Va.intersectsObject(g))){C(g,b);var n=f,l=n.buffer,q=void 0,t=q=void 0,t=n.object.material;if(t instanceof THREE.MeshFaceMaterial)q=l.materialIndex,q=t.materials[q],q.transparent?(n.transparent=q,n.opaque=null):(n.opaque=q,n.transparent=null);else if(q=t)q.transparent?(n.transparent=q,n.opaque=null):(n.opaque=q,n.transparent=null);f.render=!0;!0===this.sortObjects&&(null!==g.renderDepth?f.z=g.renderDepth:(Qa.getPositionFromMatrix(g.matrixWorld),Qa.applyProjection(gc),
+f.z=Qa.z),f.id=g.id)}this.sortObjects&&i.sort(m);i=a.__webglObjectsImmediate;d=0;for(e=i.length;d<e;d++)f=i[d],g=f.object,g.visible&&(C(g,b),g=f.object.material,g.transparent?(f.transparent=g,f.opaque=null):(f.opaque=g,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),E(d.polygonOffset,d.polygonOffsetFactor,d.polygonOffsetUnits),r(a.__webglObjects,!1,"",b,
+h,k,!0,d),p(a.__webglObjectsImmediate,"",b,h,k,!1,d)):(d=null,this.setBlending(THREE.NoBlending),r(a.__webglObjects,!0,"opaque",b,h,k,!1,d),p(a.__webglObjectsImmediate,"opaque",b,h,k,!1,d),r(a.__webglObjects,!1,"transparent",b,h,k,!0,d),p(a.__webglObjectsImmediate,"transparent",b,h,k,!0,d));s(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=A(a,b,c,d,e);ka=-1;N.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,j,Va):e.render(function(a){N.renderBufferImmediate(a,f,d)})};this.initWebGLObjects=function(a){a.__webglObjects||(a.__webglObjects=
+[],a.__webglObjectsImmediate=[],a.__webglSprites=[],a.__webglFlares=[]);for(;a.__objectsAdded.length;){var b=a.__objectsAdded[0],k=a,l=void 0,m=void 0,p=void 0,r=void 0;if(!b.__webglInit)if(b.__webglInit=!0,b._modelViewMatrix=new THREE.Matrix4,b._normalMatrix=new THREE.Matrix3,void 0!==b.geometry&&void 0===b.geometry.__webglInit&&(b.geometry.__webglInit=!0,b.geometry.addEventListener("dispose",wd)),b instanceof THREE.Mesh)if(m=b.geometry,p=b.material,m instanceof THREE.Geometry){if(void 0===m.geometryGroups){var s=
+m,x=void 0,C=void 0,B=void 0,A=void 0,F=void 0,E=void 0,G={},I=s.morphTargets.length,J=s.morphNormals.length,K=p instanceof THREE.MeshFaceMaterial;s.geometryGroups={};x=0;for(C=s.faces.length;x<C;x++)B=s.faces[x],A=K?B.materialIndex:0,void 0===G[A]&&(G[A]={hash:A,counter:0}),E=G[A].hash+"_"+G[A].counter,void 0===s.geometryGroups[E]&&(s.geometryGroups[E]={faces3:[],faces4:[],materialIndex:A,vertices:0,numMorphTargets:I,numMorphNormals:J}),F=B instanceof THREE.Face3?3:4,65535<s.geometryGroups[E].vertices+
+F&&(G[A].counter+=1,E=G[A].hash+"_"+G[A].counter,void 0===s.geometryGroups[E]&&(s.geometryGroups[E]={faces3:[],faces4:[],materialIndex:A,vertices:0,numMorphTargets:I,numMorphNormals:J})),B instanceof THREE.Face3?s.geometryGroups[E].faces3.push(x):s.geometryGroups[E].faces4.push(x),s.geometryGroups[E].vertices+=F;s.geometryGroupsList=[];var L=void 0;for(L in s.geometryGroups)s.geometryGroups[L].id=pa++,s.geometryGroupsList.push(s.geometryGroups[L])}for(l in m.geometryGroups)if(r=m.geometryGroups[l],
+!r.__webglVertexBuffer){var H=r;H.__webglVertexBuffer=j.createBuffer();H.__webglNormalBuffer=j.createBuffer();H.__webglTangentBuffer=j.createBuffer();H.__webglColorBuffer=j.createBuffer();H.__webglUVBuffer=j.createBuffer();H.__webglUV2Buffer=j.createBuffer();H.__webglSkinIndicesBuffer=j.createBuffer();H.__webglSkinWeightsBuffer=j.createBuffer();H.__webglFaceBuffer=j.createBuffer();H.__webglLineBuffer=j.createBuffer();var M=void 0,P=void 0;if(H.numMorphTargets){H.__webglMorphTargetsBuffers=[];M=0;
+for(P=H.numMorphTargets;M<P;M++)H.__webglMorphTargetsBuffers.push(j.createBuffer())}if(H.numMorphNormals){H.__webglMorphNormalsBuffers=[];M=0;for(P=H.numMorphNormals;M<P;M++)H.__webglMorphNormalsBuffers.push(j.createBuffer())}N.info.memory.geometries++;d(r,b);m.verticesNeedUpdate=!0;m.morphTargetsNeedUpdate=!0;m.elementsNeedUpdate=!0;m.uvsNeedUpdate=!0;m.normalsNeedUpdate=!0;m.tangentsNeedUpdate=!0;m.colorsNeedUpdate=!0}}else m instanceof THREE.BufferGeometry&&h(m);else if(b instanceof THREE.Ribbon){if(m=
+b.geometry,!m.__webglVertexBuffer){var U=m;U.__webglVertexBuffer=j.createBuffer();U.__webglColorBuffer=j.createBuffer();U.__webglNormalBuffer=j.createBuffer();N.info.memory.geometries++;var aa=m,W=b,Y=aa.vertices.length;aa.__vertexArray=new Float32Array(3*Y);aa.__colorArray=new Float32Array(3*Y);aa.__normalArray=new Float32Array(3*Y);aa.__webglVertexCount=Y;c(aa,W);m.verticesNeedUpdate=!0;m.colorsNeedUpdate=!0;m.normalsNeedUpdate=!0}}else if(b instanceof THREE.Line){if(m=b.geometry,!m.__webglVertexBuffer)if(m instanceof
+THREE.Geometry){var Z=m;Z.__webglVertexBuffer=j.createBuffer();Z.__webglColorBuffer=j.createBuffer();Z.__webglLineDistanceBuffer=j.createBuffer();N.info.memory.geometries++;var X=m,da=b,ka=X.vertices.length;X.__vertexArray=new Float32Array(3*ka);X.__colorArray=new Float32Array(3*ka);X.__lineDistanceArray=new Float32Array(1*ka);X.__webglLineCount=ka;c(X,da);m.verticesNeedUpdate=!0;m.colorsNeedUpdate=!0;m.lineDistancesNeedUpdate=!0}else m instanceof THREE.BufferGeometry&&h(m)}else if(b instanceof THREE.ParticleSystem&&
+(m=b.geometry,!m.__webglVertexBuffer))if(m instanceof THREE.Geometry){var fa=m;fa.__webglVertexBuffer=j.createBuffer();fa.__webglColorBuffer=j.createBuffer();N.info.memory.geometries++;var ca=m,Ma=b,ha=ca.vertices.length;ca.__vertexArray=new Float32Array(3*ha);ca.__colorArray=new Float32Array(3*ha);ca.__sortArray=[];ca.__webglParticleCount=ha;c(ca,Ma);m.verticesNeedUpdate=!0;m.colorsNeedUpdate=!0}else m instanceof THREE.BufferGeometry&&h(m);if(!b.__webglActive){if(b instanceof THREE.Mesh)if(m=b.geometry,
+m instanceof THREE.BufferGeometry)q(k.__webglObjects,m,b);else{if(m instanceof THREE.Geometry)for(l in m.geometryGroups)r=m.geometryGroups[l],q(k.__webglObjects,r,b)}else b instanceof THREE.Ribbon||b instanceof THREE.Line||b instanceof THREE.ParticleSystem?(m=b.geometry,q(k.__webglObjects,m,b)):b instanceof THREE.ImmediateRenderObject||b.immediateRenderCallback?k.__webglObjectsImmediate.push({object:b,opaque:null,transparent:null}):b instanceof THREE.Sprite?k.__webglSprites.push(b):b instanceof THREE.LensFlare&&
+k.__webglFlares.push(b);b.__webglActive=!0}a.__objectsAdded.splice(0,1)}for(;a.__objectsRemoved.length;){var Na=a.__objectsRemoved[0],la=a;Na instanceof THREE.Mesh||Na instanceof THREE.ParticleSystem||Na instanceof THREE.Ribbon||Na instanceof THREE.Line?z(la.__webglObjects,Na):Na instanceof THREE.Sprite?t(la.__webglSprites,Na):Na instanceof THREE.LensFlare?t(la.__webglFlares,Na):(Na instanceof THREE.ImmediateRenderObject||Na.immediateRenderCallback)&&z(la.__webglObjectsImmediate,Na);Na.__webglActive=
+!1;a.__objectsRemoved.splice(0,1)}for(var oa=0,ra=a.__webglObjects.length;oa<ra;oa++){var ta=a.__webglObjects[oa].object,O=ta.geometry,mb=void 0,qa=void 0,ia=void 0;if(ta instanceof THREE.Mesh)if(O instanceof THREE.BufferGeometry)(O.verticesNeedUpdate||O.elementsNeedUpdate||O.uvsNeedUpdate||O.normalsNeedUpdate||O.colorsNeedUpdate||O.tangentsNeedUpdate)&&i(O,j.DYNAMIC_DRAW,!O.dynamic),O.verticesNeedUpdate=!1,O.elementsNeedUpdate=!1,O.uvsNeedUpdate=!1,O.normalsNeedUpdate=!1,O.colorsNeedUpdate=!1,O.tangentsNeedUpdate=
+!1;else{for(var Ca=0,Ka=O.geometryGroupsList.length;Ca<Ka;Ca++)if(mb=O.geometryGroupsList[Ca],ia=e(ta,mb),O.buffersNeedUpdate&&d(mb,ta),qa=ia.attributes&&y(ia),O.verticesNeedUpdate||O.morphTargetsNeedUpdate||O.elementsNeedUpdate||O.uvsNeedUpdate||O.normalsNeedUpdate||O.colorsNeedUpdate||O.tangentsNeedUpdate||qa){var sa=mb,La=ta,Pa=j.DYNAMIC_DRAW,Va=!O.dynamic,Fa=ia;if(sa.__inittedArrays){var gb=f(Fa),Wa=Fa.vertexColors?Fa.vertexColors:!1,bb=g(Fa),$a=gb===THREE.SmoothShading,D=void 0,V=void 0,Ra=void 0,
+Q=void 0,ab=void 0,Xa=void 0,Sa=void 0,nb=void 0,cb=void 0,pb=void 0,ub=void 0,R=void 0,S=void 0,T=void 0,na=void 0,Mb=void 0,Nb=void 0,Ob=void 0,xb=void 0,Pb=void 0,Qb=void 0,Rb=void 0,yb=void 0,Sb=void 0,Tb=void 0,Ub=void 0,zb=void 0,Vb=void 0,Wb=void 0,Xb=void 0,Ib=void 0,Yb=void 0,Zb=void 0,$b=void 0,Jb=void 0,xa=void 0,fc=void 0,nc=void 0,Ab=void 0,yc=void 0,db=void 0,mc=void 0,Ya=void 0,Za=void 0,oc=void 0,hc=void 0,Oa=0,Ua=0,ic=0,jc=0,Eb=0,kb=0,Aa=0,ob=0,Ta=0,ba=0,ja=0,w=0,ya=void 0,eb=sa.__vertexArray,
+Dc=sa.__uvArray,Ec=sa.__uv2Array,Fb=sa.__normalArray,Ga=sa.__tangentArray,fb=sa.__colorArray,Ha=sa.__skinIndexArray,Ia=sa.__skinWeightArray,sc=sa.__morphTargetsArrays,tc=sa.__morphNormalsArrays,od=sa.__webglCustomAttributesList,u=void 0,ac=sa.__faceArray,wb=sa.__lineArray,qb=La.geometry,Mc=qb.elementsNeedUpdate,Cc=qb.uvsNeedUpdate,Nc=qb.normalsNeedUpdate,Oc=qb.tangentsNeedUpdate,Pc=qb.colorsNeedUpdate,fd=qb.morphTargetsNeedUpdate,uc=qb.vertices,ua=sa.faces3,va=sa.faces4,lb=qb.faces,pd=qb.faceVertexUvs[0],
+qd=qb.faceVertexUvs[1],vc=qb.skinIndices,pc=qb.skinWeights,qc=qb.morphTargets,Qc=qb.morphNormals;if(qb.verticesNeedUpdate){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],R=uc[Q.a],S=uc[Q.b],T=uc[Q.c],eb[Ua]=R.x,eb[Ua+1]=R.y,eb[Ua+2]=R.z,eb[Ua+3]=S.x,eb[Ua+4]=S.y,eb[Ua+5]=S.z,eb[Ua+6]=T.x,eb[Ua+7]=T.y,eb[Ua+8]=T.z,Ua+=9;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],R=uc[Q.a],S=uc[Q.b],T=uc[Q.c],na=uc[Q.d],eb[Ua]=R.x,eb[Ua+1]=R.y,eb[Ua+2]=R.z,eb[Ua+3]=S.x,eb[Ua+4]=S.y,eb[Ua+5]=S.z,eb[Ua+6]=T.x,eb[Ua+7]=T.y,eb[Ua+
+8]=T.z,eb[Ua+9]=na.x,eb[Ua+10]=na.y,eb[Ua+11]=na.z,Ua+=12;j.bindBuffer(j.ARRAY_BUFFER,sa.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,eb,Pa)}if(fd){db=0;for(mc=qc.length;db<mc;db++){D=ja=0;for(V=ua.length;D<V;D++)oc=ua[D],Q=lb[oc],R=qc[db].vertices[Q.a],S=qc[db].vertices[Q.b],T=qc[db].vertices[Q.c],Ya=sc[db],Ya[ja]=R.x,Ya[ja+1]=R.y,Ya[ja+2]=R.z,Ya[ja+3]=S.x,Ya[ja+4]=S.y,Ya[ja+5]=S.z,Ya[ja+6]=T.x,Ya[ja+7]=T.y,Ya[ja+8]=T.z,Fa.morphNormals&&($a?(hc=Qc[db].vertexNormals[oc],Pb=hc.a,Qb=hc.b,Rb=hc.c):
+Rb=Qb=Pb=Qc[db].faceNormals[oc],Za=tc[db],Za[ja]=Pb.x,Za[ja+1]=Pb.y,Za[ja+2]=Pb.z,Za[ja+3]=Qb.x,Za[ja+4]=Qb.y,Za[ja+5]=Qb.z,Za[ja+6]=Rb.x,Za[ja+7]=Rb.y,Za[ja+8]=Rb.z),ja+=9;D=0;for(V=va.length;D<V;D++)oc=va[D],Q=lb[oc],R=qc[db].vertices[Q.a],S=qc[db].vertices[Q.b],T=qc[db].vertices[Q.c],na=qc[db].vertices[Q.d],Ya=sc[db],Ya[ja]=R.x,Ya[ja+1]=R.y,Ya[ja+2]=R.z,Ya[ja+3]=S.x,Ya[ja+4]=S.y,Ya[ja+5]=S.z,Ya[ja+6]=T.x,Ya[ja+7]=T.y,Ya[ja+8]=T.z,Ya[ja+9]=na.x,Ya[ja+10]=na.y,Ya[ja+11]=na.z,Fa.morphNormals&&($a?
+(hc=Qc[db].vertexNormals[oc],Pb=hc.a,Qb=hc.b,Rb=hc.c,yb=hc.d):yb=Rb=Qb=Pb=Qc[db].faceNormals[oc],Za=tc[db],Za[ja]=Pb.x,Za[ja+1]=Pb.y,Za[ja+2]=Pb.z,Za[ja+3]=Qb.x,Za[ja+4]=Qb.y,Za[ja+5]=Qb.z,Za[ja+6]=Rb.x,Za[ja+7]=Rb.y,Za[ja+8]=Rb.z,Za[ja+9]=yb.x,Za[ja+10]=yb.y,Za[ja+11]=yb.z),ja+=12;j.bindBuffer(j.ARRAY_BUFFER,sa.__webglMorphTargetsBuffers[db]);j.bufferData(j.ARRAY_BUFFER,sc[db],Pa);Fa.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,sa.__webglMorphNormalsBuffers[db]),j.bufferData(j.ARRAY_BUFFER,tc[db],
+Pa))}}if(pc.length){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],Vb=pc[Q.a],Wb=pc[Q.b],Xb=pc[Q.c],Ia[ba]=Vb.x,Ia[ba+1]=Vb.y,Ia[ba+2]=Vb.z,Ia[ba+3]=Vb.w,Ia[ba+4]=Wb.x,Ia[ba+5]=Wb.y,Ia[ba+6]=Wb.z,Ia[ba+7]=Wb.w,Ia[ba+8]=Xb.x,Ia[ba+9]=Xb.y,Ia[ba+10]=Xb.z,Ia[ba+11]=Xb.w,Yb=vc[Q.a],Zb=vc[Q.b],$b=vc[Q.c],Ha[ba]=Yb.x,Ha[ba+1]=Yb.y,Ha[ba+2]=Yb.z,Ha[ba+3]=Yb.w,Ha[ba+4]=Zb.x,Ha[ba+5]=Zb.y,Ha[ba+6]=Zb.z,Ha[ba+7]=Zb.w,Ha[ba+8]=$b.x,Ha[ba+9]=$b.y,Ha[ba+10]=$b.z,Ha[ba+11]=$b.w,ba+=12;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],
+Vb=pc[Q.a],Wb=pc[Q.b],Xb=pc[Q.c],Ib=pc[Q.d],Ia[ba]=Vb.x,Ia[ba+1]=Vb.y,Ia[ba+2]=Vb.z,Ia[ba+3]=Vb.w,Ia[ba+4]=Wb.x,Ia[ba+5]=Wb.y,Ia[ba+6]=Wb.z,Ia[ba+7]=Wb.w,Ia[ba+8]=Xb.x,Ia[ba+9]=Xb.y,Ia[ba+10]=Xb.z,Ia[ba+11]=Xb.w,Ia[ba+12]=Ib.x,Ia[ba+13]=Ib.y,Ia[ba+14]=Ib.z,Ia[ba+15]=Ib.w,Yb=vc[Q.a],Zb=vc[Q.b],$b=vc[Q.c],Jb=vc[Q.d],Ha[ba]=Yb.x,Ha[ba+1]=Yb.y,Ha[ba+2]=Yb.z,Ha[ba+3]=Yb.w,Ha[ba+4]=Zb.x,Ha[ba+5]=Zb.y,Ha[ba+6]=Zb.z,Ha[ba+7]=Zb.w,Ha[ba+8]=$b.x,Ha[ba+9]=$b.y,Ha[ba+10]=$b.z,Ha[ba+11]=$b.w,Ha[ba+12]=Jb.x,Ha[ba+
+13]=Jb.y,Ha[ba+14]=Jb.z,Ha[ba+15]=Jb.w,ba+=16;0<ba&&(j.bindBuffer(j.ARRAY_BUFFER,sa.__webglSkinIndicesBuffer),j.bufferData(j.ARRAY_BUFFER,Ha,Pa),j.bindBuffer(j.ARRAY_BUFFER,sa.__webglSkinWeightsBuffer),j.bufferData(j.ARRAY_BUFFER,Ia,Pa))}if(Pc&&Wa){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],Sa=Q.vertexColors,nb=Q.color,3===Sa.length&&Wa===THREE.VertexColors?(Sb=Sa[0],Tb=Sa[1],Ub=Sa[2]):Ub=Tb=Sb=nb,fb[Ta]=Sb.r,fb[Ta+1]=Sb.g,fb[Ta+2]=Sb.b,fb[Ta+3]=Tb.r,fb[Ta+4]=Tb.g,fb[Ta+5]=Tb.b,fb[Ta+6]=Ub.r,fb[Ta+7]=
+Ub.g,fb[Ta+8]=Ub.b,Ta+=9;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],Sa=Q.vertexColors,nb=Q.color,4===Sa.length&&Wa===THREE.VertexColors?(Sb=Sa[0],Tb=Sa[1],Ub=Sa[2],zb=Sa[3]):zb=Ub=Tb=Sb=nb,fb[Ta]=Sb.r,fb[Ta+1]=Sb.g,fb[Ta+2]=Sb.b,fb[Ta+3]=Tb.r,fb[Ta+4]=Tb.g,fb[Ta+5]=Tb.b,fb[Ta+6]=Ub.r,fb[Ta+7]=Ub.g,fb[Ta+8]=Ub.b,fb[Ta+9]=zb.r,fb[Ta+10]=zb.g,fb[Ta+11]=zb.b,Ta+=12;0<Ta&&(j.bindBuffer(j.ARRAY_BUFFER,sa.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,fb,Pa))}if(Oc&&qb.hasTangents){D=0;for(V=ua.length;D<V;D++)Q=
+lb[ua[D]],cb=Q.vertexTangents,Mb=cb[0],Nb=cb[1],Ob=cb[2],Ga[Aa]=Mb.x,Ga[Aa+1]=Mb.y,Ga[Aa+2]=Mb.z,Ga[Aa+3]=Mb.w,Ga[Aa+4]=Nb.x,Ga[Aa+5]=Nb.y,Ga[Aa+6]=Nb.z,Ga[Aa+7]=Nb.w,Ga[Aa+8]=Ob.x,Ga[Aa+9]=Ob.y,Ga[Aa+10]=Ob.z,Ga[Aa+11]=Ob.w,Aa+=12;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],cb=Q.vertexTangents,Mb=cb[0],Nb=cb[1],Ob=cb[2],xb=cb[3],Ga[Aa]=Mb.x,Ga[Aa+1]=Mb.y,Ga[Aa+2]=Mb.z,Ga[Aa+3]=Mb.w,Ga[Aa+4]=Nb.x,Ga[Aa+5]=Nb.y,Ga[Aa+6]=Nb.z,Ga[Aa+7]=Nb.w,Ga[Aa+8]=Ob.x,Ga[Aa+9]=Ob.y,Ga[Aa+10]=Ob.z,Ga[Aa+11]=Ob.w,Ga[Aa+
+12]=xb.x,Ga[Aa+13]=xb.y,Ga[Aa+14]=xb.z,Ga[Aa+15]=xb.w,Aa+=16;j.bindBuffer(j.ARRAY_BUFFER,sa.__webglTangentBuffer);j.bufferData(j.ARRAY_BUFFER,Ga,Pa)}if(Nc&&gb){D=0;for(V=ua.length;D<V;D++)if(Q=lb[ua[D]],ab=Q.vertexNormals,Xa=Q.normal,3===ab.length&&$a)for(xa=0;3>xa;xa++)nc=ab[xa],Fb[kb]=nc.x,Fb[kb+1]=nc.y,Fb[kb+2]=nc.z,kb+=3;else for(xa=0;3>xa;xa++)Fb[kb]=Xa.x,Fb[kb+1]=Xa.y,Fb[kb+2]=Xa.z,kb+=3;D=0;for(V=va.length;D<V;D++)if(Q=lb[va[D]],ab=Q.vertexNormals,Xa=Q.normal,4===ab.length&&$a)for(xa=0;4>xa;xa++)nc=
+ab[xa],Fb[kb]=nc.x,Fb[kb+1]=nc.y,Fb[kb+2]=nc.z,kb+=3;else for(xa=0;4>xa;xa++)Fb[kb]=Xa.x,Fb[kb+1]=Xa.y,Fb[kb+2]=Xa.z,kb+=3;j.bindBuffer(j.ARRAY_BUFFER,sa.__webglNormalBuffer);j.bufferData(j.ARRAY_BUFFER,Fb,Pa)}if(Cc&&pd&&bb){D=0;for(V=ua.length;D<V;D++)if(Ra=ua[D],pb=pd[Ra],void 0!==pb)for(xa=0;3>xa;xa++)Ab=pb[xa],Dc[ic]=Ab.x,Dc[ic+1]=Ab.y,ic+=2;D=0;for(V=va.length;D<V;D++)if(Ra=va[D],pb=pd[Ra],void 0!==pb)for(xa=0;4>xa;xa++)Ab=pb[xa],Dc[ic]=Ab.x,Dc[ic+1]=Ab.y,ic+=2;0<ic&&(j.bindBuffer(j.ARRAY_BUFFER,
+sa.__webglUVBuffer),j.bufferData(j.ARRAY_BUFFER,Dc,Pa))}if(Cc&&qd&&bb){D=0;for(V=ua.length;D<V;D++)if(Ra=ua[D],ub=qd[Ra],void 0!==ub)for(xa=0;3>xa;xa++)yc=ub[xa],Ec[jc]=yc.x,Ec[jc+1]=yc.y,jc+=2;D=0;for(V=va.length;D<V;D++)if(Ra=va[D],ub=qd[Ra],void 0!==ub)for(xa=0;4>xa;xa++)yc=ub[xa],Ec[jc]=yc.x,Ec[jc+1]=yc.y,jc+=2;0<jc&&(j.bindBuffer(j.ARRAY_BUFFER,sa.__webglUV2Buffer),j.bufferData(j.ARRAY_BUFFER,Ec,Pa))}if(Mc){D=0;for(V=ua.length;D<V;D++)ac[Eb]=Oa,ac[Eb+1]=Oa+1,ac[Eb+2]=Oa+2,Eb+=3,wb[ob]=Oa,wb[ob+
+1]=Oa+1,wb[ob+2]=Oa,wb[ob+3]=Oa+2,wb[ob+4]=Oa+1,wb[ob+5]=Oa+2,ob+=6,Oa+=3;D=0;for(V=va.length;D<V;D++)ac[Eb]=Oa,ac[Eb+1]=Oa+1,ac[Eb+2]=Oa+3,ac[Eb+3]=Oa+1,ac[Eb+4]=Oa+2,ac[Eb+5]=Oa+3,Eb+=6,wb[ob]=Oa,wb[ob+1]=Oa+1,wb[ob+2]=Oa,wb[ob+3]=Oa+3,wb[ob+4]=Oa+1,wb[ob+5]=Oa+2,wb[ob+6]=Oa+2,wb[ob+7]=Oa+3,ob+=8,Oa+=4;j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,sa.__webglFaceBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,ac,Pa);j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,sa.__webglLineBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,
+wb,Pa)}if(od){xa=0;for(fc=od.length;xa<fc;xa++)if(u=od[xa],u.__original.needsUpdate){w=0;if(1===u.size)if(void 0===u.boundTo||"vertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],u.array[w]=u.value[Q.a],u.array[w+1]=u.value[Q.b],u.array[w+2]=u.value[Q.c],w+=3;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],u.array[w]=u.value[Q.a],u.array[w+1]=u.value[Q.b],u.array[w+2]=u.value[Q.c],u.array[w+3]=u.value[Q.d],w+=4}else{if("faces"===u.boundTo){D=0;for(V=ua.length;D<V;D++)ya=u.value[ua[D]],u.array[w]=
+ya,u.array[w+1]=ya,u.array[w+2]=ya,w+=3;D=0;for(V=va.length;D<V;D++)ya=u.value[va[D]],u.array[w]=ya,u.array[w+1]=ya,u.array[w+2]=ya,u.array[w+3]=ya,w+=4}}else if(2===u.size)if(void 0===u.boundTo||"vertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=S.x,u.array[w+3]=S.y,u.array[w+4]=T.x,u.array[w+5]=T.y,w+=6;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],na=u.value[Q.d],
+u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=S.x,u.array[w+3]=S.y,u.array[w+4]=T.x,u.array[w+5]=T.y,u.array[w+6]=na.x,u.array[w+7]=na.y,w+=8}else{if("faces"===u.boundTo){D=0;for(V=ua.length;D<V;D++)T=S=R=ya=u.value[ua[D]],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=S.x,u.array[w+3]=S.y,u.array[w+4]=T.x,u.array[w+5]=T.y,w+=6;D=0;for(V=va.length;D<V;D++)na=T=S=R=ya=u.value[va[D]],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=S.x,u.array[w+3]=S.y,u.array[w+4]=T.x,u.array[w+5]=T.y,u.array[w+6]=na.x,u.array[w+
+7]=na.y,w+=8}}else if(3===u.size){var $;$="c"===u.type?["r","g","b"]:["x","y","z"];if(void 0===u.boundTo||"vertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],u.array[w+8]=T[$[2]],w+=9;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],na=u.value[Q.d],
+u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],u.array[w+8]=T[$[2]],u.array[w+9]=na[$[0]],u.array[w+10]=na[$[1]],u.array[w+11]=na[$[2]],w+=12}else if("faces"===u.boundTo){D=0;for(V=ua.length;D<V;D++)T=S=R=ya=u.value[ua[D]],u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],
+u.array[w+8]=T[$[2]],w+=9;D=0;for(V=va.length;D<V;D++)na=T=S=R=ya=u.value[va[D]],u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],u.array[w+8]=T[$[2]],u.array[w+9]=na[$[0]],u.array[w+10]=na[$[1]],u.array[w+11]=na[$[2]],w+=12}else if("faceVertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)ya=u.value[ua[D]],R=ya[0],S=ya[1],T=ya[2],u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=
+R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],u.array[w+8]=T[$[2]],w+=9;D=0;for(V=va.length;D<V;D++)ya=u.value[va[D]],R=ya[0],S=ya[1],T=ya[2],na=ya[3],u.array[w]=R[$[0]],u.array[w+1]=R[$[1]],u.array[w+2]=R[$[2]],u.array[w+3]=S[$[0]],u.array[w+4]=S[$[1]],u.array[w+5]=S[$[2]],u.array[w+6]=T[$[0]],u.array[w+7]=T[$[1]],u.array[w+8]=T[$[2]],u.array[w+9]=na[$[0]],u.array[w+10]=na[$[1]],u.array[w+11]=na[$[2]],w+=12}}else if(4===u.size)if(void 0===
+u.boundTo||"vertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)Q=lb[ua[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+3]=R.w,u.array[w+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,w+=12;D=0;for(V=va.length;D<V;D++)Q=lb[va[D]],R=u.value[Q.a],S=u.value[Q.b],T=u.value[Q.c],na=u.value[Q.d],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+3]=R.w,u.array[w+
+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,u.array[w+12]=na.x,u.array[w+13]=na.y,u.array[w+14]=na.z,u.array[w+15]=na.w,w+=16}else if("faces"===u.boundTo){D=0;for(V=ua.length;D<V;D++)T=S=R=ya=u.value[ua[D]],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+3]=R.w,u.array[w+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,w+=12;
+D=0;for(V=va.length;D<V;D++)na=T=S=R=ya=u.value[va[D]],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+3]=R.w,u.array[w+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,u.array[w+12]=na.x,u.array[w+13]=na.y,u.array[w+14]=na.z,u.array[w+15]=na.w,w+=16}else if("faceVertices"===u.boundTo){D=0;for(V=ua.length;D<V;D++)ya=u.value[ua[D]],R=ya[0],S=ya[1],T=ya[2],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+
+3]=R.w,u.array[w+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,w+=12;D=0;for(V=va.length;D<V;D++)ya=u.value[va[D]],R=ya[0],S=ya[1],T=ya[2],na=ya[3],u.array[w]=R.x,u.array[w+1]=R.y,u.array[w+2]=R.z,u.array[w+3]=R.w,u.array[w+4]=S.x,u.array[w+5]=S.y,u.array[w+6]=S.z,u.array[w+7]=S.w,u.array[w+8]=T.x,u.array[w+9]=T.y,u.array[w+10]=T.z,u.array[w+11]=T.w,u.array[w+12]=na.x,u.array[w+13]=na.y,u.array[w+14]=na.z,u.array[w+
+15]=na.w,w+=16}j.bindBuffer(j.ARRAY_BUFFER,u.buffer);j.bufferData(j.ARRAY_BUFFER,u.array,Pa)}}Va&&(delete sa.__inittedArrays,delete sa.__colorArray,delete sa.__normalArray,delete sa.__tangentArray,delete sa.__uvArray,delete sa.__uv2Array,delete sa.__faceArray,delete sa.__vertexArray,delete sa.__lineArray,delete sa.__skinIndexArray,delete sa.__skinWeightArray)}}O.verticesNeedUpdate=!1;O.morphTargetsNeedUpdate=!1;O.elementsNeedUpdate=!1;O.uvsNeedUpdate=!1;O.normalsNeedUpdate=!1;O.colorsNeedUpdate=!1;
+O.tangentsNeedUpdate=!1;O.buffersNeedUpdate=!1;ia.attributes&&v(ia)}else if(ta instanceof THREE.Ribbon){ia=e(ta,O);qa=ia.attributes&&y(ia);if(O.verticesNeedUpdate||O.colorsNeedUpdate||O.normalsNeedUpdate||qa){var Gb=O,Rc=j.DYNAMIC_DRAW,Fc=void 0,Gc=void 0,Hc=void 0,Sc=void 0,za=void 0,Tc=void 0,Uc=void 0,Vc=void 0,xd=void 0,ib=void 0,zc=void 0,Da=void 0,rb=void 0,yd=Gb.vertices,zd=Gb.colors,Ad=Gb.normals,gd=yd.length,hd=zd.length,id=Ad.length,Wc=Gb.__vertexArray,Xc=Gb.__colorArray,Yc=Gb.__normalArray,
+jd=Gb.colorsNeedUpdate,kd=Gb.normalsNeedUpdate,rd=Gb.__webglCustomAttributesList;if(Gb.verticesNeedUpdate){for(Fc=0;Fc<gd;Fc++)Sc=yd[Fc],za=3*Fc,Wc[za]=Sc.x,Wc[za+1]=Sc.y,Wc[za+2]=Sc.z;j.bindBuffer(j.ARRAY_BUFFER,Gb.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,Wc,Rc)}if(jd){for(Gc=0;Gc<hd;Gc++)Tc=zd[Gc],za=3*Gc,Xc[za]=Tc.r,Xc[za+1]=Tc.g,Xc[za+2]=Tc.b;j.bindBuffer(j.ARRAY_BUFFER,Gb.__webglColorBuffer);j.bufferData(j.ARRAY_BUFFER,Xc,Rc)}if(kd){for(Hc=0;Hc<id;Hc++)Uc=Ad[Hc],za=3*Hc,Yc[za]=Uc.x,Yc[za+
+1]=Uc.y,Yc[za+2]=Uc.z;j.bindBuffer(j.ARRAY_BUFFER,Gb.__webglNormalBuffer);j.bufferData(j.ARRAY_BUFFER,Yc,Rc)}if(rd){Vc=0;for(xd=rd.length;Vc<xd;Vc++)if(Da=rd[Vc],Da.needsUpdate&&(void 0===Da.boundTo||"vertices"===Da.boundTo)){za=0;zc=Da.value.length;if(1===Da.size)for(ib=0;ib<zc;ib++)Da.array[ib]=Da.value[ib];else if(2===Da.size)for(ib=0;ib<zc;ib++)rb=Da.value[ib],Da.array[za]=rb.x,Da.array[za+1]=rb.y,za+=2;else if(3===Da.size)if("c"===Da.type)for(ib=0;ib<zc;ib++)rb=Da.value[ib],Da.array[za]=rb.r,
+Da.array[za+1]=rb.g,Da.array[za+2]=rb.b,za+=3;else for(ib=0;ib<zc;ib++)rb=Da.value[ib],Da.array[za]=rb.x,Da.array[za+1]=rb.y,Da.array[za+2]=rb.z,za+=3;else if(4===Da.size)for(ib=0;ib<zc;ib++)rb=Da.value[ib],Da.array[za]=rb.x,Da.array[za+1]=rb.y,Da.array[za+2]=rb.z,Da.array[za+3]=rb.w,za+=4;j.bindBuffer(j.ARRAY_BUFFER,Da.buffer);j.bufferData(j.ARRAY_BUFFER,Da.array,Rc)}}}O.verticesNeedUpdate=!1;O.colorsNeedUpdate=!1;O.normalsNeedUpdate=!1;ia.attributes&&v(ia)}else if(ta instanceof THREE.Line)if(O instanceof
+THREE.BufferGeometry)(O.verticesNeedUpdate||O.colorsNeedUpdate)&&i(O,j.DYNAMIC_DRAW,!O.dynamic),O.verticesNeedUpdate=!1,O.colorsNeedUpdate=!1;else{ia=e(ta,O);qa=ia.attributes&&y(ia);if(O.verticesNeedUpdate||O.colorsNeedUpdate||O.lineDistancesNeedUpdate||qa){var Hb=O,Zc=j.DYNAMIC_DRAW,Ic=void 0,Jc=void 0,Kc=void 0,$c=void 0,Ja=void 0,ad=void 0,Bd=Hb.vertices,Cd=Hb.colors,Dd=Hb.lineDistances,ld=Bd.length,Jd=Cd.length,Kd=Dd.length,bd=Hb.__vertexArray,cd=Hb.__colorArray,Ed=Hb.__lineDistanceArray,Ld=Hb.colorsNeedUpdate,
+Md=Hb.lineDistancesNeedUpdate,sd=Hb.__webglCustomAttributesList,dd=void 0,Fd=void 0,jb=void 0,Ac=void 0,sb=void 0,Ea=void 0;if(Hb.verticesNeedUpdate){for(Ic=0;Ic<ld;Ic++)$c=Bd[Ic],Ja=3*Ic,bd[Ja]=$c.x,bd[Ja+1]=$c.y,bd[Ja+2]=$c.z;j.bindBuffer(j.ARRAY_BUFFER,Hb.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,bd,Zc)}if(Ld){for(Jc=0;Jc<Jd;Jc++)ad=Cd[Jc],Ja=3*Jc,cd[Ja]=ad.r,cd[Ja+1]=ad.g,cd[Ja+2]=ad.b;j.bindBuffer(j.ARRAY_BUFFER,Hb.__webglColorBuffer);j.bufferData(j.ARRAY_BUFFER,cd,Zc)}if(Md){for(Kc=0;Kc<
+Kd;Kc++)Ed[Kc]=Dd[Kc];j.bindBuffer(j.ARRAY_BUFFER,Hb.__webglLineDistanceBuffer);j.bufferData(j.ARRAY_BUFFER,Ed,Zc)}if(sd){dd=0;for(Fd=sd.length;dd<Fd;dd++)if(Ea=sd[dd],Ea.needsUpdate&&(void 0===Ea.boundTo||"vertices"===Ea.boundTo)){Ja=0;Ac=Ea.value.length;if(1===Ea.size)for(jb=0;jb<Ac;jb++)Ea.array[jb]=Ea.value[jb];else if(2===Ea.size)for(jb=0;jb<Ac;jb++)sb=Ea.value[jb],Ea.array[Ja]=sb.x,Ea.array[Ja+1]=sb.y,Ja+=2;else if(3===Ea.size)if("c"===Ea.type)for(jb=0;jb<Ac;jb++)sb=Ea.value[jb],Ea.array[Ja]=
+sb.r,Ea.array[Ja+1]=sb.g,Ea.array[Ja+2]=sb.b,Ja+=3;else for(jb=0;jb<Ac;jb++)sb=Ea.value[jb],Ea.array[Ja]=sb.x,Ea.array[Ja+1]=sb.y,Ea.array[Ja+2]=sb.z,Ja+=3;else if(4===Ea.size)for(jb=0;jb<Ac;jb++)sb=Ea.value[jb],Ea.array[Ja]=sb.x,Ea.array[Ja+1]=sb.y,Ea.array[Ja+2]=sb.z,Ea.array[Ja+3]=sb.w,Ja+=4;j.bindBuffer(j.ARRAY_BUFFER,Ea.buffer);j.bufferData(j.ARRAY_BUFFER,Ea.array,Zc)}}}O.verticesNeedUpdate=!1;O.colorsNeedUpdate=!1;O.lineDistancesNeedUpdate=!1;ia.attributes&&v(ia)}else if(ta instanceof THREE.ParticleSystem)if(O instanceof
+THREE.BufferGeometry)(O.verticesNeedUpdate||O.colorsNeedUpdate)&&i(O,j.DYNAMIC_DRAW,!O.dynamic),O.verticesNeedUpdate=!1,O.colorsNeedUpdate=!1;else{ia=e(ta,O);qa=ia.attributes&&y(ia);if(O.verticesNeedUpdate||O.colorsNeedUpdate||ta.sortParticles||qa){var bc=O,td=j.DYNAMIC_DRAW,Lc=ta,tb=void 0,cc=void 0,dc=void 0,ga=void 0,ec=void 0,rc=void 0,ed=bc.vertices,ud=ed.length,vd=bc.colors,Gd=vd.length,wc=bc.__vertexArray,xc=bc.__colorArray,kc=bc.__sortArray,Hd=bc.verticesNeedUpdate,Id=bc.colorsNeedUpdate,
+lc=bc.__webglCustomAttributesList,Kb=void 0,Bc=void 0,ma=void 0,Lb=void 0,Ba=void 0,ea=void 0;if(Lc.sortParticles){vb.copy(gc);vb.multiply(Lc.matrixWorld);for(tb=0;tb<ud;tb++)dc=ed[tb],Qa.copy(dc),Qa.applyProjection(vb),kc[tb]=[Qa.z,tb];kc.sort(n);for(tb=0;tb<ud;tb++)dc=ed[kc[tb][1]],ga=3*tb,wc[ga]=dc.x,wc[ga+1]=dc.y,wc[ga+2]=dc.z;for(cc=0;cc<Gd;cc++)ga=3*cc,rc=vd[kc[cc][1]],xc[ga]=rc.r,xc[ga+1]=rc.g,xc[ga+2]=rc.b;if(lc){Kb=0;for(Bc=lc.length;Kb<Bc;Kb++)if(ea=lc[Kb],void 0===ea.boundTo||"vertices"===
+ea.boundTo)if(ga=0,Lb=ea.value.length,1===ea.size)for(ma=0;ma<Lb;ma++)ec=kc[ma][1],ea.array[ma]=ea.value[ec];else if(2===ea.size)for(ma=0;ma<Lb;ma++)ec=kc[ma][1],Ba=ea.value[ec],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ga+=2;else if(3===ea.size)if("c"===ea.type)for(ma=0;ma<Lb;ma++)ec=kc[ma][1],Ba=ea.value[ec],ea.array[ga]=Ba.r,ea.array[ga+1]=Ba.g,ea.array[ga+2]=Ba.b,ga+=3;else for(ma=0;ma<Lb;ma++)ec=kc[ma][1],Ba=ea.value[ec],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ea.array[ga+2]=Ba.z,ga+=3;else if(4===
+ea.size)for(ma=0;ma<Lb;ma++)ec=kc[ma][1],Ba=ea.value[ec],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ea.array[ga+2]=Ba.z,ea.array[ga+3]=Ba.w,ga+=4}}else{if(Hd)for(tb=0;tb<ud;tb++)dc=ed[tb],ga=3*tb,wc[ga]=dc.x,wc[ga+1]=dc.y,wc[ga+2]=dc.z;if(Id)for(cc=0;cc<Gd;cc++)rc=vd[cc],ga=3*cc,xc[ga]=rc.r,xc[ga+1]=rc.g,xc[ga+2]=rc.b;if(lc){Kb=0;for(Bc=lc.length;Kb<Bc;Kb++)if(ea=lc[Kb],ea.needsUpdate&&(void 0===ea.boundTo||"vertices"===ea.boundTo))if(Lb=ea.value.length,ga=0,1===ea.size)for(ma=0;ma<Lb;ma++)ea.array[ma]=
+ea.value[ma];else if(2===ea.size)for(ma=0;ma<Lb;ma++)Ba=ea.value[ma],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ga+=2;else if(3===ea.size)if("c"===ea.type)for(ma=0;ma<Lb;ma++)Ba=ea.value[ma],ea.array[ga]=Ba.r,ea.array[ga+1]=Ba.g,ea.array[ga+2]=Ba.b,ga+=3;else for(ma=0;ma<Lb;ma++)Ba=ea.value[ma],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ea.array[ga+2]=Ba.z,ga+=3;else if(4===ea.size)for(ma=0;ma<Lb;ma++)Ba=ea.value[ma],ea.array[ga]=Ba.x,ea.array[ga+1]=Ba.y,ea.array[ga+2]=Ba.z,ea.array[ga+3]=Ba.w,ga+=4}}if(Hd||
+Lc.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,bc.__webglVertexBuffer),j.bufferData(j.ARRAY_BUFFER,wc,td);if(Id||Lc.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,bc.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,xc,td);if(lc){Kb=0;for(Bc=lc.length;Kb<Bc;Kb++)if(ea=lc[Kb],ea.needsUpdate||Lc.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,ea.buffer),j.bufferData(j.ARRAY_BUFFER,ea.array,td)}}O.verticesNeedUpdate=!1;O.colorsNeedUpdate=!1;ia.attributes&&v(ia)}}};this.initMaterial=function(a,b,c,d){var e,f,g,i;a.addEventListener("dispose",
+X);var h,k,m,n,l;a instanceof THREE.MeshDepthMaterial?l="depth":a instanceof THREE.MeshNormalMaterial?l="normal":a instanceof THREE.MeshBasicMaterial?l="basic":a instanceof THREE.MeshLambertMaterial?l="lambert":a instanceof THREE.MeshPhongMaterial?l="phong":a instanceof THREE.LineBasicMaterial?l="basic":a instanceof THREE.LineDashedMaterial?l="dashed":a instanceof THREE.ParticleBasicMaterial&&(l="particle_basic");if(l){var p=THREE.ShaderLib[l];a.uniforms=THREE.UniformsUtils.clone(p.uniforms);a.vertexShader=
+p.vertexShader;a.fragmentShader=p.fragmentShader}var q,s,r;e=g=s=r=p=0;for(f=b.length;e<f;e++)q=b[e],q.onlyShadow||(q instanceof THREE.DirectionalLight&&g++,q instanceof THREE.PointLight&&s++,q instanceof THREE.SpotLight&&r++,q instanceof THREE.HemisphereLight&&p++);e=g;f=s;g=r;i=p;p=q=0;for(r=b.length;p<r;p++)s=b[p],s.castShadow&&(s instanceof THREE.SpotLight&&q++,s instanceof THREE.DirectionalLight&&!s.shadowCascade&&q++);n=q;tc&&d&&d.useVertexTexture?m=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)")),m=b);a:{s=a.fragmentShader;r=a.vertexShader;p=a.uniforms;b=a.attributes;q=a.defines;var 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:m,useVertexTexture:tc&&d&&d.useVertexTexture,boneTextureWidth:d&&d.boneTextureWidth,boneTextureHeight:d&&d.boneTextureHeight,morphTargets:a.morphTargets,morphNormals:a.morphNormals,maxMorphTargets:this.maxMorphTargets,maxMorphNormals:this.maxMorphNormals,maxDirLights:e,maxPointLights:f,maxSpotLights:g,maxHemiLights:i,maxShadows:n,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},t,v,y,d=[];l?d.push(l):(d.push(s),d.push(r));for(v in q)d.push(v),d.push(q[v]);for(t in c)d.push(t),d.push(c[t]);l=d.join();t=0;for(v=Ma.length;t<v;t++)if(d=Ma[t],d.code===l){d.usedTimes++;k=d.program;break a}t="SHADOWMAP_TYPE_BASIC";c.shadowMapType===THREE.PCFShadowMap?t="SHADOWMAP_TYPE_PCF":
+c.shadowMapType===THREE.PCFSoftShadowMap&&(t="SHADOWMAP_TYPE_PCF_SOFT");v=[];for(y in q)d=q[y],!1!==d&&(d="#define "+y+" "+d,v.push(d));d=v.join("\n");y=j.createProgram();v=["precision "+fa+" float;",d,sc?"#define VERTEX_TEXTURES":"",N.gammaInput?"#define GAMMA_INPUT":"",N.gammaOutput?"#define GAMMA_OUTPUT":"",N.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.boneTextureWidth?"#define N_BONE_PIXEL_X "+c.boneTextureWidth.toFixed(1):
+"",c.boneTextureHeight?"#define N_BONE_PIXEL_Y "+c.boneTextureHeight.toFixed(1):"",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 "+t:"",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");
+t=["precision "+fa+" float;",c.bumpMap||c.normalMap?"#extension GL_OES_standard_derivatives : enable":"",d,"#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:"",N.gammaInput?"#define GAMMA_INPUT":"",N.gammaOutput?"#define GAMMA_OUTPUT":"",N.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 "+t:"",c.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",c.shadowMapCascade?"#define SHADOWMAP_CASCADE":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n");t=B("fragment",t+s);v=B("vertex",v+r);j.attachShader(y,v);j.attachShader(y,t);j.linkProgram(y);j.getProgramParameter(y,j.LINK_STATUS)||console.error("Could not initialise shader\nVALIDATE_STATUS: "+j.getProgramParameter(y,j.VALIDATE_STATUS)+", gl error ["+
+j.getError()+"]");j.deleteShader(t);j.deleteShader(v);y.uniforms={};y.attributes={};var x;t="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences".split(" ");c.useVertexTexture?t.push("boneTexture"):t.push("boneGlobalMatrices");for(x in p)t.push(x);x=t;t=0;for(v=x.length;t<v;t++)p=x[t],y.uniforms[p]=j.getUniformLocation(y,p);t="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(x=0;x<c.maxMorphTargets;x++)t.push("morphTarget"+
+x);for(x=0;x<c.maxMorphNormals;x++)t.push("morphNormal"+x);for(k in b)t.push(k);k=t;x=0;for(b=k.length;x<b;x++)t=k[x],y.attributes[t]=j.getAttribLocation(y,t);y.id=Na++;Ma.push({program:y,code:l,usedTimes:1});N.info.memory.programs=Ma.length;k=y}a.program=k;x=a.program.attributes;if(a.morphTargets){a.numSupportedMorphTargets=0;b="morphTarget";for(k=0;k<this.maxMorphTargets;k++)y=b+k,0<=x[y]&&a.numSupportedMorphTargets++}if(a.morphNormals){a.numSupportedMorphNormals=0;b="morphNormal";for(k=0;k<this.maxMorphNormals;k++)y=
+b+k,0<=x[y]&&a.numSupportedMorphNormals++}a.uniformsList=[];for(h in a.uniforms)a.uniformsList.push([a.uniforms[h],h])};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;da!==
+b&&(b?j.disable(j.CULL_FACE):j.enable(j.CULL_FACE),da=b);la!==a&&(a?j.frontFace(j.CW):j.frontFace(j.CCW),la=a)};this.setDepthTest=function(a){ia!==a&&(a?j.enable(j.DEPTH_TEST):j.disable(j.DEPTH_TEST),ia=a)};this.setDepthWrite=function(a){Wa!==a&&(j.depthMask(a),Wa=a)};this.setBlending=function(a,b,c,d){a!==Z&&(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)),Z=a);if(a===THREE.CustomBlending){if(b!==oa&&(j.blendEquation(L(b)),oa=b),c!==gb||d!==nb)j.blendFunc(L(c),L(d)),gb=c,nb=d}else nb=
+gb=oa=null};this.setTexture=function(a,b){if(a.needsUpdate){a.__webglInit||(a.__webglInit=!0,a.addEventListener("dispose",Oc),a.__webglTexture=j.createTexture(),N.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=L(a.format),
+f=L(a.type);W(j.TEXTURE_2D,a,d);var g=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<g.length&&d){for(var i=0,h=g.length;i<h;i++)c=g[i],j.texImage2D(j.TEXTURE_2D,i,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){i=0;for(h=g.length;i<h;i++)c=g[i],j.compressedTexImage2D(j.TEXTURE_2D,i,e,c.width,c.height,0,c.data)}else if(0<g.length&&d){i=0;for(h=g.length;i<h;i++)c=g[i],j.texImage2D(j.TEXTURE_2D,
+i,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",P);a.__webglTexture=
+j.createTexture();N.info.memory.textures++;var c=0===(a.width&a.width-1)&&0===(a.height&a.height-1),d=L(a.format),e=L(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];j.bindTexture(j.TEXTURE_CUBE_MAP,a.__webglTexture);W(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 g=a,i=j.TEXTURE_CUBE_MAP_POSITIVE_X+f;j.bindFramebuffer(j.FRAMEBUFFER,
+a.__webglFramebuffer[f]);j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,i,g.__webglTexture,0);F(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),W(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):F(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=fc,a=Ab,d=Ib,e=Jb);b!==Pa&&(j.bindFramebuffer(j.FRAMEBUFFER,b),j.viewport(d,e,c,a),Pa=b);mc=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){THREE.EventDispatcher.call(this);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.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};
+THREE.WebGLRenderTarget.prototype.dispose=function(){this.dispatchEvent({type:"dispose"})};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.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=null};THREE.RenderableFace4=function(){this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.v3=new THREE.RenderableVertex;this.v4=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,new THREE.Vector3];this.vertexNormalsModelView=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];
+this.material=this.color=null;this.uvs=[[]];this.z=null};THREE.RenderableObject=function(){this.z=this.object=null};THREE.RenderableParticle=function(){this.rotation=this.z=this.y=this.x=this.object=null;this.scale=new THREE.Vector2;this.material=null};THREE.RenderableLine=function(){this.z=null;this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.material=null};THREE.GeometryUtils={merge:function(a,b){var c,d,e=a.vertices.length,f=b instanceof THREE.Mesh?b.geometry:b,g=a.vertices,h=f.vertices,i=a.faces,k=f.faces,l=a.faceVertexUvs[0],f=f.faceVertexUvs[0];b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),c=b.matrix,d=new THREE.Matrix3,d.getInverse(c),d.transpose());for(var m=0,n=h.length;m<n;m++){var s=h[m].clone();c&&s.applyMatrix4(c);g.push(s)}m=0;for(n=k.length;m<n;m++){var s=k[m],r,p,q=s.vertexNormals,y=s.vertexColors;s instanceof THREE.Face3?
+r=new THREE.Face3(s.a+e,s.b+e,s.c+e):s instanceof THREE.Face4&&(r=new THREE.Face4(s.a+e,s.b+e,s.c+e,s.d+e));r.normal.copy(s.normal);d&&r.normal.applyMatrix3(d).normalize();g=0;for(h=q.length;g<h;g++)p=q[g].clone(),d&&p.applyMatrix3(d).normalize(),r.vertexNormals.push(p);r.color.copy(s.color);g=0;for(h=y.length;g<h;g++)p=y[g],r.vertexColors.push(p.clone());r.materialIndex=s.materialIndex;r.centroid.copy(s.centroid);c&&r.centroid.applyMatrix4(c);i.push(r)}m=0;for(n=f.length;m<n;m++){c=f[m];d=[];g=0;
+for(h=c.length;g<h;g++)d.push(new THREE.Vector2(c[g].x,c[g].y));l.push(d)}},removeMaterials:function(a,b){for(var c={},d=0,e=b.length;d<e;d++)c[b[d]]=!0;for(var f,g=[],d=0,e=a.faces.length;d<e;d++)f=a.faces[d],f.materialIndex in c||g.push(f);a.faces=g},randomPointInTriangle:function(a,b,c){var d,e,f,g=new THREE.Vector3,h=THREE.GeometryUtils.__v1;d=THREE.GeometryUtils.random();e=THREE.GeometryUtils.random();1<d+e&&(d=1-d,e=1-e);f=1-d-e;g.copy(a);g.multiplyScalar(d);h.copy(b);h.multiplyScalar(e);g.add(h);
+h.copy(c);h.multiplyScalar(f);g.add(h);return g},randomPointInFace:function(a,b,c){var d,e,f;if(a instanceof THREE.Face3)return d=b.vertices[a.a],e=b.vertices[a.b],f=b.vertices[a.c],THREE.GeometryUtils.randomPointInTriangle(d,e,f);if(a instanceof THREE.Face4){d=b.vertices[a.a];e=b.vertices[a.b];f=b.vertices[a.c];var b=b.vertices[a.d],g;c?a._area1&&a._area2?(c=a._area1,g=a._area2):(c=THREE.GeometryUtils.triangleArea(d,e,b),g=THREE.GeometryUtils.triangleArea(e,f,b),a._area1=c,a._area2=g):(c=THREE.GeometryUtils.triangleArea(d,
+e,b),g=THREE.GeometryUtils.triangleArea(e,f,b));return THREE.GeometryUtils.random()*(c+g)<c?THREE.GeometryUtils.randomPointInTriangle(d,e,b):THREE.GeometryUtils.randomPointInTriangle(e,f,b)}},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,g=a.vertices,h=f.length,i=0,k=[],l,m,n,s;for(e=0;e<h;e++)d=f[e],d instanceof THREE.Face3?(l=g[d.a],m=g[d.b],n=g[d.c],
+d._area=THREE.GeometryUtils.triangleArea(l,m,n)):d instanceof THREE.Face4&&(l=g[d.a],m=g[d.b],n=g[d.c],s=g[d.d],d._area1=THREE.GeometryUtils.triangleArea(l,m,s),d._area2=THREE.GeometryUtils.triangleArea(m,n,s),d._area=d._area1+d._area2),i+=d._area,k[e]=i;d=[];for(e=0;e<b;e++)g=THREE.GeometryUtils.random()*i,g=c(g),d[e]=THREE.GeometryUtils.randomPointInFace(f[g],a,!0);return d},triangleArea:function(a,b,c){var d=THREE.GeometryUtils.__v1,e=THREE.GeometryUtils.__v2;d.subVectors(b,a);e.subVectors(c,a);
+d.cross(e);return 0.5*d.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},normalizeUVs:function(a){for(var a=a.faceVertexUvs[0],b=0,c=a.length;b<c;b++)for(var d=a[b],e=0,f=d.length;e<f;e++)1!==d[e].x&&(d[e].x-=Math.floor(d[e].x)),1!==d[e].y&&(d[e].y-=Math.floor(d[e].y))},triangulateQuads:function(a){var b,c,d,e,
+f=[],g=[],h=[];b=0;for(c=a.faceUvs.length;b<c;b++)g[b]=[];b=0;for(c=a.faceVertexUvs.length;b<c;b++)h[b]=[];b=0;for(c=a.faces.length;b<c;b++)if(d=a.faces[b],d instanceof THREE.Face4){e=d.a;var i=d.b,k=d.c,l=d.d,m=new THREE.Face3,n=new THREE.Face3;m.color.copy(d.color);n.color.copy(d.color);m.materialIndex=d.materialIndex;n.materialIndex=d.materialIndex;m.a=e;m.b=i;m.c=l;n.a=i;n.b=k;n.c=l;4===d.vertexColors.length&&(m.vertexColors[0]=d.vertexColors[0].clone(),m.vertexColors[1]=d.vertexColors[1].clone(),
+m.vertexColors[2]=d.vertexColors[3].clone(),n.vertexColors[0]=d.vertexColors[1].clone(),n.vertexColors[1]=d.vertexColors[2].clone(),n.vertexColors[2]=d.vertexColors[3].clone());f.push(m,n);d=0;for(e=a.faceVertexUvs.length;d<e;d++)a.faceVertexUvs[d].length&&(m=a.faceVertexUvs[d][b],i=m[1],k=m[2],l=m[3],m=[m[0].clone(),i.clone(),l.clone()],i=[i.clone(),k.clone(),l.clone()],h[d].push(m,i));d=0;for(e=a.faceUvs.length;d<e;d++)a.faceUvs[d].length&&(i=a.faceUvs[d][b],g[d].push(i,i))}else{f.push(d);d=0;for(e=
+a.faceUvs.length;d<e;d++)g[d].push(a.faceUvs[d][b]);d=0;for(e=a.faceVertexUvs.length;d<e;d++)h[d].push(a.faceVertexUvs[d][b])}a.faces=f;a.faceUvs=g;a.faceVertexUvs=h;a.computeCentroids();a.computeFaceNormals();a.computeVertexNormals();a.hasTangents&&a.computeTangents()},setMaterialIndex:function(a,b,c,d){a=a.faces;d=d||a.length-1;for(c=c||0;c<=d;c++)a[c].materialIndex=b}};THREE.GeometryUtils.random=THREE.Math.random16;THREE.GeometryUtils.__v1=new THREE.Vector3;THREE.GeometryUtils.__v2=new THREE.Vector3;THREE.ImageUtils={crossOrigin:"anonymous",loadTexture:function(a,b,c,d){var e=new Image,f=new THREE.Texture(e,b),b=new THREE.ImageLoader;b.addEventListener("load",function(a){f.image=a.content;f.needsUpdate=!0;c&&c(f)});b.addEventListener("error",function(a){d&&d(a.message)});b.crossOrigin=this.crossOrigin;b.load(a,e);f.sourceFile=a;return f},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,g=a.length;b<g;++b){var h=new Image;e[b]=h;h.onload=function(){e.loadCount+=1;6===e.loadCount&&(f.needsUpdate=!0,c&&c(f))};h.onerror=d;h.crossOrigin=
+this.crossOrigin;h.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 g=0,h=a.length;g<h;++g){var i={};
+e[g]=i;var k=new XMLHttpRequest;k.onload=b(k,i);k.onerror=d;i=a[g];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 g=0;g<a.mipmapCount;g++)e[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+g]),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},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"),g=c("DXT5"),h=new Int32Array(a,0,31);if(542327876!==h[0])return console.error("ImageUtils.parseDDS(): Invalid magic number in DDS header"),d;if(!h[20]&4)return console.error("ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code"),
+d;var i=h[21];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 g:e=16;d.format=THREE.RGBA_S3TC_DXT5_Format;break;default: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;h[2]&131072&&!1!==b&&(d.mipmapCount=Math.max(1,h[7]));d.isCubemap=h[28]&512?!0:!1;d.width=h[4];d.height=h[3];for(var h=h[1]+4,f=d.width,g=d.height,i=d.isCubemap?
+6:1,k=0;k<i;k++){for(var l=0;l<d.mipmapCount;l++){var m=Math.max(4,f)/4*Math.max(4,g)/4*e,n={data:new Uint8Array(a,h,m),width:f,height:g};d.mipmaps.push(n);h+=m;f=Math.max(0.5*f,1);g=Math.max(0.5*g,1)}f=d.width;g=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 g=f.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,
+0,d,e).data,i=g.createImageData(d,e),k=i.data,l=0;l<d;l++)for(var m=0;m<e;m++){var n=0>m-1?0:m-1,s=m+1>e-1?e-1:m+1,r=0>l-1?0:l-1,p=l+1>d-1?d-1:l+1,q=[],y=[0,0,h[4*(m*d+l)]/255*b];q.push([-1,0,h[4*(m*d+r)]/255*b]);q.push([-1,-1,h[4*(n*d+r)]/255*b]);q.push([0,-1,h[4*(n*d+l)]/255*b]);q.push([1,-1,h[4*(n*d+p)]/255*b]);q.push([1,0,h[4*(m*d+p)]/255*b]);q.push([1,1,h[4*(s*d+p)]/255*b]);q.push([0,1,h[4*(s*d+l)]/255*b]);q.push([-1,1,h[4*(s*d+r)]/255*b]);n=[];r=q.length;for(s=0;s<r;s++){var p=q[s],v=q[(s+1)%
+r],p=[p[0]-y[0],p[1]-y[1],p[2]-y[2]],v=[v[0]-y[0],v[1]-y[1],v[2]-y[2]];n.push(c([p[1]*v[2]-p[2]*v[1],p[2]*v[0]-p[0]*v[2],p[0]*v[1]-p[1]*v[0]]))}q=[0,0,0];for(s=0;s<n.length;s++)q[0]+=n[s][0],q[1]+=n[s][1],q[2]+=n[s][2];q[0]/=n.length;q[1]/=n.length;q[2]/=n.length;y=4*(m*d+l);k[y]=255*((q[0]+1)/2)|0;k[y+1]=255*((q[1]+1)/2)|0;k[y+2]=255*q[2]|0;k[y+3]=255}g.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),g=Math.floor(255*c.g),
+c=Math.floor(255*c.b),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(){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,g=[],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,i,k,l,m,n,s,r,p,q,y=b.glyphs[a]||b.glyphs["?"];if(y){if(y.o){b=y._cachedOutline||(y._cachedOutline=y.o.split(" "));k=b.length;for(a=0;a<k;)switch(i=b[a++],i){case "m":i=b[a++]*c+d;l=b[a++]*c;e.moveTo(i,l);break;case "l":i=b[a++]*c+d;l=b[a++]*c;e.lineTo(i,l);break;case "q":i=b[a++]*
+c+d;l=b[a++]*c;s=b[a++]*c+d;r=b[a++]*c;e.quadraticCurveTo(s,r,i,l);if(g=f[f.length-1]){m=g.x;n=g.y;g=1;for(h=this.divisions;g<=h;g++){var v=g/h;THREE.Shape.Utils.b2(v,m,s,i);THREE.Shape.Utils.b2(v,n,r,l)}}break;case "b":if(i=b[a++]*c+d,l=b[a++]*c,s=b[a++]*c+d,r=b[a++]*-c,p=b[a++]*c+d,q=b[a++]*-c,e.bezierCurveTo(i,l,s,r,p,q),g=f[f.length-1]){m=g.x;n=g.y;g=1;for(h=this.divisions;g<=h;g++)v=g/h,THREE.Shape.Utils.b3(v,m,s,p,i),THREE.Shape.Utils.b3(v,n,r,q,l)}}}return{offset:y.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,g=0;g<b;f=g++)e+=a[f].x*a[g].y-a[g].x*a[f].y;return 0.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],g=[],h=[],i,k,l;if(0<b(a))for(k=0;k<e;k++)g[k]=k;else for(k=0;k<e;k++)g[k]=e-1-k;var m=2*e;for(k=e-1;2<e;){if(0>=m--){console.log("Warning, unable to triangulate polygon!");break}i=k;e<=i&&(i=0);k=i+1;e<=k&&(k=0);l=k+1;e<=l&&(l=0);var n;a:{var s=n=void 0,r=void 0,p=void 0,q=void 0,y=void 0,v=void 0,z=void 0,t=
+void 0,s=a[g[i]].x,r=a[g[i]].y,p=a[g[k]].x,q=a[g[k]].y,y=a[g[l]].x,v=a[g[l]].y;if(1E-10>(p-s)*(v-r)-(q-r)*(y-s))n=!1;else{var A=void 0,I=void 0,C=void 0,x=void 0,G=void 0,J=void 0,E=void 0,H=void 0,B=void 0,W=void 0,B=H=E=t=z=void 0,A=y-p,I=v-q,C=s-y,x=r-v,G=p-s,J=q-r;for(n=0;n<e;n++)if(!(n===i||n===k||n===l))if(z=a[g[n]].x,t=a[g[n]].y,E=z-s,H=t-r,B=z-p,W=t-q,z-=y,t-=v,B=A*W-I*B,E=G*H-J*E,H=C*t-x*z,0<=B&&0<=H&&0<=E){n=!1;break a}n=!0}}if(n){f.push([a[g[i]],a[g[k]],a[g[l]]]);h.push([g[i],g[k],g[l]]);
+i=k;for(l=k+1;l<e;i++,l++)g[i]=g[l];e--;m=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.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.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 g=0,h=e-1,i;g<=h;)if(d=Math.floor(g+(h-g)/2),i=c[d]-f,0>i)g=d+1;else if(0<i)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=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.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,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.getPoint=function(a){var b=this.aEndAngle-this.aStartAngle;this.aClockwise||(a=1-a);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.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.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]],g=d[c[2]],c=d[c[3]];b.x=THREE.Curve.Utils.interpolate(e.x,f.x,g.x,c.x,a);b.y=THREE.Curve.Utils.interpolate(e.y,f.y,g.y,c.y,a);b.z=THREE.Curve.Utils.interpolate(e.z,f.z,g.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.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.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,g;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var h,i,k,l,m=a[0]instanceof THREE.Vector3;l=m?new THREE.Vector3:new THREE.Vector2;i=0;for(k=a.length;i<k;i++)h=a[i],h.x>b?b=h.x:h.x<e&&(e=h.x),h.y>c?c=h.y:h.y<f&&(f=h.y),m&&(h.z>d?d=h.z:h.z<g&&(g=h.z)),l.add(h);a={minX:e,minY:f,maxX:b,maxY:c,centroid:l.divideScalar(k)};m&&(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,g,h,i;d=0;for(e=a.length;d<e;d++)f=a[d],g=f.x,h=f.y,i=g/c.maxX,i=b.getUtoTmapping(i,g),g=b.getPoint(i),h=b.getNormalVector(i).multiplyScalar(h),f.x=g.x+h.x,f.y=g.y+h.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.rotationWorld,this.scaleWorld),this.matrix.decompose(this.translationObject,this.rotationObject,this.scaleObject),this.matrixWorld.compose(this.translationWorld,this.rotationObject,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.rotationWorld=new THREE.Quaternion;THREE.Gyroscope.prototype.rotationObject=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.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 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),i=new THREE.EllipseCurve(a,b,c,d,e,f,g);this.curves.push(i);i=i.getPoint(g?1:0);h.push(i.x);h.push(i.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:h})};
+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,g,h,i,k,l,m,n,s,r,p;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];i=f[3];m=f[0];n=f[1];0<c.length?(g=c[c.length-1],s=g.x,
+r=g.y):(g=this.actions[d-1].args,s=g[g.length-2],r=g[g.length-1]);for(f=1;f<=a;f++)p=f/a,g=THREE.Shape.Utils.b2(p,s,m,h),p=THREE.Shape.Utils.b2(p,r,n,i),c.push(new THREE.Vector2(g,p));break;case THREE.PathActions.BEZIER_CURVE_TO:h=f[4];i=f[5];m=f[0];n=f[1];k=f[2];l=f[3];0<c.length?(g=c[c.length-1],s=g.x,r=g.y):(g=this.actions[d-1].args,s=g[g.length-2],r=g[g.length-1]);for(f=1;f<=a;f++)p=f/a,g=THREE.Shape.Utils.b3(p,s,m,k,h),p=THREE.Shape.Utils.b3(p,r,n,l,i),c.push(new THREE.Vector2(g,p));break;case THREE.PathActions.CSPLINE_THRU:g=
+this.actions[d-1].args;p=[new THREE.Vector2(g[g.length-2],g[g.length-1])];g=a*f[0].length;p=p.concat(f[0]);p=new THREE.SplineCurve(p);for(f=1;f<=g;f++)c.push(p.getPointAt(f/g));break;case THREE.PathActions.ARC:h=f[0];i=f[1];n=f[2];k=f[3];g=f[4];m=!!f[5];s=g-k;r=2*a;for(f=1;f<=r;f++)p=f/r,m||(p=1-p),p=k+p*s,g=h+n*Math.cos(p),p=i+n*Math.sin(p),c.push(new THREE.Vector2(g,p));break;case THREE.PathActions.ELLIPSE:h=f[0];i=f[1];n=f[2];l=f[3];k=f[4];g=f[5];m=!!f[6];s=g-k;r=2*a;for(f=1;f<=r;f++)p=f/r,m||
+(p=1-p),p=k+p*s,g=h+n*Math.cos(p),p=i+l*Math.sin(p),c.push(new THREE.Vector2(g,p))}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(){var a,b,c,d,e=[],f=new THREE.Path;a=0;for(b=this.actions.length;a<b;a++)c=this.actions[a],d=c.args,c=c.action,c==THREE.PathActions.MOVE_TO&&0!=f.actions.length&&(e.push(f),f=new THREE.Path),f[c].apply(f,d);0!=f.actions.length&&e.push(f);if(0==e.length)return[];var g;d=[];a=!THREE.Shape.Utils.isClockWise(e[0].getPoints());if(1==e.length)return f=e[0],g=new THREE.Shape,g.actions=f.actions,g.curves=f.curves,d.push(g),d;if(a){g=new THREE.Shape;a=0;for(b=e.length;a<
+b;a++)f=e[a],THREE.Shape.Utils.isClockWise(f.getPoints())?(g.actions=f.actions,g.curves=f.curves,d.push(g),g=new THREE.Shape):g.holes.push(f)}else{a=0;for(b=e.length;a<b;a++)f=e[a],THREE.Shape.Utils.isClockWise(f.getPoints())?(g&&d.push(g),g=new THREE.Shape,g.actions=f.actions,g.curves=f.curves):g.holes.push(f);d.push(g)}return d};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,g,h,i,k,l,m,n,s,r=[];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++){n=k[e];s=[];for(m=0;m<c.length;m++)l=c[m],l=n.distanceToSquared(l),s.push(l),l<f&&(f=l,g=e,h=m)}e=0<=h-1?h-1:c.length-1;f=0<=g-1?g-1:k.length-1;var p=[k[g],c[h],c[e]];m=THREE.FontUtils.Triangulate.area(p);var q=[k[g],k[f],c[h]];n=THREE.FontUtils.Triangulate.area(q);s=h;l=g;h+=1;g+=-1;0>
+h&&(h+=c.length);h%=c.length;0>g&&(g+=k.length);g%=k.length;e=0<=h-1?h-1:c.length-1;f=0<=g-1?g-1:k.length-1;p=[k[g],c[h],c[e]];p=THREE.FontUtils.Triangulate.area(p);q=[k[g],k[f],c[h]];q=THREE.FontUtils.Triangulate.area(q);m+n>p+q&&(h=s,g=l,0>h&&(h+=c.length),h%=c.length,0>g&&(g+=k.length),g%=k.length,e=0<=h-1?h-1:c.length-1,f=0<=g-1?g-1:k.length-1);m=c.slice(0,h);n=c.slice(h);s=k.slice(g);l=k.slice(0,g);f=[k[g],k[f],c[h]];r.push([k[g],c[h],c[e]]);r.push(f);c=m.concat(s).concat(l).concat(n)}return{shape:c,
+isolatedPts:r,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,g,h,i,k={};f=0;for(g=d.length;f<g;f++)i=d[f].x+":"+d[f].y,void 0!==k[i]&&console.log("Duplicate point",i),k[i]=f;f=0;for(g=c.length;f<g;f++){h=c[f];for(d=0;3>d;d++)i=h[d].x+":"+h[d].y,i=k[i],void 0!==i&&(h[d]=i)}f=0;for(g=e.length;f<g;f++){h=e[f];for(d=0;3>d;d++)i=h[d].x+":"+h[d].y,i=k[i],void 0!==i&&(h[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.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 h=a.hierarchy[c].keys[d].rot;a.hierarchy[c].keys[d].rot=new THREE.Quaternion(h[0],h[1],h[2],h[3])}if(a.hierarchy[c].keys.length&&void 0!==a.hierarchy[c].keys[0].morphTargets){h={};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];h[k]=-1}a.hierarchy[c].usedMorphTargets=h;
+for(d=0;d<a.hierarchy[c].keys.length;d++){var l={};for(k in h){for(i=0;i<a.hierarchy[c].keys[d].morphTargets.length;i++)if(a.hierarchy[c].keys[d].morphTargets[i]===k){l[k]=a.hierarchy[c].keys[d].morphTargetsInfluences[i];break}i===a.hierarchy[c].keys[d].morphTargets.length&&(l[k]=0)}a.hierarchy[c].keys[d].morphTargetsInfluences=l}}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];this.interpolationType!==THREE.AnimationHandler.CATMULLROM_FORWARD&&(e.useQuaternion=!0);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,g,h,i,k,l;l=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 m=0,n=this.hierarchy.length;m<n;m++){a=this.hierarchy[m];i=a.animationCache;for(var s=0;3>s;s++){c=b[s];g=i.prevKey[c];h=i.nextKey[c];if(h.time<=l){if(k<l)if(this.loop){g=this.data.hierarchy[m].keys[0];for(h=this.getNextKeyWith(c,m,1);h.time<
+k;)g=h,h=this.getNextKeyWith(c,m,h.index+1)}else{this.stop();return}else{do g=h,h=this.getNextKeyWith(c,m,h.index+1);while(h.time<k)}i.prevKey[c]=g;i.nextKey[c]=h}a.matrixAutoUpdate=!0;a.matrixWorldNeedsUpdate=!0;d=(k-g.time)/(h.time-g.time);e=g[c];f=h[c];if(0>d||1<d)console.log("THREE.Animation.update: Warning! Scale out of bounds:"+d+" on bone "+m),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",m,g.index-1).pos,this.points[1]=e,this.points[2]=f,this.points[3]=this.getNextKeyWith("pos",m,h.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,g,h,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]];h=a[c[1]];i=a[c[2]];k=a[c[3]];c=e*e;g=e*c;d[0]=this.interpolate(f[0],h[0],i[0],k[0],e,c,g);d[1]=this.interpolate(f[1],h[1],i[1],k[1],e,c,g);d[2]=this.interpolate(f[2],h[2],i[2],k[2],e,c,g);return d};
+THREE.Animation.prototype.interpolate=function(a,b,c,d,e,f,g){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)*g+(-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],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],e.useQuaternion=!0,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,g,h,i;h=this.currentTime+=a*this.timeScale;g=this.currentTime%=this.data.length;g<this.startTimeMs&&(g=this.currentTime=this.startTimeMs+g);e=parseInt(Math.min(g*this.data.fps,this.data.length*this.data.fps),10);if((i=g<h)&&!this.loop){for(var a=0,k=this.hierarchy.length;a<k;a++){var l=this.data.hierarchy[a].keys,f=this.data.hierarchy[a].sids;d=l.length-1;e=this.hierarchy[a];if(l.length){for(l=
+0;l<f.length;l++)g=f[l],(h=this.getPrevKeyWith(g,a,d))&&h.apply(g);this.data.hierarchy[a].node.updateMatrix();e.matrixWorldNeedsUpdate=!0}}this.stop()}else if(!(g<this.startTime)){a=0;for(k=this.hierarchy.length;a<k;a++){d=this.hierarchy[a];b=this.data.hierarchy[a];var l=b.keys,m=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(l.length){this.JITCompile&&m&&(d instanceof
+THREE.Bone?d.skinMatrix=m.originalMatrix:d.matrix=m.originalMatrix);b=m.prevKey;c=m.nextKey;if(b&&c){if(c.time<=h){if(i&&this.loop){b=l[0];for(c=l[1];c.time<g;)b=c,c=l[b.index+1]}else if(!i)for(var n=l.length-1;c.time<g&&c.index!==n;)b=c,c=l[b.index+1];m.prevKey=b;m.nextKey=c}c.time>=g?b.interpolate(c,g):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 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 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,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,i,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.CombinedCamera=function(a,b,c,d,e,f,g){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,g);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.AsteriskGeometry=function(a,b){THREE.Geometry.call(this);for(var c=0.707*a,d=0.707*b,c=[[a,0,0],[b,0,0],[-a,0,0],[-b,0,0],[0,a,0],[0,b,0],[0,-a,0],[0,-b,0],[0,0,a],[0,0,b],[0,0,-a],[0,0,-b],[c,c,0],[d,d,0],[-c,-c,0],[-d,-d,0],[c,-c,0],[d,-d,0],[-c,c,0],[-d,d,0],[c,0,c],[d,0,d],[-c,0,-c],[-d,0,-d],[c,0,-c],[d,0,-d],[-c,0,c],[-d,0,d],[0,c,c],[0,d,d],[0,-c,-c],[0,-d,-d],[0,c,-c],[0,d,-d],[0,-c,c],[0,-d,d]],d=0,e=c.length;d<e;d++)this.vertices.push(new THREE.Vector3(c[d][0],c[d][1],c[d][2]))};
+THREE.AsteriskGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);var a=a||50,c=void 0!==c?c:0,d=void 0!==d?d:2*Math.PI,b=void 0!==b?Math.max(3,b):8,e,f=[];e=new THREE.Vector3;var g=new THREE.Vector2(0.5,0.5);this.vertices.push(e);f.push(g);for(e=0;e<=b;e++){var h=new THREE.Vector3;h.x=a*Math.cos(c+e/b*d);h.y=a*Math.sin(c+e/b*d);this.vertices.push(h);f.push(new THREE.Vector2((h.x/a+1)/2,-(h.y/a+1)/2+1))}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],g]);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 g(a,b,c,d,e,f,g,p){var q,y=h.widthSegments,v=h.heightSegments,z=e/2,t=f/2,A=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)q="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)q="y",v=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)q="x",y=h.depthSegments;var I=y+1,C=v+1,x=e/y,G=f/v,J=new THREE.Vector3;J[q]=0<g?1:-1;for(e=0;e<C;e++)for(f=0;f<I;f++){var E=new THREE.Vector3;E[a]=(f*x-z)*c;E[b]=(e*G-t)*d;E[q]=g;h.vertices.push(E)}for(e=
+0;e<v;e++)for(f=0;f<y;f++)a=new THREE.Face4(f+I*e+A,f+I*(e+1)+A,f+1+I*(e+1)+A,f+1+I*e+A),a.normal.copy(J),a.vertexNormals.push(J.clone(),J.clone(),J.clone(),J.clone()),a.materialIndex=p,h.faces.push(a),h.faceVertexUvs[0].push([new THREE.Vector2(f/y,1-e/v),new THREE.Vector2(f/y,1-(e+1)/v),new THREE.Vector2((f+1)/y,1-(e+1)/v),new THREE.Vector2((f+1)/y,1-e/v)])}THREE.Geometry.call(this);var h=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;g("z","y",-1,-1,this.depth,this.height,a,0);g("z","y",1,-1,this.depth,this.height,-a,1);g("x","z",1,1,this.width,this.depth,b,2);g("x","z",1,-1,this.width,this.depth,-b,3);g("x","y",1,-1,this.width,this.height,c,4);g("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);var a=void 0!==a?a:20,b=void 0!==b?b:20,c=void 0!==c?c:100,g=c/2,d=d||8,e=e||1,h,i,k=[],l=[];for(i=0;i<=e;i++){var m=[],n=[],s=i/e,r=s*(b-a)+a;for(h=0;h<=d;h++){var p=h/d,q=new THREE.Vector3;q.x=r*Math.sin(2*p*Math.PI);q.y=-s*c+g;q.z=r*Math.cos(2*p*Math.PI);this.vertices.push(q);m.push(this.vertices.length-1);n.push(new THREE.Vector2(p,1-s))}k.push(m);l.push(n)}c=(b-a)/c;for(h=0;h<d;h++){0!==a?(m=this.vertices[k[0][h]].clone(),
+n=this.vertices[k[0][h+1]].clone()):(m=this.vertices[k[1][h]].clone(),n=this.vertices[k[1][h+1]].clone());m.setY(Math.sqrt(m.x*m.x+m.z*m.z)*c).normalize();n.setY(Math.sqrt(n.x*n.x+n.z*n.z)*c).normalize();for(i=0;i<e;i++){var s=k[i][h],r=k[i+1][h],p=k[i+1][h+1],q=k[i][h+1],y=m.clone(),v=m.clone(),z=n.clone(),t=n.clone(),A=l[i][h].clone(),I=l[i+1][h].clone(),C=l[i+1][h+1].clone(),x=l[i][h+1].clone();this.faces.push(new THREE.Face4(s,r,p,q,[y,v,z,t]));this.faceVertexUvs[0].push([A,I,C,x])}}if(!f&&0<
+a){this.vertices.push(new THREE.Vector3(0,g,0));for(h=0;h<d;h++)s=k[0][h],r=k[0][h+1],p=this.vertices.length-1,y=new THREE.Vector3(0,1,0),v=new THREE.Vector3(0,1,0),z=new THREE.Vector3(0,1,0),A=l[0][h].clone(),I=l[0][h+1].clone(),C=new THREE.Vector2(I.u,0),this.faces.push(new THREE.Face3(s,r,p,[y,v,z])),this.faceVertexUvs[0].push([A,I,C])}if(!f&&0<b){this.vertices.push(new THREE.Vector3(0,-g,0));for(h=0;h<d;h++)s=k[i][h+1],r=k[i][h],p=this.vertices.length-1,y=new THREE.Vector3(0,-1,0),v=new THREE.Vector3(0,
+-1,0),z=new THREE.Vector3(0,-1,0),A=l[i][h+1].clone(),I=l[i][h].clone(),C=new THREE.Vector2(I.u,1),this.faces.push(new THREE.Face3(s,r,p,[y,v,z])),this.faceVertexUvs[0].push([A,I,C])}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,i=THREE.ExtrudeGeometry.__v5,h=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);i.copy(a).add(f);h.copy(a).add(g);if(i.equals(h))return g.clone();
+i.copy(b).add(f);h.copy(c).add(g);f=d.dot(g);g=h.sub(i).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(i).sub(a).clone()}function e(c,d){var e,f;for(M=c.length;0<=--M;){e=M;f=M-1;0>f&&(f=c.length-1);for(var g=0,i=s+2*l,
+g=0;g<i;g++){var h=fa*g,k=fa*(g+1),m=d+e+h,h=d+f+h,n=d+f+k,k=d+e+k,p=c,q=g,r=i,t=e,y=f,m=m+H,h=h+H,n=n+H,k=k+H;E.faces.push(new THREE.Face4(m,h,n,k,null,null,v));m=z.generateSideWallUV(E,a,p,b,m,h,n,k,q,r,t,y);E.faceVertexUvs[0].push(m)}}}function f(a,b,c){E.vertices.push(new THREE.Vector3(a,b,c))}function g(c,d,e,f){c+=H;d+=H;e+=H;E.faces.push(new THREE.Face3(c,d,e,null,null,y));c=f?z.generateBottomUV(E,a,b,c,d,e):z.generateTopUV(E,a,b,c,d,e);E.faceVertexUvs[0].push(c)}var h=void 0!==b.amount?b.amount:
+100,i=void 0!==b.bevelThickness?b.bevelThickness:6,k=void 0!==b.bevelSize?b.bevelSize:i-2,l=void 0!==b.bevelSegments?b.bevelSegments:3,m=void 0!==b.bevelEnabled?b.bevelEnabled:!0,n=void 0!==b.curveSegments?b.curveSegments:12,s=void 0!==b.steps?b.steps:1,r=b.extrudePath,p,q=!1,y=b.material,v=b.extrudeMaterial,z=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,t,A,I,C;r&&(p=r.getSpacedPoints(s),q=!0,m=!1,t=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(r,s,
+!1),A=new THREE.Vector3,I=new THREE.Vector3,C=new THREE.Vector3);m||(k=i=l=0);var x,G,J,E=this,H=this.vertices.length,n=a.extractPoints(n),B=n.shape,n=n.holes;if(r=!THREE.Shape.Utils.isClockWise(B)){B=B.reverse();G=0;for(J=n.length;G<J;G++)x=n[G],THREE.Shape.Utils.isClockWise(x)&&(n[G]=x.reverse());r=!1}var W=THREE.Shape.Utils.triangulateShape(B,n),r=B;G=0;for(J=n.length;G<J;G++)x=n[G],B=B.concat(x);var F,K,L,U,fa=B.length,Ca=W.length,$a=[],M=0,ca=r.length;F=ca-1;for(K=M+1;M<ca;M++,F++,K++)F===ca&&
+(F=0),K===ca&&(K=0),$a[M]=d(r[M],r[F],r[K]);var qa=[],ha,ra=$a.concat();G=0;for(J=n.length;G<J;G++){x=n[G];ha=[];M=0;ca=x.length;F=ca-1;for(K=M+1;M<ca;M++,F++,K++)F===ca&&(F=0),K===ca&&(K=0),ha[M]=d(x[M],x[F],x[K]);qa.push(ha);ra=ra.concat(ha)}for(F=0;F<l;F++){x=F/l;L=i*(1-x);K=k*Math.sin(x*Math.PI/2);M=0;for(ca=r.length;M<ca;M++)U=c(r[M],$a[M],K),f(U.x,U.y,-L);G=0;for(J=n.length;G<J;G++){x=n[G];ha=qa[G];M=0;for(ca=x.length;M<ca;M++)U=c(x[M],ha[M],K),f(U.x,U.y,-L)}}K=k;for(M=0;M<fa;M++)U=m?c(B[M],
+ra[M],K):B[M],q?(I.copy(t.normals[0]).multiplyScalar(U.x),A.copy(t.binormals[0]).multiplyScalar(U.y),C.copy(p[0]).add(I).add(A),f(C.x,C.y,C.z)):f(U.x,U.y,0);for(x=1;x<=s;x++)for(M=0;M<fa;M++)U=m?c(B[M],ra[M],K):B[M],q?(I.copy(t.normals[x]).multiplyScalar(U.x),A.copy(t.binormals[x]).multiplyScalar(U.y),C.copy(p[x]).add(I).add(A),f(C.x,C.y,C.z)):f(U.x,U.y,h/s*x);for(F=l-1;0<=F;F--){x=F/l;L=i*(1-x);K=k*Math.sin(x*Math.PI/2);M=0;for(ca=r.length;M<ca;M++)U=c(r[M],$a[M],K),f(U.x,U.y,h+L);G=0;for(J=n.length;G<
+J;G++){x=n[G];ha=qa[G];M=0;for(ca=x.length;M<ca;M++)U=c(x[M],ha[M],K),q?f(U.x,U.y+p[s-1].y,p[s-1].x+L):f(U.x,U.y,h+L)}}if(m){i=0*fa;for(M=0;M<Ca;M++)h=W[M],g(h[2]+i,h[1]+i,h[0]+i,!0);i=fa*(s+2*l);for(M=0;M<Ca;M++)h=W[M],g(h[0]+i,h[1]+i,h[2]+i,!1)}else{for(M=0;M<Ca;M++)h=W[M],g(h[2],h[1],h[0],!0);for(M=0;M<Ca;M++)h=W[M],g(h[0]+fa*s,h[1]+fa*s,h[2]+fa*s,!1)}h=0;e(r,h);h+=r.length;G=0;for(J=n.length;G<J;G++)x=n[G],e(x,h),h+=x.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,g,h){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[g].x,
+l=a.vertices[g].y,g=a.vertices[g].z,m=a.vertices[h].x,n=a.vertices[h].y,a=a.vertices[h].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-g),new THREE.Vector2(m,1-a)]:[new THREE.Vector2(c,1-e),new THREE.Vector2(i,1-f),new THREE.Vector2(l,1-g),new THREE.Vector2(n,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,g,h=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++)g=k[e],THREE.Shape.Utils.isClockWise(g)&&(k[e]=g.reverse())}var l=THREE.Shape.Utils.triangulateShape(i,k);e=0;for(f=k.length;e<f;e++)g=k[e],
+i=i.concat(g);k=i.length;f=l.length;for(e=0;e<k;e++)g=i[e],this.vertices.push(new THREE.Vector3(g.x,g.y,0));for(e=0;e<f;e++)k=l[e],i=k[0]+h,g=k[1]+h,k=k[2]+h,this.faces.push(new THREE.Face3(i,g,k,null,null,c)),this.faceVertexUvs[0].push(d.generateBottomUV(this,a,b,i,g,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,g=0,h=b;g<=h;g++)for(var i=c+g*f*d,k=Math.cos(i),l=Math.sin(i),i=0,m=a.length;i<m;i++){var n=a[i],s=new THREE.Vector3;s.x=k*n.x-l*n.y;s.y=l*n.x+k*n.y;s.z=n.z;this.vertices.push(s)}c=a.length;g=0;for(h=b;g<h;g++){i=0;for(m=a.length-1;i<m;i++)d=b=i+c*g,l=b+c,k=b+1+c,this.faces.push(new THREE.Face4(d,l,k,b+1)),k=g*f,b=i*e,d=k+f,l=b+e,this.faceVertexUvs[0].push([new THREE.Vector2(k,
+b),new THREE.Vector2(d,b),new THREE.Vector2(d,l),new THREE.Vector2(k,l)])}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 c=a/2,e=b/2,d=this.widthSegments,f=this.heightSegments,g=d+1,h=f+1,i=this.width/d,k=this.height/f,l=new THREE.Vector3(0,0,1),a=0;a<h;a++)for(b=0;b<g;b++)this.vertices.push(new THREE.Vector3(b*i-c,-(a*k-e),0));for(a=0;a<f;a++)for(b=0;b<d;b++)c=new THREE.Face4(b+g*a,b+g*(a+1),b+1+g*(a+1),b+1+g*a),c.normal.copy(l),c.vertexNormals.push(l.clone(),l.clone(),
+l.clone(),l.clone()),this.faces.push(c),this.faceVertexUvs[0].push([new THREE.Vector2(b/d,1-a/f),new THREE.Vector2(b/d,1-(a+1)/f),new THREE.Vector2((b+1)/d,1-(a+1)/f),new THREE.Vector2((b+1)/d,1-a/f)]);this.computeCentroids()};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.SphereGeometry=function(a,b,c,d,e,f,g){THREE.Geometry.call(this);this.radius=a||50;this.widthSegments=Math.max(3,Math.floor(b)||8);this.heightSegments=Math.max(2,Math.floor(c)||6);for(var 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,h=[],i=[],c=0;c<=this.heightSegments;c++){for(var k=[],l=[],b=0;b<=this.widthSegments;b++){var m=b/this.widthSegments,n=c/this.heightSegments,s=new THREE.Vector3;s.x=-this.radius*Math.cos(d+m*e)*Math.sin(f+n*g);s.y=this.radius*
+Math.cos(f+n*g);s.z=this.radius*Math.sin(d+m*e)*Math.sin(f+n*g);this.vertices.push(s);k.push(this.vertices.length-1);l.push(new THREE.Vector2(m,1-n))}h.push(k);i.push(l)}for(c=0;c<this.heightSegments;c++)for(b=0;b<this.widthSegments;b++){var d=h[c][b+1],e=h[c][b],f=h[c+1][b],g=h[c+1][b+1],k=this.vertices[d].clone().normalize(),l=this.vertices[e].clone().normalize(),m=this.vertices[f].clone().normalize(),n=this.vertices[g].clone().normalize(),s=i[c][b+1].clone(),r=i[c][b].clone(),p=i[c+1][b].clone(),
+q=i[c+1][b+1].clone();Math.abs(this.vertices[d].y)===this.radius?(this.faces.push(new THREE.Face3(d,f,g,[k,m,n])),this.faceVertexUvs[0].push([s,p,q])):Math.abs(this.vertices[f].y)===this.radius?(this.faces.push(new THREE.Face3(d,e,f,[k,l,m])),this.faceVertexUvs[0].push([s,r,p])):(this.faces.push(new THREE.Face4(d,e,f,g,[k,l,m,n])),this.faceVertexUvs[0].push([s,r,p,q]))}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 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,g=2*c/this.radialSegments*Math.PI;e.x=this.radius*Math.cos(f);e.y=this.radius*Math.sin(f);var h=new THREE.Vector3;h.x=(this.radius+this.tube*Math.cos(g))*Math.cos(f);h.y=(this.radius+this.tube*
+Math.cos(g))*Math.sin(f);h.z=this.tube*Math.sin(g);this.vertices.push(h);a.push(new THREE.Vector2(d/this.tubularSegments,c/this.radialSegments));b.push(h.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,g=(this.tubularSegments+1)*(c-1)+d,h=(this.tubularSegments+1)*c+d,i=new THREE.Face4(e,f,g,h,[b[e],b[f],b[g],b[h]]);i.normal.add(b[e]);i.normal.add(b[f]);i.normal.add(b[g]);i.normal.add(b[h]);
+i.normal.normalize();this.faces.push(i);this.faceVertexUvs[0].push([a[e].clone(),a[f].clone(),a[g].clone(),a[h].clone()])}this.computeCentroids()};THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TorusKnotGeometry=function(a,b,c,d,e,f,g){function h(a,b,c,d,e,f){var g=Math.cos(a);Math.cos(b);b=Math.sin(a);a*=c/d;c=Math.cos(a);g*=0.5*e*(2+c);b=0.5*e*(2+c)*b;e=0.5*f*e*Math.sin(a);return new THREE.Vector3(g,b,e)}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=g||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);for(b=0;b<this.tubularSegments;++b){var i=2*(a/this.radialSegments)*this.p*Math.PI,g=2*(b/this.tubularSegments)*Math.PI,f=h(i,g,this.q,this.p,this.radius,this.heightScale),i=h(i+0.01,g,this.q,this.p,this.radius,this.heightScale);c.subVectors(i,f);d.addVectors(i,f);e.crossVectors(c,d);d.crossVectors(e,c);e.normalize();d.normalize();i=-this.tube*Math.cos(g);g=this.tube*Math.sin(g);f.x+=i*d.x+g*e.x;f.y+=i*d.y+g*e.y;f.z+=i*d.z+g*e.z;this.grid[a][b]=this.vertices.push(new THREE.Vector3(f.x,
+f.y,f.z))-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],g=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),l=new THREE.Vector2(a/this.radialSegments,(b+1)/this.tubularSegments);this.faces.push(new THREE.Face4(c,
+d,e,f));this.faceVertexUvs[0].push([g,i,k,l])}this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TubeGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.path=a;this.segments=b||64;this.radius=c||1;this.radiusSegments=d||8;this.closed=e||!1;f&&(this.debug=new THREE.Object3D);this.grid=[];var g,h,e=this.segments+1,i,k,l,f=new THREE.Vector3,m,n,s,b=new THREE.TubeGeometry.FrenetFrames(this.path,this.segments,this.closed);m=b.tangents;n=b.normals;s=b.binormals;this.tangents=m;this.normals=n;this.binormals=s;for(b=0;b<e;b++){this.grid[b]=[];d=b/(e-1);l=a.getPointAt(d);d=m[b];g=n[b];
+h=s[b];this.debug&&(this.debug.add(new THREE.ArrowHelper(d,l,c,255)),this.debug.add(new THREE.ArrowHelper(g,l,c,16711680)),this.debug.add(new THREE.ArrowHelper(h,l,c,65280)));for(d=0;d<this.radiusSegments;d++)i=2*(d/this.radiusSegments)*Math.PI,k=-this.radius*Math.cos(i),i=this.radius*Math.sin(i),f.copy(l),f.x+=k*g.x+i*h.x,f.y+=k*g.y+i*h.y,f.z+=k*g.z+i*h.z,this.grid[b][d]=this.vertices.push(new THREE.Vector3(f.x,f.y,f.z))-1}for(b=0;b<this.segments;b++)for(d=0;d<this.radiusSegments;d++)e=this.closed?
+(b+1)%this.segments:b+1,f=(d+1)%this.radiusSegments,a=this.grid[b][d],c=this.grid[e][d],e=this.grid[e][f],f=this.grid[b][f],m=new THREE.Vector2(b/this.segments,d/this.radiusSegments),n=new THREE.Vector2((b+1)/this.segments,d/this.radiusSegments),s=new THREE.Vector2((b+1)/this.segments,(d+1)/this.radiusSegments),g=new THREE.Vector2(b/this.segments,(d+1)/this.radiusSegments),this.faces.push(new THREE.Face4(a,c,e,f)),this.faceVertexUvs[0].push([m,n,s,g]);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=[],g=[],h=new THREE.Vector3,i=new THREE.Matrix4,b=b+1,k,l,m;this.tangents=e;this.normals=f;this.binormals=g;for(k=0;k<b;k++)l=k/(b-1),e[k]=a.getTangentAt(l),e[k].normalize();f[0]=new THREE.Vector3;g[0]=new THREE.Vector3;a=Number.MAX_VALUE;k=Math.abs(e[0].x);l=Math.abs(e[0].y);m=Math.abs(e[0].z);k<=a&&(a=k,d.set(1,0,0));l<=a&&(a=l,d.set(0,1,0));m<=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(k=1;k<b;k++)f[k]=f[k-1].clone(),g[k]=g[k-1].clone(),h.crossVectors(e[k-1],e[k]),1E-4<h.length()&&(h.normalize(),d=Math.acos(e[k-1].dot(e[k])),f[k].applyMatrix4(i.makeRotationAxis(h,d))),g[k].crossVectors(e[k],f[k]);if(c){d=Math.acos(f[0].dot(f[b-1]));d/=b-1;0<e[0].dot(h.crossVectors(f[0],f[b-1]))&&(d=-d);for(k=1;k<b;k++)f[k].applyMatrix4(i.makeRotationAxis(e[k],d*k)),g[k].crossVectors(e[k],f[k])}};THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=i.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,d){1>d?(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),d.normal=d.centroid.clone().normalize(),i.faces.push(d),d=Math.atan2(d.centroid.z,-d.centroid.x),i.faceVertexUvs[0].push([h(a.uv,
+a,d),h(b.uv,b,d),h(c.uv,c,d)])):(d-=1,f(a,g(a,b),g(a,c),d),f(g(a,b),b,g(b,c),d),f(g(a,c),g(b,c),c,d),f(g(a,b),g(b,c),g(a,c),d))}function g(a,b){m[a.index]||(m[a.index]=[]);m[b.index]||(m[b.index]=[]);var c=m[a.index][b.index];void 0===c&&(m[a.index][b.index]=m[b.index][a.index]=c=e((new THREE.Vector3).addVectors(a,b).divideScalar(2)));return c}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}THREE.Geometry.call(this);
+for(var c=c||1,d=d||0,i=this,k=0,l=a.length;k<l;k++)e(new THREE.Vector3(a[k][0],a[k][1],a[k][2]));for(var m=[],a=this.vertices,k=0,l=b.length;k<l;k++)f(a[b[k][0]],a[b[k][1]],a[b[k][2]],d);this.mergeVertices();k=0;for(l=this.vertices.length;k<l;k++)this.vertices[k].multiplyScalar(c);this.computeCentroids();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);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)};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,d){THREE.Geometry.call(this);var e=this.vertices,f=this.faces,g=this.faceVertexUvs[0],d=void 0===d?!1:d,h,i,k,l,m=b+1;for(h=0;h<=c;h++){l=h/c;for(i=0;i<=b;i++)k=i/b,k=a(k,l),e.push(k)}var n,s,r,p;for(h=0;h<c;h++)for(i=0;i<b;i++)a=h*m+i,e=h*m+i+1,l=(h+1)*m+i,k=(h+1)*m+i+1,n=new THREE.Vector2(i/b,h/c),s=new THREE.Vector2((i+1)/b,h/c),r=new THREE.Vector2(i/b,(h+1)/c),p=new THREE.Vector2((i+1)/b,(h+1)/c),d?(f.push(new THREE.Face3(a,e,l)),f.push(new THREE.Face3(e,
+k,l)),g.push([n,s,r]),g.push([s,p,r])):(f.push(new THREE.Face4(a,e,k,l)),g.push([n,s,p,r]));this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ConvexGeometry=function(a){function b(a){var b=a.length();return new THREE.Vector2(a.x/b,a.y/b)}THREE.Geometry.call(this);for(var c=[[0,1,2],[0,2,1]],d=3;d<a.length;d++){var e=d,f=a[e].clone(),g=f.length();f.x+=g*2E-6*(Math.random()-0.5);f.y+=g*2E-6*(Math.random()-0.5);f.z+=g*2E-6*(Math.random()-0.5);for(var g=[],h=0;h<c.length;){var i=c[h],k=f,l=a[i[0]],m;m=l;var n=a[i[1]],s=a[i[2]],r=new THREE.Vector3,p=new THREE.Vector3;r.subVectors(s,n);p.subVectors(m,n);r.cross(p);r.normalize();m=r;l=m.dot(l);
+if(m.dot(k)>=l){for(k=0;3>k;k++){l=[i[k],i[(k+1)%3]];m=!0;for(n=0;n<g.length;n++)if(g[n][0]===l[1]&&g[n][1]===l[0]){g[n]=g[g.length-1];g.pop();m=!1;break}m&&g.push(l)}c[h]=c[c.length-1];c.pop()}else h++}for(n=0;n<g.length;n++)c.push([g[n][0],g[n][1],e])}e=0;f=Array(a.length);for(d=0;d<c.length;d++){g=c[d];for(h=0;3>h;h++)void 0===f[g[h]]&&(f[g[h]]=e++,this.vertices.push(a[g[h]])),g[h]=f[g[h]]}for(d=0;d<c.length;d++)this.faces.push(new THREE.Face3(c[d][0],c[d][1],c[d][2]));for(d=0;d<this.faces.length;d++)g=
+this.faces[d],this.faceVertexUvs[0].push([b(this.vertices[g.a]),b(this.vertices[g.b]),b(this.vertices[g.c])]);this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.ConvexGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.AxisHelper=function(a){var b=new THREE.Geometry;b.vertices.push(new THREE.Vector3,new THREE.Vector3(a||1,0,0),new THREE.Vector3,new THREE.Vector3(0,a||1,0),new THREE.Vector3,new THREE.Vector3(0,0,a||1));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===c&&(c=20);void 0===d&&(d=16776960);var e=new THREE.Geometry;e.vertices.push(new THREE.Vector3(0,0,0));e.vertices.push(new THREE.Vector3(0,1,0));this.line=new THREE.Line(e,new THREE.LineBasicMaterial({color:d}));this.add(this.line);e=new THREE.CylinderGeometry(0,0.05,0.25,5,1);this.cone=new THREE.Mesh(e,new THREE.MeshBasicMaterial({color:d}));this.cone.position.set(0,1,0);this.add(this.cone);b instanceof THREE.Vector3&&(this.position=
+b);this.setDirection(a);this.setLength(c)};THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.setDirection=function(a){var b=THREE.ArrowHelper.__v1.copy(a).normalize();0.999<b.y?this.rotation.set(0,0,0):-0.999>b.y?this.rotation.set(Math.PI,0,0):(a=THREE.ArrowHelper.__v2.set(b.z,0,-b.x).normalize(),b=Math.acos(b.y),a=THREE.ArrowHelper.__q1.setFromAxisAngle(a,b),this.rotation.setEulerFromQuaternion(a,this.eulerOrder))};
+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.ArrowHelper.__v1=new THREE.Vector3;THREE.ArrowHelper.__v2=new THREE.Vector3;THREE.ArrowHelper.__q1=new THREE.Quaternion;THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.geometry.vertices.push(new THREE.Vector3);d.geometry.colors.push(new THREE.Color(b));void 0===d.pointMap[a]&&(d.pointMap[a]=[]);d.pointMap[a].push(d.geometry.vertices.length-1)}THREE.Line.call(this);var d=this;this.geometry=new THREE.Geometry;this.material=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors});this.type=THREE.LinePieces;this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=
+{};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);this.camera=a;this.update(a)};THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.CameraHelper.prototype.update=function(){function a(a,d,e,f){THREE.CameraHelper.__v.set(d,e,f);THREE.CameraHelper.__projector.unprojectVector(THREE.CameraHelper.__v,THREE.CameraHelper.__c);a=b.pointMap[a];if(void 0!==a){d=0;for(e=a.length;d<e;d++)b.geometry.vertices[a[d]].copy(THREE.CameraHelper.__v)}}var b=this;THREE.CameraHelper.__c.projectionMatrix.copy(this.camera.projectionMatrix);a("c",0,0,-1);a("t",0,0,1);a("n1",-1,-1,-1);a("n2",1,-1,-1);a("n3",-1,1,-1);a("n4",1,1,-1);a("f1",-1,-1,1);
+a("f2",1,-1,1);a("f3",-1,1,1);a("f4",1,1,1);a("u1",0.7,1.1,-1);a("u2",-0.7,1.1,-1);a("u3",0,2,-1);a("cf1",-1,0,1);a("cf2",1,0,1);a("cf3",0,-1,1);a("cf4",0,1,1);a("cn1",-1,0,-1);a("cn2",1,0,-1);a("cn3",0,-1,-1);a("cn4",0,1,-1);this.geometry.verticesNeedUpdate=!0};THREE.CameraHelper.__projector=new THREE.Projector;THREE.CameraHelper.__v=new THREE.Vector3;THREE.CameraHelper.__c=new THREE.Camera;THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.position=a.position;this.direction=new THREE.Vector3;this.direction.subVectors(a.target.position,a.position);var c=THREE.Math.clamp(a.intensity,0,1);this.color=a.color.clone();this.color.multiplyScalar(c);var c=this.color.getHex(),d=new THREE.SphereGeometry(b,16,8),e=new THREE.AsteriskGeometry(1.25*b,2.25*b),f=new THREE.MeshBasicMaterial({color:c,fog:!1}),g=new THREE.LineBasicMaterial({color:c,fog:!1});this.lightSphere=
+new THREE.Mesh(d,f);this.lightRays=new THREE.Line(e,g,THREE.LinePieces);this.add(this.lightSphere);this.add(this.lightRays);this.lightSphere.properties.isGizmo=!0;this.lightSphere.properties.gizmoSubject=a;this.lightSphere.properties.gizmoRoot=this;this.targetSphere=null;void 0!==a.target.properties.targetInverse&&(d=new THREE.SphereGeometry(b,8,4),e=new THREE.MeshBasicMaterial({color:c,wireframe:!0,fog:!1}),this.targetSphere=new THREE.Mesh(d,e),this.targetSphere.position=a.target.position,this.targetSphere.properties.isGizmo=
+!0,this.targetSphere.properties.gizmoSubject=a.target,this.targetSphere.properties.gizmoRoot=this.targetSphere,c=new THREE.LineDashedMaterial({color:c,dashSize:4,gapSize:4,opacity:0.75,transparent:!0,fog:!1}),d=new THREE.Geometry,d.vertices.push(this.position.clone()),d.vertices.push(this.targetSphere.position.clone()),d.computeLineDistances(),this.targetLine=new THREE.Line(d,c),this.targetLine.properties.isGizmo=!0);this.properties.isGizmo=!0};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.DirectionalLightHelper.prototype.update=function(){this.direction.subVectors(this.light.target.position,this.light.position);var a=THREE.Math.clamp(this.light.intensity,0,1);this.color.copy(this.light.color);this.color.multiplyScalar(a);this.lightSphere.material.color.copy(this.color);this.lightRays.material.color.copy(this.color);null!==this.targetSphere&&(this.targetSphere.material.color.copy(this.color),this.targetLine.material.color.copy(this.color),this.targetLine.geometry.vertices[0].copy(this.light.position),
+this.targetLine.geometry.vertices[1].copy(this.light.target.position),this.targetLine.geometry.computeLineDistances(),this.targetLine.geometry.verticesNeedUpdate=!0)};THREE.HemisphereLightHelper=function(a,b,c){THREE.Object3D.call(this);this.light=a;this.position=a.position;var d=THREE.Math.clamp(a.intensity,0,1);this.color=a.color.clone();this.color.multiplyScalar(d);var e=this.color.getHex();this.groundColor=a.groundColor.clone();this.groundColor.multiplyScalar(d);for(var d=this.groundColor.getHex(),f=new THREE.SphereGeometry(b,16,8,0,2*Math.PI,0,0.5*Math.PI),g=new THREE.SphereGeometry(b,16,8,0,2*Math.PI,0.5*Math.PI,Math.PI),h=new THREE.MeshBasicMaterial({color:e,
+fog:!1}),i=new THREE.MeshBasicMaterial({color:d,fog:!1}),k=0,l=f.faces.length;k<l;k++)f.faces[k].materialIndex=0;k=0;for(l=g.faces.length;k<l;k++)g.faces[k].materialIndex=1;THREE.GeometryUtils.merge(f,g);this.lightSphere=new THREE.Mesh(f,new THREE.MeshFaceMaterial([h,i]));this.lightArrow=new THREE.ArrowHelper(new THREE.Vector3(0,1,0),new THREE.Vector3(0,1.1*(b+c),0),c,e);this.lightArrow.rotation.x=Math.PI;this.lightArrowGround=new THREE.ArrowHelper(new THREE.Vector3(0,1,0),new THREE.Vector3(0,-1.1*
+(b+c),0),c,d);b=new THREE.Object3D;b.rotation.x=0.5*-Math.PI;b.add(this.lightSphere);b.add(this.lightArrow);b.add(this.lightArrowGround);this.add(b);this.lightSphere.properties.isGizmo=!0;this.lightSphere.properties.gizmoSubject=a;this.lightSphere.properties.gizmoRoot=this;this.properties.isGizmo=!0;this.target=new THREE.Vector3;this.lookAt(this.target)};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.HemisphereLightHelper.prototype.update=function(){var a=THREE.Math.clamp(this.light.intensity,0,1);this.color.copy(this.light.color);this.color.multiplyScalar(a);this.groundColor.copy(this.light.groundColor);this.groundColor.multiplyScalar(a);this.lightSphere.material.materials[0].color.copy(this.color);this.lightSphere.material.materials[1].color.copy(this.groundColor);this.lightArrow.setColor(this.color.getHex());this.lightArrowGround.setColor(this.groundColor.getHex());this.lookAt(this.target)};THREE.PointLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.position=a.position;var c=THREE.Math.clamp(a.intensity,0,1);this.color=a.color.clone();this.color.multiplyScalar(c);var d=this.color.getHex(),c=new THREE.SphereGeometry(b,16,8),e=new THREE.AsteriskGeometry(1.25*b,2.25*b),f=new THREE.IcosahedronGeometry(1,2),g=new THREE.MeshBasicMaterial({color:d,fog:!1}),h=new THREE.LineBasicMaterial({color:d,fog:!1}),d=new THREE.MeshBasicMaterial({color:d,fog:!1,wireframe:!0,opacity:0.1,
+transparent:!0});this.lightSphere=new THREE.Mesh(c,g);this.lightRays=new THREE.Line(e,h,THREE.LinePieces);this.lightDistance=new THREE.Mesh(f,d);c=a.distance;0===c?this.lightDistance.visible=!1:this.lightDistance.scale.set(c,c,c);this.add(this.lightSphere);this.add(this.lightRays);this.add(this.lightDistance);this.lightSphere.properties.isGizmo=!0;this.lightSphere.properties.gizmoSubject=a;this.lightSphere.properties.gizmoRoot=this;this.properties.isGizmo=!0};THREE.PointLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.PointLightHelper.prototype.update=function(){var a=THREE.Math.clamp(this.light.intensity,0,1);this.color.copy(this.light.color);this.color.multiplyScalar(a);this.lightSphere.material.color.copy(this.color);this.lightRays.material.color.copy(this.color);this.lightDistance.material.color.copy(this.color);a=this.light.distance;0===a?this.lightDistance.visible=!1:(this.lightDistance.visible=!0,this.lightDistance.scale.set(a,a,a))};THREE.SpotLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.position=a.position;this.direction=new THREE.Vector3;this.direction.subVectors(a.target.position,a.position);var c=THREE.Math.clamp(a.intensity,0,1);this.color=a.color.clone();this.color.multiplyScalar(c);var c=this.color.getHex(),d=new THREE.SphereGeometry(b,16,8),e=new THREE.AsteriskGeometry(1.25*b,2.25*b),f=new THREE.CylinderGeometry(1E-4,1,1,8,1,!0),g=new THREE.Matrix4;g.rotateX(-Math.PI/2);g.translate(new THREE.Vector3(0,
+-0.5,0));f.applyMatrix(g);var h=new THREE.MeshBasicMaterial({color:c,fog:!1}),g=new THREE.LineBasicMaterial({color:c,fog:!1}),i=new THREE.MeshBasicMaterial({color:c,fog:!1,wireframe:!0,opacity:0.3,transparent:!0});this.lightSphere=new THREE.Mesh(d,h);this.lightCone=new THREE.Mesh(f,i);d=a.distance?a.distance:1E4;f=2*d*Math.tan(0.5*a.angle);this.lightCone.scale.set(f,f,d);this.lightRays=new THREE.Line(e,g,THREE.LinePieces);this.gyroscope=new THREE.Gyroscope;this.gyroscope.add(this.lightSphere);this.gyroscope.add(this.lightRays);
+this.add(this.gyroscope);this.add(this.lightCone);this.lookAt(a.target.position);this.lightSphere.properties.isGizmo=!0;this.lightSphere.properties.gizmoSubject=a;this.lightSphere.properties.gizmoRoot=this;this.targetSphere=null;void 0!==a.target.properties.targetInverse&&(e=new THREE.SphereGeometry(b,8,4),g=new THREE.MeshBasicMaterial({color:c,wireframe:!0,fog:!1}),this.targetSphere=new THREE.Mesh(e,g),this.targetSphere.position=a.target.position,this.targetSphere.properties.isGizmo=!0,this.targetSphere.properties.gizmoSubject=
+a.target,this.targetSphere.properties.gizmoRoot=this.targetSphere,c=new THREE.LineDashedMaterial({color:c,dashSize:4,gapSize:4,opacity:0.75,transparent:!0,fog:!1}),e=new THREE.Geometry,e.vertices.push(this.position.clone()),e.vertices.push(this.targetSphere.position.clone()),e.computeLineDistances(),this.targetLine=new THREE.Line(e,c),this.targetLine.properties.isGizmo=!0);this.properties.isGizmo=!0};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.SpotLightHelper.prototype.update=function(){this.direction.subVectors(this.light.target.position,this.light.position);this.lookAt(this.light.target.position);var a=this.light.distance?this.light.distance:1E4,b=2*a*Math.tan(0.5*this.light.angle);this.lightCone.scale.set(b,b,a);a=THREE.Math.clamp(this.light.intensity,0,1);this.color.copy(this.light.color);this.color.multiplyScalar(a);this.lightSphere.material.color.copy(this.color);this.lightRays.material.color.copy(this.color);this.lightCone.material.color.copy(this.color);
+null!==this.targetSphere&&(this.targetSphere.material.color.copy(this.color),this.targetLine.material.color.copy(this.color),this.targetLine.geometry.vertices[0].copy(this.light.position),this.targetLine.geometry.vertices[1].copy(this.light.target.position),this.targetLine.geometry.computeLineDistances(),this.targetLine.geometry.verticesNeedUpdate=!0)};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.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,g=e.morphTargets.length;f<g;f++){var h=e.morphTargets[f].name.match(b);if(h&&1<h.length){var i=h[1];d[i]||(d[i]={start:Infinity,end:-Infinity});h=d[i];f<h.start&&(h.start=f);f>h.end&&(h.end=f);c||(c=i)}}for(i in d)h=d[i],this.createAnimation(i,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),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}}};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,g,h,i,k,l,m,n,s;this.init=function(r){b=r.context;c=r;d=r.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);r=0;e[r++]=-1;e[r++]=-1;
+e[r++]=0;e[r++]=0;e[r++]=1;e[r++]=-1;e[r++]=1;e[r++]=0;e[r++]=1;e[r++]=1;e[r++]=1;e[r++]=1;e[r++]=-1;e[r++]=1;e[r++]=0;e[r++]=1;r=0;f[r++]=0;f[r++]=1;f[r++]=2;f[r++]=0;f[r++]=2;f[r++]=3;g=b.createBuffer();h=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,g);b.bufferData(b.ARRAY_BUFFER,e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,h);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)?(l=!1,m=a(THREE.ShaderFlares.lensFlare,d)):(l=!0,m=a(THREE.ShaderFlares.lensFlareVertexTexture,d));n={};s={};n.vertex=b.getAttribLocation(m,"position");n.uv=b.getAttribLocation(m,"uv");s.renderType=b.getUniformLocation(m,"renderType");s.map=b.getUniformLocation(m,"map");s.occlusionMap=b.getUniformLocation(m,"occlusionMap");s.opacity=
+b.getUniformLocation(m,"opacity");s.color=b.getUniformLocation(m,"color");s.scale=b.getUniformLocation(m,"scale");s.rotation=b.getUniformLocation(m,"rotation");s.screenPosition=b.getUniformLocation(m,"screenPosition")};this.render=function(a,d,e,f){var a=a.__webglFlares,v=a.length;if(v){var z=new THREE.Vector3,t=f/e,A=0.5*e,I=0.5*f,C=16/f,x=new THREE.Vector2(C*t,C),G=new THREE.Vector3(1,1,0),J=new THREE.Vector2(1,1),E=s,C=n;b.useProgram(m);b.enableVertexAttribArray(n.vertex);b.enableVertexAttribArray(n.uv);
+b.uniform1i(E.occlusionMap,0);b.uniform1i(E.map,1);b.bindBuffer(b.ARRAY_BUFFER,g);b.vertexAttribPointer(C.vertex,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(C.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,h);b.disable(b.CULL_FACE);b.depthMask(!1);var H,B,W,F,K;for(H=0;H<v;H++)if(C=16/f,x.set(C*t,C),F=a[H],z.set(F.matrixWorld.elements[12],F.matrixWorld.elements[13],F.matrixWorld.elements[14]),z.applyMatrix4(d.matrixWorldInverse),z.applyProjection(d.projectionMatrix),G.copy(z),J.x=G.x*A+A,
+J.y=G.y*I+I,l||0<J.x&&J.x<e&&0<J.y&&J.y<f){b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,i);b.copyTexImage2D(b.TEXTURE_2D,0,b.RGB,J.x-8,J.y-8,16,16,0);b.uniform1i(E.renderType,0);b.uniform2f(E.scale,x.x,x.y);b.uniform3f(E.screenPosition,G.x,G.y,G.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,J.x-8,J.y-8,16,16,0);b.uniform1i(E.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);F.positionScreen.copy(G);F.customUpdateCallback?F.customUpdateCallback(F):F.updateLensFlares();b.uniform1i(E.renderType,2);b.enable(b.BLEND);B=0;for(W=F.lensFlares.length;B<W;B++)K=F.lensFlares[B],0.001<K.opacity&&0.001<K.scale&&(G.x=K.x,G.y=K.y,G.z=K.z,C=K.size*K.scale/f,x.x=C*t,x.y=C,b.uniform3f(E.screenPosition,G.x,G.y,G.z),b.uniform2f(E.scale,x.x,x.y),b.uniform1f(E.rotation,K.rotation),b.uniform1f(E.opacity,
+K.opacity),b.uniform3f(E.color,K.color.r,K.color.g,K.color.b),c.setBlending(K.blending,K.blendEquation,K.blendSrc,K.blendDst),c.setTexture(K.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,g=new THREE.Frustum,h=new THREE.Matrix4,i=new THREE.Vector3,k=new THREE.Vector3,l=new THREE.Vector3;this.init=function(g){a=g.context;b=g;var g=THREE.ShaderLib.depthRGBA,i=THREE.UniformsUtils.clone(g.uniforms);c=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:i});d=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:i,morphTargets:!0});e=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,
+vertexShader:g.vertexShader,uniforms:i,skinning:!0});f=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:i,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(m,n){var s,r,p,q,y,v,z,t,A,I=[];q=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(r=m.__lights.length;s<r;s++)if(p=m.__lights[s],p.castShadow)if(p instanceof THREE.DirectionalLight&&p.shadowCascade)for(y=0;y<p.shadowCascadeCount;y++){var C;if(p.shadowCascadeArray[y])C=p.shadowCascadeArray[y];else{A=p;z=y;C=new THREE.DirectionalLight;C.isVirtual=!0;C.onlyShadow=!0;C.castShadow=!0;C.shadowCameraNear=A.shadowCameraNear;C.shadowCameraFar=A.shadowCameraFar;C.shadowCameraLeft=A.shadowCameraLeft;C.shadowCameraRight=A.shadowCameraRight;
+C.shadowCameraBottom=A.shadowCameraBottom;C.shadowCameraTop=A.shadowCameraTop;C.shadowCameraVisible=A.shadowCameraVisible;C.shadowDarkness=A.shadowDarkness;C.shadowBias=A.shadowCascadeBias[z];C.shadowMapWidth=A.shadowCascadeWidth[z];C.shadowMapHeight=A.shadowCascadeHeight[z];C.pointsWorld=[];C.pointsFrustum=[];t=C.pointsWorld;v=C.pointsFrustum;for(var x=0;8>x;x++)t[x]=new THREE.Vector3,v[x]=new THREE.Vector3;t=A.shadowCascadeNearZ[z];A=A.shadowCascadeFarZ[z];v[0].set(-1,-1,t);v[1].set(1,-1,t);v[2].set(-1,
+1,t);v[3].set(1,1,t);v[4].set(-1,-1,A);v[5].set(1,-1,A);v[6].set(-1,1,A);v[7].set(1,1,A);C.originalCamera=n;v=new THREE.Gyroscope;v.position=p.shadowCascadeOffset;v.add(C);v.add(C.target);n.add(v);p.shadowCascadeArray[y]=C;console.log("Created virtualLight",C)}z=p;t=y;A=z.shadowCascadeArray[t];A.position.copy(z.position);A.target.position.copy(z.target.position);A.lookAt(A.target);A.shadowCameraVisible=z.shadowCameraVisible;A.shadowDarkness=z.shadowDarkness;A.shadowBias=z.shadowCascadeBias[t];v=z.shadowCascadeNearZ[t];
+z=z.shadowCascadeFarZ[t];A=A.pointsFrustum;A[0].z=v;A[1].z=v;A[2].z=v;A[3].z=v;A[4].z=z;A[5].z=z;A[6].z=z;A[7].z=z;I[q]=C;q++}else I[q]=p,q++;s=0;for(r=I.length;s<r;s++){p=I[s];p.shadowMap||(y=THREE.LinearFilter,b.shadowMapType===THREE.PCFSoftShadowMap&&(y=THREE.NearestFilter),p.shadowMap=new THREE.WebGLRenderTarget(p.shadowMapWidth,p.shadowMapHeight,{minFilter:y,magFilter:y,format:THREE.RGBAFormat}),p.shadowMapSize=new THREE.Vector2(p.shadowMapWidth,p.shadowMapHeight),p.shadowMatrix=new THREE.Matrix4);
+if(!p.shadowCamera){if(p instanceof THREE.SpotLight)p.shadowCamera=new THREE.PerspectiveCamera(p.shadowCameraFov,p.shadowMapWidth/p.shadowMapHeight,p.shadowCameraNear,p.shadowCameraFar);else if(p instanceof THREE.DirectionalLight)p.shadowCamera=new THREE.OrthographicCamera(p.shadowCameraLeft,p.shadowCameraRight,p.shadowCameraTop,p.shadowCameraBottom,p.shadowCameraNear,p.shadowCameraFar);else{console.error("Unsupported light type for shadow");continue}m.add(p.shadowCamera);b.autoUpdateScene&&m.updateMatrixWorld()}p.shadowCameraVisible&&
+!p.cameraHelper&&(p.cameraHelper=new THREE.CameraHelper(p.shadowCamera),p.shadowCamera.add(p.cameraHelper));if(p.isVirtual&&C.originalCamera==n){y=n;q=p.shadowCamera;v=p.pointsFrustum;A=p.pointsWorld;i.set(Infinity,Infinity,Infinity);k.set(-Infinity,-Infinity,-Infinity);for(z=0;8>z;z++)t=A[z],t.copy(v[z]),THREE.ShadowMapPlugin.__projector.unprojectVector(t,y),t.applyMatrix4(q.matrixWorldInverse),t.x<i.x&&(i.x=t.x),t.x>k.x&&(k.x=t.x),t.y<i.y&&(i.y=t.y),t.y>k.y&&(k.y=t.y),t.z<i.z&&(i.z=t.z),t.z>k.z&&
+(k.z=t.z);q.left=i.x;q.right=k.x;q.top=k.y;q.bottom=i.y;q.updateProjectionMatrix()}q=p.shadowMap;v=p.shadowMatrix;y=p.shadowCamera;y.position.getPositionFromMatrix(p.matrixWorld);l.getPositionFromMatrix(p.target.matrixWorld);y.lookAt(l);y.updateMatrixWorld();y.matrixWorldInverse.getInverse(y.matrixWorld);p.cameraHelper&&(p.cameraHelper.visible=p.shadowCameraVisible);p.shadowCameraVisible&&p.cameraHelper.update();v.set(0.5,0,0,0.5,0,0.5,0,0.5,0,0,0.5,0.5,0,0,0,1);v.multiply(y.projectionMatrix);v.multiply(y.matrixWorldInverse);
+h.multiplyMatrices(y.projectionMatrix,y.matrixWorldInverse);g.setFromMatrix(h);b.setRenderTarget(q);b.clear();A=m.__webglObjects;p=0;for(q=A.length;p<q;p++)if(z=A[p],v=z.object,z.render=!1,v.visible&&v.castShadow&&(!(v instanceof THREE.Mesh||v instanceof THREE.ParticleSystem)||!v.frustumCulled||g.intersectsObject(v)))v._modelViewMatrix.multiplyMatrices(y.matrixWorldInverse,v.matrixWorld),z.render=!0;p=0;for(q=A.length;p<q;p++)z=A[p],z.render&&(v=z.object,z=z.buffer,x=v.material instanceof THREE.MeshFaceMaterial?
+v.material.materials[0]:v.material,t=0<v.geometry.morphTargets.length&&x.morphTargets,x=v instanceof THREE.SkinnedMesh&&x.skinning,t=v.customDepthMaterial?v.customDepthMaterial:x?t?f:e:t?d:c,z instanceof THREE.BufferGeometry?b.renderBufferDirect(y,m.__lights,null,t,z,v):b.renderBuffer(y,m.__lights,null,t,z,v));A=m.__webglObjectsImmediate;p=0;for(q=A.length;p<q;p++)z=A[p],v=z.object,v.visible&&v.castShadow&&(v._modelViewMatrix.multiplyMatrices(y.matrixWorldInverse,v.matrixWorld),b.renderImmediateObject(y,
+m.__lights,null,c,v))}s=b.getClearColor();r=b.getClearAlpha();a.clearColor(s.r,s.g,s.b,r);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,g,h,i,k,l;this.init=function(a){b=a.context;c=a;d=a.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);a=0;e[a++]=-1;e[a++]=-1;e[a++]=0;e[a++]=0;e[a++]=1;e[a++]=-1;e[a++]=1;e[a++]=0;e[a++]=1;e[a++]=1;e[a++]=1;e[a++]=1;e[a++]=-1;e[a++]=1;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;g=b.createBuffer();h=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,g);b.bufferData(b.ARRAY_BUFFER,
+e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,h);b.bufferData(b.ELEMENT_ARRAY_BUFFER,f,b.STATIC_DRAW);var a=THREE.ShaderSprite.sprite,n=b.createProgram(),s=b.createShader(b.FRAGMENT_SHADER),r=b.createShader(b.VERTEX_SHADER),p="precision "+d+" float;\n";b.shaderSource(s,p+a.fragmentShader);b.shaderSource(r,p+a.vertexShader);b.compileShader(s);b.compileShader(r);b.attachShader(n,s);b.attachShader(n,r);b.linkProgram(n);i=n;k={};l={};k.position=b.getAttribLocation(i,"position");k.uv=b.getAttribLocation(i,
+"uv");l.uvOffset=b.getUniformLocation(i,"uvOffset");l.uvScale=b.getUniformLocation(i,"uvScale");l.rotation=b.getUniformLocation(i,"rotation");l.scale=b.getUniformLocation(i,"scale");l.alignment=b.getUniformLocation(i,"alignment");l.color=b.getUniformLocation(i,"color");l.map=b.getUniformLocation(i,"map");l.opacity=b.getUniformLocation(i,"opacity");l.useScreenCoordinates=b.getUniformLocation(i,"useScreenCoordinates");l.sizeAttenuation=b.getUniformLocation(i,"sizeAttenuation");l.screenPosition=b.getUniformLocation(i,
+"screenPosition");l.modelViewMatrix=b.getUniformLocation(i,"modelViewMatrix");l.projectionMatrix=b.getUniformLocation(i,"projectionMatrix");l.fogType=b.getUniformLocation(i,"fogType");l.fogDensity=b.getUniformLocation(i,"fogDensity");l.fogNear=b.getUniformLocation(i,"fogNear");l.fogFar=b.getUniformLocation(i,"fogFar");l.fogColor=b.getUniformLocation(i,"fogColor");l.alphaTest=b.getUniformLocation(i,"alphaTest")};this.render=function(d,e,f,r){var p=d.__webglSprites,q=p.length;if(q){var y=k,v=l,z=r/
+f,f=0.5*f,t=0.5*r;b.useProgram(i);b.enableVertexAttribArray(y.position);b.enableVertexAttribArray(y.uv);b.disable(b.CULL_FACE);b.enable(b.BLEND);b.bindBuffer(b.ARRAY_BUFFER,g);b.vertexAttribPointer(y.position,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(y.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,h);b.uniformMatrix4fv(v.projectionMatrix,!1,e.projectionMatrix.elements);b.activeTexture(b.TEXTURE0);b.uniform1i(v.map,0);var A=y=0,I=d.fog;I?(b.uniform3f(v.fogColor,I.color.r,I.color.g,I.color.b),
+I instanceof THREE.Fog?(b.uniform1f(v.fogNear,I.near),b.uniform1f(v.fogFar,I.far),b.uniform1i(v.fogType,1),A=y=1):I instanceof THREE.FogExp2&&(b.uniform1f(v.fogDensity,I.density),b.uniform1i(v.fogType,2),A=y=2)):(b.uniform1i(v.fogType,0),A=y=0);for(var C,x,G=[],I=0;I<q;I++)C=p[I],x=C.material,C.visible&&0!==x.opacity&&(x.useScreenCoordinates?C.z=-C.position.z:(C._modelViewMatrix.multiplyMatrices(e.matrixWorldInverse,C.matrixWorld),C.z=-C._modelViewMatrix.elements[14]));p.sort(a);for(I=0;I<q;I++)C=
+p[I],x=C.material,C.visible&&0!==x.opacity&&(x.map&&x.map.image&&x.map.image.width)&&(b.uniform1f(v.alphaTest,x.alphaTest),!0===x.useScreenCoordinates?(b.uniform1i(v.useScreenCoordinates,1),b.uniform3f(v.screenPosition,(C.position.x*c.devicePixelRatio-f)/f,(t-C.position.y*c.devicePixelRatio)/t,Math.max(0,Math.min(1,C.position.z))),G[0]=c.devicePixelRatio,G[1]=c.devicePixelRatio):(b.uniform1i(v.useScreenCoordinates,0),b.uniform1i(v.sizeAttenuation,x.sizeAttenuation?1:0),b.uniformMatrix4fv(v.modelViewMatrix,
+!1,C._modelViewMatrix.elements),G[0]=1,G[1]=1),e=d.fog&&x.fog?A:0,y!==e&&(b.uniform1i(v.fogType,e),y=e),e=1/(x.scaleByViewport?r:1),G[0]*=e*z*C.scale.x,G[1]*=e*C.scale.y,b.uniform2f(v.uvScale,x.uvScale.x,x.uvScale.y),b.uniform2f(v.uvOffset,x.uvOffset.x,x.uvOffset.y),b.uniform2f(v.alignment,x.alignment.x,x.alignment.y),b.uniform1f(v.opacity,x.opacity),b.uniform3f(v.color,x.color.r,x.color.g,x.color.b),b.uniform1f(v.rotation,C.rotation),b.uniform2fv(v.scale,G),c.setBlending(x.blending,x.blendEquation,
+x.blendSrc,x.blendDst),c.setDepthTest(x.depthTest),c.setDepthWrite(x.depthWrite),c.setTexture(x.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,g=new THREE.Frustum,h=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 l,m,n,s,r,p;a.clearColor(1,1,1,1);a.disable(a.BLEND);b.setDepthTest(!0);b.autoUpdateScene&&i.updateMatrixWorld();k.matrixWorldInverse.getInverse(k.matrixWorld);h.multiplyMatrices(k.projectionMatrix,
+k.matrixWorldInverse);g.setFromMatrix(h);b.setRenderTarget(this.renderTarget);b.clear();p=i.__webglObjects;l=0;for(m=p.length;l<m;l++)if(n=p[l],r=n.object,n.render=!1,r.visible&&(!(r instanceof THREE.Mesh||r instanceof THREE.ParticleSystem)||!r.frustumCulled||g.intersectsObject(r)))r._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,r.matrixWorld),n.render=!0;var q;l=0;for(m=p.length;l<m;l++)if(n=p[l],n.render&&(r=n.object,n=n.buffer,!(r instanceof THREE.ParticleSystem)||r.customDepthMaterial))(q=
+r.material instanceof THREE.MeshFaceMaterial?r.material.materials[0]:r.material)&&b.setMaterialFaces(r.material),s=0<r.geometry.morphTargets.length&&q.morphTargets,q=r instanceof THREE.SkinnedMesh&&q.skinning,s=r.customDepthMaterial?r.customDepthMaterial:q?s?f:e:s?d:c,n instanceof THREE.BufferGeometry?b.renderBufferDirect(k,i.__lights,null,s,n,r):b.renderBuffer(k,i.__lights,null,s,n,r);p=i.__webglObjectsImmediate;l=0;for(m=p.length;l<m;l++)n=p[l],r=n.object,r.visible&&(r._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,
+r.matrixWorld),b.renderImmediateObject(k,i.__lights,null,c,r));l=b.getClearColor();m=b.getClearAlpha();a.clearColor(l.r,l.g,l.b,m);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 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.1 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.1 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.5 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.1, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.1, 0.5 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility = (       visibility.r / 9.0 ) *\n( 1.0 - visibility.g / 9.0 ) *\n(       visibility.b / 9.0 ) *\n( 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 +\ntexture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a +\ntexture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a +\ntexture2D( 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;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = position + alignment;\nvec2 rotatedPosition;\nrotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;\nrotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;\nvec4 finalPosition;\nif( useScreenCoordinates != 0 ) {\nfinalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );\n} else {\nfinalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );\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}"}};
diff --git a/DUBREUIL/lib/tween.min.js b/DUBREUIL/lib/tween.min.js
new file mode 100644
index 0000000..90a4463
--- /dev/null
+++ b/DUBREUIL/lib/tween.min.js
@@ -0,0 +1,13 @@
+// tween.js - http://github.com/sole/tween.js
+'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b<d;)a[b].update(c)?b++:(a.splice(b,1),d--);return!0}}}();
+TWEEN.Tween=function(a){var c={},b={},d=1E3,e=0,f=null,h=TWEEN.Easing.Linear.None,r=TWEEN.Interpolation.Linear,k=[],l=null,m=!1,n=null,p=null;this.to=function(a,c){null!==c&&(d=c);b=a;return this};this.start=function(d){TWEEN.add(this);m=!1;f=void 0!==d?d:Date.now();f+=e;for(var g in b)if(null!==a[g]){if(b[g]instanceof Array){if(0===b[g].length)continue;b[g]=[a[g]].concat(b[g])}c[g]=a[g]}return this};this.stop=function(){TWEEN.remove(this);return this};this.delay=function(a){e=a;return this};this.easing=
+function(a){h=a;return this};this.interpolation=function(a){r=a;return this};this.chain=function(){k=arguments;return this};this.onStart=function(a){l=a;return this};this.onUpdate=function(a){n=a;return this};this.onComplete=function(a){p=a;return this};this.update=function(e){if(e<f)return!0;!1===m&&(null!==l&&l.call(a),m=!0);var g=(e-f)/d,g=1<g?1:g,i=h(g),j;for(j in c){var s=c[j],q=b[j];a[j]=q instanceof Array?r(q,i):s+(q-s)*i}null!==n&&n.call(a,i);if(1==g){null!==p&&p.call(a);g=0;for(i=k.length;g<
+i;g++)k[g].start(e);return!1}return!0}};
+TWEEN.Easing={Linear:{None:function(a){return a}},Quadratic:{In:function(a){return a*a},Out:function(a){return a*(2-a)},InOut:function(a){return 1>(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a*
+a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1-
+Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)*
+2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1-
+TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}};
+TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1<c?f(a[b],a[b-1],b-d):f(a[e],a[e+1>b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e-
+1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1<c?a[b]-(f(a[b],a[b],a[b-1],a[b-1],d-b)-a[b]):f(a[e?e-1:0],a[e],a[b<e+1?b:e+1],a[b<e+2?b:e+2],d-e)},Utils:{Linear:function(a,c,b){return(c-a)*b+a},Bernstein:function(a,c){var b=TWEEN.Interpolation.Utils.Factorial;return b(a)/b(c)/b(a-c)},Factorial:function(){var a=[1];return function(c){var b=1,d;if(a[c])return a[c];for(d=c;1<d;d--)b*=d;return a[c]=b}}(),CatmullRom:function(a,c,b,d,e){var a=0.5*(b-a),d=0.5*(d-c),f=
+e*e;return(2*c-2*b+a+d)*e*f+(-3*c+3*b-2*a-d)*f+a*e+c}}};
diff --git a/DUBREUIL/lib/uclass_BeveledBlockGeometry.js b/DUBREUIL/lib/uclass_BeveledBlockGeometry.js
new file mode 100644
index 0000000..ad40d31
--- /dev/null
+++ b/DUBREUIL/lib/uclass_BeveledBlockGeometry.js
@@ -0,0 +1,264 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author Eric Haines / http://erichaines.com/
+ *
+ * Make a block with beveled edges, to catch highlights.
+ *
+ * TODO: bug when bevel >= depth/2 (or width/2, or height/2)
+ */
+/*global THREE */
+
+THREE.BeveledBlockGeometry = function ( width, height, depth, bevel, widthSegments, heightSegments, depthSegments ) {
+	"use strict";
+
+	THREE.Geometry.call( this );
+
+	var scope = this;
+
+	this.width = width;
+	this.height = height;
+	this.depth = depth;
+	
+	this.bevel = bevel || 0;
+
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+	this.depthSegments = depthSegments || 1;
+
+	var width_half = this.width / 2;
+	var height_half = this.height / 2;
+	var depth_half = this.depth / 2;
+	
+	var adjWidth = this.width - this.bevel*2;
+	var adjHeight = this.height - this.bevel*2;
+	var adjDepth = this.depth - this.bevel*2;
+
+	if ( adjDepth > 0 && adjHeight > 0 ) {
+		buildPlane( 'z', 'y', - 1, - 1, adjDepth, adjHeight, width_half, 0 ); // px
+		buildPlane( 'z', 'y',   1, - 1, adjDepth, adjHeight, - width_half, 0 ); // nx
+	}
+	if ( adjWidth > 0 && adjDepth > 0 ) {
+		buildPlane( 'x', 'z',   1,   1, adjWidth, adjDepth, height_half, 0 ); // py
+		buildPlane( 'x', 'z',   1, - 1, adjWidth, adjDepth, - height_half, 0 ); // ny
+	}
+	if ( adjWidth > 0 && adjHeight > 0 ) {
+		buildPlane( 'x', 'y',   1, - 1, adjWidth, adjHeight, depth_half, 0 ); // pz
+		buildPlane( 'x', 'y', - 1, - 1, adjWidth, adjHeight, - depth_half, 0 ); // nz
+	}
+	
+	// bevels
+	if ( this.bevel > 0 )
+	{
+		if ( adjWidth < 0 ) { adjWidth = 0; }
+		if ( adjHeight < 0 ) { adjHeight = 0; }
+		if ( adjDepth < 0 ) { adjDepth = 0; }
+		var adjHalfWidth = adjWidth / 2;
+		var adjHalfHeight = adjHeight / 2;
+		var adjHalfDepth = adjDepth / 2;
+		
+		// 12 edges
+		// -Y face neighbors
+		buildBevelSide( 'x', -adjHalfWidth, -height_half, -adjHalfDepth,  adjHalfWidth, -adjHalfHeight, -depth_half, 0, -1, 0, 0, 0, -1 ); 
+		buildBevelSide( 'x',  adjHalfWidth, -height_half,  adjHalfDepth, -adjHalfWidth, -adjHalfHeight,  depth_half, 0, -1, 0, 0, 0, 1  ); 
+		buildBevelSide( 'z', -adjHalfWidth, -height_half,  adjHalfDepth,  -width_half, -adjHalfHeight, -adjHalfDepth, 0, -1, 0, -1, 0, 0  ); 
+		buildBevelSide( 'z',  adjHalfWidth, -height_half, -adjHalfDepth,   width_half, -adjHalfHeight,  adjHalfDepth, 0, -1, 0, 1, 0, 0  ); 
+		// +Y face neighbors
+		buildBevelSide( 'x',  adjHalfWidth, height_half, -adjHalfDepth, -adjHalfWidth, adjHalfHeight, -depth_half, 0, 1, 0, 0, 0, -1 ); 
+		buildBevelSide( 'x', -adjHalfWidth, height_half,  adjHalfDepth,  adjHalfWidth, adjHalfHeight,  depth_half, 0, 1, 0, 0, 0, 1  ); 
+		buildBevelSide( 'z', -adjHalfWidth, height_half, -adjHalfDepth,  -width_half, adjHalfHeight, adjHalfDepth, 0, 1, 0, -1, 0, 0  ); 
+		buildBevelSide( 'z',  adjHalfWidth, height_half,  adjHalfDepth,   width_half, adjHalfHeight,  -adjHalfDepth, 0, 1, 0, 1, 0, 0  );
+		// Y side face neighbors
+		buildBevelSide( 'y',  -width_half,  adjHalfHeight, -adjHalfDepth, -adjHalfWidth, -adjHalfHeight, -depth_half, -1, 0, 0, 0, 0, -1 ); 
+		buildBevelSide( 'y',  -width_half, -adjHalfHeight,  adjHalfDepth, -adjHalfWidth,  adjHalfHeight,  depth_half, -1, 0, 0, 0, 0, 1 ); 
+		buildBevelSide( 'y',   width_half, -adjHalfHeight, -adjHalfDepth,  adjHalfWidth,  adjHalfHeight, -depth_half, 1, 0, 0, 0, 0, -1 ); 
+		buildBevelSide( 'y',   width_half,  adjHalfHeight,  adjHalfDepth,  adjHalfWidth, -adjHalfHeight,  depth_half, 1, 0, 0, 0, 0, 1 );
+
+		// 8 corners
+		buildBevelCorners( adjHalfWidth, width_half, adjHalfHeight, height_half, adjHalfDepth, depth_half );
+	}
+	
+	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+
+		var w, ix, iy,
+		gridX = scope.widthSegments,
+		gridY = scope.heightSegments,
+		width_half = width / 2,
+		height_half = height / 2,
+		offset = scope.vertices.length;
+
+		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+
+			w = 'z';
+
+		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+
+			w = 'y';
+			gridY = scope.depthSegments;
+
+		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+
+			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 ( ix = 0; ix < gridX1; 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;
+
+				scope.vertices.push( vector );
+
+			}
+
+		}
+
+		for ( iy = 0; iy < gridY; iy++ ) {
+
+			for ( ix = 0; ix < gridX; ix++ ) {
+
+				var a = ix + gridX1 * iy;
+				var b = ix + gridX1 * ( iy + 1 );
+				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+				var d = ( ix + 1 ) + gridX1 * iy;
+
+				var face = new THREE.Face4( a + offset, b + offset, c + offset, d + offset );
+				// not needed? We don't compute others: face.normal.copy( normal );
+				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
+				face.materialIndex = materialIndex;
+
+				scope.faces.push( face );
+				scope.faceVertexUvs[ 0 ].push( [
+							new THREE.Vector2( ix / gridX, 1 - iy / gridY ),
+							new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ),
+							new THREE.Vector2( ( ix + 1 ) / gridX, 1- ( iy + 1 ) / gridY ),
+							new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY )
+						] );
+			}
+		}
+	}
+	
+	function buildBevelSide( longSide, xlo, ylo, zlo, xhi, yhi, zhi, xn1, yn1, zn1, xn2, yn2, zn2 ) {
+		
+		var vector;
+		var offset = scope.vertices.length;
+		
+		vector = new THREE.Vector3(xlo,ylo,zlo);
+		scope.vertices.push( vector );
+		if ( longSide === 'x' ) {
+			vector = new THREE.Vector3(xlo,yhi,zhi);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xhi,yhi,zhi);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xhi,ylo,zlo);
+			scope.vertices.push( vector );
+		}
+		else if ( longSide === 'y' ) {
+			vector = new THREE.Vector3(xhi,ylo,zhi);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xhi,yhi,zhi);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xlo,yhi,zlo);
+			scope.vertices.push( vector );
+		}
+		else {
+			vector = new THREE.Vector3(xhi,yhi,zlo);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xhi,yhi,zhi);
+			scope.vertices.push( vector );
+			vector = new THREE.Vector3(xlo,ylo,zhi);
+			scope.vertices.push( vector );
+		}
+
+		var face = new THREE.Face4( offset, offset+1, offset+2, offset+3 );
+		var vnorm1 = new THREE.Vector3( xn1, yn1, zn1 );
+		var vnorm2 = new THREE.Vector3( xn2, yn2, zn2 );
+		face.vertexNormals.push( vnorm1.clone(), vnorm2.clone(), vnorm2.clone(), vnorm1.clone() );
+		face.materialIndex = 0;
+
+		scope.faces.push( face );
+		// these are certainly wrong
+		scope.faceVertexUvs[ 0 ].push( [
+					new THREE.Vector2( 0, 0 ),
+					new THREE.Vector2( 0, 1 ),
+					new THREE.Vector2( 1, 1 ),
+					new THREE.Vector2( 1, 0 )
+				] );
+	}
+
+	function buildBevelCorners( xah, xh, yah, yh, zah, zh ) {
+		
+		var vector;
+		var offset = scope.vertices.length;
+		
+		var count = 0;
+		for ( var i = 0; i < 2; i++ ) {
+			var xsign = i ? 1 : -1;
+			for ( var j = 0; j < 2; j++ ) {
+				var ysign = j ? 1 : -1;
+				for ( var k = 0; k < 2; k++ ) {
+					var zsign = k ? 1 : -1;
+					
+					// first vertex
+					vector = new THREE.Vector3(xsign*xh,ysign*yah,zsign*zah);
+					scope.vertices.push( vector );
+					var vnorm1 = new THREE.Vector3(xsign,0,0);
+					
+					vector = new THREE.Vector3(xsign*xah,ysign*yh,zsign*zah);
+					scope.vertices.push( vector );
+					var vnorm2 = new THREE.Vector3(0,ysign,0);
+					
+					vector = new THREE.Vector3(xsign*xah,ysign*yah,zsign*zh);
+					scope.vertices.push( vector );
+					var vnorm3 = new THREE.Vector3(0,0,zsign);
+					
+					var face;
+					face = new THREE.Face3( offset, offset+1, offset+2 );
+
+					if ( xsign * ysign * zsign === 1 )
+					{
+						face = new THREE.Face3( offset, offset+1, offset+2 );
+						face.vertexNormals.push( vnorm1, vnorm2, vnorm3 );
+					}
+					else
+					{
+						face = new THREE.Face3( offset+2, offset+1, offset );
+						face.vertexNormals.push( vnorm3, vnorm2, vnorm1 );
+					}
+					face.materialIndex = 0;
+
+					scope.faces.push( face );
+					// these are certainly wrong
+					scope.faceVertexUvs[ 0 ].push( [
+								new THREE.Vector2( 0, 0 ),
+								new THREE.Vector2( 0, 1 ),
+								new THREE.Vector2( 1, 1 )
+							] );
+
+					offset += 3;
+
+					count++;
+				}
+			}
+		}
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.mergeVertices();
+
+};
+
+THREE.BeveledBlockGeometry.prototype = Object.create( THREE.Geometry.prototype );
\ No newline at end of file
diff --git a/DUBREUIL/lib/uclass_TeacupGeometry.js b/DUBREUIL/lib/uclass_TeacupGeometry.js
new file mode 100644
index 0000000..878beb8
--- /dev/null
+++ b/DUBREUIL/lib/uclass_TeacupGeometry.js
@@ -0,0 +1,529 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author Eric Haines / http://erichaines.com/
+ *
+ * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
+ *
+ * Tessellate a teacup into triangular patches.
+ *
+ * See http://www.sjbaker.org/wiki/index.php?title=The_History_of_The_Teacup for
+ *	the history of the Teacup and teacup
+ *
+ * THREE.TeacupGeometry = function ( size, segments )
+ *
+ * defaults: size = 50, segments = 10
+ *
+ * size is a relative scale: I've scaled the Teacup to fit vertically between -1 and 1.
+ *	Think of it as a "radius".
+ * segments - number of line segments to subdivide each patch edge;
+ *	1 is possible but gives degenerates, so two is the real minimum.
+ *
+ * segments 'n' determines the number of objects output.
+ *  Total patches = 26*2*n*n
+ *
+ *  size_factor  # triangles
+ *       1			52
+ *       2         208
+ *       3         468
+ *       4         832
+ *       5        1300
+ *       6        1872
+ *
+ *		10        5200
+ *		20       20800
+ *		30       46800
+ *		40       83200
+ */
+/*global THREE */
+
+THREE.TeacupGeometry = function ( size, segments ) {
+	"use strict";
+
+	// 26 * 4 * 4 Bezier spline patches, note +1 start
+	// Data from ftp://ftp.funet.fi/pub/sci/graphics/packages/objects/teaset.tar.Z
+	var TeacupPatches = [
+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
+4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28,
+19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40,
+31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13,
+13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60,
+16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69,
+28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78,
+40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57,
+193,194,195,196,197,198,199,200,201,202,203,204,1,2,3,4,
+196,205,206,207,200,208,209,210,204,211,212,213,4,17,18,19,
+207,214,215,216,210,217,218,219,213,220,221,222,19,29,30,31,
+216,223,224,193,219,225,226,197,222,227,228,201,31,41,42,1,
+229,230,231,28,232,233,234,235,236,237,238,239,240,241,242,243,
+28,244,245,229,235,246,247,232,239,248,249,236,243,250,251,240,
+57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96,
+60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105,
+69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114,
+78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93,
+93,94,95,96,121,122,123,124,125,126,127,128,129,130,131,132,
+96,103,104,105,124,133,134,135,128,136,137,138,132,139,140,141,
+105,112,113,114,135,142,143,144,138,145,146,147,141,148,149,150,
+114,119,120,93,144,151,152,121,147,153,154,125,150,155,156,129,
+129,130,131,132,157,158,159,160,161,162,163,164,165,166,167,168,
+132,139,140,141,160,169,170,171,164,172,173,174,168,175,176,177,
+141,148,149,150,171,178,179,180,174,181,182,183,177,184,185,186,
+150,155,156,129,180,187,188,157,183,189,190,161,186,191,192,165
+	] ;
+
+	var TeacupVertices = [
+0.409091,0.772727,0.0,
+0.409091,0.772727,-0.229091,
+0.229091,0.772727,-0.409091,
+0.0,0.772727,-0.409091,
+0.409091,0.886364,0.0,
+0.409091,0.886364,-0.229091,
+0.229091,0.886364,-0.409091,
+0.0,0.886364,-0.409091,
+0.454545,0.886364,0.0,
+0.454545,0.886364,-0.254545,
+0.254545,0.886364,-0.454545,
+0.0,0.886364,-0.454545,
+0.454545,0.772727,0.0,
+0.454545,0.772727,-0.254545,
+0.254545,0.772727,-0.454545,
+0.0,0.772727,-0.454545,
+-0.229091,0.772727,-0.409091,
+-0.409091,0.772727,-0.229091,
+-0.409091,0.772727,0.0,
+-0.229091,0.886364,-0.409091,
+-0.409091,0.886364,-0.229091,
+-0.409091,0.886364,0.0,
+-0.254545,0.886364,-0.454545,
+-0.454545,0.886364,-0.254545,
+-0.454545,0.886364,0.0,
+-0.254545,0.772727,-0.454545,
+-0.454545,0.772727,-0.254545,
+-0.454545,0.772727,0.0,
+-0.409091,0.772727,0.229091,
+-0.229091,0.772727,0.409091,
+0.0,0.772727,0.409091,
+-0.409091,0.886364,0.229091,
+-0.229091,0.886364,0.409091,
+0.0,0.886364,0.409091,
+-0.454545,0.886364,0.254545,
+-0.254545,0.886364,0.454545,
+0.0,0.886364,0.454545,
+-0.454545,0.772727,0.254545,
+-0.254545,0.772727,0.454545,
+0.0,0.772727,0.454545,
+0.229091,0.772727,0.409091,
+0.409091,0.772727,0.229091,
+0.229091,0.886364,0.409091,
+0.409091,0.886364,0.229091,
+0.254545,0.886364,0.454545,
+0.454545,0.886364,0.254545,
+0.254545,0.772727,0.454545,
+0.454545,0.772727,0.254545,
+0.454545,0.545455,0.0,
+0.454545,0.545455,-0.254545,
+0.254545,0.545455,-0.454545,
+0.0,0.545455,-0.454545,
+0.454545,0.272727,0.0,
+0.454545,0.272727,-0.254545,
+0.254545,0.272727,-0.454545,
+0.0,0.272727,-0.454545,
+0.318182,0.0454545,0.0,
+0.318182,0.0454545,-0.178182,
+0.178182,0.0454545,-0.318182,
+0.0,0.0454545,-0.318182,
+-0.254545,0.545455,-0.454545,
+-0.454545,0.545455,-0.254545,
+-0.454545,0.545455,0.0,
+-0.254545,0.272727,-0.454545,
+-0.454545,0.272727,-0.254545,
+-0.454545,0.272727,0.0,
+-0.178182,0.0454545,-0.318182,
+-0.318182,0.0454545,-0.178182,
+-0.318182,0.0454545,0.0,
+-0.454545,0.545455,0.254545,
+-0.254545,0.545455,0.454545,
+0.0,0.545455,0.454545,
+-0.454545,0.272727,0.254545,
+-0.254545,0.272727,0.454545,
+0.0,0.272727,0.454545,
+-0.318182,0.0454545,0.178182,
+-0.178182,0.0454545,0.318182,
+0.0,0.0454545,0.318182,
+0.254545,0.545455,0.454545,
+0.454545,0.545455,0.254545,
+0.254545,0.272727,0.454545,
+0.454545,0.272727,0.254545,
+0.178182,0.0454545,0.318182,
+0.318182,0.0454545,0.178182,
+0.545455,0.0454545,0.0,
+0.545455,0.0454545,-0.305455,
+0.305455,0.0454545,-0.545455,
+0.0,0.0454545,-0.545455,
+0.727273,0.136364,0.0,
+0.727273,0.136364,-0.407273,
+0.407273,0.136364,-0.727273,
+0.0,0.136364,-0.727273,
+0.909091,0.136364,0.0,
+0.909091,0.136364,-0.509091,
+0.509091,0.136364,-0.909091,
+0.0,0.136364,-0.909091,
+-0.305455,0.0454545,-0.545455,
+-0.545455,0.0454545,-0.305455,
+-0.545455,0.0454545,0.0,
+-0.407273,0.136364,-0.727273,
+-0.727273,0.136364,-0.407273,
+-0.727273,0.136364,0.0,
+-0.509091,0.136364,-0.909091,
+-0.909091,0.136364,-0.509091,
+-0.909091,0.136364,0.0,
+-0.545455,0.0454545,0.305455,
+-0.305455,0.0454545,0.545455,
+0.0,0.0454545,0.545455,
+-0.727273,0.136364,0.407273,
+-0.407273,0.136364,0.727273,
+0.0,0.136364,0.727273,
+-0.909091,0.136364,0.509091,
+-0.509091,0.136364,0.909091,
+0.0,0.136364,0.909091,
+0.305455,0.0454545,0.545455,
+0.545455,0.0454545,0.305455,
+0.407273,0.136364,0.727273,
+0.727273,0.136364,0.407273,
+0.509091,0.136364,0.909091,
+0.909091,0.136364,0.509091,
+1.0,0.136364,0.0,
+1.0,0.136364,-0.56,
+0.56,0.136364,-1.0,
+0.0,0.136364,-1.0,
+1.0,0.0909091,0.0,
+1.0,0.0909091,-0.56,
+0.56,0.0909091,-1.0,
+0.0,0.0909091,-1.0,
+0.909091,0.0909091,0.0,
+0.909091,0.0909091,-0.509091,
+0.509091,0.0909091,-0.909091,
+0.0,0.0909091,-0.909091,
+-0.56,0.136364,-1.0,
+-1.0,0.136364,-0.56,
+-1.0,0.136364,0.0,
+-0.56,0.0909091,-1.0,
+-1.0,0.0909091,-0.56,
+-1.0,0.0909091,0.0,
+-0.509091,0.0909091,-0.909091,
+-0.909091,0.0909091,-0.509091,
+-0.909091,0.0909091,0.0,
+-1.0,0.136364,0.56,
+-0.56,0.136364,1.0,
+0.0,0.136364,1.0,
+-1.0,0.0909091,0.56,
+-0.56,0.0909091,1.0,
+0.0,0.0909091,1.0,
+-0.909091,0.0909091,0.509091,
+-0.509091,0.0909091,0.909091,
+0.0,0.0909091,0.909091,
+0.56,0.136364,1.0,
+1.0,0.136364,0.56,
+0.56,0.0909091,1.0,
+1.0,0.0909091,0.56,
+0.509091,0.0909091,0.909091,
+0.909091,0.0909091,0.509091,
+0.727273,0.0909091,0.0,
+0.727273,0.0909091,-0.407273,
+0.407273,0.0909091,-0.727273,
+0.0,0.0909091,-0.727273,
+0.545455,0.0,0.0,
+0.545455,0.0,-0.305455,
+0.305455,0.0,-0.545455,
+0.0,0.0,-0.545455,
+0.318182,0.0,0.0,
+0.318182,0.0,-0.178182,
+0.178182,0.0,-0.318182,
+0.0,0.0,-0.318182,
+-0.407273,0.0909091,-0.727273,
+-0.727273,0.0909091,-0.407273,
+-0.727273,0.0909091,0.0,
+-0.305455,0.0,-0.545455,
+-0.545455,0.0,-0.305455,
+-0.545455,0.0,0.0,
+-0.178182,0.0,-0.318182,
+-0.318182,0.0,-0.178182,
+-0.318182,0.0,0.0,
+-0.727273,0.0909091,0.407273,
+-0.407273,0.0909091,0.727273,
+0.0,0.0909091,0.727273,
+-0.545455,0.0,0.305455,
+-0.305455,0.0,0.545455,
+0.0,0.0,0.545455,
+-0.318182,0.0,0.178182,
+-0.178182,0.0,0.318182,
+0.0,0.0,0.318182,
+0.407273,0.0909091,0.727273,
+0.727273,0.0909091,0.407273,
+0.305455,0.0,0.545455,
+0.545455,0.0,0.305455,
+0.178182,0.0,0.318182,
+0.318182,0.0,0.178182,
+0.272727,0.0454545,0.0,
+0.272727,0.0454545,-0.152727,
+0.152727,0.0454545,-0.272727,
+0.0,0.0454545,-0.272727,
+0.409091,0.272727,0.0,
+0.409091,0.272727,-0.229091,
+0.229091,0.272727,-0.409091,
+0.0,0.272727,-0.409091,
+0.409091,0.545455,0.0,
+0.409091,0.545455,-0.229091,
+0.229091,0.545455,-0.409091,
+0.0,0.545455,-0.409091,
+-0.152727,0.0454545,-0.272727,
+-0.272727,0.0454545,-0.152727,
+-0.272727,0.0454545,0.0,
+-0.229091,0.272727,-0.409091,
+-0.409091,0.272727,-0.229091,
+-0.409091,0.272727,0.0,
+-0.229091,0.545455,-0.409091,
+-0.409091,0.545455,-0.229091,
+-0.409091,0.545455,0.0,
+-0.272727,0.0454545,0.152727,
+-0.152727,0.0454545,0.272727,
+0.0,0.0454545,0.272727,
+-0.409091,0.272727,0.229091,
+-0.229091,0.272727,0.409091,
+0.0,0.272727,0.409091,
+-0.409091,0.545455,0.229091,
+-0.229091,0.545455,0.409091,
+0.0,0.545455,0.409091,
+0.152727,0.0454545,0.272727,
+0.272727,0.0454545,0.152727,
+0.229091,0.272727,0.409091,
+0.409091,0.272727,0.229091,
+0.229091,0.545455,0.409091,
+0.409091,0.545455,0.229091,
+-0.454545,0.704545,0.0,
+-0.454545,0.704545,-0.0454545,
+-0.454545,0.772727,-0.0454545,
+-0.772727,0.863636,0.0,
+-0.772727,0.863636,-0.0454545,
+-0.818182,0.954545,-0.0454545,
+-0.818182,0.954545,0.0,
+-0.772727,0.522727,0.0,
+-0.772727,0.522727,-0.0454545,
+-0.909091,0.477273,-0.0454545,
+-0.909091,0.477273,0.0,
+-0.409091,0.363636,0.0,
+-0.409091,0.363636,-0.0454545,
+-0.409091,0.295455,-0.0454545,
+-0.409091,0.295455,0.0,
+-0.454545,0.772727,0.0454545,
+-0.454545,0.704545,0.0454545,
+-0.818182,0.954545,0.0454545,
+-0.772727,0.863636,0.0454545,
+-0.909091,0.477273,0.0454545,
+-0.772727,0.522727,0.0454545,
+-0.409091,0.295455,0.0454545,
+-0.409091,0.363636,0.0454545
+	] ;
+
+	var minPatches = 0;
+	var maxPatches = 26;
+
+	THREE.Geometry.call( this );
+
+	this.size = size || 50;
+
+	// number of segments per patch
+	this.segments = Math.max( 2, Math.floor( segments ) || 10 );
+
+	// scale the size to be the real scaling factor
+	var maxHeight = 0.857954740524292;
+
+	var maxHeight2 = maxHeight/2;
+	var trueSize = this.size / maxHeight2;
+
+	var normals = [], uvs = [];
+	// bezier form
+	var ms = new THREE.Matrix4(  -1.0,  3.0, -3.0,  1.0,
+                                  3.0, -6.0,  3.0,  0.0,
+                                 -3.0,  3.0,  0.0,  0.0,
+                                  1.0,  0.0,  0.0,  0.0 ) ;
+
+	var g = [];
+	var i, r, c;
+
+	var sp = [];
+	var tp = [];
+	var dsp = [];
+	var dtp = [];
+
+	// M * G * M matrix, sort of see
+	// http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
+	var mgm = [];
+
+	var vert = [];
+	var sdir = [];
+	var tdir = [];
+
+	var norm = new THREE.Vector3();
+
+	var tcoord;
+
+	var sstep, tstep;
+	var gmx, tmtx;
+
+	var vertPerRow;
+
+	var s, t, sval, tval, p, dsval, dtval;
+
+	var vsp, vtp, vdsp, vdtp;
+	var vsdir, vtdir, vertOut;
+	var v1, v2, v3, v4;
+
+	var mst = ms.clone();
+	mst.transpose();
+
+	// internal function: test if triangle has any matching vertices;
+	// if so, don't output, since it won't display anything.
+	var notDegenerate = function ( vtx1, vtx2, vtx3 ) {
+		if ( vtx1.equals( vtx2 ) ) { return false; }
+		if ( vtx1.equals( vtx3 ) ) { return false; }
+		if ( vtx2.equals( vtx3 ) ) { return false; }
+		return true;
+	};
+
+
+	for ( i = 0; i < 3; i++ )
+	{
+		mgm[i] = new THREE.Matrix4();
+	}
+
+	vertPerRow = (this.segments+1);
+
+	var surfCount = 0;
+	//var faceCount = 0;
+
+	for ( var surf = minPatches ; surf < maxPatches ; surf++ ) {
+		// get M * G * M matrix for x,y,z
+		for ( i = 0 ; i < 3 ; i++ ) {
+			// get control patches
+			for ( r = 0 ; r < 4 ; r++ ) {
+				for ( c = 0 ; c < 4 ; c++ ) {
+					// transposed; note subtraction of 1 for index
+					g[c*4+r] = TeacupVertices[(TeacupPatches[surf*16 + r*4 + c]-1)*3 + i] ;
+				}
+			}
+
+			// Shockingly, the following three.js does NOT work. Setting this way appears to give the order
+			// g[0], g[4], g[8], etc. to the elements! I could avoid the transpose above
+			// and things would "just work", but this weird ordering would be mysterious.
+			//var gmx = new THREE.Matrix4();
+			//gmx.elements.set( g );
+			// So, explicitly set the matrix this way:
+			gmx = new THREE.Matrix4( g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7], g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15] );
+
+			tmtx = new THREE.Matrix4();
+			tmtx.multiplyMatrices( gmx, ms );
+			mgm[i].multiplyMatrices( mst, tmtx );
+		}
+
+		// step along, get points, and output
+		for ( sstep = 0 ; sstep <= this.segments ; sstep++ ) {
+			s = sstep / this.segments;
+
+			for ( tstep = 0 ; tstep <= this.segments ; tstep++ ) {
+				t = tstep / this.segments;
+
+				// point from basis
+				// get power vectors and their derivatives
+				for ( p = 4, sval = tval = 1.0 ; p-- ; ) {
+					sp[p] = sval ;
+					tp[p] = tval ;
+					sval *= s ;
+					tval *= t ;
+
+					if ( p === 3 ) {
+						dsp[p] = dtp[p] = 0.0 ;
+						dsval = dtval = 1.0 ;
+					} else {
+						dsp[p] = dsval * (3-p) ;
+						dtp[p] = dtval * (3-p) ;
+						dsval *= s ;
+						dtval *= t ;
+					}
+				}
+
+				vsp = new THREE.Vector4( sp[0], sp[1], sp[2], sp[3] );
+				vtp = new THREE.Vector4( tp[0], tp[1], tp[2], tp[3] );
+				vdsp = new THREE.Vector4( dsp[0], dsp[1], dsp[2], dsp[3] );
+				vdtp = new THREE.Vector4( dtp[0], dtp[1], dtp[2], dtp[3] );
+
+				// do for x,y,z
+				for ( i = 0 ; i < 3 ; i++ ) {
+					// multiply power vectors times matrix to get value
+					tcoord = vsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					vert[i] = tcoord.dot( vtp );
+
+					// get s and t tangent vectors
+					tcoord = vdsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					sdir[i] = tcoord.dot( vtp ) ;
+
+					tcoord = vsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					tdir[i] = tcoord.dot( vdtp ) ;
+				}
+
+				// find normal
+				vsdir = new THREE.Vector3( sdir[0], sdir[1], sdir[2] );
+				vtdir = new THREE.Vector3( tdir[0], tdir[1], tdir[2] );
+				norm.crossVectors( vtdir, vsdir );
+				norm.normalize();
+
+				// rotate on X axis
+				// interestingly, normals need to be reversed; I suspect the patch
+				// has opposite handedness from the teapot. Also, Y is up for the teacup.
+				normals.push( new THREE.Vector3( -norm.x, -norm.y, -norm.z ) );
+
+				// TODO: check texturing
+				uvs.push( new THREE.Vector2( 1-t, 1-s ) );
+
+				// three.js uses Y up, the code makes Y up, all is fine.
+				// Move teacup to be centered around origin, three.js style.
+				vertOut = new THREE.Vector3( trueSize*vert[0], trueSize*(vert[1]-maxHeight2), trueSize*vert[2] );
+
+				this.vertices.push( vertOut );
+
+			}
+		}
+
+		// save the faces
+		for ( sstep = 0 ; sstep < this.segments ; sstep++ ) {
+			for ( tstep = 0 ; tstep < this.segments ; tstep++ ) {
+				v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
+				v2 = v1 + 1;
+				v3 = v2 + vertPerRow;
+				v4 = v1 + vertPerRow;
+
+				if ( notDegenerate ( this.vertices[v1], this.vertices[v2], this.vertices[v3] ) ) {
+					this.faces.push( new THREE.Face3( v1, v2, v3, [ normals[v1], normals[v2], normals[v3] ] ) );
+					this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v2], uvs[v3] ] );
+				}
+				if ( notDegenerate ( this.vertices[v1], this.vertices[v3], this.vertices[v4] ) ) {
+					this.faces.push( new THREE.Face3( v1, v3, v4, [ normals[v1], normals[v3], normals[v4] ] ) );
+					this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v3], uvs[v4] ] );
+				}
+				//faceCount+=2;
+			}
+		}
+		// increment only if a surface was used
+		surfCount++;
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.mergeVertices();
+
+};
+
+
+THREE.TeacupGeometry.prototype = Object.create( THREE.Geometry.prototype );
diff --git a/DUBREUIL/lib/uclass_TeapotGeometry.js b/DUBREUIL/lib/uclass_TeapotGeometry.js
new file mode 100644
index 0000000..05efc78
--- /dev/null
+++ b/DUBREUIL/lib/uclass_TeapotGeometry.js
@@ -0,0 +1,737 @@
+/**
+ * @author Eric Haines / http://erichaines.com/
+ *
+ * Tessellates the famous Utah teapot database by Martin Newell into triangles.
+ *
+ * THREE.TeapotGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn )
+ *
+ * defaults: size = 50, segments = 10, bottom = true, lid = true, body = true,
+ *   fitLid = false, blinn = true
+ *
+ * size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1.
+ * Think of it as a "radius".
+ * segments - number of line segments to subdivide each patch edge;
+ *   1 is possible but gives degenerates, so two is the real minimum.
+ * bottom - boolean, if true (default) then the bottom patches are added. Some consider
+ *   adding the bottom heresy, so set this to "false" to adhere to the One True Way.
+ * lid - to remove the lid and look inside, set to true.
+ * body - to remove the body and leave the lid, set this and "bottom" to false.
+ * fitLid - the lid is a tad small in the original. This stretches it a bit so you can't
+ *   see the teapot's insides through the gap.
+ * blinn - Jim Blinn scaled the original data vertically by dividing by about 1.3 to look
+ *   nicer. If you want to see the original teapot, similar to the real-world model, set
+ *   this to false. True by default.
+ *   See http://en.wikipedia.org/wiki/File:Original_Utah_Teapot.jpg for the original
+ *   real-world teapot (from http://en.wikipedia.org/wiki/Utah_teapot).
+ *
+ * Note that the bottom (the last four patches) is not flat - blame Frank Crow, not me.
+ *
+ * The teapot should normally be rendered as a double sided object, since for some 
+ * patches both sides can be seen, e.g., the gap around the lid and inside the spout.
+ *
+ * Segments 'n' determines the number of triangles output.
+ *   Total triangles = 32*2*n*n - 8*n    [degenerates at the top and bottom cusps are deleted]
+ *
+ *   size_factor   # triangles
+ *       1          56
+ *       2         240
+ *       3         552
+ *       4         992
+ *
+ *      10        6320
+ *      20       25440
+ *      30       57360
+ *
+ * Code converted from my ancient SPD software, http://tog.acm.org/resources/SPD/
+ * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
+ * Lesson: https://www.udacity.com/course/viewer#!/c-cs291/l-68866048/m-106482448
+ * YouTube video on teapot history: https://www.youtube.com/watch?v=DxMfblPzFNc
+ *
+ * See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot
+ *
+ */
+/*global THREE */
+
+THREE.TeapotGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn ) {
+
+	"use strict";
+
+	// 32 * 4 * 4 Bezier spline patches
+	var teapotPatches = [
+/*rim*/
+0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+3,16,17,18,7,19,20,21,11,22,23,24,15,25,26,27,
+18,28,29,30,21,31,32,33,24,34,35,36,27,37,38,39,
+30,40,41,0,33,42,43,4,36,44,45,8,39,46,47,12,
+/*body*/
+12,13,14,15,48,49,50,51,52,53,54,55,56,57,58,59,
+15,25,26,27,51,60,61,62,55,63,64,65,59,66,67,68,
+27,37,38,39,62,69,70,71,65,72,73,74,68,75,76,77,
+39,46,47,12,71,78,79,48,74,80,81,52,77,82,83,56,
+56,57,58,59,84,85,86,87,88,89,90,91,92,93,94,95,
+59,66,67,68,87,96,97,98,91,99,100,101,95,102,103,104,
+68,75,76,77,98,105,106,107,101,108,109,110,104,111,112,113,
+77,82,83,56,107,114,115,84,110,116,117,88,113,118,119,92,
+/*handle*/
+120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,
+123,136,137,120,127,138,139,124,131,140,141,128,135,142,143,132,
+132,133,134,135,144,145,146,147,148,149,150,151,68,152,153,154,
+135,142,143,132,147,155,156,144,151,157,158,148,154,159,160,68,
+/*spout*/
+161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,
+164,177,178,161,168,179,180,165,172,181,182,169,176,183,184,173,
+173,174,175,176,185,186,187,188,189,190,191,192,193,194,195,196,
+176,183,184,173,188,197,198,185,192,199,200,189,196,201,202,193,
+/*lid*/
+203,203,203,203,204,205,206,207,208,208,208,208,209,210,211,212,
+203,203,203,203,207,213,214,215,208,208,208,208,212,216,217,218,
+203,203,203,203,215,219,220,221,208,208,208,208,218,222,223,224,
+203,203,203,203,221,225,226,204,208,208,208,208,224,227,228,209,
+209,210,211,212,229,230,231,232,233,234,235,236,237,238,239,240,
+212,216,217,218,232,241,242,243,236,244,245,246,240,247,248,249,
+218,222,223,224,243,250,251,252,246,253,254,255,249,256,257,258,
+224,227,228,209,252,259,260,229,255,261,262,233,258,263,264,237,
+/*bottom*/
+265,265,265,265,266,267,268,269,270,271,272,273,92,119,118,113,
+265,265,265,265,269,274,275,276,273,277,278,279,113,112,111,104,
+265,265,265,265,276,280,281,282,279,283,284,285,104,103,102,95,
+265,265,265,265,282,286,287,266,285,288,289,270,95,94,93,92
+	] ;
+
+	var teapotVertices = [
+1.4,0,2.4,
+1.4,-0.784,2.4,
+0.784,-1.4,2.4,
+0,-1.4,2.4,
+1.3375,0,2.53125,
+1.3375,-0.749,2.53125,
+0.749,-1.3375,2.53125,
+0,-1.3375,2.53125,
+1.4375,0,2.53125,
+1.4375,-0.805,2.53125,
+0.805,-1.4375,2.53125,
+0,-1.4375,2.53125,
+1.5,0,2.4,
+1.5,-0.84,2.4,
+0.84,-1.5,2.4,
+0,-1.5,2.4,
+-0.784,-1.4,2.4,
+-1.4,-0.784,2.4,
+-1.4,0,2.4,
+-0.749,-1.3375,2.53125,
+-1.3375,-0.749,2.53125,
+-1.3375,0,2.53125,
+-0.805,-1.4375,2.53125,
+-1.4375,-0.805,2.53125,
+-1.4375,0,2.53125,
+-0.84,-1.5,2.4,
+-1.5,-0.84,2.4,
+-1.5,0,2.4,
+-1.4,0.784,2.4,
+-0.784,1.4,2.4,
+0,1.4,2.4,
+-1.3375,0.749,2.53125,
+-0.749,1.3375,2.53125,
+0,1.3375,2.53125,
+-1.4375,0.805,2.53125,
+-0.805,1.4375,2.53125,
+0,1.4375,2.53125,
+-1.5,0.84,2.4,
+-0.84,1.5,2.4,
+0,1.5,2.4,
+0.784,1.4,2.4,
+1.4,0.784,2.4,
+0.749,1.3375,2.53125,
+1.3375,0.749,2.53125,
+0.805,1.4375,2.53125,
+1.4375,0.805,2.53125,
+0.84,1.5,2.4,
+1.5,0.84,2.4,
+1.75,0,1.875,
+1.75,-0.98,1.875,
+0.98,-1.75,1.875,
+0,-1.75,1.875,
+2,0,1.35,
+2,-1.12,1.35,
+1.12,-2,1.35,
+0,-2,1.35,
+2,0,0.9,
+2,-1.12,0.9,
+1.12,-2,0.9,
+0,-2,0.9,
+-0.98,-1.75,1.875,
+-1.75,-0.98,1.875,
+-1.75,0,1.875,
+-1.12,-2,1.35,
+-2,-1.12,1.35,
+-2,0,1.35,
+-1.12,-2,0.9,
+-2,-1.12,0.9,
+-2,0,0.9,
+-1.75,0.98,1.875,
+-0.98,1.75,1.875,
+0,1.75,1.875,
+-2,1.12,1.35,
+-1.12,2,1.35,
+0,2,1.35,
+-2,1.12,0.9,
+-1.12,2,0.9,
+0,2,0.9,
+0.98,1.75,1.875,
+1.75,0.98,1.875,
+1.12,2,1.35,
+2,1.12,1.35,
+1.12,2,0.9,
+2,1.12,0.9,
+2,0,0.45,
+2,-1.12,0.45,
+1.12,-2,0.45,
+0,-2,0.45,
+1.5,0,0.225,
+1.5,-0.84,0.225,
+0.84,-1.5,0.225,
+0,-1.5,0.225,
+1.5,0,0.15,
+1.5,-0.84,0.15,
+0.84,-1.5,0.15,
+0,-1.5,0.15,
+-1.12,-2,0.45,
+-2,-1.12,0.45,
+-2,0,0.45,
+-0.84,-1.5,0.225,
+-1.5,-0.84,0.225,
+-1.5,0,0.225,
+-0.84,-1.5,0.15,
+-1.5,-0.84,0.15,
+-1.5,0,0.15,
+-2,1.12,0.45,
+-1.12,2,0.45,
+0,2,0.45,
+-1.5,0.84,0.225,
+-0.84,1.5,0.225,
+0,1.5,0.225,
+-1.5,0.84,0.15,
+-0.84,1.5,0.15,
+0,1.5,0.15,
+1.12,2,0.45,
+2,1.12,0.45,
+0.84,1.5,0.225,
+1.5,0.84,0.225,
+0.84,1.5,0.15,
+1.5,0.84,0.15,
+-1.6,0,2.025,
+-1.6,-0.3,2.025,
+-1.5,-0.3,2.25,
+-1.5,0,2.25,
+-2.3,0,2.025,
+-2.3,-0.3,2.025,
+-2.5,-0.3,2.25,
+-2.5,0,2.25,
+-2.7,0,2.025,
+-2.7,-0.3,2.025,
+-3,-0.3,2.25,
+-3,0,2.25,
+-2.7,0,1.8,
+-2.7,-0.3,1.8,
+-3,-0.3,1.8,
+-3,0,1.8,
+-1.5,0.3,2.25,
+-1.6,0.3,2.025,
+-2.5,0.3,2.25,
+-2.3,0.3,2.025,
+-3,0.3,2.25,
+-2.7,0.3,2.025,
+-3,0.3,1.8,
+-2.7,0.3,1.8,
+-2.7,0,1.575,
+-2.7,-0.3,1.575,
+-3,-0.3,1.35,
+-3,0,1.35,
+-2.5,0,1.125,
+-2.5,-0.3,1.125,
+-2.65,-0.3,0.9375,
+-2.65,0,0.9375,
+-2,-0.3,0.9,
+-1.9,-0.3,0.6,
+-1.9,0,0.6,
+-3,0.3,1.35,
+-2.7,0.3,1.575,
+-2.65,0.3,0.9375,
+-2.5,0.3,1.125,
+-1.9,0.3,0.6,
+-2,0.3,0.9,
+1.7,0,1.425,
+1.7,-0.66,1.425,
+1.7,-0.66,0.6,
+1.7,0,0.6,
+2.6,0,1.425,
+2.6,-0.66,1.425,
+3.1,-0.66,0.825,
+3.1,0,0.825,
+2.3,0,2.1,
+2.3,-0.25,2.1,
+2.4,-0.25,2.025,
+2.4,0,2.025,
+2.7,0,2.4,
+2.7,-0.25,2.4,
+3.3,-0.25,2.4,
+3.3,0,2.4,
+1.7,0.66,0.6,
+1.7,0.66,1.425,
+3.1,0.66,0.825,
+2.6,0.66,1.425,
+2.4,0.25,2.025,
+2.3,0.25,2.1,
+3.3,0.25,2.4,
+2.7,0.25,2.4,
+2.8,0,2.475,
+2.8,-0.25,2.475,
+3.525,-0.25,2.49375,
+3.525,0,2.49375,
+2.9,0,2.475,
+2.9,-0.15,2.475,
+3.45,-0.15,2.5125,
+3.45,0,2.5125,
+2.8,0,2.4,
+2.8,-0.15,2.4,
+3.2,-0.15,2.4,
+3.2,0,2.4,
+3.525,0.25,2.49375,
+2.8,0.25,2.475,
+3.45,0.15,2.5125,
+2.9,0.15,2.475,
+3.2,0.15,2.4,
+2.8,0.15,2.4,
+0,0,3.15,
+0.8,0,3.15,
+0.8,-0.45,3.15,
+0.45,-0.8,3.15,
+0,-0.8,3.15,
+0,0,2.85,
+0.2,0,2.7,
+0.2,-0.112,2.7,
+0.112,-0.2,2.7,
+0,-0.2,2.7,
+-0.45,-0.8,3.15,
+-0.8,-0.45,3.15,
+-0.8,0,3.15,
+-0.112,-0.2,2.7,
+-0.2,-0.112,2.7,
+-0.2,0,2.7,
+-0.8,0.45,3.15,
+-0.45,0.8,3.15,
+0,0.8,3.15,
+-0.2,0.112,2.7,
+-0.112,0.2,2.7,
+0,0.2,2.7,
+0.45,0.8,3.15,
+0.8,0.45,3.15,
+0.112,0.2,2.7,
+0.2,0.112,2.7,
+0.4,0,2.55,
+0.4,-0.224,2.55,
+0.224,-0.4,2.55,
+0,-0.4,2.55,
+1.3,0,2.55,
+1.3,-0.728,2.55,
+0.728,-1.3,2.55,
+0,-1.3,2.55,
+1.3,0,2.4,
+1.3,-0.728,2.4,
+0.728,-1.3,2.4,
+0,-1.3,2.4,
+-0.224,-0.4,2.55,
+-0.4,-0.224,2.55,
+-0.4,0,2.55,
+-0.728,-1.3,2.55,
+-1.3,-0.728,2.55,
+-1.3,0,2.55,
+-0.728,-1.3,2.4,
+-1.3,-0.728,2.4,
+-1.3,0,2.4,
+-0.4,0.224,2.55,
+-0.224,0.4,2.55,
+0,0.4,2.55,
+-1.3,0.728,2.55,
+-0.728,1.3,2.55,
+0,1.3,2.55,
+-1.3,0.728,2.4,
+-0.728,1.3,2.4,
+0,1.3,2.4,
+0.224,0.4,2.55,
+0.4,0.224,2.55,
+0.728,1.3,2.55,
+1.3,0.728,2.55,
+0.728,1.3,2.4,
+1.3,0.728,2.4,
+0,0,0,
+1.425,0,0,
+1.425,0.798,0,
+0.798,1.425,0,
+0,1.425,0,
+1.5,0,0.075,
+1.5,0.84,0.075,
+0.84,1.5,0.075,
+0,1.5,0.075,
+-0.798,1.425,0,
+-1.425,0.798,0,
+-1.425,0,0,
+-0.84,1.5,0.075,
+-1.5,0.84,0.075,
+-1.5,0,0.075,
+-1.425,-0.798,0,
+-0.798,-1.425,0,
+0,-1.425,0,
+-1.5,-0.84,0.075,
+-0.84,-1.5,0.075,
+0,-1.5,0.075,
+0.798,-1.425,0,
+1.425,-0.798,0,
+0.84,-1.5,0.075,
+1.5,-0.84,0.075
+	] ;
+
+	THREE.Geometry.call( this );
+
+	this.type = 'TeapotGeometry';
+
+	this.parameters = {
+		size: size,
+		segments: segments,
+		bottom: bottom,
+		lid: lid,
+		body: body,
+		fitLid: fitLid,
+		blinn: blinn
+	};
+
+	size = size || 50;
+
+	// number of segments per patch
+	segments = segments !== undefined ? Math.max( 2, Math.floor( segments ) || 10 ) : 10;
+
+	// which parts should be visible
+	bottom = bottom === undefined ? true : bottom;
+	lid = lid === undefined ? true : lid;
+	body = body === undefined ? true : body;
+
+	// Should the lid be snug? It's not traditional, so off by default
+	fitLid = fitLid === undefined ? false : fitLid;
+
+	// Jim Blinn scaled the teapot down in size by about 1.3 for
+	// some rendering tests. He liked the new proportions that he kept
+	// the data in this form. The model was distributed with these new
+	// proportions and became the norm. Trivia: comparing images of the
+	// real teapot and the computer model, the ratio for the bowl of the
+	// real teapot is more like 1.25, but since 1.3 is the traditional
+	// value given, we use it here.
+	var blinnScale = 1.3;
+	blinn = blinn === undefined ? true : blinn;
+
+	// scale the size to be the real scaling factor
+	var maxHeight = 3.15 * ( blinn ? 1 : blinnScale );
+
+	var maxHeight2 = maxHeight / 2;
+	var trueSize = size / maxHeight2;
+
+	// Number of elements depends on what is needed. Subtract degenerate
+	// triangles at tip of bottom and lid out in advance.
+	var numTriangles = bottom ? ( 8 * segments - 4 ) * segments : 0;
+	numTriangles += lid ? ( 16 * segments - 4 ) * segments : 0;
+	numTriangles += body ? 40 * segments * segments : 0;
+
+	var numVertices = bottom ? 4 : 0;
+	numVertices += lid ? 8 : 0;
+	numVertices += body ? 20 : 0;
+	numVertices *= ( segments + 1 ) * ( segments + 1 );
+
+	var i;
+	var normals = [];
+	var uvs = [];
+	for ( i = 0; i < numVertices; i++ ) {
+		this.vertices.push( new THREE.Vector3() );
+		normals.push( new THREE.Vector3() );
+		uvs.push( new THREE.Vector2() );
+	}
+
+	// Bezier form
+	var ms = new THREE.Matrix4();
+	ms.set( -1.0,  3.0, -3.0,  1.0,
+			 3.0, -6.0,  3.0,  0.0,
+			-3.0,  3.0,  0.0,  0.0,
+			 1.0,  0.0,  0.0,  0.0 ) ;
+
+	var g = [];
+	var i, r, c;
+
+	var sp = [];
+	var tp = [];
+	var dsp = [];
+	var dtp = [];
+
+	// M * G * M matrix, sort of see
+	// http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
+	var mgm = [];
+
+	var vert = [];
+	var sdir = [];
+	var tdir = [];
+
+	var norm = new THREE.Vector3();
+
+	var tcoord;
+
+	var sstep, tstep;
+	var vertPerRow, eps;
+
+	var s, t, sval, tval, p, dsval, dtval;
+
+	var normOut = new THREE.Vector3();
+	var v1, v2, v3, v4;
+
+	var gmx = new THREE.Matrix4();
+	var tmtx = new THREE.Matrix4();
+
+	var vsp = new THREE.Vector4();
+	var vtp = new THREE.Vector4();
+	var vdsp = new THREE.Vector4();
+	var vdtp = new THREE.Vector4();
+
+	var vsdir = new THREE.Vector3();
+	var vtdir = new THREE.Vector3();
+
+	var mst = ms.clone();
+	mst.transpose();
+
+	// internal function: test if triangle has any matching vertices;
+	// if so, don't save triangle, since it won't display anything.
+	var notDegenerate = function ( vtx1, vtx2, vtx3 ) {
+
+		// if any vertex matches, return false
+		if ( vtx1.equals( vtx2 ) ) { return false; }
+		if ( vtx1.equals( vtx3 ) ) { return false; }
+		if ( vtx2.equals( vtx3 ) ) { return false; }
+		return true;
+
+	};
+
+
+	for ( i = 0; i < 3; i ++ )
+	{
+
+		mgm[ i ] = new THREE.Matrix4();
+
+	}
+
+	var minPatches = body ? 0 : 20;
+	var maxPatches = bottom ? 32 : 28;
+
+	vertPerRow = segments + 1;
+
+	eps = 0.0000001;
+
+	var surfCount = 0;
+	var vertCount = 0;
+	var normCount = 0;
+	var uvCount = 0;
+
+	for ( var surf = minPatches ; surf < maxPatches ; surf ++ ) {
+
+		// lid is in the middle of the data, patches 20-27,
+		// so ignore it for this part of the loop if the lid is not desired
+		if ( lid || ( surf < 20 || surf >= 28 ) ) {
+
+			// get M * G * M matrix for x,y,z
+			for ( i = 0 ; i < 3 ; i ++ ) {
+
+				// get control patches
+				for ( r = 0 ; r < 4 ; r ++ ) {
+
+					for ( c = 0 ; c < 4 ; c ++ ) {
+
+						// transposed
+						g[ c * 4 + r ] = teapotVertices[ teapotPatches[ surf * 16 + r * 4 + c ] * 3 + i ] ;
+
+						// is the lid to be made larger, and is this a point on the lid
+						// that is X or Y?
+						if ( fitLid && ( surf >= 20 && surf < 28 ) && ( i !== 2 ) ) {
+
+							// increase XY size by 7.7%, found empirically. I don't
+							// increase Z so that the teapot will continue to fit in the
+							// space -1 to 1 for Y (Y is up for the final model).
+							g[ c * 4 + r ] *= 1.077;
+
+						}
+
+						// Blinn "fixed" the teapot by dividing Z by blinnScale, and that's the
+						// data we now use. The original teapot is taller. Fix it:
+						if ( ! blinn && ( i === 2 ) ) {
+
+							g[ c * 4 + r ] *= blinnScale;
+
+						}
+
+					}
+
+				}
+
+				gmx.set( g[ 0 ], g[ 1 ], g[ 2 ], g[ 3 ], g[ 4 ], g[ 5 ], g[ 6 ], g[ 7 ], g[ 8 ], g[ 9 ], g[ 10 ], g[ 11 ], g[ 12 ], g[ 13 ], g[ 14 ], g[ 15 ] );
+
+				tmtx.multiplyMatrices( gmx, ms );
+				mgm[ i ].multiplyMatrices( mst, tmtx );
+
+			}
+
+			// step along, get points, and output
+			for ( sstep = 0 ; sstep <= segments ; sstep ++ ) {
+
+				s = sstep / segments;
+
+				for ( tstep = 0 ; tstep <= segments ; tstep ++ ) {
+
+					t = tstep / segments;
+
+					// point from basis
+					// get power vectors and their derivatives
+					for ( p = 4, sval = tval = 1.0 ; p -- ; ) {
+
+						sp[ p ] = sval ;
+						tp[ p ] = tval ;
+						sval *= s ;
+						tval *= t ;
+
+						if ( p === 3 ) {
+
+							dsp[ p ] = dtp[ p ] = 0.0 ;
+							dsval = dtval = 1.0 ;
+
+						} else {
+
+							dsp[ p ] = dsval * ( 3 - p ) ;
+							dtp[ p ] = dtval * ( 3 - p ) ;
+							dsval *= s ;
+							dtval *= t ;
+
+						}
+
+					}
+
+					vsp.set( sp[0], sp[1], sp[2], sp[3] );
+					vtp.set( tp[0], tp[1], tp[2], tp[3] );
+					vdsp.set( dsp[0], dsp[1], dsp[2], dsp[3] );
+					vdtp.set( dtp[0], dtp[1], dtp[2], dtp[3] );
+
+					// do for x,y,z
+					for ( i = 0 ; i < 3 ; i ++ ) {
+
+						// multiply power vectors times matrix to get value
+						tcoord = vsp.clone();
+						tcoord.applyMatrix4( mgm[ i ] );
+						vert[ i ] = tcoord.dot( vtp );
+
+						// get s and t tangent vectors
+						tcoord = vdsp.clone();
+						tcoord.applyMatrix4( mgm[ i ] );
+						sdir[ i ] = tcoord.dot( vtp ) ;
+
+						tcoord = vsp.clone();
+						tcoord.applyMatrix4( mgm[ i ] );
+						tdir[ i ] = tcoord.dot( vdtp ) ;
+
+					}
+
+					// find normal
+					vsdir.set( sdir[0], sdir[1], sdir[2], sdir[3] );
+					vtdir.set( tdir[0], tdir[1], tdir[2], tdir[3] );
+					norm.crossVectors( vtdir, vsdir );
+					norm.normalize();
+
+					// if X and Z length is 0, at the cusp, so point the normal up or down, depending on patch number
+					if ( vert[ 0 ] === 0 && vert[ 1 ] === 0 )
+					{
+
+						// if above the middle of the teapot, normal points up, else down
+						normOut.set( 0, vert[ 2 ] > maxHeight2 ? 1 : - 1, 0 );
+
+					}
+					else
+					{
+
+						// standard output: rotate on X axis
+						normOut.set( norm.x, norm.z, - norm.y );
+
+					}
+
+					// three.js uses Y up, the code makes Z up, so time for a trick:
+					// rotate on X axis, and offset down on Y axis so object ranges from -1 to 1 in Y
+					this.vertices[vertCount++].set( trueSize * vert[ 0 ], trueSize * ( vert[ 2 ] - maxHeight2 ), - trueSize * vert[ 1 ] );
+
+					normals[ normCount ++ ].set( normOut.x, normOut.y, normOut.z );
+
+					uvs[ uvCount ++ ].set( 1 - t, 1 - s );
+
+				}
+
+			}
+
+			// save the faces
+			for ( sstep = 0 ; sstep < segments ; sstep ++ ) {
+
+				for ( tstep = 0 ; tstep < segments ; tstep ++ ) {
+
+					v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
+					v2 = v1 + 1;
+					v3 = v2 + vertPerRow;
+					v4 = v1 + vertPerRow;
+
+					// Normals and UVs cannot be shared. Without clone(), you can see the consequences
+					// of sharing if you call geometry.applyMatrix( matrix ).
+					if ( notDegenerate ( this.vertices[v1], this.vertices[v2], this.vertices[v3] ) ) {
+
+						this.faces.push( new THREE.Face3( v1, v2, v3, [ normals[v1], normals[v2], normals[v3] ] ) );
+						this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v2], uvs[v3] ] );
+
+					}
+					if ( notDegenerate ( this.vertices[v1], this.vertices[v3], this.vertices[v4] ) ) {
+
+						this.faces.push( new THREE.Face3( v1, v3, v4, [ normals[v1], normals[v3], normals[v4] ] ) );
+						this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v3], uvs[v4] ] );
+
+					}
+
+				}
+
+			}
+
+			// increment only if a surface was used
+			surfCount ++;
+
+		}
+
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.mergeVertices();
+
+};
+
+
+THREE.TeapotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TeapotGeometry.prototype.constructor = THREE.TeapotGeometry;
+
+THREE.TeapotGeometry.prototype.clone = function () {
+
+	var geometry = new THREE.TeapotGeometry(
+		this.parameters.size,
+		this.parameters.segments,
+		this.parameters.bottom,
+		this.parameters.lid,
+		this.parameters.body,
+		this.parameters.fitLid,
+		this.parameters.blinn
+	);
+
+	return geometry;
+
+};
diff --git a/DUBREUIL/lib/uclass_TeaspoonGeometry.js b/DUBREUIL/lib/uclass_TeaspoonGeometry.js
new file mode 100644
index 0000000..4df9ae5
--- /dev/null
+++ b/DUBREUIL/lib/uclass_TeaspoonGeometry.js
@@ -0,0 +1,527 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author Eric Haines / http://erichaines.com/
+ *
+ * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
+ *
+ * Tessellate a teaspoon into triangular patches.
+ *
+ * See http://www.sjbaker.org/wiki/index.php?title=The_History_of_The_Teaspoon for
+ *	the history of the Teaspoon and teaspoon
+ *
+ * THREE.TeaspoonGeometry = function ( size, segments )
+ *
+ * defaults: size = 50, segments = 10
+ *
+ * size is a relative scale: I've scaled the Teaspoon to fit vertically between -1 and 1.
+ *	Think of it as a "radius".
+ * segments - number of line segments to subdivide each patch edge;
+ *	1 is possible but gives degenerates, so two is the real minimum.
+ *
+ * segments 'n' determines the number of objects output.
+ *  Total patches = 16*2*n*n
+ *
+ *  size_factor  # triangles
+ *       1			32
+ *       2         128
+ *       3         288
+ *       4         512
+ *       5         800
+ *       6        1152
+ *
+ *		10        3200
+ *		20       12800
+ *		30       28800
+ *		40       51200
+ */
+/*global THREE */
+
+THREE.TeaspoonGeometry = function ( size, segments ) {
+	"use strict";
+
+	// 26 * 4 * 4 Bezier spline patches, note +1 start
+	// Data from ftp://ftp.funet.fi/pub/sci/graphics/packages/objects/teaset.tar.Z
+	var TeaspoonPatches = [
+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
+17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
+33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
+49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
+65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
+81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
+97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
+113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
+129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,
+145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,
+161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,
+177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,
+193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,
+209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,
+225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,
+241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256
+	] ;
+
+	var TeaspoonVertices = [
+-0.000107143,0.205357,0.0,
+0.0,0.196429,-0.0178571,
+0.0,0.196429,-0.0178571,
+0.000107143,0.205357,0.0,
+-0.0535714,0.205357,0.0,
+-0.0222714,0.178571,-0.0534286,
+0.0222714,0.178571,-0.0534286,
+0.0535714,0.205357,0.0,
+-0.107143,0.0952429,-0.0178571,
+-0.0446429,0.0952429,-0.0892857,
+0.0446429,0.0952429,-0.0892857,
+0.107143,0.0952429,-0.0178571,
+-0.107143,0.0,-0.0178571,
+-0.0446429,0.0,-0.0892857,
+0.0446429,0.0,-0.0892857,
+0.107143,0.0,-0.0178571,
+0.000107143,0.205357,0.0,
+0.000135714,0.207589,0.00446429,
+0.000157143,0.216518,0.00446429,
+0.000125,0.214286,0.0,
+0.0535714,0.205357,0.0,
+0.0613964,0.212054,0.0133571,
+0.0714286,0.220982,0.015625,
+0.0625,0.214286,0.0,
+0.107143,0.0952429,-0.0178571,
+0.122768,0.0952429,0.0,
+0.142857,0.0952429,0.00446429,
+0.125,0.0952429,-0.0178571,
+0.107143,0.0,-0.0178571,
+0.122768,0.0,0.0,
+0.142857,0.0,0.00446429,
+0.125,0.0,-0.0178571,
+0.000125,0.214286,0.0,
+0.0,0.205357,-0.0178571,
+0.0,0.205357,-0.0178571,
+-0.000125,0.214286,0.0,
+0.0625,0.214286,0.0,
+0.0267857,0.1875,-0.0625,
+-0.0267857,0.1875,-0.0625,
+-0.0625,0.214286,0.0,
+0.125,0.0952429,-0.0178571,
+0.0535714,0.0952429,-0.107143,
+-0.0535714,0.0952429,-0.107143,
+-0.125,0.0952429,-0.0178571,
+0.125,0.0,-0.0178571,
+0.0535714,0.0,-0.107143,
+-0.0535714,0.0,-0.107143,
+-0.125,0.0,-0.0178571,
+-0.000125,0.214286,0.0,
+-0.000157143,0.216518,0.00446429,
+-0.000135714,0.207589,0.00446429,
+-0.000107143,0.205357,0.0,
+-0.0625,0.214286,0.0,
+-0.0714286,0.220982,0.015625,
+-0.0613964,0.212054,0.0133571,
+-0.0535714,0.205357,0.0,
+-0.125,0.0952429,-0.0178571,
+-0.142857,0.0952429,0.00446429,
+-0.122768,0.0952429,0.0,
+-0.107143,0.0952429,-0.0178571,
+-0.125,0.0,-0.0178571,
+-0.142857,0.0,0.00446429,
+-0.122768,0.0,0.0,
+-0.107143,0.0,-0.0178571,
+-0.107143,0.0,-0.0178571,
+-0.0446429,0.0,-0.0892857,
+0.0446429,0.0,-0.0892857,
+0.107143,0.0,-0.0178571,
+-0.107143,-0.142857,-0.0178571,
+-0.0446429,-0.142857,-0.0892857,
+0.0446429,-0.142857,-0.0892857,
+0.107143,-0.142857,-0.0178571,
+-0.0133929,-0.160714,0.0386893,
+-0.00557857,-0.160714,0.0386893,
+0.00557857,-0.160714,0.0386893,
+0.0133929,-0.160714,0.0386893,
+-0.0133929,-0.25,0.0535714,
+-0.00557857,-0.25,0.0535714,
+0.00557857,-0.25,0.0535714,
+0.0133929,-0.25,0.0535714,
+0.107143,0.0,-0.0178571,
+0.122768,0.0,0.0,
+0.142857,0.0,0.00446429,
+0.125,0.0,-0.0178571,
+0.107143,-0.142857,-0.0178571,
+0.122768,-0.142857,0.0,
+0.142857,-0.142857,0.00446429,
+0.125,-0.142857,-0.0178571,
+0.0133929,-0.160714,0.0386893,
+0.0153464,-0.160714,0.0386893,
+0.0178571,-0.160714,0.0314357,
+0.015625,-0.160714,0.0297607,
+0.0133929,-0.25,0.0535714,
+0.0153464,-0.25,0.0535714,
+0.0178571,-0.25,0.0463179,
+0.015625,-0.25,0.0446429,
+0.125,0.0,-0.0178571,
+0.0535714,0.0,-0.107143,
+-0.0535714,0.0,-0.107143,
+-0.125,0.0,-0.0178571,
+0.125,-0.142857,-0.0178571,
+0.0535714,-0.142857,-0.107143,
+-0.0535714,-0.142857,-0.107143,
+-0.125,-0.142857,-0.0178571,
+0.015625,-0.160714,0.0297607,
+0.00669643,-0.160714,0.0230643,
+-0.00781071,-0.160714,0.0208321,
+-0.015625,-0.160714,0.0297607,
+0.015625,-0.25,0.0446429,
+0.00669643,-0.25,0.0379464,
+-0.00781071,-0.25,0.0357143,
+-0.015625,-0.25,0.0446429,
+-0.125,0.0,-0.0178571,
+-0.142857,0.0,0.00446429,
+-0.122768,0.0,0.0,
+-0.107143,0.0,-0.0178571,
+-0.125,-0.142857,-0.0178571,
+-0.142857,-0.142857,0.00446429,
+-0.122768,-0.142857,0.0,
+-0.107143,-0.142857,-0.0178571,
+-0.015625,-0.160714,0.0297607,
+-0.0175786,-0.160714,0.0319929,
+-0.0153464,-0.160714,0.0386893,
+-0.0133929,-0.160714,0.0386893,
+-0.015625,-0.25,0.0446429,
+-0.0175786,-0.25,0.046875,
+-0.0153464,-0.25,0.0535714,
+-0.0133929,-0.25,0.0535714,
+-0.0133929,-0.25,0.0535714,
+-0.00557857,-0.25,0.0535714,
+0.00557857,-0.25,0.0535714,
+0.0133929,-0.25,0.0535714,
+-0.0133929,-0.46425,0.0892857,
+-0.00557857,-0.46425,0.0892857,
+0.00557857,-0.46425,0.0892857,
+0.0133929,-0.46425,0.0892857,
+-0.0446429,-0.678571,0.0535714,
+-0.00892857,-0.678571,0.0625,
+0.00892857,-0.678571,0.0625,
+0.0446429,-0.678571,0.0535714,
+-0.0446429,-0.857143,0.0357143,
+-0.00892857,-0.857143,0.0446429,
+0.00892857,-0.857143,0.0446429,
+0.0446429,-0.857143,0.0357143,
+0.0133929,-0.25,0.0535714,
+0.0153464,-0.25,0.0535714,
+0.0178571,-0.25,0.0463179,
+0.015625,-0.25,0.0446429,
+0.0133929,-0.46425,0.0892857,
+0.0153464,-0.464286,0.0892857,
+0.0178571,-0.46425,0.0820321,
+0.015625,-0.46425,0.0803571,
+0.0446429,-0.678571,0.0535714,
+0.0535714,-0.678571,0.0513393,
+0.0535714,-0.678571,0.0334821,
+0.0446429,-0.678571,0.0357143,
+0.0446429,-0.857143,0.0357143,
+0.0535714,-0.857143,0.0334821,
+0.0535714,-0.857143,0.015625,
+0.0446429,-0.857143,0.0178571,
+0.015625,-0.25,0.0446429,
+0.00669643,-0.25,0.0379464,
+-0.00781071,-0.25,0.0357143,
+-0.015625,-0.25,0.0446429,
+0.015625,-0.46425,0.0803571,
+0.00669643,-0.464286,0.0736607,
+-0.00781071,-0.46425,0.0714286,
+-0.015625,-0.46425,0.0803571,
+0.0446429,-0.678571,0.0357143,
+0.00892857,-0.678571,0.0446429,
+-0.00892857,-0.678571,0.0446429,
+-0.0446429,-0.678571,0.0357143,
+0.0446429,-0.857143,0.0178571,
+0.00892857,-0.857143,0.0267857,
+-0.00892857,-0.857143,0.0267857,
+-0.0446429,-0.857143,0.0178571,
+-0.015625,-0.25,0.0446429,
+-0.0175786,-0.25,0.046875,
+-0.0153464,-0.25,0.0535714,
+-0.0133929,-0.25,0.0535714,
+-0.015625,-0.46425,0.0803571,
+-0.0175786,-0.464286,0.0825893,
+-0.0153464,-0.464286,0.0892857,
+-0.0133929,-0.46425,0.0892857,
+-0.0446429,-0.678571,0.0357143,
+-0.0535714,-0.678571,0.0334821,
+-0.0535714,-0.678571,0.0513393,
+-0.0446429,-0.678571,0.0535714,
+-0.0446429,-0.857143,0.0178571,
+-0.0535714,-0.857143,0.015625,
+-0.0535714,-0.857143,0.0334821,
+-0.0446429,-0.857143,0.0357143,
+-0.0446429,-0.857143,0.0357143,
+-0.00892857,-0.857143,0.0446429,
+0.00892857,-0.857143,0.0446429,
+0.0446429,-0.857143,0.0357143,
+-0.0446429,-0.928571,0.0285714,
+-0.00892857,-0.928571,0.0375,
+0.00892857,-0.928571,0.0375,
+0.0446429,-0.928571,0.0285714,
+-0.0539286,-0.999643,0.0178571,
+0.000357143,-0.999643,0.0178571,
+0.0,-0.999643,0.0178571,
+0.0535714,-0.999643,0.0178571,
+-0.000357143,-1,0.0178571,
+0.000357143,-1,0.0178571,
+0.0,-1,0.0178571,
+0.0,-1,0.0178571,
+0.0446429,-0.857143,0.0357143,
+0.0535714,-0.857143,0.0334821,
+0.0535714,-0.857143,0.015625,
+0.0446429,-0.857143,0.0178571,
+0.0446429,-0.928571,0.0285714,
+0.0535714,-0.928571,0.0263393,
+0.0535714,-0.928571,0.00848214,
+0.0446429,-0.928571,0.0107143,
+0.0535714,-0.999643,0.0178571,
+0.0669643,-0.999643,0.0178571,
+0.0673214,-0.999643,0.0,
+0.0539286,-0.999643,0.0,
+0.0,-1,0.0178571,
+0.0,-1,0.0178571,
+0.000357143,-1,0.0,
+0.000357143,-1,0.0,
+0.0446429,-0.857143,0.0178571,
+0.00892857,-0.857143,0.0267857,
+-0.00892857,-0.857143,0.0267857,
+-0.0446429,-0.857143,0.0178571,
+0.0446429,-0.928571,0.0107143,
+0.00892857,-0.928571,0.0196429,
+-0.00892857,-0.928571,0.0196429,
+-0.0446429,-0.928571,0.0107143,
+0.0539286,-0.999643,0.0,
+0.000357143,-0.999643,0.0,
+-0.000357143,-0.999643,0.0,
+-0.0539286,-0.999643,0.0,
+0.000357143,-1,0.0,
+0.000357143,-1,0.0,
+-0.000357143,-1,0.0,
+-0.000357143,-1,0.0,
+-0.0446429,-0.857143,0.0178571,
+-0.0535714,-0.857143,0.015625,
+-0.0535714,-0.857143,0.0334821,
+-0.0446429,-0.857143,0.0357143,
+-0.0446429,-0.928571,0.0107143,
+-0.0535714,-0.928571,0.00848214,
+-0.0535714,-0.928571,0.0263393,
+-0.0446429,-0.928571,0.0285714,
+-0.0539286,-0.999643,0.0,
+-0.0673214,-0.999643,0.0,
+-0.0675,-0.999643,0.0178571,
+-0.0539286,-0.999643,0.0178571,
+-0.000357143,-1,0.0,
+-0.000357143,-1,0.0,
+-0.000535714,-1,0.0178571,
+-0.000357143,-1,0.0178571
+	] ;
+
+	var minPatches = 0;
+	var maxPatches = 16;
+
+	THREE.Geometry.call( this );
+
+	this.size = size || 50;
+
+	// number of segments per patch
+	this.segments = Math.max( 2, Math.floor( segments ) || 10 );
+
+	// scale the size to be the real scaling factor
+	var maxHeight = 0.21463862761855126;
+	var minHeight = -1;
+
+	var fullHeight = maxHeight - minHeight;
+	var fullHeight2 = fullHeight/2;
+	var heightOffset = - fullHeight2 - minHeight;
+	var trueSize = this.size / fullHeight2;
+
+	var normals = [], uvs = [];
+	// bezier form
+	var ms = new THREE.Matrix4(  -1.0,  3.0, -3.0,  1.0,
+                                  3.0, -6.0,  3.0,  0.0,
+                                 -3.0,  3.0,  0.0,  0.0,
+                                  1.0,  0.0,  0.0,  0.0 ) ;
+
+	var g = [];
+	var i, r, c;
+
+	var sp = [];
+	var tp = [];
+	var dsp = [];
+	var dtp = [];
+
+	// M * G * M matrix, sort of see
+	// http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
+	var mgm = [];
+
+	var vert = [];
+	var sdir = [];
+	var tdir = [];
+
+	var norm = new THREE.Vector3();
+
+	var tcoord;
+
+	var sstep, tstep;
+	var gmx, tmtx;
+
+	var vertPerRow;
+
+	var s, t, sval, tval, p, dsval, dtval;
+
+	var vsp, vtp, vdsp, vdtp;
+	var vsdir, vtdir, vertOut;
+	var v1, v2, v3, v4;
+
+	var mst = ms.clone();
+	mst.transpose();
+
+	// internal function: test if triangle has any matching vertices;
+	// if so, don't output, since it won't display anything.
+	var notDegenerate = function ( vtx1, vtx2, vtx3 ) {
+		if ( vtx1.equals( vtx2 ) ) { return false; }
+		if ( vtx1.equals( vtx3 ) ) { return false; }
+		if ( vtx2.equals( vtx3 ) ) { return false; }
+		return true;
+	};
+
+
+	for ( i = 0; i < 3; i++ )
+	{
+		mgm[i] = new THREE.Matrix4();
+	}
+
+	vertPerRow = (this.segments+1);
+
+	var surfCount = 0;
+	//var faceCount = 0;
+
+	for ( var surf = minPatches ; surf < maxPatches ; surf++ ) {
+		// get M * G * M matrix for x,y,z
+		for ( i = 0 ; i < 3 ; i++ ) {
+			// get control patches
+			for ( r = 0 ; r < 4 ; r++ ) {
+				for ( c = 0 ; c < 4 ; c++ ) {
+					// transposed; note subtraction of 1 for index
+					g[c*4+r] = TeaspoonVertices[(TeaspoonPatches[surf*16 + r*4 + c]-1)*3 + i] ;
+				}
+			}
+
+			// Shockingly, the following three.js does NOT work. Setting this way appears to give the order
+			// g[0], g[4], g[8], etc. to the elements! I could avoid the transpose above
+			// and things would "just work", but this weird ordering would be mysterious.
+			//var gmx = new THREE.Matrix4();
+			//gmx.elements.set( g );
+			// So, explicitly set the matrix this way:
+			gmx = new THREE.Matrix4( g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7], g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15] );
+
+			tmtx = new THREE.Matrix4();
+			tmtx.multiplyMatrices( gmx, ms );
+			mgm[i].multiplyMatrices( mst, tmtx );
+		}
+
+		// step along, get points, and output
+		for ( sstep = 0 ; sstep <= this.segments ; sstep++ ) {
+			s = sstep / this.segments;
+
+			for ( tstep = 0 ; tstep <= this.segments ; tstep++ ) {
+				t = tstep / this.segments;
+
+				// point from basis
+				// get power vectors and their derivatives
+				for ( p = 4, sval = tval = 1.0 ; p-- ; ) {
+					sp[p] = sval ;
+					tp[p] = tval ;
+					sval *= s ;
+					tval *= t ;
+
+					if ( p === 3 ) {
+						dsp[p] = dtp[p] = 0.0 ;
+						dsval = dtval = 1.0 ;
+					} else {
+						dsp[p] = dsval * (3-p) ;
+						dtp[p] = dtval * (3-p) ;
+						dsval *= s ;
+						dtval *= t ;
+					}
+				}
+
+				vsp = new THREE.Vector4( sp[0], sp[1], sp[2], sp[3] );
+				vtp = new THREE.Vector4( tp[0], tp[1], tp[2], tp[3] );
+				vdsp = new THREE.Vector4( dsp[0], dsp[1], dsp[2], dsp[3] );
+				vdtp = new THREE.Vector4( dtp[0], dtp[1], dtp[2], dtp[3] );
+
+				// do for x,y,z
+				for ( i = 0 ; i < 3 ; i++ ) {
+					// multiply power vectors times matrix to get value
+					tcoord = vsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					vert[i] = tcoord.dot( vtp );
+
+					// get s and t tangent vectors
+					tcoord = vdsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					sdir[i] = tcoord.dot( vtp ) ;
+
+					tcoord = vsp.clone();
+					tcoord.applyMatrix4( mgm[i] );
+					tdir[i] = tcoord.dot( vdtp ) ;
+				}
+
+				// find normal
+				vsdir = new THREE.Vector3( sdir[0], sdir[1], sdir[2] );
+				vtdir = new THREE.Vector3( tdir[0], tdir[1], tdir[2] );
+				norm.crossVectors( vtdir, vsdir );
+				norm.normalize();
+
+				// rotate on X axis
+				// interestingly, normals need to be reversed; I suspect the patch
+				// has opposite handedness from the teapot. Also, Y is along axis for the teaspoon.
+				normals.push( new THREE.Vector3( -norm.x, -norm.y, -norm.z ) );
+
+				// TODO: check texturing
+				uvs.push( new THREE.Vector2( 1-t, 1-s ) );
+
+				// three.js uses Y up, the code makes Y up, all is fine.
+				// Move teaspoon to be centered around origin, three.js style.
+				vertOut = new THREE.Vector3( trueSize*vert[0], trueSize*(vert[1]+heightOffset), trueSize*vert[2] );
+
+				this.vertices.push( vertOut );
+
+			}
+		}
+
+		// save the faces
+		for ( sstep = 0 ; sstep < this.segments ; sstep++ ) {
+			for ( tstep = 0 ; tstep < this.segments ; tstep++ ) {
+				v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
+				v2 = v1 + 1;
+				v3 = v2 + vertPerRow;
+				v4 = v1 + vertPerRow;
+
+				if ( notDegenerate ( this.vertices[v1], this.vertices[v2], this.vertices[v3] ) ) {
+					this.faces.push( new THREE.Face3( v1, v2, v3, [ normals[v1], normals[v2], normals[v3] ] ) );
+					this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v2], uvs[v3] ] );
+				}
+				if ( notDegenerate ( this.vertices[v1], this.vertices[v3], this.vertices[v4] ) ) {
+					this.faces.push( new THREE.Face3( v1, v3, v4, [ normals[v1], normals[v3], normals[v4] ] ) );
+					this.faceVertexUvs[ 0 ].push( [ uvs[v1], uvs[v3], uvs[v4] ] );
+				}
+				//faceCount+=2;
+			}
+		}
+		// increment only if a surface was used
+		surfCount++;
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	this.mergeVertices();
+
+};
+
+
+THREE.TeaspoonGeometry.prototype = Object.create( THREE.Geometry.prototype );
diff --git a/DUBREUIL/lib/uclass_shaders.js b/DUBREUIL/lib/uclass_shaders.js
new file mode 100644
index 0000000..462ec57
--- /dev/null
+++ b/DUBREUIL/lib/uclass_shaders.js
@@ -0,0 +1,286 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+/**
+ * @author Eric Haines / http://erichaines.com/
+ *
+ * Various useful shaders for the Udacity course "Interactive Rendering"
+ * http://bit.ly/ericity
+ */
+/*global THREE */
+
+THREE.ShaderTypes = {
+
+'gouraud' : {
+
+	uniforms: {
+
+		"uDirLightPos":	{ type: "v3", value: new THREE.Vector3() },
+		"uDirLightColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		"uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) },
+
+		"uMaterialColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+		"uSpecularColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		uKd: {
+			type: "f",
+			value: 0.7
+		},
+		uKs: {
+			type: "f",
+			value: 0.3
+		},
+		shininess: {
+			type: "f",
+			value: 100.0
+		}
+	},
+
+	vertexShader: [
+
+		"uniform vec3 uMaterialColor;",
+		"uniform vec3 uSpecularColor;",
+
+		"uniform vec3 uDirLightPos;",
+		"uniform vec3 uDirLightColor;",
+
+		"uniform vec3 uAmbientLightColor;",
+
+		"uniform float uKd;",
+		"uniform float uKs;",
+		"uniform float shininess;",
+
+		"varying vec3 vColor;",
+
+		"void main() {",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+			"vec3 vNormal = normalize( normalMatrix * normal );",
+			"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+			"vec3 vViewPosition = -mvPosition.xyz;",
+
+			"vColor = uAmbientLightColor * uMaterialColor;",
+
+			"vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );",
+			"vec3 lVector = normalize( lDirection.xyz );",
+
+			// diffuse: N * L
+			"float diffuse = max( dot( vNormal, lVector ), 0.0);",
+
+			"vColor += uKd * uMaterialColor * uDirLightColor * diffuse;",
+
+			// specular: N * H to a power. H is light vector + view vector
+			"vec3 viewPosition = normalize( vViewPosition );",
+			"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+			"float pointDotNormalHalf = max( dot( vNormal, pointHalfVector ), 0.0 );",
+			"float specular = uKs * pow( pointDotNormalHalf, shininess );",
+			// however, if N * L is < 0, the light is below the horizon and should not affect the surface
+			// This can give a hard termination to the highlight, but it's better than some weird sparkle.
+			"if (diffuse <= 0.0) {",
+				"specular = 0.0;",
+			"}",
+
+			"vColor += uDirLightColor * uSpecularColor * specular;",
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"varying vec3 vColor;",
+
+		"void main() {",
+			"gl_FragColor = vec4(vColor, 1.0);",
+		"}"
+
+	].join("\n")
+
+},
+
+
+'phong' : {
+
+	uniforms: {
+
+		"uDirLightPos":	{ type: "v3", value: new THREE.Vector3() },
+		"uDirLightColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		"uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) },
+
+		"uMaterialColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+		"uSpecularColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		uKd: {
+			type: "f",
+			value: 0.7
+		},
+		uKs: {
+			type: "f",
+			value: 0.3
+		},
+		shininess: {
+			type: "f",
+			value: 100.0
+		}
+	},
+
+	vertexShader: [
+
+		"varying vec3 vNormal;",
+		"varying vec3 vViewPosition;",
+
+		"void main() {",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+			"vNormal = normalize( normalMatrix * normal );",
+			"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+			"vViewPosition = -mvPosition.xyz;",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform vec3 uMaterialColor;",
+		"uniform vec3 uSpecularColor;",
+
+		"uniform vec3 uDirLightPos;",
+		"uniform vec3 uDirLightColor;",
+
+		"uniform vec3 uAmbientLightColor;",
+
+		"uniform float uKd;",
+		"uniform float uKs;",
+		"uniform float shininess;",
+
+		"varying vec3 vNormal;",
+		"varying vec3 vViewPosition;",
+
+		"void main() {",
+
+			// ambient
+			"gl_FragColor = vec4( uAmbientLightColor * uMaterialColor, 1.0 );",
+
+			"vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );",
+			"vec3 lVector = normalize( lDirection.xyz );",
+
+			// diffuse: N * L. Normal must be normalized, since it's interpolated.
+			"vec3 normal = normalize( vNormal );",
+			"float diffuse = max( dot( normal, lVector ), 0.0);",
+
+			"gl_FragColor.xyz += uKd * uMaterialColor * uDirLightColor * diffuse;",
+
+			// specular: N * H to a power. H is light vector + view vector
+			"vec3 viewPosition = normalize( vViewPosition );",
+			"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+			"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+			"float specular = uKs * pow( pointDotNormalHalf, shininess );",
+			// however, if N * L is < 0, the light is below the horizon and should not affect the surface
+			// This can give a hard termination to the highlight, but it's better than some weird sparkle.
+			"if (diffuse <= 0.0) {",
+				"specular = 0.0;",
+			"}",
+
+			"gl_FragColor.xyz += uDirLightColor * uSpecularColor * specular;",
+
+		"}"
+
+	].join("\n")
+
+},
+
+'phongBalanced' : {
+
+	uniforms: {
+
+		"uDirLightPos":	{ type: "v3", value: new THREE.Vector3() },
+		"uDirLightColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		"uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) },
+
+		"uMaterialColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+		"uSpecularColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) },
+
+		uKd: {
+			type: "f",
+			value: 0.7
+		},
+		uKs: {
+			type: "f",
+			value: 0.3
+		},
+		shininess: {
+			type: "f",
+			value: 100.0
+		}
+	},
+
+	vertexShader: [
+
+		"varying vec3 vNormal;",
+		"varying vec3 vViewPosition;",
+
+		"void main() {",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+			"vNormal = normalize( normalMatrix * normal );",
+			"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+			"vViewPosition = -mvPosition.xyz;",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform vec3 uMaterialColor;",
+		"uniform vec3 uSpecularColor;",
+
+		"uniform vec3 uDirLightPos;",
+		"uniform vec3 uDirLightColor;",
+
+		"uniform vec3 uAmbientLightColor;",
+
+		"uniform float uKd;",
+		"uniform float uKs;",
+		"uniform float shininess;",
+
+		"varying vec3 vNormal;",
+		"varying vec3 vViewPosition;",
+
+		"void main() {",
+
+			// ambient
+			"gl_FragColor = vec4( uAmbientLightColor * uMaterialColor, 1.0 );",
+
+			"vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );",
+			"vec3 lVector = normalize( lDirection.xyz );",
+
+			// diffuse: N * L. Normal must be normalized, since it's interpolated.
+			"vec3 normal = normalize( vNormal );",
+			"float diffuse = max( dot( normal, lVector ), 0.0);",
+
+			"gl_FragColor.xyz += uKd * uMaterialColor * uDirLightColor * diffuse;",
+
+			// specular: N * H to a power. H is light vector + view vector
+			"vec3 viewPosition = normalize( vViewPosition );",
+			"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+			"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+			"float specular = uKs * pow( pointDotNormalHalf, shininess );",
+			"specular *= (8.0 + shininess)/(8.0*3.14);",
+			//"float specular = uKs * ((float)shininess + 8.0 ) * pow( pointDotNormalHalf, shininess ) / (8 * 3.14);",
+			// however, if N * L is < 0, the light is below the horizon and should not affect the surface
+			// This can give a hard termination to the highlight, but it's better than some weird sparkle.
+			"if (diffuse <= 0.0) {",
+				"specular = 0.0;",
+			"}",
+
+			"gl_FragColor.xyz += uDirLightColor * uSpecularColor * specular;",
+
+		"}"
+
+	].join("\n")
+
+}
+
+};
diff --git a/DUBREUIL/peinture.js b/DUBREUIL/peinture.js
new file mode 100644
index 0000000..e3f1617
--- /dev/null
+++ b/DUBREUIL/peinture.js
@@ -0,0 +1,348 @@
+"use strict"; // good practice - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+////////////////////////////////////////////////////////////////////////////////
+// Focus camera on the travelling bird
+////////////////////////////////////////////////////////////////////////////////
+/*global THREE, Coordinates, document, window, $*/
+
+import * as THREE from 'three';
+import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
+import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+import {dat} from './lib/dat.gui.min.js';
+
+var camera, renderer;
+var cameraControls;
+var clock = new THREE.Clock();
+var headlight;
+var bevelRadius = 1.9;	// TODO: 2.0 causes some geometry bug.
+
+window.scene = new THREE.Scene();
+import {Coordinates} from './lib/Coordinates.js';
+
+function init() {
+	var canvasWidth = 783;
+	var canvasHeight = 600;
+	// For grading the window is fixed in size; here's general code:
+	//var canvasWidth = window.innerWidth;
+	//var canvasHeight = window.innerHeight;
+
+	// RENDERER
+	renderer = new THREE.WebGLRenderer( { antialias: true } );
+	renderer.gammaInput = true;
+	renderer.gammaOutput = true;
+	renderer.setSize(canvasWidth, canvasHeight);
+	renderer.setClearColor( 0xAAAAAA, 1.0 );
+
+	// CAMERA
+	var viewSize = 900;
+	// aspect ratio of width of window divided by height of window
+	var aspectRatio = canvasWidth/canvasHeight;
+	// OrthographicCamera( left, right, top, bottom, near, far )
+	camera = new THREE.OrthographicCamera(
+		-aspectRatio*viewSize / 2, aspectRatio*viewSize / 2,
+		viewSize / 2, -viewSize / 2,
+		0, 10000 );
+	camera.position.set( -890, 600, -480 );
+
+	// CONTROLS
+	cameraControls = new OrbitControls(camera, renderer.domElement);
+
+	// Student: set the target for the camera.
+	// The last known position of the drinking bird is x: -2800, y: 360, z: -1600
+    cameraControls.target.set(-2800, 360, -1600);
+}
+
+function fillScene() {
+	window.scene.fog = new THREE.Fog( 0xAAAAAA, 2000, 4000 );
+
+	// LIGHTS
+	headlight = new THREE.PointLight( 0xFFFFFF, 10000000.0 );
+	window.scene.add( headlight );
+
+	//////////////////////////////
+	// Glass
+	var glass = createGlass(260);
+	glass.position.set(-245-2800, 125, 0-1600);
+	window.scene.add(glass);
+
+	//////////////////////////////
+	// Bird
+	var bird = new THREE.Object3D();
+	createDrinkingBird( bird );
+	bird.position.set(-2800, 0, -1600);
+
+	window.scene.add( bird );
+}
+
+function createGlass(height) {
+	var cupMaterial = new THREE.MeshPhongMaterial( { color: 0x0, specular: 0xFFFFFF, shininess: 100, opacity: 0.3, transparent: true } );
+	var waterMaterial = new THREE.MeshLambertMaterial( {
+		color: 0x1F8BAF
+		//opacity: 0.7,
+		//transparent: true
+	} );
+
+	var glassGeometry = new THREE.CylinderGeometry(120, 100, height, 32);
+	var glassMesh = new THREE.Mesh( glassGeometry, cupMaterial );
+	var glassObject = new THREE.Object3D();
+	glassObject.add(glassMesh);
+
+	var glass1 = new THREE.CylinderGeometry(120, 100, height, 32);
+	glass1.scale = new THREE.Vector3(0.9, 0.85, 0.9);
+	glass1.position = new THREE.Vector3(0, -10, 0);	
+	var glassWater = new THREE.Mesh( glass1, waterMaterial );
+	//var glassWater = new THREE.CylinderGeometry(120, 100, height, 32);//, waterMaterial);
+
+	glassObject.add(glassWater);
+	return glassObject;
+}
+
+
+// Supporting frame for the bird - base + legs + feet
+function createSupport( bsupport ) {
+	var legMaterial = new THREE.MeshPhongMaterial( { shininess: 4 } );
+	legMaterial.color.setHex( 0xAdA79b );
+	legMaterial.specular.setRGB( 0.5, 0.5, 0.5 );
+
+	var footMaterial = new THREE.MeshPhongMaterial( { color: 0x960f0b, shininess: 30 } );
+	footMaterial.specular.setRGB( 0.5, 0.5, 0.5 );
+
+	// base
+	var cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 20+64+110, 4, 2*77+12  ), footMaterial );
+	cube.position.x = -45;	// (20+32) - half of width (20+64+110)/2
+	cube.position.y = 4/2;	// half of height
+	cube.position.z = 0;	// centered at origin
+	bsupport.add( cube );
+
+	// feet
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 20+64+110, 52, 6  ), footMaterial );
+	cube.position.x = -45;	// (20+32) - half of width (20+64+110)/2
+	cube.position.y = 52/2;	// half of height
+	cube.position.z = 77 + 6/2;	// offset 77 + half of depth 6/2
+	bsupport.add( cube );
+
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 20+64+110, 52, 6  ), footMaterial );
+	cube.position.x = -45;	// (20+32) - half of width (20+64+110)/2
+	cube.position.y = 52/2;	// half of height
+	cube.position.z = -(77 + 6/2);	// negative offset 77 + half of depth 6/2
+	bsupport.add( cube );
+
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 64, 104, 6  ), footMaterial );
+	cube.position.x = 0;	// centered on origin along X
+	cube.position.y = 104/2;
+	cube.position.z = 77 + 6/2;	// negative offset 77 + half of depth 6/2
+	bsupport.add( cube );
+
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 64, 104, 6  ), footMaterial );
+	cube.position.x = 0;	// centered on origin along X
+	cube.position.y = 104/2;
+	cube.position.z = -(77 + 6/2);	// negative offset 77 + half of depth 6/2
+	bsupport.add( cube );
+
+	// legs
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 60, 282+4, 4  ), legMaterial );
+	cube.position.x = 0;	// centered on origin along X
+	cube.position.y = 104 + 282/2 - 2;
+	cube.position.z = 77 + 6/2;	// negative offset 77 + half of depth 6/2
+	bsupport.add( cube );
+
+	cube = new THREE.Mesh(
+		new THREE.BoxGeometry( 60, 282+4, 4  ), legMaterial );
+	cube.position.x = 0;	// centered on origin along X
+	cube.position.y = 104 + 282/2 - 2;
+	cube.position.z = -(77 + 6/2);	// negative offset 77 + half of depth 6/2
+	bsupport.add( cube );
+}
+
+// Body of the bird - body and the connector of body and head
+function createBody(bbody) {
+	var bodyMaterial = new THREE.MeshPhongMaterial( { shininess: 100 } );
+	bodyMaterial.color.setRGB( 31/255, 86/255, 169/255 );
+	bodyMaterial.specular.setRGB( 0.5, 0.5, 0.5 );
+
+	var glassMaterial = new THREE.MeshPhongMaterial( { color: 0x0, specular: 0xFFFFFF, shininess: 100, opacity: 0.3, transparent: true } );
+
+	var crossbarMaterial = new THREE.MeshPhongMaterial( { color: 0x808080, specular: 0xFFFFFF, shininess: 400 } );
+
+	// body
+	var sphere = new THREE.Mesh(
+		new THREE.SphereGeometry( 104/2, 32, 16, 0, Math.PI * 2, Math.PI/2, Math.PI ), bodyMaterial );
+	sphere.position.x = 0;
+	sphere.position.y = 160;
+	sphere.position.z = 0;
+	bbody.add( sphere );
+
+	// cap for top of hemisphere
+	var cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 104/2, 104/2, 0, 32 ), bodyMaterial );
+	cylinder.position.x = 0;
+	cylinder.position.y = 160;
+	cylinder.position.z = 0;
+	bbody.add( cylinder );
+
+	cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 12/2, 12/2, 390 - 100, 32 ), bodyMaterial );
+	cylinder.position.x = 0;
+	cylinder.position.y = 160 + 390/2 - 100;
+	cylinder.position.z = 0;
+	bbody.add( cylinder );
+
+	// glass stem
+	sphere = new THREE.Mesh(
+		new THREE.SphereGeometry( 116/2, 32, 16 ), glassMaterial );
+	sphere.position.x = 0;
+	sphere.position.y = 160;
+	sphere.position.z = 0;
+	bbody.add( sphere );
+
+	cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 24/2, 24/2, 390, 32 ), glassMaterial );
+	cylinder.position.x = 0;
+	cylinder.position.y = 160 + 390/2;
+	cylinder.position.z = 0;
+	bbody.add( cylinder );
+
+	// crossbar
+	cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 5, 5, 200, 32 ), crossbarMaterial );
+	cylinder.position.set( 0, 360, 0 );
+	cylinder.rotation.x = 90 * Math.PI / 180.0;
+	bbody.add( cylinder );
+}
+
+// Head of the bird - head + hat
+function createHead(bhead) {
+	var headMaterial = new THREE.MeshLambertMaterial( );
+	headMaterial.color.r = 104/255;
+	headMaterial.color.g = 1/255;
+	headMaterial.color.b = 5/255;
+
+	var hatMaterial = new THREE.MeshPhongMaterial( { shininess: 100 } );
+	hatMaterial.color.r = 24/255;
+	hatMaterial.color.g = 38/255;
+	hatMaterial.color.b = 77/255;
+	hatMaterial.specular.setRGB( 0.5, 0.5, 0.5 );
+
+	var eyeMaterial = new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x303030, shininess: 4 } );
+
+	// head
+	var sphere = new THREE.Mesh(
+		new THREE.SphereGeometry( 104/2, 32, 16 ), headMaterial );
+	sphere.position.x = 0;
+	sphere.position.y = 160 + 390;
+	sphere.position.z = 0;
+	bhead.add( sphere );
+
+	// hat
+	var cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 142/2, 142/2, 10, 32 ), hatMaterial );
+	cylinder.position.x = 0;
+	cylinder.position.y = 160 + 390 + 40 + 10/2;
+	cylinder.position.z = 0;
+	bhead.add( cylinder );
+
+	cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 80/2, 80/2, 70, 32 ), hatMaterial );
+	cylinder.position.x = 0;
+	cylinder.position.y = 160 + 390 + 40 + 10 + 70/2;
+	cylinder.position.z = 0;
+	bhead.add( cylinder );
+
+	// nose
+	cylinder = new THREE.Mesh(
+		new THREE.CylinderGeometry( 6, 14, 70, 32 ), headMaterial );
+	cylinder.position.set( -70, 530, 0 );
+	cylinder.rotation.z = 90 * Math.PI / 180.0;
+	bhead.add( cylinder );
+
+	// eyes
+	var sphGeom = new THREE.SphereGeometry( 10, 32, 16 );
+
+	// left eye
+	sphere = new THREE.Mesh( sphGeom, eyeMaterial );
+	sphere.position.set( -48, 560, 0 );
+	var eye = new THREE.Object3D();
+	eye.add( sphere );
+	eye.rotation.y = 20 * Math.PI / 180.0;
+	bhead.add( eye );
+
+	// right eye
+	sphere = new THREE.Mesh( sphGeom, eyeMaterial );
+	sphere.position.set( -48, 560, 0 );
+	eye = new THREE.Object3D();
+	eye.add( sphere );
+	eye.rotation.y = -20 * Math.PI / 180.0;
+	bhead.add( eye );
+}
+
+function createDrinkingBird(bbird) {
+	var support = new THREE.Object3D();
+	var body = new THREE.Object3D();
+	var head = new THREE.Object3D();
+
+	// MODELS
+	// base + legs + feet
+	createSupport(support);
+
+	// body + body/head connector
+	createBody(body);
+
+	// head + hat
+	createHead(head);
+
+	// make moving piece
+
+	var bodyhead = new THREE.Object3D();
+	bodyhead.add(body);
+	bodyhead.add(head);
+
+	// add field for animated part, for simplicity
+	bbird.animated = bodyhead;
+
+	bbird.add(support);
+	bbird.add(bodyhead);
+}
+
+function drawHelpers() {
+	Coordinates.drawGround({size:10000});
+	Coordinates.drawGrid({size:10000,scale:0.01});
+}
+
+function addToDOM() {
+	var container = document.getElementById('webGL');
+	var canvas = container.getElementsByTagName('canvas');
+	if (canvas.length>0) {
+		container.removeChild(canvas[0]);
+	}
+	container.appendChild( renderer.domElement );
+}
+
+function animate() {
+	window.requestAnimationFrame(animate);
+	render();
+}
+
+function render() {
+	var delta = clock.getDelta();
+	cameraControls.update(delta);
+
+	headlight.position.copy( camera.position );
+
+	renderer.render(window.scene, camera);
+}
+
+try {
+	init();
+	fillScene();
+	drawHelpers();
+	addToDOM();
+	animate();
+} catch(e) {
+	var errorReport = "Your program encountered an unrecoverable error, can not draw on canvas. Error was:<br/><br/>";
+	$('#webGL').append(errorReport+e);
+}
\ No newline at end of file
-- 
GitLab