text: /**
   * @author mrdoob / http://mrdoob.com/
   * @author mikael emtinger / http://gomo.se/
   * @author alteredq / http://alteredqualia.com/
   * @author WestLangley / http://github.com/WestLangley
   * @author elephantatwork / www.elephantatwork.ch
   */
  
  THREE.Object3D = function () {
  
  	Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } );
  
  	this.uuid = THREE.Math.generateUUID();
  
  	this.name = '';
  	this.type = 'Object3D';
  
  	this.parent = null;
  	this.children = [];
  
  	this.up = THREE.Object3D.DefaultUp.clone();
  
  	var position = new THREE.Vector3();
  	var rotation = new THREE.Euler();
  	var quaternion = new THREE.Quaternion();
  	var scale = new THREE.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 THREE.Matrix4()
  		},
  		normalMatrix: {
  			value: new THREE.Matrix3()
  		}
  	} );
  
  	this.matrix = new THREE.Matrix4();
  	this.matrixWorld = new THREE.Matrix4();
  
  	this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate;
  	this.matrixWorldNeedsUpdate = false;
  
  	this.layers = new THREE.Layers();
  	this.visible = true;
  
  	this.castShadow = false;
  	this.receiveShadow = false;
  
  	this.frustumCulled = true;
  	this.renderOrder = 0;
  
  	this.userData = {};
  
  };
  
  THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 );
  THREE.Object3D.DefaultMatrixAutoUpdate = true;
  
  Object.assign( THREE.Object3D.prototype, THREE.EventDispatcher.prototype, {
  
  	applyMatrix: function ( matrix ) {
  
  		this.matrix.multiplyMatrices( matrix, this.matrix );
  
  		this.matrix.decompose( this.position, this.quaternion, this.scale );
  
  	},
  
  	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 THREE.Quaternion();
  
  		return function ( axis, angle ) {
  
  			q1.setFromAxisAngle( axis, angle );
  
  			this.quaternion.multiply( q1 );
  
  			return this;
  
  		};
  
  	}(),
  
  	rotateX: function () {
  
  		var v1 = new THREE.Vector3( 1, 0, 0 );
  
  		return function ( angle ) {
  
  			return this.rotateOnAxis( v1, angle );
  
  		};
  
  	}(),
  
  	rotateY: function () {
  
  		var v1 = new THREE.Vector3( 0, 1, 0 );
  
  		return function ( angle ) {
  
  			return this.rotateOnAxis( v1, angle );
  
  		};
  
  	}(),
  
  	rotateZ: function () {
  
  		var v1 = new THREE.Vector3( 0, 0, 1 );
  
  		return function ( 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 THREE.Vector3();
  
  		return function ( axis, distance ) {
  
  			v1.copy( axis ).applyQuaternion( this.quaternion );
  
  			this.position.add( v1.multiplyScalar( distance ) );
  
  			return this;
  
  		};
  
  	}(),
  
  	translateX: function () {
  
  		var v1 = new THREE.Vector3( 1, 0, 0 );
  
  		return function ( distance ) {
  
  			return this.translateOnAxis( v1, distance );
  
  		};
  
  	}(),
  
  	translateY: function () {
  
  		var v1 = new THREE.Vector3( 0, 1, 0 );
  
  		return function ( distance ) {
  
  			return this.translateOnAxis( v1, distance );
  
  		};
  
  	}(),
  
  	translateZ: function () {
  
  		var v1 = new THREE.Vector3( 0, 0, 1 );
  
  		return function ( distance ) {
  
  			return this.translateOnAxis( v1, distance );
  
  		};
  
  	}(),
  
  	localToWorld: function ( vector ) {
  
  		return vector.applyMatrix4( this.matrixWorld );
  
  	},
  
  	worldToLocal: function () {
  
  		var m1 = new THREE.Matrix4();
  
  		return function ( vector ) {
  
  			return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );
  
  		};
  
  	}(),
  
  	lookAt: function () {
  
  		// This routine does not support objects with rotated and/or translated parent(s)
  
  		var m1 = new THREE.Matrix4();
  
  		return function ( vector ) {
  
  			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( "THREE.Object3D.add: object can't be added as a child of itself.", object );
  			return this;
  
  		}
  
  		if ( object instanceof THREE.Object3D ) {
  
  			if ( object.parent !== null ) {
  
  				object.parent.remove( object );
  
  			}
  
  			object.parent = this;
  			object.dispatchEvent( { type: 'added' } );
  
  			this.children.push( object );
  
  		} else {
  
  			console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
  
  		}
  
  		return this;
  
  	},
  
  	remove: function ( object ) {
  
  		if ( arguments.length > 1 ) {
  
  			for ( var i = 0; i < arguments.length; i ++ ) {
  
  				this.remove( arguments[ i ] );
  
  			}
  
  		}
  
  		var index = this.children.indexOf( object );
  
  		if ( index !== - 1 ) {
  
  			object.parent = null;
  
  			object.dispatchEvent( { type: 'removed' } );
  
  			this.children.splice( index, 1 );
  
  		}
  
  	},
  
  	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 ( optionalTarget ) {
  
  		var result = optionalTarget || new THREE.Vector3();
  
  		this.updateMatrixWorld( true );
  
  		return result.setFromMatrixPosition( this.matrixWorld );
  
  	},
  
  	getWorldQuaternion: function () {
  
  		var position = new THREE.Vector3();
  		var scale = new THREE.Vector3();
  
  		return function ( optionalTarget ) {
  
  			var result = optionalTarget || new THREE.Quaternion();
  
  			this.updateMatrixWorld( true );
  
  			this.matrixWorld.decompose( position, result, scale );
  
  			return result;
  
  		};
  
  	}(),
  
  	getWorldRotation: function () {
  
  		var quaternion = new THREE.Quaternion();
  
  		return function ( optionalTarget ) {
  
  			var result = optionalTarget || new THREE.Euler();
  
  			this.getWorldQuaternion( quaternion );
  
  			return result.setFromQuaternion( quaternion, this.rotation.order, false );
  
  		};
  
  	}(),
  
  	getWorldScale: function () {
  
  		var position = new THREE.Vector3();
  		var quaternion = new THREE.Quaternion();
  
  		return function ( optionalTarget ) {
  
  			var result = optionalTarget || new THREE.Vector3();
  
  			this.updateMatrixWorld( true );
  
  			this.matrixWorld.decompose( position, quaternion, result );
  
  			return result;
  
  		};
  
  	}(),
  
  	getWorldDirection: function () {
  
  		var quaternion = new THREE.Quaternion();
  
  		return function ( optionalTarget ) {
  
  			var result = optionalTarget || new THREE.Vector3();
  
  			this.getWorldQuaternion( quaternion );
  
  			return result.set( 0, 0, 1 ).applyQuaternion( quaternion );
  
  		};
  
  	}(),
  
  	raycast: function () {},
  
  	traverse: function ( callback ) {
  
  		callback( this );
  
  		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 === true ) this.updateMatrix();
  
  		if ( this.matrixWorldNeedsUpdate === true || force === true ) {
  
  			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
  
  		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
  
  			this.children[ i ].updateMatrixWorld( force );
  
  		}
  
  	},
  
  	toJSON: function ( meta ) {
  
  		// meta is '' when called from JSON.stringify
  		var isRootObject = ( meta === undefined || meta === '' );
  
  		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: {}
  			};
  
  			output.metadata = {
  				version: 4.4,
  				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 ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
  		if ( this.castShadow === true ) object.castShadow = true;
  		if ( this.receiveShadow === true ) object.receiveShadow = true;
  		if ( this.visible === false ) object.visible = false;
  
  		object.matrix = this.matrix.toArray();
  
  		//
  
  		if ( this.geometry !== undefined ) {
  
  			if ( meta.geometries[ this.geometry.uuid ] === undefined ) {
  
  				meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta );
  
  			}
  
  			object.geometry = this.geometry.uuid;
  
  		}
  
  		if ( this.material !== undefined ) {
  
  			if ( meta.materials[ this.material.uuid ] === undefined ) {
  
  				meta.materials[ this.material.uuid ] = this.material.toJSON( meta );
  
  			}
  
  			object.material = this.material.uuid;
  
  		}
  
  		//
  
  		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 );
  
  			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;
  
  		}
  
  		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.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;
  
  	}
  
  } );
  
  THREE.Object3DIdCount = 0;
  