text: var Three = (function (exports) {
  	'use strict';
  
  	var _Math = {
  
  		DEG2RAD: Math.PI / 180,
  		RAD2DEG: 180 / Math.PI,
  
  		generateUUID: ( function () {
  
  			// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  
  			var lut = [];
  
  			for ( var i = 0; i < 256; i ++ ) {
  
  				lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
  
  			}
  
  			return function generateUUID() {
  
  				var d0 = Math.random() * 0xffffffff | 0;
  				var d1 = Math.random() * 0xffffffff | 0;
  				var d2 = Math.random() * 0xffffffff | 0;
  				var d3 = Math.random() * 0xffffffff | 0;
  				var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
  					lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
  					lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
  					lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
  
  				// .toUpperCase() here flattens concatenated strings to save heap memory space.
  				return uuid.toUpperCase();
  
  			};
  
  		} )(),
  
  		clamp: function ( value, min, max ) {
  
  			return Math.max( min, Math.min( max, value ) );
  
  		},
  
  		// compute euclidian modulo of m % n
  		// https://en.wikipedia.org/wiki/Modulo_operation
  
  		euclideanModulo: function ( n, m ) {
  
  			return ( ( n % m ) + m ) % m;
  
  		},
  
  		// 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 );
  
  		},
  
  		// https://en.wikipedia.org/wiki/Linear_interpolation
  
  		lerp: function ( x, y, t ) {
  
  			return ( 1 - t ) * x + t * y;
  
  		},
  
  		// 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 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() );
  
  		},
  
  		degToRad: function ( degrees ) {
  
  			return degrees * _Math.DEG2RAD;
  
  		},
  
  		radToDeg: function ( radians ) {
  
  			return radians * _Math.RAD2DEG;
  
  		},
  
  		isPowerOfTwo: function ( value ) {
  
  			return ( value & ( value - 1 ) ) === 0 && value !== 0;
  
  		},
  
  		ceilPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
  
  		},
  
  		floorPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
  
  		}
  
  	};
  
  	function Quaternion( x, y, z, w ) {
  
  		this._x = x || 0;
  		this._y = y || 0;
  		this._z = z || 0;
  		this._w = ( w !== undefined ) ? w : 1;
  
  	}
  
  	Object.assign( Quaternion, {
  
  		slerp: function ( qa, qb, qm, t ) {
  
  			return qm.copy( qa ).slerp( qb, t );
  
  		},
  
  		slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
  
  			// fuzz-free, array-based Quaternion SLERP operation
  
  			var x0 = src0[ srcOffset0 + 0 ],
  				y0 = src0[ srcOffset0 + 1 ],
  				z0 = src0[ srcOffset0 + 2 ],
  				w0 = src0[ srcOffset0 + 3 ],
  
  				x1 = src1[ srcOffset1 + 0 ],
  				y1 = src1[ srcOffset1 + 1 ],
  				z1 = src1[ srcOffset1 + 2 ],
  				w1 = src1[ srcOffset1 + 3 ];
  
  			if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
  
  				var s = 1 - t,
  
  					cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
  
  					dir = ( cos >= 0 ? 1 : - 1 ),
  					sqrSin = 1 - cos * cos;
  
  				// Skip the Slerp for tiny steps to avoid numeric problems:
  				if ( sqrSin > Number.EPSILON ) {
  
  					var sin = Math.sqrt( sqrSin ),
  						len = Math.atan2( sin, cos * dir );
  
  					s = Math.sin( s * len ) / sin;
  					t = Math.sin( t * len ) / sin;
  
  				}
  
  				var tDir = t * dir;
  
  				x0 = x0 * s + x1 * tDir;
  				y0 = y0 * s + y1 * tDir;
  				z0 = z0 * s + z1 * tDir;
  				w0 = w0 * s + w1 * tDir;
  
  				// Normalize in case we just did a lerp:
  				if ( s === 1 - t ) {
  
  					var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
  
  					x0 *= f;
  					y0 *= f;
  					z0 *= f;
  					w0 *= f;
  
  				}
  
  			}
  
  			dst[ dstOffset ] = x0;
  			dst[ dstOffset + 1 ] = y0;
  			dst[ dstOffset + 2 ] = z0;
  			dst[ dstOffset + 3 ] = w0;
  
  		}
  
  	} );
  
  	Object.defineProperties( Quaternion.prototype, {
  
  		x: {
  
  			get: function () {
  
  				return this._x;
  
  			},
  
  			set: function ( value ) {
  
  				this._x = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		y: {
  
  			get: function () {
  
  				return this._y;
  
  			},
  
  			set: function ( value ) {
  
  				this._y = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		z: {
  
  			get: function () {
  
  				return this._z;
  
  			},
  
  			set: function ( value ) {
  
  				this._z = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		w: {
  
  			get: function () {
  
  				return this._w;
  
  			},
  
  			set: function ( value ) {
  
  				this._w = value;
  				this.onChangeCallback();
  
  			}
  
  		}
  
  	} );
  
  	Object.assign( Quaternion.prototype, {
  
  		set: function ( x, y, z, w ) {
  
  			this._x = x;
  			this._y = y;
  			this._z = z;
  			this._w = w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this._x, this._y, this._z, this._w );
  
  		},
  
  		copy: function ( quaternion ) {
  
  			this._x = quaternion.x;
  			this._y = quaternion.y;
  			this._z = quaternion.z;
  			this._w = quaternion.w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromEuler: function ( euler, update ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				throw new Error( 'Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
  
  			// http://www.mathworks.com/matlabcentral/fileexchange/
  			// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
  			//	content/SpinCalc.m
  
  			var cos = Math.cos;
  			var sin = Math.sin;
  
  			var c1 = cos( x / 2 );
  			var c2 = cos( y / 2 );
  			var c3 = cos( z / 2 );
  
  			var s1 = sin( x / 2 );
  			var s2 = sin( y / 2 );
  			var s3 = sin( z / 2 );
  
  			if ( 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;
  
  			}
  
  			if ( update !== false ) this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromAxisAngle: function ( axis, angle ) {
  
  			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  
  			// assumes axis is 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 );
  
  			this.onChangeCallback();
  
  			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;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromUnitVectors: function () {
  
  			// assumes direction vectors vFrom and vTo are normalized
  
  			var v1 = new Vector3();
  			var r;
  
  			var EPS = 0.000001;
  
  			return function setFromUnitVectors( vFrom, vTo ) {
  
  				if ( v1 === undefined ) v1 = new Vector3();
  
  				r = vFrom.dot( vTo ) + 1;
  
  				if ( r < EPS ) {
  
  					r = 0;
  
  					if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
  
  						v1.set( - vFrom.y, vFrom.x, 0 );
  
  					} else {
  
  						v1.set( 0, - vFrom.z, vFrom.y );
  
  					}
  
  				} else {
  
  					v1.crossVectors( vFrom, vTo );
  
  				}
  
  				this._x = v1.x;
  				this._y = v1.y;
  				this._z = v1.z;
  				this._w = r;
  
  				return this.normalize();
  
  			};
  
  		}(),
  
  		inverse: function () {
  
  			// quaternion is assumed to have unit length
  
  			return this.conjugate();
  
  		},
  
  		conjugate: function () {
  
  			this._x *= - 1;
  			this._y *= - 1;
  			this._z *= - 1;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
  
  		},
  
  		lengthSq: function () {
  
  			return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
  
  		},
  
  		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;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		multiply: function ( q, p ) {
  
  			if ( p !== undefined ) {
  
  				console.warn( 'Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
  				return this.multiplyQuaternions( q, p );
  
  			}
  
  			return this.multiplyQuaternions( this, q );
  
  		},
  
  		premultiply: function ( q ) {
  
  			return this.multiplyQuaternions( q, this );
  
  		},
  
  		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;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		slerp: function ( qb, t ) {
  
  			if ( t === 0 ) return this;
  			if ( t === 1 ) return this.copy( qb );
  
  			var x = this._x, y = this._y, z = this._z, w = this._w;
  
  			// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
  
  			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 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 halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
  			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 );
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		equals: function ( quaternion ) {
  
  			return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this._x = array[ offset ];
  			this._y = array[ offset + 1 ];
  			this._z = array[ offset + 2 ];
  			this._w = array[ offset + 3 ];
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this._x;
  			array[ offset + 1 ] = this._y;
  			array[ offset + 2 ] = this._z;
  			array[ offset + 3 ] = this._w;
  
  			return array;
  
  		},
  
  		onChange: function ( callback ) {
  
  			this.onChangeCallback = callback;
  
  			return this;
  
  		},
  
  		onChangeCallback: function () {}
  
  	} );
  
  	function Vector3( x, y, z ) {
  
  		this.x = x || 0;
  		this.y = y || 0;
  		this.z = z || 0;
  
  	}
  
  	Object.assign( Vector3.prototype, {
  
  		isVector3: true,
  
  		set: function ( x, y, z ) {
  
  			this.x = x;
  			this.y = y;
  			this.z = z;
  
  			return this;
  
  		},
  
  		setScalar: function ( scalar ) {
  
  			this.x = scalar;
  			this.y = scalar;
  			this.z = scalar;
  
  			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 );
  
  			}
  
  			return this;
  
  		},
  
  		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 );
  
  			}
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this.x, this.y, this.z );
  
  		},
  
  		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( 'Vector3: .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;
  
  		},
  
  		addScaledVector: function ( v, s ) {
  
  			this.x += v.x * s;
  			this.y += v.y * s;
  			this.z += v.z * s;
  
  			return this;
  
  		},
  
  		sub: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .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;
  
  		},
  
  		subScalar: function ( s ) {
  
  			this.x -= s;
  			this.y -= s;
  			this.z -= s;
  
  			return this;
  
  		},
  
  		subVectors: function ( a, b ) {
  
  			this.x = a.x - b.x;
  			this.y = a.y - b.y;
  			this.z = a.z - b.z;
  
  			return this;
  
  		},
  
  		multiply: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .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 ( scalar ) {
  
  			this.x *= scalar;
  			this.y *= scalar;
  			this.z *= scalar;
  
  			return this;
  
  		},
  
  		multiplyVectors: function ( a, b ) {
  
  			this.x = a.x * b.x;
  			this.y = a.y * b.y;
  			this.z = a.z * b.z;
  
  			return this;
  
  		},
  
  		applyEuler: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyEuler( euler ) {
  
  				if ( ! ( euler && euler.isEuler ) ) {
  
  					console.error( 'Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  				}
  
  				return this.applyQuaternion( quaternion.setFromEuler( euler ) );
  
  			};
  
  		}(),
  
  		applyAxisAngle: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyAxisAngle( axis, angle ) {
  
  				return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
  
  			};
  
  		}(),
  
  		applyMatrix3: function ( m ) {
  
  			var x = this.x, y = this.y, 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 ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
  
  			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;
  
  			return this;
  
  		},
  
  		applyQuaternion: function ( q ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var qx = q.x, qy = q.y, qz = q.z, 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;
  
  		},
  
  		project: function () {
  
  			var matrix = new Matrix4();
  
  			return function project( camera ) {
  
  				matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
  				return this.applyMatrix4( matrix );
  
  			};
  
  		}(),
  
  		unproject: function () {
  
  			var matrix = new Matrix4();
  
  			return function unproject( camera ) {
  
  				matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
  				return this.applyMatrix4( matrix );
  
  			};
  
  		}(),
  
  		transformDirection: function ( m ) {
  
  			// input: 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;
  
  			return this.normalize();
  
  		},
  
  		divide: function ( v ) {
  
  			this.x /= v.x;
  			this.y /= v.y;
  			this.z /= v.z;
  
  			return this;
  
  		},
  
  		divideScalar: function ( scalar ) {
  
  			return this.multiplyScalar( 1 / scalar );
  
  		},
  
  		min: function ( v ) {
  
  			this.x = Math.min( this.x, v.x );
  			this.y = Math.min( this.y, v.y );
  			this.z = Math.min( this.z, v.z );
  
  			return this;
  
  		},
  
  		max: function ( v ) {
  
  			this.x = Math.max( this.x, v.x );
  			this.y = Math.max( this.y, v.y );
  			this.z = Math.max( this.z, v.z );
  
  			return this;
  
  		},
  
  		clamp: function ( min, max ) {
  
  			// assumes min < max, componentwise
  
  			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
  			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
  			this.z = Math.max( min.z, Math.min( max.z, this.z ) );
  
  			return this;
  
  		},
  
  		clampScalar: function () {
  
  			var min = new Vector3();
  			var max = new Vector3();
  
  			return function clampScalar( minVal, maxVal ) {
  
  				min.set( minVal, minVal, minVal );
  				max.set( maxVal, maxVal, maxVal );
  
  				return this.clamp( min, max );
  
  			};
  
  		}(),
  
  		clampLength: function ( min, max ) {
  
  			var length = this.length();
  
  			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
  		},
  
  		floor: function () {
  
  			this.x = Math.floor( this.x );
  			this.y = Math.floor( this.y );
  			this.z = Math.floor( this.z );
  
  			return this;
  
  		},
  
  		ceil: function () {
  
  			this.x = Math.ceil( this.x );
  			this.y = Math.ceil( this.y );
  			this.z = Math.ceil( this.z );
  
  			return this;
  
  		},
  
  		round: function () {
  
  			this.x = Math.round( this.x );
  			this.y = Math.round( this.y );
  			this.z = Math.round( this.z );
  
  			return this;
  
  		},
  
  		roundToZero: function () {
  
  			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
  			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
  			this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
  
  			return this;
  
  		},
  
  		negate: function () {
  
  			this.x = - this.x;
  			this.y = - this.y;
  			this.z = - this.z;
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this.x * v.x + this.y * v.y + this.z * v.z;
  
  		},
  
  		// TODO lengthSquared?
  
  		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 );
  
  		},
  
  		manhattanLength: function () {
  
  			return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
  
  		},
  
  		normalize: function () {
  
  			return this.divideScalar( this.length() || 1 );
  
  		},
  
  		setLength: function ( length ) {
  
  			return this.normalize().multiplyScalar( length );
  
  		},
  
  		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;
  
  		},
  
  		lerpVectors: function ( v1, v2, alpha ) {
  
  			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
  		},
  
  		cross: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
  				return this.crossVectors( v, w );
  
  			}
  
  			return this.crossVectors( this, v );
  
  		},
  
  		crossVectors: function ( a, b ) {
  
  			var ax = a.x, ay = a.y, az = a.z;
  			var bx = b.x, by = b.y, bz = b.z;
  
  			this.x = ay * bz - az * by;
  			this.y = az * bx - ax * bz;
  			this.z = ax * by - ay * bx;
  
  			return this;
  
  		},
  
  		projectOnVector: function ( vector ) {
  
  			var scalar = vector.dot( this ) / vector.lengthSq();
  
  			return this.copy( vector ).multiplyScalar( scalar );
  
  		},
  
  		projectOnPlane: function () {
  
  			var v1 = new Vector3();
  
  			return function projectOnPlane( planeNormal ) {
  
  				v1.copy( this ).projectOnVector( planeNormal );
  
  				return this.sub( v1 );
  
  			};
  
  		}(),
  
  		reflect: function () {
  
  			// reflect incident vector off plane orthogonal to normal
  			// normal is assumed to have unit length
  
  			var v1 = new Vector3();
  
  			return function reflect( normal ) {
  
  				return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
  
  			};
  
  		}(),
  
  		angleTo: function ( v ) {
  
  			var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
  
  			// clamp, to handle numerical problems
  
  			return Math.acos( _Math.clamp( theta, - 1, 1 ) );
  
  		},
  
  		distanceTo: function ( v ) {
  
  			return Math.sqrt( this.distanceToSquared( v ) );
  
  		},
  
  		distanceToSquared: function ( v ) {
  
  			var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
  
  			return dx * dx + dy * dy + dz * dz;
  
  		},
  
  		manhattanDistanceTo: function ( v ) {
  
  			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
  
  		},
  
  		setFromSpherical: function ( s ) {
  
  			var sinPhiRadius = Math.sin( s.phi ) * s.radius;
  
  			this.x = sinPhiRadius * Math.sin( s.theta );
  			this.y = Math.cos( s.phi ) * s.radius;
  			this.z = sinPhiRadius * Math.cos( s.theta );
  
  			return this;
  
  		},
  
  		setFromCylindrical: function ( c ) {
  
  			this.x = c.radius * Math.sin( c.theta );
  			this.y = c.y;
  			this.z = c.radius * Math.cos( c.theta );
  
  			return this;
  
  		},
  
  		setFromMatrixPosition: function ( m ) {
  
  			var e = m.elements;
  
  			this.x = e[ 12 ];
  			this.y = e[ 13 ];
  			this.z = e[ 14 ];
  
  			return this;
  
  		},
  
  		setFromMatrixScale: function ( m ) {
  
  			var sx = this.setFromMatrixColumn( m, 0 ).length();
  			var sy = this.setFromMatrixColumn( m, 1 ).length();
  			var sz = this.setFromMatrixColumn( m, 2 ).length();
  
  			this.x = sx;
  			this.y = sy;
  			this.z = sz;
  
  			return this;
  
  		},
  
  		setFromMatrixColumn: function ( m, index ) {
  
  			return this.fromArray( m.elements, index * 4 );
  
  		},
  
  		equals: function ( v ) {
  
  			return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this.x = array[ offset ];
  			this.y = array[ offset + 1 ];
  			this.z = array[ offset + 2 ];
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this.x;
  			array[ offset + 1 ] = this.y;
  			array[ offset + 2 ] = this.z;
  
  			return array;
  
  		},
  
  		fromBufferAttribute: function ( attribute, index, offset ) {
  
  			if ( offset !== undefined ) {
  
  				console.warn( 'Vector3: offset has been removed from .fromBufferAttribute().' );
  
  			}
  
  			this.x = attribute.getX( index );
  			this.y = attribute.getY( index );
  			this.z = attribute.getZ( index );
  
  			return this;
  
  		}
  
  	} );
  
  	function Matrix4() {
  
  		this.elements = [
  
  			1, 0, 0, 0,
  			0, 1, 0, 0,
  			0, 0, 1, 0,
  			0, 0, 0, 1
  
  		];
  
  		if ( arguments.length > 0 ) {
  
  			console.error( 'Matrix4: the constructor no longer reads arguments. use .set() instead.' );
  
  		}
  
  	}
  
  	Object.assign( Matrix4.prototype, {
  
  		isMatrix4: true,
  
  		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;
  
  		},
  
  		clone: function () {
  
  			return new Matrix4().fromArray( this.elements );
  
  		},
  
  		copy: function ( m ) {
  
  			var te = this.elements;
  			var me = m.elements;
  
  			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
  			te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
  			te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
  			te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
  
  			return this;
  
  		},
  
  		copyPosition: function ( m ) {
  
  			var te = this.elements, me = m.elements;
  
  			te[ 12 ] = me[ 12 ];
  			te[ 13 ] = me[ 13 ];
  			te[ 14 ] = me[ 14 ];
  
  			return this;
  
  		},
  
  		extractBasis: function ( xAxis, yAxis, zAxis ) {
  
  			xAxis.setFromMatrixColumn( this, 0 );
  			yAxis.setFromMatrixColumn( this, 1 );
  			zAxis.setFromMatrixColumn( this, 2 );
  
  			return this;
  
  		},
  
  		makeBasis: function ( xAxis, yAxis, zAxis ) {
  
  			this.set(
  				xAxis.x, yAxis.x, zAxis.x, 0,
  				xAxis.y, yAxis.y, zAxis.y, 0,
  				xAxis.z, yAxis.z, zAxis.z, 0,
  				0, 0, 0, 1
  			);
  
  			return this;
  
  		},
  
  		extractRotation: function () {
  
  			var v1 = new Vector3();
  
  			return function extractRotation( m ) {
  
  				// this method does not support reflection matrices
  
  				var te = this.elements;
  				var me = m.elements;
  
  				var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
  				var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
  				var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
  
  				te[ 0 ] = me[ 0 ] * scaleX;
  				te[ 1 ] = me[ 1 ] * scaleX;
  				te[ 2 ] = me[ 2 ] * scaleX;
  				te[ 3 ] = 0;
  
  				te[ 4 ] = me[ 4 ] * scaleY;
  				te[ 5 ] = me[ 5 ] * scaleY;
  				te[ 6 ] = me[ 6 ] * scaleY;
  				te[ 7 ] = 0;
  
  				te[ 8 ] = me[ 8 ] * scaleZ;
  				te[ 9 ] = me[ 9 ] * scaleZ;
  				te[ 10 ] = me[ 10 ] * scaleZ;
  				te[ 11 ] = 0;
  
  				te[ 12 ] = 0;
  				te[ 13 ] = 0;
  				te[ 14 ] = 0;
  				te[ 15 ] = 1;
  
  				return this;
  
  			};
  
  		}(),
  
  		makeRotationFromEuler: function ( euler ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				console.error( 'Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var te = this.elements;
  
  			var x = euler.x, y = euler.y, z = euler.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 ( euler.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 ( euler.order === 'YXZ' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce + df * b;
  				te[ 4 ] = de * b - cf;
  				te[ 8 ] = a * d;
  
  				te[ 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 ( euler.order === 'ZXY' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce - df * b;
  				te[ 4 ] = - a * f;
  				te[ 8 ] = de + cf * b;
  
  				te[ 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 ( euler.order === 'ZYX' ) {
  
  				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = be * d - af;
  				te[ 8 ] = ae * d + bf;
  
  				te[ 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 ( euler.order === 'YZX' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = bd - ac * f;
  				te[ 8 ] = bc * f + ad;
  
  				te[ 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 ( euler.order === 'XZY' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = - f;
  				te[ 8 ] = d * e;
  
  				te[ 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;
  
  			}
  
  			// bottom row
  			te[ 3 ] = 0;
  			te[ 7 ] = 0;
  			te[ 11 ] = 0;
  
  			// last column
  			te[ 12 ] = 0;
  			te[ 13 ] = 0;
  			te[ 14 ] = 0;
  			te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		makeRotationFromQuaternion: function () {
  
  			var zero = new Vector3( 0, 0, 0 );
  			var one = new Vector3( 1, 1, 1 );
  
  			return function makeRotationFromQuaternion( q ) {
  
  				return this.compose( zero, q, one );
  
  			};
  
  		}(),
  
  		lookAt: function () {
  
  			var x = new Vector3();
  			var y = new Vector3();
  			var z = new Vector3();
  
  			return function lookAt( eye, target, up ) {
  
  				var te = this.elements;
  
  				z.subVectors( eye, target );
  
  				if ( z.lengthSq() === 0 ) {
  
  					// eye and target are in the same position
  
  					z.z = 1;
  
  				}
  
  				z.normalize();
  				x.crossVectors( up, z );
  
  				if ( x.lengthSq() === 0 ) {
  
  					// up and z are parallel
  
  					if ( Math.abs( up.z ) === 1 ) {
  
  						z.x += 0.0001;
  
  					} else {
  
  						z.z += 0.0001;
  
  					}
  
  					z.normalize();
  					x.crossVectors( up, z );
  
  				}
  
  				x.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( 'Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
  				return this.multiplyMatrices( m, n );
  
  			}
  
  			return this.multiplyMatrices( this, m );
  
  		},
  
  		premultiply: function ( m ) {
  
  			return this.multiplyMatrices( m, this );
  
  		},
  
  		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;
  
  		},
  
  		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;
  
  		},
  
  		applyToBufferAttribute: function () {
  
  			var v1 = new Vector3();
  
  			return function applyToBufferAttribute( attribute ) {
  
  				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
  					v1.x = attribute.getX( i );
  					v1.y = attribute.getY( i );
  					v1.z = attribute.getZ( i );
  
  					v1.applyMatrix4( this );
  
  					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
  				}
  
  				return attribute;
  
  			};
  
  		}(),
  
  		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;
  
  		},
  
  		setPosition: function ( v ) {
  
  			var te = this.elements;
  
  			te[ 12 ] = v.x;
  			te[ 13 ] = v.y;
  			te[ 14 ] = v.z;
  
  			return this;
  
  		},
  
  		getInverse: function ( m, throwOnDegenerate ) {
  
  			// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
  			var te = this.elements,
  				me = m.elements,
  
  				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
  				n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
  				n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
  				n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
  
  				t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
  				t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
  				t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
  				t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
  
  			var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
  
  			if ( det === 0 ) {
  
  				var msg = "Matrix4: .getInverse() can't invert matrix, determinant is 0";
  
  				if ( throwOnDegenerate === true ) {
  
  					throw new Error( msg );
  
  				} else {
  
  					console.warn( msg );
  
  				}
  
  				return this.identity();
  
  			}
  
  			var detInv = 1 / det;
  
  			te[ 0 ] = t11 * detInv;
  			te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
  			te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
  			te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
  
  			te[ 4 ] = t12 * detInv;
  			te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
  			te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
  			te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
  
  			te[ 8 ] = t13 * detInv;
  			te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
  			te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
  			te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
  
  			te[ 12 ] = t14 * detInv;
  			te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
  			te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
  			te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
  
  			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, 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;
  
  		},
  
  		makeShear: function ( x, y, z ) {
  
  			this.set(
  
  				1, y, z, 0,
  				x, 1, z, 0,
  				x, y, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		compose: function ( position, quaternion, scale ) {
  
  			var te = this.elements;
  
  			var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._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;
  
  			var sx = scale.x, sy = scale.y, sz = scale.z;
  
  		        te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
  		        te[ 1 ] = ( xy + wz ) * sx;
  		        te[ 2 ] = ( xz - wy ) * sx;
  		        te[ 3 ] = 0;
  
  		        te[ 4 ] = ( xy - wz ) * sy;
  		        te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
  		        te[ 6 ] = ( yz + wx ) * sy;
  		        te[ 7 ] = 0;
  
  		        te[ 8 ] = ( xz + wy ) * sz;
  		        te[ 9 ] = ( yz - wx ) * sz;
  		        te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
  		        te[ 11 ] = 0;
  
  		        te[ 12 ] = position.x;
  		        te[ 13 ] = position.y;
  		        te[ 14 ] = position.z;
  		        te[ 15 ] = 1;
  
  		        return this;
  
  		},
  
  		decompose: function () {
  
  			var vector = new Vector3();
  			var matrix = new Matrix4();
  
  			return function decompose( position, quaternion, scale ) {
  
  				var te = this.elements;
  
  				var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
  				var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
  				var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
  
  				// if determine is negative, we need to invert one scale
  				var det = this.determinant();
  				if ( det < 0 ) sx = - sx;
  
  				position.x = te[ 12 ];
  				position.y = te[ 13 ];
  				position.z = te[ 14 ];
  
  				// scale the rotation part
  				matrix.copy( this );
  
  				var invSX = 1 / sx;
  				var invSY = 1 / sy;
  				var invSZ = 1 / sz;
  
  				matrix.elements[ 0 ] *= invSX;
  				matrix.elements[ 1 ] *= invSX;
  				matrix.elements[ 2 ] *= invSX;
  
  				matrix.elements[ 4 ] *= invSY;
  				matrix.elements[ 5 ] *= invSY;
  				matrix.elements[ 6 ] *= invSY;
  
  				matrix.elements[ 8 ] *= invSZ;
  				matrix.elements[ 9 ] *= invSZ;
  				matrix.elements[ 10 ] *= invSZ;
  
  				quaternion.setFromRotationMatrix( matrix );
  
  				scale.x = sx;
  				scale.y = sy;
  				scale.z = sz;
  
  				return this;
  
  			};
  
  		}(),
  
  		makePerspective: function ( left, right, top, bottom, near, far ) {
  
  			if ( far === undefined ) {
  
  				console.warn( 'Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
  
  			}
  
  			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;
  
  		},
  
  		makeOrthographic: function ( left, right, top, bottom, near, far ) {
  
  			var te = this.elements;
  			var w = 1.0 / ( right - left );
  			var h = 1.0 / ( top - bottom );
  			var p = 1.0 / ( 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;
  
  		},
  
  		equals: function ( matrix ) {
  
  			var te = this.elements;
  			var me = matrix.elements;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				if ( te[ i ] !== me[ i ] ) return false;
  
  			}
  
  			return true;
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				this.elements[ i ] = array[ i + offset ];
  
  			}
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			var te = this.elements;
  
  			array[ offset ] = te[ 0 ];
  			array[ offset + 1 ] = te[ 1 ];
  			array[ offset + 2 ] = te[ 2 ];
  			array[ offset + 3 ] = te[ 3 ];
  
  			array[ offset + 4 ] = te[ 4 ];
  			array[ offset + 5 ] = te[ 5 ];
  			array[ offset + 6 ] = te[ 6 ];
  			array[ offset + 7 ] = te[ 7 ];
  
  			array[ offset + 8 ] = te[ 8 ];
  			array[ offset + 9 ] = te[ 9 ];
  			array[ offset + 10 ] = te[ 10 ];
  			array[ offset + 11 ] = te[ 11 ];
  
  			array[ offset + 12 ] = te[ 12 ];
  			array[ offset + 13 ] = te[ 13 ];
  			array[ offset + 14 ] = te[ 14 ];
  			array[ offset + 15 ] = te[ 15 ];
  
  			return array;
  
  		}
  
  	} );
  
  	function EventDispatcher() {}
  
  	Object.assign( EventDispatcher.prototype, {
  
  		addEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) this._listeners = {};
  
  			var listeners = this._listeners;
  
  			if ( listeners[ type ] === undefined ) {
  
  				listeners[ type ] = [];
  
  			}
  
  			if ( listeners[ type ].indexOf( listener ) === - 1 ) {
  
  				listeners[ type ].push( listener );
  
  			}
  
  		},
  
  		hasEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) return false;
  
  			var listeners = this._listeners;
  
  			return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
  
  		},
  
  		removeEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) return;
  
  			var listeners = this._listeners;
  			var listenerArray = listeners[ type ];
  
  			if ( listenerArray !== undefined ) {
  
  				var index = listenerArray.indexOf( listener );
  
  				if ( index !== - 1 ) {
  
  					listenerArray.splice( index, 1 );
  
  				}
  
  			}
  
  		},
  
  		dispatchEvent: function ( event ) {
  
  			if ( this._listeners === undefined ) return;
  
  			var listeners = this._listeners;
  			var listenerArray = listeners[ event.type ];
  
  			if ( listenerArray !== undefined ) {
  
  				event.target = this;
  
  				var array = listenerArray.slice( 0 );
  
  				for ( var i = 0, l = array.length; i < l; i ++ ) {
  
  					array[ i ].call( this, event );
  
  				}
  
  			}
  
  		}
  
  	} );
  
  	function Euler( x, y, z, order ) {
  
  		this._x = x || 0;
  		this._y = y || 0;
  		this._z = z || 0;
  		this._order = order || Euler.DefaultOrder;
  
  	}
  
  	Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
  
  	Euler.DefaultOrder = 'XYZ';
  
  	Object.defineProperties( Euler.prototype, {
  
  		x: {
  
  			get: function () {
  
  				return this._x;
  
  			},
  
  			set: function ( value ) {
  
  				this._x = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		y: {
  
  			get: function () {
  
  				return this._y;
  
  			},
  
  			set: function ( value ) {
  
  				this._y = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		z: {
  
  			get: function () {
  
  				return this._z;
  
  			},
  
  			set: function ( value ) {
  
  				this._z = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		order: {
  
  			get: function () {
  
  				return this._order;
  
  			},
  
  			set: function ( value ) {
  
  				this._order = value;
  				this.onChangeCallback();
  
  			}
  
  		}
  
  	} );
  
  	Object.assign( Euler.prototype, {
  
  		isEuler: true,
  
  		set: function ( x, y, z, order ) {
  
  			this._x = x;
  			this._y = y;
  			this._z = z;
  			this._order = order || this._order;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this._x, this._y, this._z, this._order );
  
  		},
  
  		copy: function ( euler ) {
  
  			this._x = euler._x;
  			this._y = euler._y;
  			this._z = euler._z;
  			this._order = euler._order;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromRotationMatrix: function ( m, order, update ) {
  
  			var clamp = _Math.clamp;
  
  			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
  			var te = m.elements;
  			var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
  			var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
  			var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
  
  			order = order || this._order;
  
  			if ( order === 'XYZ' ) {
  
  				this._y = Math.asin( clamp( m13, - 1, 1 ) );
  
  				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, - 1, 1 ) );
  
  				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, - 1, 1 ) );
  
  				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, - 1, 1 ) );
  
  				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, - 1, 1 ) );
  
  				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, - 1, 1 ) );
  
  				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;
  
  				}
  
  			} else {
  
  				console.warn( 'Euler: .setFromRotationMatrix() given unsupported order: ' + order );
  
  			}
  
  			this._order = order;
  
  			if ( update !== false ) this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromQuaternion: function () {
  
  			var matrix = new Matrix4();
  
  			return function setFromQuaternion( q, order, update ) {
  
  				matrix.makeRotationFromQuaternion( q );
  
  				return this.setFromRotationMatrix( matrix, order, update );
  
  			};
  
  		}(),
  
  		setFromVector3: function ( v, order ) {
  
  			return this.set( v.x, v.y, v.z, order || this._order );
  
  		},
  
  		reorder: function () {
  
  			// WARNING: this discards revolution information -bhouston
  
  			var q = new Quaternion();
  
  			return function reorder( newOrder ) {
  
  				q.setFromEuler( this );
  
  				return this.setFromQuaternion( q, newOrder );
  
  			};
  
  		}(),
  
  		equals: function ( euler ) {
  
  			return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
  
  		},
  
  		fromArray: function ( array ) {
  
  			this._x = array[ 0 ];
  			this._y = array[ 1 ];
  			this._z = array[ 2 ];
  			if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this._x;
  			array[ offset + 1 ] = this._y;
  			array[ offset + 2 ] = this._z;
  			array[ offset + 3 ] = this._order;
  
  			return array;
  
  		},
  
  		toVector3: function ( optionalResult ) {
  
  			if ( optionalResult ) {
  
  				return optionalResult.set( this._x, this._y, this._z );
  
  			} else {
  
  				return new Vector3( this._x, this._y, this._z );
  
  			}
  
  		},
  
  		onChange: function ( callback ) {
  
  			this.onChangeCallback = callback;
  
  			return this;
  
  		},
  
  		onChangeCallback: function () {}
  
  	} );
  
  	function Layers() {
  
  		this.mask = 1 | 0;
  
  	}
  
  	Object.assign( Layers.prototype, {
  
  		set: function ( channel ) {
  
  			this.mask = 1 << channel | 0;
  
  		},
  
  		enable: function ( channel ) {
  
  			this.mask |= 1 << channel | 0;
  
  		},
  
  		toggle: function ( channel ) {
  
  			this.mask ^= 1 << channel | 0;
  
  		},
  
  		disable: function ( channel ) {
  
  			this.mask &= ~ ( 1 << channel | 0 );
  
  		},
  
  		test: function ( layers ) {
  
  			return ( this.mask & layers.mask ) !== 0;
  
  		}
  
  	} );
  
  	function Matrix3() {
  
  		this.elements = [
  
  			1, 0, 0,
  			0, 1, 0,
  			0, 0, 1
  
  		];
  
  		if ( arguments.length > 0 ) {
  
  			console.error( 'Matrix3: the constructor no longer reads arguments. use .set() instead.' );
  
  		}
  
  	}
  
  	Object.assign( Matrix3.prototype, {
  
  		isMatrix3: true,
  
  		set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
  
  			var te = this.elements;
  
  			te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
  			te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
  			te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
  
  			return this;
  
  		},
  
  		identity: function () {
  
  			this.set(
  
  				1, 0, 0,
  				0, 1, 0,
  				0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor().fromArray( this.elements );
  
  		},
  
  		copy: function ( m ) {
  
  			var te = this.elements;
  			var me = m.elements;
  
  			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
  			te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
  			te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
  
  			return this;
  
  		},
  
  		setFromMatrix4: function ( m ) {
  
  			var me = m.elements;
  
  			this.set(
  
  				me[ 0 ], me[ 4 ], me[ 8 ],
  				me[ 1 ], me[ 5 ], me[ 9 ],
  				me[ 2 ], me[ 6 ], me[ 10 ]
  
  			);
  
  			return this;
  
  		},
  
  		applyToBufferAttribute: function () {
  
  			var v1 = new Vector3();
  
  			return function applyToBufferAttribute( attribute ) {
  
  				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
  					v1.x = attribute.getX( i );
  					v1.y = attribute.getY( i );
  					v1.z = attribute.getZ( i );
  
  					v1.applyMatrix3( this );
  
  					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
  				}
  
  				return attribute;
  
  			};
  
  		}(),
  
  		multiply: function ( m ) {
  
  			return this.multiplyMatrices( this, m );
  
  		},
  
  		premultiply: function ( m ) {
  
  			return this.multiplyMatrices( m, this );
  
  		},
  
  		multiplyMatrices: function ( a, b ) {
  
  			var ae = a.elements;
  			var be = b.elements;
  			var te = this.elements;
  
  			var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
  			var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
  			var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
  
  			var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
  			var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
  			var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
  
  			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
  			te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
  			te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
  
  			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
  			te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
  			te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
  
  			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
  			te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
  			te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
  
  			return this;
  
  		},
  
  		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, throwOnDegenerate ) {
  
  			if ( matrix && matrix.isMatrix4 ) {
  
  				console.error( "Matrix3: .getInverse() no longer takes a Matrix4 argument." );
  
  			}
  
  			var me = matrix.elements,
  				te = this.elements,
  
  				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
  				n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
  				n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
  
  				t11 = n33 * n22 - n32 * n23,
  				t12 = n32 * n13 - n33 * n12,
  				t13 = n23 * n12 - n22 * n13,
  
  				det = n11 * t11 + n21 * t12 + n31 * t13;
  
  			if ( det === 0 ) {
  
  				var msg = "Matrix3: .getInverse() can't invert matrix, determinant is 0";
  
  				if ( throwOnDegenerate === true ) {
  
  					throw new Error( msg );
  
  				} else {
  
  					console.warn( msg );
  
  				}
  
  				return this.identity();
  
  			}
  
  			var detInv = 1 / det;
  
  			te[ 0 ] = t11 * detInv;
  			te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
  			te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
  
  			te[ 3 ] = t12 * detInv;
  			te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
  			te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
  
  			te[ 6 ] = t13 * detInv;
  			te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
  			te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
  
  			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 ( matrix4 ) {
  
  			return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
  
  		},
  
  		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;
  
  		},
  
  		setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {
  
  			var c = Math.cos( rotation );
  			var s = Math.sin( rotation );
  
  			this.set(
  				sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
  				- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
  				0, 0, 1
  			);
  
  		},
  
  		scale: function ( sx, sy ) {
  
  			var te = this.elements;
  
  			te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
  			te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
  
  			return this;
  
  		},
  
  		rotate: function ( theta ) {
  
  			var c = Math.cos( theta );
  			var s = Math.sin( theta );
  
  			var te = this.elements;
  
  			var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
  			var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
  
  			te[ 0 ] = c * a11 + s * a21;
  			te[ 3 ] = c * a12 + s * a22;
  			te[ 6 ] = c * a13 + s * a23;
  
  			te[ 1 ] = - s * a11 + c * a21;
  			te[ 4 ] = - s * a12 + c * a22;
  			te[ 7 ] = - s * a13 + c * a23;
  
  			return this;
  
  		},
  
  		translate: function ( tx, ty ) {
  
  			var te = this.elements;
  
  			te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
  			te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
  
  			return this;
  
  		},
  
  		equals: function ( matrix ) {
  
  			var te = this.elements;
  			var me = matrix.elements;
  
  			for ( var i = 0; i < 9; i ++ ) {
  
  				if ( te[ i ] !== me[ i ] ) return false;
  
  			}
  
  			return true;
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			for ( var i = 0; i < 9; i ++ ) {
  
  				this.elements[ i ] = array[ i + offset ];
  
  			}
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			var te = this.elements;
  
  			array[ offset ] = te[ 0 ];
  			array[ offset + 1 ] = te[ 1 ];
  			array[ offset + 2 ] = te[ 2 ];
  
  			array[ offset + 3 ] = te[ 3 ];
  			array[ offset + 4 ] = te[ 4 ];
  			array[ offset + 5 ] = te[ 5 ];
  
  			array[ offset + 6 ] = te[ 6 ];
  			array[ offset + 7 ] = te[ 7 ];
  			array[ offset + 8 ] = te[ 8 ];
  
  			return array;
  
  		}
  
  	} );
  
  	var object3DId = 0;
  
  	function Object3D() {
  
  		Object.defineProperty( this, 'id', { value: object3DId ++ } );
  
  		this.uuid = _Math.generateUUID();
  
  		this.name = '';
  		this.type = 'Object3D';
  
  		this.parent = null;
  		this.children = [];
  
  		this.up = Object3D.DefaultUp.clone();
  
  		var position = new Vector3();
  		var rotation = new Euler();
  		var quaternion = new Quaternion();
  		var scale = new Vector3( 1, 1, 1 );
  
  		function onRotationChange() {
  
  			quaternion.setFromEuler( rotation, false );
  
  		}
  
  		function onQuaternionChange() {
  
  			rotation.setFromQuaternion( quaternion, undefined, false );
  
  		}
  
  		rotation.onChange( onRotationChange );
  		quaternion.onChange( onQuaternionChange );
  
  		Object.defineProperties( this, {
  			position: {
  				enumerable: true,
  				value: position
  			},
  			rotation: {
  				enumerable: true,
  				value: rotation
  			},
  			quaternion: {
  				enumerable: true,
  				value: quaternion
  			},
  			scale: {
  				enumerable: true,
  				value: scale
  			},
  			modelViewMatrix: {
  				value: new Matrix4()
  			},
  			normalMatrix: {
  				value: new Matrix3()
  			}
  		} );
  
  		this.matrix = new Matrix4();
  		this.matrixWorld = new Matrix4();
  
  		this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
  		this.matrixWorldNeedsUpdate = false;
  
  		this.layers = new Layers();
  		this.visible = true;
  
  		this.castShadow = false;
  		this.receiveShadow = false;
  
  		this.frustumCulled = true;
  		this.renderOrder = 0;
  
  		this.userData = {};
  
  	}
  
  	Object3D.DefaultUp = new Vector3( 0, 1, 0 );
  	Object3D.DefaultMatrixAutoUpdate = true;
  
  	Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
  		constructor: Object3D,
  
  		isObject3D: true,
  
  		onBeforeRender: function () {},
  		onAfterRender: function () {},
  
  		applyMatrix: function ( matrix ) {
  
  			this.matrix.multiplyMatrices( matrix, this.matrix );
  
  			this.matrix.decompose( this.position, this.quaternion, this.scale );
  
  		},
  
  		applyQuaternion: function ( q ) {
  
  			this.quaternion.premultiply( q );
  
  			return this;
  
  		},
  
  		setRotationFromAxisAngle: function ( axis, angle ) {
  
  			// assumes axis is normalized
  
  			this.quaternion.setFromAxisAngle( axis, angle );
  
  		},
  
  		setRotationFromEuler: function ( euler ) {
  
  			this.quaternion.setFromEuler( euler, true );
  
  		},
  
  		setRotationFromMatrix: function ( m ) {
  
  			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
  			this.quaternion.setFromRotationMatrix( m );
  
  		},
  
  		setRotationFromQuaternion: function ( q ) {
  
  			// assumes q is normalized
  
  			this.quaternion.copy( q );
  
  		},
  
  		rotateOnAxis: function () {
  
  			// rotate object on axis in object space
  			// axis is assumed to be normalized
  
  			var q1 = new Quaternion();
  
  			return function rotateOnAxis( axis, angle ) {
  
  				q1.setFromAxisAngle( axis, angle );
  
  				this.quaternion.multiply( q1 );
  
  				return this;
  
  			};
  
  		}(),
  
  		rotateOnWorldAxis: function () {
  
  			// rotate object on axis in world space
  			// axis is assumed to be normalized
  			// method assumes no rotated parent
  
  			var q1 = new Quaternion();
  
  			return function rotateOnWorldAxis( axis, angle ) {
  
  				q1.setFromAxisAngle( axis, angle );
  
  				this.quaternion.premultiply( q1 );
  
  				return this;
  
  			};
  
  		}(),
  
  		rotateX: function () {
  
  			var v1 = new Vector3( 1, 0, 0 );
  
  			return function rotateX( angle ) {
  
  				return this.rotateOnAxis( v1, angle );
  
  			};
  
  		}(),
  
  		rotateY: function () {
  
  			var v1 = new Vector3( 0, 1, 0 );
  
  			return function rotateY( angle ) {
  
  				return this.rotateOnAxis( v1, angle );
  
  			};
  
  		}(),
  
  		rotateZ: function () {
  
  			var v1 = new Vector3( 0, 0, 1 );
  
  			return function rotateZ( angle ) {
  
  				return this.rotateOnAxis( v1, angle );
  
  			};
  
  		}(),
  
  		translateOnAxis: function () {
  
  			// translate object by distance along axis in object space
  			// axis is assumed to be normalized
  
  			var v1 = new Vector3();
  
  			return function translateOnAxis( axis, distance ) {
  
  				v1.copy( axis ).applyQuaternion( this.quaternion );
  
  				this.position.add( v1.multiplyScalar( distance ) );
  
  				return this;
  
  			};
  
  		}(),
  
  		translateX: function () {
  
  			var v1 = new Vector3( 1, 0, 0 );
  
  			return function translateX( distance ) {
  
  				return this.translateOnAxis( v1, distance );
  
  			};
  
  		}(),
  
  		translateY: function () {
  
  			var v1 = new Vector3( 0, 1, 0 );
  
  			return function translateY( distance ) {
  
  				return this.translateOnAxis( v1, distance );
  
  			};
  
  		}(),
  
  		translateZ: function () {
  
  			var v1 = new Vector3( 0, 0, 1 );
  
  			return function translateZ( distance ) {
  
  				return this.translateOnAxis( v1, distance );
  
  			};
  
  		}(),
  
  		localToWorld: function ( vector ) {
  
  			return vector.applyMatrix4( this.matrixWorld );
  
  		},
  
  		worldToLocal: function () {
  
  			var m1 = new Matrix4();
  
  			return function worldToLocal( vector ) {
  
  				return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );
  
  			};
  
  		}(),
  
  		lookAt: function () {
  
  			// This method does not support objects with rotated and/or translated parent(s)
  
  			var m1 = new Matrix4();
  			var vector = new Vector3();
  
  			return function lookAt( x, y, z ) {
  
  				if ( x.isVector3 ) {
  
  					vector.copy( x );
  
  				} else {
  
  					vector.set( x, y, z );
  
  				}
  
  				if ( this.isCamera ) {
  
  					m1.lookAt( this.position, vector, this.up );
  
  				} else {
  
  					m1.lookAt( vector, this.position, this.up );
  
  				}
  
  				this.quaternion.setFromRotationMatrix( m1 );
  
  			};
  
  		}(),
  
  		add: function ( object ) {
  
  			if ( arguments.length > 1 ) {
  
  				for ( var i = 0; i < arguments.length; i ++ ) {
  
  					this.add( arguments[ i ] );
  
  				}
  
  				return this;
  
  			}
  
  			if ( object === this ) {
  
  				console.error( "Object3D.add: object can't be added as a child of itself.", object );
  				return this;
  
  			}
  
  			if ( ( object && object.isObject3D ) ) {
  
  				if ( object.parent !== null ) {
  
  					object.parent.remove( object );
  
  				}
  
  				object.parent = this;
  				object.dispatchEvent( { type: 'added' } );
  
  				this.children.push( object );
  
  			} else {
  
  				console.error( "Object3D.add: object not an instance of Object3D.", object );
  
  			}
  
  			return this;
  
  		},
  
  		remove: function ( object ) {
  
  			if ( arguments.length > 1 ) {
  
  				for ( var i = 0; i < arguments.length; i ++ ) {
  
  					this.remove( arguments[ i ] );
  
  				}
  
  				return this;
  
  			}
  
  			var index = this.children.indexOf( object );
  
  			if ( index !== - 1 ) {
  
  				object.parent = null;
  
  				object.dispatchEvent( { type: 'removed' } );
  
  				this.children.splice( index, 1 );
  
  			}
  
  			return this;
  
  		},
  
  		getObjectById: function ( id ) {
  
  			return this.getObjectByProperty( 'id', id );
  
  		},
  
  		getObjectByName: function ( name ) {
  
  			return this.getObjectByProperty( 'name', name );
  
  		},
  
  		getObjectByProperty: function ( name, value ) {
  
  			if ( this[ name ] === value ) return this;
  
  			for ( var i = 0, l = this.children.length; i < l; i ++ ) {
  
  				var child = this.children[ i ];
  				var object = child.getObjectByProperty( name, value );
  
  				if ( object !== undefined ) {
  
  					return object;
  
  				}
  
  			}
  
  			return undefined;
  
  		},
  
  		getWorldPosition: function ( target ) {
  
  			if ( target === undefined ) {
  
  				console.warn( 'Object3D: .getWorldPosition() target is now required' );
  				target = new Vector3();
  
  			}
  
  			this.updateMatrixWorld( true );
  
  			return target.setFromMatrixPosition( this.matrixWorld );
  
  		},
  
  		getWorldQuaternion: function () {
  
  			var position = new Vector3();
  			var scale = new Vector3();
  
  			return function getWorldQuaternion( target ) {
  
  				if ( target === undefined ) {
  
  					console.warn( 'Object3D: .getWorldQuaternion() target is now required' );
  					target = new Quaternion();
  
  				}
  
  				this.updateMatrixWorld( true );
  
  				this.matrixWorld.decompose( position, target, scale );
  
  				return target;
  
  			};
  
  		}(),
  
  		getWorldScale: function () {
  
  			var position = new Vector3();
  			var quaternion = new Quaternion();
  
  			return function getWorldScale( target ) {
  
  				if ( target === undefined ) {
  
  					console.warn( 'Object3D: .getWorldScale() target is now required' );
  					target = new Vector3();
  
  				}
  
  				this.updateMatrixWorld( true );
  
  				this.matrixWorld.decompose( position, quaternion, target );
  
  				return target;
  
  			};
  
  		}(),
  
  		getWorldDirection: function () {
  
  			var quaternion = new Quaternion();
  
  			return function getWorldDirection( target ) {
  
  				if ( target === undefined ) {
  
  					console.warn( 'Object3D: .getWorldDirection() target is now required' );
  					target = new Vector3();
  
  				}
  
  				this.getWorldQuaternion( quaternion );
  
  				return target.set( 0, 0, 1 ).applyQuaternion( quaternion );
  
  			};
  
  		}(),
  
  		raycast: function () {},
  
  		traverse: function ( callback ) {
  
  			callback( this );
  
  			var children = this.children;
  
  			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
  				children[ i ].traverse( callback );
  
  			}
  
  		},
  
  		traverseVisible: function ( callback ) {
  
  			if ( this.visible === false ) return;
  
  			callback( this );
  
  			var children = this.children;
  
  			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
  				children[ i ].traverseVisible( callback );
  
  			}
  
  		},
  
  		traverseAncestors: function ( callback ) {
  
  			var parent = this.parent;
  
  			if ( parent !== null ) {
  
  				callback( parent );
  
  				parent.traverseAncestors( callback );
  
  			}
  
  		},
  
  		updateMatrix: function () {
  
  			this.matrix.compose( this.position, this.quaternion, this.scale );
  
  			this.matrixWorldNeedsUpdate = true;
  
  		},
  
  		updateMatrixWorld: function ( force ) {
  
  			if ( this.matrixAutoUpdate ) this.updateMatrix();
  
  			if ( this.matrixWorldNeedsUpdate || force ) {
  
  				if ( this.parent === null ) {
  
  					this.matrixWorld.copy( this.matrix );
  
  				} else {
  
  					this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
  
  				}
  
  				this.matrixWorldNeedsUpdate = false;
  
  				force = true;
  
  			}
  
  			// update children
  
  			var children = this.children;
  
  			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
  				children[ i ].updateMatrixWorld( force );
  
  			}
  
  		},
  
  		toJSON: function ( meta ) {
  
  			// meta is a string when called from JSON.stringify
  			var isRootObject = ( meta === undefined || typeof meta === 'string' );
  
  			var output = {};
  
  			// meta is a hash used to collect geometries, materials.
  			// not providing it implies that this is the root object
  			// being serialized.
  			if ( isRootObject ) {
  
  				// initialize meta obj
  				meta = {
  					geometries: {},
  					materials: {},
  					textures: {},
  					images: {},
  					shapes: {}
  				};
  
  				output.metadata = {
  					version: 4.5,
  					type: 'Object',
  					generator: 'Object3D.toJSON'
  				};
  
  			}
  
  			// standard Object3D serialization
  
  			var object = {};
  
  			object.uuid = this.uuid;
  			object.type = this.type;
  
  			if ( this.name !== '' ) object.name = this.name;
  			if ( this.castShadow === true ) object.castShadow = true;
  			if ( this.receiveShadow === true ) object.receiveShadow = true;
  			if ( this.visible === false ) object.visible = false;
  			if ( this.frustumCulled === false ) object.frustumCulled = false;
  			if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
  			if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
  
  			object.matrix = this.matrix.toArray();
  
  			if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
  
  			//
  
  			function serialize( library, element ) {
  
  				if ( library[ element.uuid ] === undefined ) {
  
  					library[ element.uuid ] = element.toJSON( meta );
  
  				}
  
  				return element.uuid;
  
  			}
  
  			if ( this.geometry !== undefined ) {
  
  				object.geometry = serialize( meta.geometries, this.geometry );
  
  				var parameters = this.geometry.parameters;
  
  				if ( parameters !== undefined && parameters.shapes !== undefined ) {
  
  					var shapes = parameters.shapes;
  
  					if ( Array.isArray( shapes ) ) {
  
  						for ( var i = 0, l = shapes.length; i < l; i ++ ) {
  
  							var shape = shapes[ i ];
  
  							serialize( meta.shapes, shape );
  
  						}
  
  					} else {
  
  						serialize( meta.shapes, shapes );
  
  					}
  
  				}
  
  			}
  
  			if ( this.material !== undefined ) {
  
  				if ( Array.isArray( this.material ) ) {
  
  					var uuids = [];
  
  					for ( var i = 0, l = this.material.length; i < l; i ++ ) {
  
  						uuids.push( serialize( meta.materials, this.material[ i ] ) );
  
  					}
  
  					object.material = uuids;
  
  				} else {
  
  					object.material = serialize( meta.materials, this.material );
  
  				}
  
  			}
  
  			//
  
  			if ( this.children.length > 0 ) {
  
  				object.children = [];
  
  				for ( var i = 0; i < this.children.length; i ++ ) {
  
  					object.children.push( this.children[ i ].toJSON( meta ).object );
  
  				}
  
  			}
  
  			if ( isRootObject ) {
  
  				var geometries = extractFromCache( meta.geometries );
  				var materials = extractFromCache( meta.materials );
  				var textures = extractFromCache( meta.textures );
  				var images = extractFromCache( meta.images );
  				var shapes = extractFromCache( meta.shapes );
  
  				if ( geometries.length > 0 ) output.geometries = geometries;
  				if ( materials.length > 0 ) output.materials = materials;
  				if ( textures.length > 0 ) output.textures = textures;
  				if ( images.length > 0 ) output.images = images;
  				if ( shapes.length > 0 ) output.shapes = shapes;
  
  			}
  
  			output.object = object;
  
  			return output;
  
  			// extract data from the cache hash
  			// remove metadata on each item
  			// and return as array
  			function extractFromCache( cache ) {
  
  				var values = [];
  				for ( var key in cache ) {
  
  					var data = cache[ key ];
  					delete data.metadata;
  					values.push( data );
  
  				}
  				return values;
  
  			}
  
  		},
  
  		clone: function ( recursive ) {
  
  			return new this.constructor().copy( this, recursive );
  
  		},
  
  		copy: function ( source, recursive ) {
  
  			if ( recursive === undefined ) recursive = true;
  
  			this.name = source.name;
  
  			this.up.copy( source.up );
  
  			this.position.copy( source.position );
  			this.quaternion.copy( source.quaternion );
  			this.scale.copy( source.scale );
  
  			this.matrix.copy( source.matrix );
  			this.matrixWorld.copy( source.matrixWorld );
  
  			this.matrixAutoUpdate = source.matrixAutoUpdate;
  			this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
  
  			this.layers.mask = source.layers.mask;
  			this.visible = source.visible;
  
  			this.castShadow = source.castShadow;
  			this.receiveShadow = source.receiveShadow;
  
  			this.frustumCulled = source.frustumCulled;
  			this.renderOrder = source.renderOrder;
  
  			this.userData = JSON.parse( JSON.stringify( source.userData ) );
  
  			if ( recursive === true ) {
  
  				for ( var i = 0; i < source.children.length; i ++ ) {
  
  					var child = source.children[ i ];
  					this.add( child.clone() );
  
  				}
  
  			}
  
  			return this;
  
  		}
  
  	} );
  
  	function Camera() {
  
  		Object3D.call( this );
  
  		this.type = 'Camera';
  
  		this.matrixWorldInverse = new Matrix4();
  		this.projectionMatrix = new Matrix4();
  
  	}
  
  	Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
  		constructor: Camera,
  
  		isCamera: true,
  
  		copy: function ( source, recursive ) {
  
  			Object3D.prototype.copy.call( this, source, recursive );
  
  			this.matrixWorldInverse.copy( source.matrixWorldInverse );
  			this.projectionMatrix.copy( source.projectionMatrix );
  
  			return this;
  
  		},
  
  		getWorldDirection: function () {
  
  			var quaternion = new Quaternion();
  
  			return function getWorldDirection( target ) {
  
  				if ( target === undefined ) {
  
  					console.warn( 'Camera: .getWorldDirection() target is now required' );
  					target = new Vector3();
  
  				}
  
  				this.getWorldQuaternion( quaternion );
  
  				return target.set( 0, 0, - 1 ).applyQuaternion( quaternion );
  
  			};
  
  		}(),
  
  		updateMatrixWorld: function ( force ) {
  
  			Object3D.prototype.updateMatrixWorld.call( this, force );
  
  			this.matrixWorldInverse.getInverse( this.matrixWorld );
  
  		},
  
  		clone: function () {
  
  			return new this.constructor().copy( this );
  
  		}
  
  	} );
  
  	function PerspectiveCamera( fov, aspect, near, far ) {
  
  		Camera.call( this );
  
  		this.type = 'PerspectiveCamera';
  
  		this.fov = fov !== undefined ? fov : 50;
  		this.zoom = 1;
  
  		this.near = near !== undefined ? near : 0.1;
  		this.far = far !== undefined ? far : 2000;
  		this.focus = 10;
  
  		this.aspect = aspect !== undefined ? aspect : 1;
  		this.view = null;
  
  		this.filmGauge = 35;	// width of the film (default in millimeters)
  		this.filmOffset = 0;	// horizontal film offset (same unit as gauge)
  
  		this.updateProjectionMatrix();
  
  	}
  
  	PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
  
  		constructor: PerspectiveCamera,
  
  		isPerspectiveCamera: true,
  
  		copy: function ( source, recursive ) {
  
  			Camera.prototype.copy.call( this, source, recursive );
  
  			this.fov = source.fov;
  			this.zoom = source.zoom;
  
  			this.near = source.near;
  			this.far = source.far;
  			this.focus = source.focus;
  
  			this.aspect = source.aspect;
  			this.view = source.view === null ? null : Object.assign( {}, source.view );
  
  			this.filmGauge = source.filmGauge;
  			this.filmOffset = source.filmOffset;
  
  			return this;
  
  		},
  
  		
  		setFocalLength: function ( focalLength ) {
  
  			// see http://www.bobatkins.com/photography/technical/field_of_view.html
  			var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
  
  			this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );
  			this.updateProjectionMatrix();
  
  		},
  
  		
  		getFocalLength: function () {
  
  			var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );
  
  			return 0.5 * this.getFilmHeight() / vExtentSlope;
  
  		},
  
  		getEffectiveFOV: function () {
  
  			return _Math.RAD2DEG * 2 * Math.atan(
  				Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );
  
  		},
  
  		getFilmWidth: function () {
  
  			// film not completely covered in portrait format (aspect < 1)
  			return this.filmGauge * Math.min( this.aspect, 1 );
  
  		},
  
  		getFilmHeight: function () {
  
  			// film not completely covered in landscape format (aspect > 1)
  			return this.filmGauge / Math.max( this.aspect, 1 );
  
  		},
  
  		
  		setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
  
  			this.aspect = fullWidth / fullHeight;
  
  			if ( this.view === null ) {
  
  				this.view = {
  					enabled: true,
  					fullWidth: 1,
  					fullHeight: 1,
  					offsetX: 0,
  					offsetY: 0,
  					width: 1,
  					height: 1
  				};
  
  			}
  
  			this.view.enabled = true;
  			this.view.fullWidth = fullWidth;
  			this.view.fullHeight = fullHeight;
  			this.view.offsetX = x;
  			this.view.offsetY = y;
  			this.view.width = width;
  			this.view.height = height;
  
  			this.updateProjectionMatrix();
  
  		},
  
  		clearViewOffset: function () {
  
  			if ( this.view !== null ) {
  
  				this.view.enabled = false;
  
  			}
  
  			this.updateProjectionMatrix();
  
  		},
  
  		updateProjectionMatrix: function () {
  
  			var near = this.near,
  				top = near * Math.tan(
  					_Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,
  				height = 2 * top,
  				width = this.aspect * height,
  				left = - 0.5 * width,
  				view = this.view;
  
  			if ( this.view !== null && this.view.enabled ) {
  
  				var fullWidth = view.fullWidth,
  					fullHeight = view.fullHeight;
  
  				left += view.offsetX * width / fullWidth;
  				top -= view.offsetY * height / fullHeight;
  				width *= view.width / fullWidth;
  				height *= view.height / fullHeight;
  
  			}
  
  			var skew = this.filmOffset;
  			if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
  
  			this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );
  
  		},
  
  		toJSON: function ( meta ) {
  
  			var data = Object3D.prototype.toJSON.call( this, meta );
  
  			data.object.fov = this.fov;
  			data.object.zoom = this.zoom;
  
  			data.object.near = this.near;
  			data.object.far = this.far;
  			data.object.focus = this.focus;
  
  			data.object.aspect = this.aspect;
  
  			if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
  
  			data.object.filmGauge = this.filmGauge;
  			data.object.filmOffset = this.filmOffset;
  
  			return data;
  
  		}
  
  	} );
  
  	function StereoCamera() {
  
  		this.type = 'StereoCamera';
  
  		this.aspect = 1;
  
  		this.eyeSep = 0.064;
  
  		this.cameraL = new PerspectiveCamera();
  		this.cameraL.layers.enable( 1 );
  		this.cameraL.matrixAutoUpdate = false;
  
  		this.cameraR = new PerspectiveCamera();
  		this.cameraR.layers.enable( 2 );
  		this.cameraR.matrixAutoUpdate = false;
  
  	}
  
  	Object.assign( StereoCamera.prototype, {
  
  		update: ( function () {
  
  			var instance, focus, fov, aspect, near, far, zoom, eyeSep;
  
  			var eyeRight = new Matrix4();
  			var eyeLeft = new Matrix4();
  
  			return function update( camera ) {
  
  				var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov ||
  													aspect !== camera.aspect * this.aspect || near !== camera.near ||
  													far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep;
  
  				if ( needsUpdate ) {
  
  					instance = this;
  					focus = camera.focus;
  					fov = camera.fov;
  					aspect = camera.aspect * this.aspect;
  					near = camera.near;
  					far = camera.far;
  					zoom = camera.zoom;
  
  					// Off-axis stereoscopic effect based on
  					// http://paulbourke.net/stereographics/stereorender/
  
  					var projectionMatrix = camera.projectionMatrix.clone();
  					eyeSep = this.eyeSep / 2;
  					var eyeSepOnProjection = eyeSep * near / focus;
  					var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom;
  					var xmin, xmax;
  
  					// translate xOffset
  
  					eyeLeft.elements[ 12 ] = - eyeSep;
  					eyeRight.elements[ 12 ] = eyeSep;
  
  					// for left eye
  
  					xmin = - ymax * aspect + eyeSepOnProjection;
  					xmax = ymax * aspect + eyeSepOnProjection;
  
  					projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );
  					projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
  
  					this.cameraL.projectionMatrix.copy( projectionMatrix );
  
  					// for right eye
  
  					xmin = - ymax * aspect - eyeSepOnProjection;
  					xmax = ymax * aspect - eyeSepOnProjection;
  
  					projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );
  					projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
  
  					this.cameraR.projectionMatrix.copy( projectionMatrix );
  
  				}
  
  				this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft );
  				this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight );
  
  			};
  
  		} )()
  
  	} );
  
  	var StereoEffect = function ( renderer ) {
  
  		var _stereo = new StereoCamera();
  		_stereo.aspect = 0.5;
  
  		this.setEyeSeparation = function ( eyeSep ) {
  
  			_stereo.eyeSep = eyeSep;
  
  		};
  
  		this.setSize = function ( width, height ) {
  
  			renderer.setSize( width, height );
  
  		};
  
  		this.render = function ( scene, camera ) {
  
  			scene.updateMatrixWorld();
  
  			if ( camera.parent === null ) camera.updateMatrixWorld();
  
  			_stereo.update( camera );
  
  			var size = renderer.getSize();
  
  			if ( renderer.autoClear ) renderer.clear();
  			renderer.setScissorTest( true );
  
  			renderer.setScissor( 0, 0, size.width / 2, size.height );
  			renderer.setViewport( 0, 0, size.width / 2, size.height );
  			renderer.render( scene, _stereo.cameraL );
  
  			renderer.setScissor( size.width / 2, 0, size.width / 2, size.height );
  			renderer.setViewport( size.width / 2, 0, size.width / 2, size.height );
  			renderer.render( scene, _stereo.cameraR );
  
  			renderer.setScissorTest( false );
  
  		};
  
  	};
  
  	exports.StereoEffect = StereoEffect;
  
  	return exports;
  
  }({}));
  