Browse Source

Merge pull request #10630 from mrdoob/revert-10627-FixCircularReferences

Revert "Fix circular references and improve closure performance"
Mr.doob 8 năm trước cách đây
mục cha
commit
a0cd79d6a7

+ 2 - 2
src/animation/AnimationAction.js

@@ -76,7 +76,7 @@ function AnimationAction( mixer, clip, localRoot ) {
 
 }
 
-Object.assign( AnimationAction.prototype, {
+AnimationAction.prototype = {
 
 	constructor: AnimationAction,
 
@@ -651,7 +651,7 @@ Object.assign( AnimationAction.prototype, {
 
 	}
 
-} );
+};
 
 
 export { AnimationAction };

+ 53 - 49
src/animation/AnimationClip.js

@@ -32,6 +32,55 @@ function AnimationClip( name, duration, tracks ) {
 
 }
 
+AnimationClip.prototype = {
+
+	constructor: AnimationClip,
+
+	resetDuration: function() {
+
+		var tracks = this.tracks,
+			duration = 0;
+
+		for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
+
+			var track = this.tracks[ i ];
+
+			duration = Math.max( duration, track.times[ track.times.length - 1 ] );
+
+		}
+
+		this.duration = duration;
+
+	},
+
+	trim: function() {
+
+		for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+			this.tracks[ i ].trim( 0, this.duration );
+
+		}
+
+		return this;
+
+	},
+
+	optimize: function() {
+
+		for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+			this.tracks[ i ].optimize();
+
+		}
+
+		return this;
+
+	}
+
+};
+
+// Static methods:
+
 Object.assign( AnimationClip, {
 
 	parse: function( json ) {
@@ -49,7 +98,8 @@ Object.assign( AnimationClip, {
 		return new AnimationClip( json.name, json.duration, tracks );
 
 	},
-	
+
+
 	toJSON: function( clip ) {
 
 		var tracks = [],
@@ -72,7 +122,8 @@ Object.assign( AnimationClip, {
 		return json;
 
 	},
-	
+
+
 	CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {
 
 		var numMorphTargets = morphTargetSequence.length;
@@ -308,52 +359,5 @@ Object.assign( AnimationClip, {
 
 } );
 
-Object.assign( AnimationClip.prototype, {
-
-	constructor: AnimationClip,
-
-	resetDuration: function() {
-
-		var tracks = this.tracks,
-			duration = 0;
-
-		for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
-
-			var track = this.tracks[ i ];
-
-			duration = Math.max( duration, track.times[ track.times.length - 1 ] );
-
-		}
-
-		this.duration = duration;
-
-	},
-
-	trim: function() {
-
-		for ( var i = 0; i < this.tracks.length; i ++ ) {
-
-			this.tracks[ i ].trim( 0, this.duration );
-
-		}
-
-		return this;
-
-	},
-
-	optimize: function() {
-
-		for ( var i = 0; i < this.tracks.length; i ++ ) {
-
-			this.tracks[ i ].optimize();
-
-		}
-
-		return this;
-
-	}
-
-} );
-
 
 export { AnimationClip };

+ 258 - 251
src/animation/AnimationMixer.js

@@ -27,10 +27,260 @@ function AnimationMixer( root ) {
 
 }
 
-Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
+AnimationMixer.prototype = {
 
 	constructor: AnimationMixer,
 
+	// return an action for a clip optionally using a custom root target
+	// object (this method allocates a lot of dynamic memory in case a
+	// previously unknown clip/root combination is specified)
+	clipAction: function ( clip, optionalRoot ) {
+
+		var root = optionalRoot || this._root,
+			rootUuid = root.uuid,
+
+			clipObject = typeof clip === 'string' ?
+					AnimationClip.findByName( root, clip ) : clip,
+
+			clipUuid = clipObject !== null ? clipObject.uuid : clip,
+
+			actionsForClip = this._actionsByClip[ clipUuid ],
+			prototypeAction = null;
+
+		if ( actionsForClip !== undefined ) {
+
+			var existingAction =
+					actionsForClip.actionByRoot[ rootUuid ];
+
+			if ( existingAction !== undefined ) {
+
+				return existingAction;
+
+			}
+
+			// we know the clip, so we don't have to parse all
+			// the bindings again but can just copy
+			prototypeAction = actionsForClip.knownActions[ 0 ];
+
+			// also, take the clip from the prototype action
+			if ( clipObject === null )
+				clipObject = prototypeAction._clip;
+
+		}
+
+		// clip must be known when specified via string
+		if ( clipObject === null ) return null;
+
+		// allocate all resources required to run it
+		var newAction = new AnimationAction( this, clipObject, optionalRoot );
+
+		this._bindAction( newAction, prototypeAction );
+
+		// and make the action known to the memory manager
+		this._addInactiveAction( newAction, clipUuid, rootUuid );
+
+		return newAction;
+
+	},
+
+	// get an existing action
+	existingAction: function ( clip, optionalRoot ) {
+
+		var root = optionalRoot || this._root,
+			rootUuid = root.uuid,
+
+			clipObject = typeof clip === 'string' ?
+					AnimationClip.findByName( root, clip ) : clip,
+
+			clipUuid = clipObject ? clipObject.uuid : clip,
+
+			actionsForClip = this._actionsByClip[ clipUuid ];
+
+		if ( actionsForClip !== undefined ) {
+
+			return actionsForClip.actionByRoot[ rootUuid ] || null;
+
+		}
+
+		return null;
+
+	},
+
+	// deactivates all previously scheduled actions
+	stopAllAction: function () {
+
+		var actions = this._actions,
+			nActions = this._nActiveActions,
+			bindings = this._bindings,
+			nBindings = this._nActiveBindings;
+
+		this._nActiveActions = 0;
+		this._nActiveBindings = 0;
+
+		for ( var i = 0; i !== nActions; ++ i ) {
+
+			actions[ i ].reset();
+
+		}
+
+		for ( var i = 0; i !== nBindings; ++ i ) {
+
+			bindings[ i ].useCount = 0;
+
+		}
+
+		return this;
+
+	},
+
+	// advance the time and update apply the animation
+	update: function ( deltaTime ) {
+
+		deltaTime *= this.timeScale;
+
+		var actions = this._actions,
+			nActions = this._nActiveActions,
+
+			time = this.time += deltaTime,
+			timeDirection = Math.sign( deltaTime ),
+
+			accuIndex = this._accuIndex ^= 1;
+
+		// run active actions
+
+		for ( var i = 0; i !== nActions; ++ i ) {
+
+			var action = actions[ i ];
+
+			if ( action.enabled ) {
+
+				action._update( time, deltaTime, timeDirection, accuIndex );
+
+			}
+
+		}
+
+		// update scene graph
+
+		var bindings = this._bindings,
+			nBindings = this._nActiveBindings;
+
+		for ( var i = 0; i !== nBindings; ++ i ) {
+
+			bindings[ i ].apply( accuIndex );
+
+		}
+
+		return this;
+
+	},
+
+	// return this mixer's root target object
+	getRoot: function () {
+
+		return this._root;
+
+	},
+
+	// free all resources specific to a particular clip
+	uncacheClip: function ( clip ) {
+
+		var actions = this._actions,
+			clipUuid = clip.uuid,
+			actionsByClip = this._actionsByClip,
+			actionsForClip = actionsByClip[ clipUuid ];
+
+		if ( actionsForClip !== undefined ) {
+
+			// note: just calling _removeInactiveAction would mess up the
+			// iteration state and also require updating the state we can
+			// just throw away
+
+			var actionsToRemove = actionsForClip.knownActions;
+
+			for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {
+
+				var action = actionsToRemove[ i ];
+
+				this._deactivateAction( action );
+
+				var cacheIndex = action._cacheIndex,
+					lastInactiveAction = actions[ actions.length - 1 ];
+
+				action._cacheIndex = null;
+				action._byClipCacheIndex = null;
+
+				lastInactiveAction._cacheIndex = cacheIndex;
+				actions[ cacheIndex ] = lastInactiveAction;
+				actions.pop();
+
+				this._removeInactiveBindingsForAction( action );
+
+			}
+
+			delete actionsByClip[ clipUuid ];
+
+		}
+
+	},
+
+	// free all resources specific to a particular root target object
+	uncacheRoot: function ( root ) {
+
+		var rootUuid = root.uuid,
+			actionsByClip = this._actionsByClip;
+
+		for ( var clipUuid in actionsByClip ) {
+
+			var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,
+				action = actionByRoot[ rootUuid ];
+
+			if ( action !== undefined ) {
+
+				this._deactivateAction( action );
+				this._removeInactiveAction( action );
+
+			}
+
+		}
+
+		var bindingsByRoot = this._bindingsByRootAndName,
+			bindingByName = bindingsByRoot[ rootUuid ];
+
+		if ( bindingByName !== undefined ) {
+
+			for ( var trackName in bindingByName ) {
+
+				var binding = bindingByName[ trackName ];
+				binding.restoreOriginalState();
+				this._removeInactiveBinding( binding );
+
+			}
+
+		}
+
+	},
+
+	// remove a targeted clip from the cache
+	uncacheAction: function ( clip, optionalRoot ) {
+
+		var action = this.existingAction( clip, optionalRoot );
+
+		if ( action !== null ) {
+
+			this._deactivateAction( action );
+			this._removeInactiveAction( action );
+
+		}
+
+	}
+
+};
+
+// Implementation details:
+
+Object.assign( AnimationMixer.prototype, {
+
 	_bindAction: function ( action, prototypeAction ) {
 
 		var root = action._localRoot || this._root,
@@ -82,8 +332,8 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 						_propertyBindings[ i ].binding.parsedPath;
 
 				binding = new PropertyMixer(
-					PropertyBinding.create( root, trackName, path ),
-					track.ValueTypeName, track.getValueSize() );
+						PropertyBinding.create( root, trackName, path ),
+						track.ValueTypeName, track.getValueSize() );
 
 				++ binding.referenceCount;
 				this._addInactiveBinding( binding, rootUuid, trackName );
@@ -112,7 +362,7 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 					actionsForClip = this._actionsByClip[ clipUuid ];
 
 				this._bindAction( action,
-					actionsForClip && actionsForClip.knownActions[ 0 ] );
+						actionsForClip && actionsForClip.knownActions[ 0 ] );
 
 				this._addInactiveAction( action, clipUuid, rootUuid );
 
@@ -460,8 +710,8 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 		if ( interpolant === undefined ) {
 
 			interpolant = new LinearInterpolant(
-				new Float32Array( 2 ), new Float32Array( 2 ),
-				1, this._controlInterpolantsResultBuffer );
+					new Float32Array( 2 ), new Float32Array( 2 ),
+						1, this._controlInterpolantsResultBuffer );
 
 			interpolant.__cacheIndex = lastActiveIndex;
 			interpolants[ lastActiveIndex ] = interpolant;
@@ -489,253 +739,10 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 
 	},
 
-	_controlInterpolantsResultBuffer: new Float32Array( 1 ),
-
-	// return an action for a clip optionally using a custom root target
-	// object (this method allocates a lot of dynamic memory in case a
-	// previously unknown clip/root combination is specified)
-	clipAction: function ( clip, optionalRoot ) {
-
-		var root = optionalRoot || this._root,
-			rootUuid = root.uuid,
-
-			clipObject = typeof clip === 'string' ?
-				AnimationClip.findByName( root, clip ) : clip,
-
-			clipUuid = clipObject !== null ? clipObject.uuid : clip,
-
-			actionsForClip = this._actionsByClip[ clipUuid ],
-			prototypeAction = null;
-
-		if ( actionsForClip !== undefined ) {
-
-			var existingAction =
-					actionsForClip.actionByRoot[ rootUuid ];
-
-			if ( existingAction !== undefined ) {
-
-				return existingAction;
-
-			}
-
-			// we know the clip, so we don't have to parse all
-			// the bindings again but can just copy
-			prototypeAction = actionsForClip.knownActions[ 0 ];
-
-			// also, take the clip from the prototype action
-			if ( clipObject === null )
-				clipObject = prototypeAction._clip;
-
-		}
-
-		// clip must be known when specified via string
-		if ( clipObject === null ) return null;
-
-		// allocate all resources required to run it
-		var newAction = new AnimationAction( this, clipObject, optionalRoot );
-
-		this._bindAction( newAction, prototypeAction );
-
-		// and make the action known to the memory manager
-		this._addInactiveAction( newAction, clipUuid, rootUuid );
-
-		return newAction;
-
-	},
-
-	// get an existing action
-	existingAction: function ( clip, optionalRoot ) {
-
-		var root = optionalRoot || this._root,
-			rootUuid = root.uuid,
-
-			clipObject = typeof clip === 'string' ?
-				AnimationClip.findByName( root, clip ) : clip,
-
-			clipUuid = clipObject ? clipObject.uuid : clip,
-
-			actionsForClip = this._actionsByClip[ clipUuid ];
-
-		if ( actionsForClip !== undefined ) {
-
-			return actionsForClip.actionByRoot[ rootUuid ] || null;
-
-		}
-
-		return null;
-
-	},
-
-	// deactivates all previously scheduled actions
-	stopAllAction: function () {
-
-		var actions = this._actions,
-			nActions = this._nActiveActions,
-			bindings = this._bindings,
-			nBindings = this._nActiveBindings;
-
-		this._nActiveActions = 0;
-		this._nActiveBindings = 0;
-
-		for ( var i = 0; i !== nActions; ++ i ) {
-
-			actions[ i ].reset();
-
-		}
-
-		for ( var i = 0; i !== nBindings; ++ i ) {
-
-			bindings[ i ].useCount = 0;
-
-		}
-
-		return this;
-
-	},
-
-	// advance the time and update apply the animation
-	update: function ( deltaTime ) {
-
-		deltaTime *= this.timeScale;
-
-		var actions = this._actions,
-			nActions = this._nActiveActions,
-
-			time = this.time += deltaTime,
-			timeDirection = Math.sign( deltaTime ),
-
-			accuIndex = this._accuIndex ^= 1;
-
-		// run active actions
-
-		for ( var i = 0; i !== nActions; ++ i ) {
-
-			var action = actions[ i ];
-
-			if ( action.enabled ) {
-
-				action._update( time, deltaTime, timeDirection, accuIndex );
-
-			}
-
-		}
-
-		// update scene graph
-
-		var bindings = this._bindings,
-			nBindings = this._nActiveBindings;
-
-		for ( var i = 0; i !== nBindings; ++ i ) {
-
-			bindings[ i ].apply( accuIndex );
-
-		}
-
-		return this;
-
-	},
-
-	// return this mixer's root target object
-	getRoot: function () {
-
-		return this._root;
-
-	},
-
-	// free all resources specific to a particular clip
-	uncacheClip: function ( clip ) {
-
-		var actions = this._actions,
-			clipUuid = clip.uuid,
-			actionsByClip = this._actionsByClip,
-			actionsForClip = actionsByClip[ clipUuid ];
-
-		if ( actionsForClip !== undefined ) {
-
-			// note: just calling _removeInactiveAction would mess up the
-			// iteration state and also require updating the state we can
-			// just throw away
-
-			var actionsToRemove = actionsForClip.knownActions;
-
-			for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {
-
-				var action = actionsToRemove[ i ];
-
-				this._deactivateAction( action );
-
-				var cacheIndex = action._cacheIndex,
-					lastInactiveAction = actions[ actions.length - 1 ];
-
-				action._cacheIndex = null;
-				action._byClipCacheIndex = null;
-
-				lastInactiveAction._cacheIndex = cacheIndex;
-				actions[ cacheIndex ] = lastInactiveAction;
-				actions.pop();
-
-				this._removeInactiveBindingsForAction( action );
-
-			}
-
-			delete actionsByClip[ clipUuid ];
-
-		}
-
-	},
-
-	// free all resources specific to a particular root target object
-	uncacheRoot: function ( root ) {
-
-		var rootUuid = root.uuid,
-			actionsByClip = this._actionsByClip;
-
-		for ( var clipUuid in actionsByClip ) {
-
-			var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,
-				action = actionByRoot[ rootUuid ];
-
-			if ( action !== undefined ) {
-
-				this._deactivateAction( action );
-				this._removeInactiveAction( action );
-
-			}
-
-		}
-
-		var bindingsByRoot = this._bindingsByRootAndName,
-			bindingByName = bindingsByRoot[ rootUuid ];
-
-		if ( bindingByName !== undefined ) {
-
-			for ( var trackName in bindingByName ) {
-
-				var binding = bindingByName[ trackName ];
-				binding.restoreOriginalState();
-				this._removeInactiveBinding( binding );
-
-			}
-
-		}
-
-	},
-
-	// remove a targeted clip from the cache
-	uncacheAction: function ( clip, optionalRoot ) {
-
-		var action = this.existingAction( clip, optionalRoot );
-
-		if ( action !== null ) {
-
-			this._deactivateAction( action );
-			this._removeInactiveAction( action );
-
-		}
-
-	}
+	_controlInterpolantsResultBuffer: new Float32Array( 1 )
 
 } );
 
+Object.assign( AnimationMixer.prototype, EventDispatcher.prototype );
 
 export { AnimationMixer };

+ 3 - 2
src/animation/AnimationObjectGroup.js

@@ -71,7 +71,7 @@ function AnimationObjectGroup( var_args ) {
 
 }
 
-Object.assign( AnimationObjectGroup.prototype, {
+AnimationObjectGroup.prototype = {
 
 	constructor: AnimationObjectGroup,
 
@@ -371,7 +371,8 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 	}
 
-} );
+};
+
 
 
 export { AnimationObjectGroup };

+ 272 - 271
src/animation/PropertyBinding.js

@@ -8,226 +8,268 @@
  * @author tschw
  */
 
-function Composite ( targetGroup, path, optionalParsedPath ) {
+function PropertyBinding( rootNode, path, parsedPath ) {
 
-	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
+	this.path = path;
+	this.parsedPath = parsedPath ||
+			PropertyBinding.parseTrackName( path );
 
-	this._targetGroup = targetGroup;
-	this._bindings = targetGroup.subscribe_( path, parsedPath );
+	this.node = PropertyBinding.findNode(
+			rootNode, this.parsedPath.nodeName ) || rootNode;
+
+	this.rootNode = rootNode;
 
 }
 
-Object.assign( Composite.prototype, {
+PropertyBinding.prototype = {
 
-	constructor: Composite,
+	constructor: PropertyBinding,
 
-	getValue: function( array, offset ) {
+	getValue: function getValue_unbound( targetArray, offset ) {
 
-		this.bind(); // bind all binding
+		this.bind();
+		this.getValue( targetArray, offset );
 
-		var firstValidIndex = this._targetGroup.nCachedObjects_,
-			binding = this._bindings[ firstValidIndex ];
+		// Note: This class uses a State pattern on a per-method basis:
+		// 'bind' sets 'this.getValue' / 'setValue' and shadows the
+		// prototype version of these methods with one that represents
+		// the bound state. When the property is not found, the methods
+		// become no-ops.
 
-		// and only call .getValue on the first
-		if ( binding !== undefined ) binding.getValue( array, offset );
+	},
+
+	setValue: function getValue_unbound( sourceArray, offset ) {
+
+		this.bind();
+		this.setValue( sourceArray, offset );
 
 	},
 
-	setValue: function( array, offset ) {
+	// create getter / setter pair for a property in the scene graph
+	bind: function() {
 
-		var bindings = this._bindings;
+		var targetObject = this.node,
+			parsedPath = this.parsedPath,
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				  n = bindings.length; i !== n; ++ i ) {
+			objectName = parsedPath.objectName,
+			propertyName = parsedPath.propertyName,
+			propertyIndex = parsedPath.propertyIndex;
 
-			bindings[ i ].setValue( array, offset );
+		if ( ! targetObject ) {
 
-		}
+			targetObject = PropertyBinding.findNode(
+					this.rootNode, parsedPath.nodeName ) || this.rootNode;
 
-	},
+			this.node = targetObject;
 
-	bind: function() {
+		}
 
-		var bindings = this._bindings;
+		// set fail state so we can just 'return' on error
+		this.getValue = this._getValue_unavailable;
+		this.setValue = this._setValue_unavailable;
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				  n = bindings.length; i !== n; ++ i ) {
+ 		// ensure there is a value node
+		if ( ! targetObject ) {
 
-			bindings[ i ].bind();
+			console.error( "  trying to update node for track: " + this.path + " but it wasn't found." );
+			return;
 
 		}
 
-	},
+		if ( objectName ) {
 
-	unbind: function() {
+			var objectIndex = parsedPath.objectIndex;
 
-		var bindings = this._bindings;
+			// special cases were we need to reach deeper into the hierarchy to get the face materials....
+			switch ( objectName ) {
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				  n = bindings.length; i !== n; ++ i ) {
+				case 'materials':
 
-			bindings[ i ].unbind();
+					if ( ! targetObject.material ) {
 
-		}
+						console.error( '  can not bind to material as node does not have a material', this );
+						return;
 
-	}
+					}
 
-} );
+					if ( ! targetObject.material.materials ) {
 
+						console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
+						return;
 
-function PropertyBinding( rootNode, path, parsedPath ) {
+					}
 
-	this.path = path;
-	this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );
+					targetObject = targetObject.material.materials;
 
-	this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;
+					break;
 
-	this.rootNode = rootNode;
+				case 'bones':
 
-}
+					if ( ! targetObject.skeleton ) {
 
-Object.assign( PropertyBinding, {
+						console.error( '  can not bind to bones as node does not have a skeleton', this );
+						return;
 
-	Composite: Composite,
+					}
 
-	create: function( root, path, parsedPath ) {
+					// potential future optimization: skip this if propertyIndex is already an integer
+					// and convert the integer string to a true integer.
 
-		if ( ! ( root && root.isAnimationObjectGroup ) ) {
+					targetObject = targetObject.skeleton.bones;
 
-			return new PropertyBinding( root, path, parsedPath );
+					// support resolving morphTarget names into indices.
+					for ( var i = 0; i < targetObject.length; i ++ ) {
 
-		} else {
+						if ( targetObject[ i ].name === objectIndex ) {
 
-			return new PropertyBinding.Composite( root, path, parsedPath );
+							objectIndex = i;
+							break;
 
-		}
+						}
 
-	},
+					}
+
+					break;
+
+				default:
+
+					if ( targetObject[ objectName ] === undefined ) {
+
+						console.error( '  can not bind to objectName of node, undefined', this );
+						return;
+
+					}
+
+					targetObject = targetObject[ objectName ];
+
+			}
 
-	parseTrackName: function( trackName ) {
 
-		// matches strings in the form of:
-		//    nodeName.property
-		//    nodeName.property[accessor]
-		//    nodeName.material.property[accessor]
-		//    uuid.property[accessor]
-		//    uuid.objectName[objectIndex].propertyName[propertyIndex]
-		//    parentName/nodeName.property
-		//    parentName/parentName/nodeName.property[index]
-		//    .bone[Armature.DEF_cog].position
-		//    scene:helium_balloon_model:helium_balloon_model.position
-		// created and tested via https://regex101.com/#javascript
+			if ( objectIndex !== undefined ) {
+
+				if ( targetObject[ objectIndex ] === undefined ) {
+
+					console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
+					return;
 
-		var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
-		var matches = re.exec( trackName );
+				}
 
-		if ( ! matches ) {
+				targetObject = targetObject[ objectIndex ];
 
-			throw new Error( "cannot parse trackName at all: " + trackName );
+			}
 
 		}
 
-		var results = {
-			// directoryName: matches[ 1 ], // (tschw) currently unused
-			nodeName: matches[ 2 ], 	// allowed to be null, specified root node.
-			objectName: matches[ 3 ],
-			objectIndex: matches[ 4 ],
-			propertyName: matches[ 5 ],
-			propertyIndex: matches[ 6 ]	// allowed to be null, specifies that the whole property is set.
-		};
+		// resolve property
+		var nodeProperty = targetObject[ propertyName ];
 
-		if ( results.propertyName === null || results.propertyName.length === 0 ) {
+		if ( nodeProperty === undefined ) {
+
+			var nodeName = parsedPath.nodeName;
 
-			throw new Error( "can not parse propertyName from trackName: " + trackName );
+			console.error( "  trying to update property for track: " + nodeName +
+					'.' + propertyName + " but it wasn't found.", targetObject );
+			return;
 
 		}
 
-		return results;
+		// determine versioning scheme
+		var versioning = this.Versioning.None;
 
-	},
+		if ( targetObject.needsUpdate !== undefined ) { // material
 
-	findNode: function( root, nodeName ) {
+			versioning = this.Versioning.NeedsUpdate;
+			this.targetObject = targetObject;
 
-		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+		} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
 
-			return root;
+			versioning = this.Versioning.MatrixWorldNeedsUpdate;
+			this.targetObject = targetObject;
 
 		}
 
-		// search into skeleton bones.
-		if ( root.skeleton ) {
-
-			var searchSkeleton = function( skeleton ) {
+		// determine how the property gets bound
+		var bindingType = this.BindingType.Direct;
 
-				for( var i = 0; i < skeleton.bones.length; i ++ ) {
+		if ( propertyIndex !== undefined ) {
+			// access a sub element of the property array (only primitives are supported right now)
 
-					var bone = skeleton.bones[ i ];
+			if ( propertyName === "morphTargetInfluences" ) {
+				// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
 
-					if ( bone.name === nodeName ) {
+				// support resolving morphTarget names into indices.
+				if ( ! targetObject.geometry ) {
 
-						return bone;
+					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
+					return;
 
-					}
 				}
 
-				return null;
+				if ( ! targetObject.geometry.morphTargets ) {
+
+					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
+					return;
 
-			};
+				}
 
-			var bone = searchSkeleton( root.skeleton );
+				for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
 
-			if ( bone ) {
+					if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
 
-				return bone;
+						propertyIndex = i;
+						break;
 
-			}
-		}
+					}
 
-		// search into node subtree.
-		if ( root.children ) {
+				}
 
-			var searchNodeSubtree = function( children ) {
+			}
 
-				for( var i = 0; i < children.length; i ++ ) {
+			bindingType = this.BindingType.ArrayElement;
 
-					var childNode = children[ i ];
+			this.resolvedProperty = nodeProperty;
+			this.propertyIndex = propertyIndex;
 
-					if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
+		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
+			// must use copy for Object3D.Euler/Quaternion
 
-						return childNode;
+			bindingType = this.BindingType.HasFromToArray;
 
-					}
+			this.resolvedProperty = nodeProperty;
 
-					var result = searchNodeSubtree( childNode.children );
+		} else if ( Array.isArray( nodeProperty ) ) {
 
-					if ( result ) return result;
+			bindingType = this.BindingType.EntireArray;
 
-				}
+			this.resolvedProperty = nodeProperty;
 
-				return null;
+		} else {
 
-			};
+			this.propertyName = propertyName;
 
-			var subTreeNode = searchNodeSubtree( root.children );
+		}
 
-			if ( subTreeNode ) {
+		// select getter / setter
+		this.getValue = this.GetterByBindingType[ bindingType ];
+		this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
 
-				return subTreeNode;
+	},
 
-			}
+	unbind: function() {
 
-		}
+		this.node = null;
 
-		return null;
+		// back to the prototype version of getValue / setValue
+		// note: avoiding to mutate the shape of 'this' via 'delete'
+		this.getValue = this._getValue_unbound;
+		this.setValue = this._setValue_unbound;
 
 	}
 
-} );
+};
 
 Object.assign( PropertyBinding.prototype, { // prototype, continued
 
-	constructor: PropertyBinding,
-
 	// these are used to "bind" a nonexistent property
 	_getValue_unavailable: function() {},
 	_setValue_unavailable: function() {},
@@ -402,250 +444,209 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 		]
 
-	],
+	]
 
-	getValue: function getValue_unbound( targetArray, offset ) {
+} );
 
-		this.bind();
-		this.getValue( targetArray, offset );
+PropertyBinding.Composite =
+		function( targetGroup, path, optionalParsedPath ) {
 
-		// Note: This class uses a State pattern on a per-method basis:
-		// 'bind' sets 'this.getValue' / 'setValue' and shadows the
-		// prototype version of these methods with one that represents
-		// the bound state. When the property is not found, the methods
-		// become no-ops.
-
-	},
+	var parsedPath = optionalParsedPath ||
+			PropertyBinding.parseTrackName( path );
 
-	setValue: function getValue_unbound( sourceArray, offset ) {
+	this._targetGroup = targetGroup;
+	this._bindings = targetGroup.subscribe_( path, parsedPath );
 
-		this.bind();
-		this.setValue( sourceArray, offset );
+};
 
-	},
+PropertyBinding.Composite.prototype = {
 
-	// create getter / setter pair for a property in the scene graph
-	bind: function() {
+	constructor: PropertyBinding.Composite,
 
-		var targetObject = this.node,
-			parsedPath = this.parsedPath,
+	getValue: function( array, offset ) {
 
-			objectName = parsedPath.objectName,
-			propertyName = parsedPath.propertyName,
-			propertyIndex = parsedPath.propertyIndex;
+		this.bind(); // bind all binding
 
-		if ( ! targetObject ) {
+		var firstValidIndex = this._targetGroup.nCachedObjects_,
+			binding = this._bindings[ firstValidIndex ];
 
-			targetObject = PropertyBinding.findNode(
-					this.rootNode, parsedPath.nodeName ) || this.rootNode;
+		// and only call .getValue on the first
+		if ( binding !== undefined ) binding.getValue( array, offset );
 
-			this.node = targetObject;
+	},
 
-		}
+	setValue: function( array, offset ) {
 
-		// set fail state so we can just 'return' on error
-		this.getValue = this._getValue_unavailable;
-		this.setValue = this._setValue_unavailable;
+		var bindings = this._bindings;
 
-		// ensure there is a value node
-		if ( ! targetObject ) {
+		for ( var i = this._targetGroup.nCachedObjects_,
+				n = bindings.length; i !== n; ++ i ) {
 
-			console.error( "  trying to update node for track: " + this.path + " but it wasn't found." );
-			return;
+			bindings[ i ].setValue( array, offset );
 
 		}
 
-		if ( objectName ) {
+	},
 
-			var objectIndex = parsedPath.objectIndex;
+	bind: function() {
 
-			// special cases were we need to reach deeper into the hierarchy to get the face materials....
-			switch ( objectName ) {
+		var bindings = this._bindings;
 
-				case 'materials':
+		for ( var i = this._targetGroup.nCachedObjects_,
+				n = bindings.length; i !== n; ++ i ) {
 
-					if ( ! targetObject.material ) {
+			bindings[ i ].bind();
 
-						console.error( '  can not bind to material as node does not have a material', this );
-						return;
+		}
 
-					}
+	},
 
-					if ( ! targetObject.material.materials ) {
+	unbind: function() {
 
-						console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
-						return;
+		var bindings = this._bindings;
 
-					}
+		for ( var i = this._targetGroup.nCachedObjects_,
+				n = bindings.length; i !== n; ++ i ) {
 
-					targetObject = targetObject.material.materials;
+			bindings[ i ].unbind();
 
-					break;
+		}
 
-				case 'bones':
+	}
 
-					if ( ! targetObject.skeleton ) {
+};
 
-						console.error( '  can not bind to bones as node does not have a skeleton', this );
-						return;
+PropertyBinding.create = function( root, path, parsedPath ) {
 
-					}
+	if ( ! ( root && root.isAnimationObjectGroup ) ) {
 
-					// potential future optimization: skip this if propertyIndex is already an integer
-					// and convert the integer string to a true integer.
+		return new PropertyBinding( root, path, parsedPath );
 
-					targetObject = targetObject.skeleton.bones;
+	} else {
 
-					// support resolving morphTarget names into indices.
-					for ( var i = 0; i < targetObject.length; i ++ ) {
+		return new PropertyBinding.Composite( root, path, parsedPath );
 
-						if ( targetObject[ i ].name === objectIndex ) {
+	}
 
-							objectIndex = i;
-							break;
+};
 
-						}
+PropertyBinding.parseTrackName = function( trackName ) {
 
-					}
+	// matches strings in the form of:
+	//    nodeName.property
+	//    nodeName.property[accessor]
+	//    nodeName.material.property[accessor]
+	//    uuid.property[accessor]
+	//    uuid.objectName[objectIndex].propertyName[propertyIndex]
+	//    parentName/nodeName.property
+	//    parentName/parentName/nodeName.property[index]
+	//    .bone[Armature.DEF_cog].position
+	//    scene:helium_balloon_model:helium_balloon_model.position
+	// created and tested via https://regex101.com/#javascript
 
-					break;
+	var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
+	var matches = re.exec( trackName );
 
-				default:
+	if ( ! matches ) {
 
-					if ( targetObject[ objectName ] === undefined ) {
-
-						console.error( '  can not bind to objectName of node, undefined', this );
-						return;
+		throw new Error( "cannot parse trackName at all: " + trackName );
 
-					}
+	}
 
-					targetObject = targetObject[ objectName ];
+	var results = {
+		// directoryName: matches[ 1 ], // (tschw) currently unused
+		nodeName: matches[ 2 ], 	// allowed to be null, specified root node.
+		objectName: matches[ 3 ],
+		objectIndex: matches[ 4 ],
+		propertyName: matches[ 5 ],
+		propertyIndex: matches[ 6 ]	// allowed to be null, specifies that the whole property is set.
+	};
 
-			}
+	if ( results.propertyName === null || results.propertyName.length === 0 ) {
 
+		throw new Error( "can not parse propertyName from trackName: " + trackName );
 
-			if ( objectIndex !== undefined ) {
+	}
 
-				if ( targetObject[ objectIndex ] === undefined ) {
+	return results;
 
-					console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
-					return;
+};
 
-				}
+PropertyBinding.findNode = function( root, nodeName ) {
 
-				targetObject = targetObject[ objectIndex ];
+	if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
 
-			}
+		return root;
 
-		}
+	}
 
-		// resolve property
-		var nodeProperty = targetObject[ propertyName ];
+	// search into skeleton bones.
+	if ( root.skeleton ) {
 
-		if ( nodeProperty === undefined ) {
+		var searchSkeleton = function( skeleton ) {
 
-			var nodeName = parsedPath.nodeName;
+			for( var i = 0; i < skeleton.bones.length; i ++ ) {
 
-			console.error( "  trying to update property for track: " + nodeName +
-				'.' + propertyName + " but it wasn't found.", targetObject );
-			return;
+				var bone = skeleton.bones[ i ];
 
-		}
+				if ( bone.name === nodeName ) {
 
-		// determine versioning scheme
-		var versioning = this.Versioning.None;
+					return bone;
 
-		if ( targetObject.needsUpdate !== undefined ) { // material
+				}
+			}
 
-			versioning = this.Versioning.NeedsUpdate;
-			this.targetObject = targetObject;
+			return null;
 
-		} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
+		};
 
-			versioning = this.Versioning.MatrixWorldNeedsUpdate;
-			this.targetObject = targetObject;
+		var bone = searchSkeleton( root.skeleton );
 
-		}
+		if ( bone ) {
 
-		// determine how the property gets bound
-		var bindingType = this.BindingType.Direct;
+			return bone;
 
-		if ( propertyIndex !== undefined ) {
-			// access a sub element of the property array (only primitives are supported right now)
+		}
+	}
 
-			if ( propertyName === "morphTargetInfluences" ) {
-				// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
+	// search into node subtree.
+	if ( root.children ) {
 
-				// support resolving morphTarget names into indices.
-				if ( ! targetObject.geometry ) {
+		var searchNodeSubtree = function( children ) {
 
-					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
-					return;
+			for( var i = 0; i < children.length; i ++ ) {
 
-				}
+				var childNode = children[ i ];
 
-				if ( ! targetObject.geometry.morphTargets ) {
+				if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
 
-					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
-					return;
+					return childNode;
 
 				}
 
-				for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
-
-					if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
-
-						propertyIndex = i;
-						break;
-
-					}
+				var result = searchNodeSubtree( childNode.children );
 
-				}
+				if ( result ) return result;
 
 			}
 
-			bindingType = this.BindingType.ArrayElement;
-
-			this.resolvedProperty = nodeProperty;
-			this.propertyIndex = propertyIndex;
-
-		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
-			// must use copy for Object3D.Euler/Quaternion
-
-			bindingType = this.BindingType.HasFromToArray;
-
-			this.resolvedProperty = nodeProperty;
+			return null;
 
-		} else if ( Array.isArray( nodeProperty ) ) {
-      
-			bindingType = this.BindingType.EntireArray;
+		};
 
-			this.resolvedProperty = nodeProperty;
+		var subTreeNode = searchNodeSubtree( root.children );
 
-		} else {
+		if ( subTreeNode ) {
 
-			this.propertyName = propertyName;
+			return subTreeNode;
 
 		}
 
-		// select getter / setter
-		this.getValue = this.GetterByBindingType[ bindingType ];
-		this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
-
-	},
-
-	unbind: function() {
-
-		this.node = null;
-
-		// back to the prototype version of getValue / setValue
-		// note: avoiding to mutate the shape of 'this' via 'delete'
-		this.getValue = this._getValue_unbound;
-		this.setValue = this._setValue_unbound;
-
 	}
 
-} );
+	return null;
+
+};
 
 
 export { PropertyBinding };

+ 4 - 4
src/animation/PropertyMixer.js

@@ -56,7 +56,7 @@ function PropertyMixer( binding, typeName, valueSize ) {
 
 }
 
-Object.assign( PropertyMixer.prototype, {
+PropertyMixer.prototype = {
 
 	constructor: PropertyMixer,
 
@@ -118,7 +118,7 @@ Object.assign( PropertyMixer.prototype, {
 			var originalValueOffset = stride * 3;
 
 			this._mixBufferRegion(
-				buffer, offset, originalValueOffset, 1 - weight, stride );
+					buffer, offset, originalValueOffset, 1 - weight, stride );
 
 		}
 
@@ -188,7 +188,7 @@ Object.assign( PropertyMixer.prototype, {
 	_slerp: function( buffer, dstOffset, srcOffset, t, stride ) {
 
 		Quaternion.slerpFlat( buffer, dstOffset,
-			buffer, dstOffset, buffer, srcOffset, t );
+				buffer, dstOffset, buffer, srcOffset, t );
 
 	},
 
@@ -206,7 +206,7 @@ Object.assign( PropertyMixer.prototype, {
 
 	}
 
-} );
+};
 
 
 export { PropertyMixer };

+ 2 - 2
src/core/BufferAttribute.js

@@ -32,7 +32,7 @@ function BufferAttribute( array, itemSize, normalized ) {
 
 }
 
-Object.assign( BufferAttribute.prototype, {
+BufferAttribute.prototype = {
 
 	constructor: BufferAttribute,
 
@@ -335,7 +335,7 @@ Object.assign( BufferAttribute.prototype, {
 
 	}
 
-} );
+};
 
 //
 

+ 36 - 23
src/core/BufferGeometry.js

@@ -39,9 +39,7 @@ function BufferGeometry() {
 
 }
 
-BufferGeometry.MaxIndex = 65535;
-
-Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
+BufferGeometry.prototype = {
 
 	constructor: BufferGeometry,
 
@@ -175,10 +173,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world x-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateX( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationX( angle );
 
 			this.applyMatrix( m1 );
@@ -193,10 +193,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world y-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateY( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationY( angle );
 
 			this.applyMatrix( m1 );
@@ -211,10 +213,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world z-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateZ( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationZ( angle );
 
 			this.applyMatrix( m1 );
@@ -229,10 +233,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		// translate geometry
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function translate( x, y, z ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeTranslation( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -247,10 +253,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		// scale geometry
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function scale( x, y, z ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeScale( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -263,10 +271,12 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 	lookAt: function () {
 
-		var obj = new Object3D();
+		var obj;
 
 		return function lookAt( vector ) {
 
+			if ( obj === undefined ) obj = new Object3D();
+
 			obj.lookAt( vector );
 
 			obj.updateMatrix();
@@ -969,28 +979,28 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 	clone: function () {
 
 		/*
-		 // Handle primitives
+		// Handle primitives
 
-		 var parameters = this.parameters;
+		var parameters = this.parameters;
 
-		 if ( parameters !== undefined ) {
+		if ( parameters !== undefined ) {
 
-		 var values = [];
+			var values = [];
 
-		 for ( var key in parameters ) {
+			for ( var key in parameters ) {
 
-		 values.push( parameters[ key ] );
+				values.push( parameters[ key ] );
 
-		 }
+			}
 
-		 var geometry = Object.create( this.constructor.prototype );
-		 this.constructor.apply( geometry, values );
-		 return geometry;
+			var geometry = Object.create( this.constructor.prototype );
+			this.constructor.apply( geometry, values );
+			return geometry;
 
-		 }
+		}
 
-		 return new this.constructor().copy( this );
-		 */
+		return new this.constructor().copy( this );
+		*/
 
 		return new BufferGeometry().copy( this );
 
@@ -1099,7 +1109,10 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
+
+BufferGeometry.MaxIndex = 65535;
 
+Object.assign( BufferGeometry.prototype, EventDispatcher.prototype );
 
 export { BufferGeometry };

+ 2 - 2
src/core/Clock.js

@@ -14,7 +14,7 @@ function Clock( autoStart ) {
 
 }
 
-Object.assign( Clock.prototype, {
+Clock.prototype = {
 
 	constructor: Clock,
 
@@ -67,7 +67,7 @@ Object.assign( Clock.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Clock };

+ 2 - 2
src/core/EventDispatcher.js

@@ -4,7 +4,7 @@
 
 function EventDispatcher() {}
 
-Object.assign( EventDispatcher.prototype, {
+EventDispatcher.prototype = {
 
 	addEventListener: function ( type, listener ) {
 
@@ -87,7 +87,7 @@ Object.assign( EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
 
 export { EventDispatcher };

+ 2 - 2
src/core/Face3.js

@@ -22,7 +22,7 @@ function Face3( a, b, c, normal, color, materialIndex ) {
 
 }
 
-Object.assign( Face3.prototype, {
+Face3.prototype = {
 
 	constructor: Face3,
 
@@ -59,7 +59,7 @@ Object.assign( Face3.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Face3 };

+ 45 - 32
src/core/Geometry.js

@@ -59,7 +59,7 @@ function Geometry() {
 
 }
 
-Object.assign( Geometry.prototype, EventDispatcher.prototype, {
+Geometry.prototype = {
 
 	constructor: Geometry,
 
@@ -112,10 +112,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world x-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateX( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationX( angle );
 
 			this.applyMatrix( m1 );
@@ -130,10 +132,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world y-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateY( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationY( angle );
 
 			this.applyMatrix( m1 );
@@ -148,10 +152,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 		// rotate geometry around world z-axis
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function rotateZ( angle ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeRotationZ( angle );
 
 			this.applyMatrix( m1 );
@@ -166,10 +172,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 		// translate geometry
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function translate( x, y, z ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeTranslation( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -184,10 +192,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 		// scale geometry
 
-		var m1 = new Matrix4();
+		var m1;
 
 		return function scale( x, y, z ) {
 
+			if ( m1 === undefined ) m1 = new Matrix4();
+
 			m1.makeScale( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -200,10 +210,12 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 	lookAt: function () {
 
-		var obj = new Object3D();
+		var obj;
 
 		return function lookAt( vector ) {
 
+			if ( obj === undefined ) obj = new Object3D();
+
 			obj.lookAt( vector );
 
 			obj.updateMatrix();
@@ -702,15 +714,15 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 		}
 
 		var normalMatrix,
-			vertexOffset = this.vertices.length,
-			vertices1 = this.vertices,
-			vertices2 = geometry.vertices,
-			faces1 = this.faces,
-			faces2 = geometry.faces,
-			uvs1 = this.faceVertexUvs[ 0 ],
-			uvs2 = geometry.faceVertexUvs[ 0 ],
-			colors1 = this.colors,
-			colors2 = geometry.colors;
+		vertexOffset = this.vertices.length,
+		vertices1 = this.vertices,
+		vertices2 = geometry.vertices,
+		faces1 = this.faces,
+		faces2 = geometry.faces,
+		uvs1 = this.faceVertexUvs[ 0 ],
+		uvs2 = geometry.faceVertexUvs[ 0 ],
+		colors1 = this.colors,
+		colors2 = geometry.colors;
 
 		if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
 
@@ -747,8 +759,8 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 		for ( i = 0, il = faces2.length; i < il; i ++ ) {
 
 			var face = faces2[ i ], faceCopy, normal, color,
-				faceVertexNormals = face.vertexNormals,
-				faceVertexColors = face.vertexColors;
+			faceVertexNormals = face.vertexNormals,
+			faceVertexColors = face.vertexColors;
 
 			faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
 			faceCopy.normal.copy( face.normal );
@@ -1159,28 +1171,28 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 	clone: function () {
 
 		/*
-		 // Handle primitives
+		// Handle primitives
 
-		 var parameters = this.parameters;
+		var parameters = this.parameters;
 
-		 if ( parameters !== undefined ) {
+		if ( parameters !== undefined ) {
 
-		 var values = [];
+			var values = [];
 
-		 for ( var key in parameters ) {
+			for ( var key in parameters ) {
 
-		 values.push( parameters[ key ] );
+				values.push( parameters[ key ] );
 
-		 }
+			}
 
-		 var geometry = Object.create( this.constructor.prototype );
-		 this.constructor.apply( geometry, values );
-		 return geometry;
+			var geometry = Object.create( this.constructor.prototype );
+			this.constructor.apply( geometry, values );
+			return geometry;
 
-		 }
+		}
 
-		 return new this.constructor().copy( this );
-		 */
+		return new this.constructor().copy( this );
+		*/
 
 		return new Geometry().copy( this );
 
@@ -1426,7 +1438,8 @@ Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
+Object.assign( Geometry.prototype, EventDispatcher.prototype );
 
 export { GeometryIdCount, Geometry };

+ 2 - 2
src/core/InterleavedBuffer.js

@@ -21,7 +21,7 @@ function InterleavedBuffer( array, stride ) {
 
 }
 
-Object.assign( InterleavedBuffer.prototype, {
+InterleavedBuffer.prototype = {
 
 	constructor: InterleavedBuffer,
 
@@ -104,7 +104,7 @@ Object.assign( InterleavedBuffer.prototype, {
 
 	}
 
-} );
+};
 
 
 export { InterleavedBuffer };

+ 13 - 8
src/core/InterleavedBufferAttribute.js

@@ -16,19 +16,24 @@ function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normal
 
 }
 
-Object.defineProperties( InterleavedBufferAttribute.prototype, {
 
-	"count" : { get: function () { return this.data.count; } },
+InterleavedBufferAttribute.prototype = {
 
-	"array" : { get: function () { return this.data.array; } }
+	constructor: InterleavedBufferAttribute,
 
-} );
+	isInterleavedBufferAttribute: true,
 
-Object.assign( InterleavedBufferAttribute.prototype, {
+	get count() {
 
-	constructor: InterleavedBufferAttribute,
+		return this.data.count;
 
-	isInterleavedBufferAttribute: true,
+	},
+
+	get array() {
+
+		return this.data.array;
+
+	},
 
 	setX: function ( index, x ) {
 
@@ -122,7 +127,7 @@ Object.assign( InterleavedBufferAttribute.prototype, {
 
 	}
 
-} );
+};
 
 
 export { InterleavedBufferAttribute };

+ 2 - 2
src/core/Layers.js

@@ -8,7 +8,7 @@ function Layers() {
 
 }
 
-Object.assign( Layers.prototype, {
+Layers.prototype = {
 
 	constructor: Layers,
 
@@ -42,7 +42,7 @@ Object.assign( Layers.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Layers };

+ 3 - 2
src/core/Object3D.js

@@ -101,7 +101,7 @@ function Object3D() {
 Object3D.DefaultUp = new Vector3( 0, 1, 0 );
 Object3D.DefaultMatrixAutoUpdate = true;
 
-Object.assign( Object3D.prototype, EventDispatcher.prototype, {
+Object3D.prototype = {
 
 	constructor: Object3D,
 
@@ -730,7 +730,8 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
+Object.assign( Object3D.prototype, EventDispatcher.prototype );
 
 export { Object3D };

+ 4 - 2
src/core/Raycaster.js

@@ -59,7 +59,9 @@ function intersectObject( object, raycaster, intersects, recursive ) {
 
 }
 
-Object.assign( Raycaster.prototype, {
+//
+
+Raycaster.prototype = {
 
 	constructor: Raycaster,
 
@@ -128,7 +130,7 @@ Object.assign( Raycaster.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Raycaster };

+ 2 - 3
src/extras/core/Curve.js

@@ -39,7 +39,7 @@ import { Matrix4 } from '../../math/Matrix4';
 
 function Curve() {}
 
-Object.assign( Curve.prototype, {
+Curve.prototype = {
 
 	constructor: Curve,
 
@@ -382,7 +382,6 @@ Object.assign( Curve.prototype, {
 
 	}
 
-} );
-
+};
 
 export { Curve };

+ 7 - 7
src/extras/core/ShapePath.js

@@ -1,12 +1,13 @@
-import { Path } from './Path';
-import { Shape } from './Shape';
-import { ShapeUtils } from '../ShapeUtils';
-
 /**
  * @author zz85 / http://www.lab4games.net/zz85/blog
  * minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
  **/
 
+import { Path } from './Path';
+import { Shape } from './Shape';
+import { ShapeUtils } from '../ShapeUtils';
+
+
 function ShapePath() {
 
 	this.subPaths = [];
@@ -14,7 +15,7 @@ function ShapePath() {
 
 }
 
-Object.assign( ShapePath.prototype, {
+ShapePath.prototype = {
 
 	moveTo: function ( x, y ) {
 
@@ -275,7 +276,6 @@ Object.assign( ShapePath.prototype, {
 
 	}
 
-} );
-
+};
 
 export { ShapePath };

+ 40 - 38
src/loaders/Loader.js

@@ -32,40 +32,7 @@ function Loader() {
 
 }
 
-Loader.Handlers = {
-
-	handlers: [],
-
-	add: function ( regex, loader ) {
-
-		this.handlers.push( regex, loader );
-
-	},
-
-	get: function ( file ) {
-
-		var handlers = this.handlers;
-
-		for ( var i = 0, l = handlers.length; i < l; i += 2 ) {
-
-			var regex = handlers[ i ];
-			var loader = handlers[ i + 1 ];
-
-			if ( regex.test( file ) ) {
-
-				return loader;
-
-			}
-
-		}
-
-		return null;
-
-	}
-
-};
-
-Object.assign( Loader.prototype, {
+Loader.prototype = {
 
 	constructor: Loader,
 
@@ -108,12 +75,14 @@ Object.assign( Loader.prototype, {
 			CustomBlending: CustomBlending
 		};
 
-		var color = new Color();
-		var textureLoader = new TextureLoader();
-		var materialLoader = new MaterialLoader();
+		var color, textureLoader, materialLoader;
 
 		return function createMaterial( m, texturePath, crossOrigin ) {
 
+			if ( color === undefined ) color = new Color();
+			if ( textureLoader === undefined ) textureLoader = new TextureLoader();
+			if ( materialLoader === undefined ) materialLoader = new MaterialLoader();
+
 			// convert from old material format
 
 			var textures = {};
@@ -351,7 +320,40 @@ Object.assign( Loader.prototype, {
 
 	} )()
 
-} );
+};
+
+Loader.Handlers = {
+
+	handlers: [],
+
+	add: function ( regex, loader ) {
+
+		this.handlers.push( regex, loader );
+
+	},
+
+	get: function ( file ) {
+
+		var handlers = this.handlers;
+
+		for ( var i = 0, l = handlers.length; i < l; i += 2 ) {
+
+			var regex = handlers[ i ];
+			var loader = handlers[ i + 1 ];
+
+			if ( regex.test( file ) ) {
+
+				return loader;
+
+			}
+
+		}
+
+		return null;
+
+	}
+
+};
 
 
 export { Loader };

+ 3 - 2
src/materials/Material.js

@@ -63,7 +63,7 @@ function Material() {
 
 }
 
-Object.assign( Material.prototype, EventDispatcher.prototype, {
+Material.prototype = {
 
 	constructor: Material,
 
@@ -346,7 +346,8 @@ Object.assign( Material.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
+Object.assign( Material.prototype, EventDispatcher.prototype );
 
 export { Material };

+ 2 - 2
src/materials/MultiMaterial.js

@@ -16,7 +16,7 @@ function MultiMaterial( materials ) {
 
 }
 
-Object.assign( MultiMaterial.prototype, {
+MultiMaterial.prototype = {
 
 	constructor: MultiMaterial,
 
@@ -68,7 +68,7 @@ Object.assign( MultiMaterial.prototype, {
 
 	}
 
-} );
+};
 
 
 export { MultiMaterial };

+ 2 - 2
src/math/Box2.js

@@ -11,7 +11,7 @@ function Box2( min, max ) {
 
 }
 
-Object.assign( Box2.prototype, {
+Box2.prototype = {
 
 	constructor: Box2,
 
@@ -216,7 +216,7 @@ Object.assign( Box2.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Box2 };

+ 5 - 3
src/math/Box3.js

@@ -13,7 +13,7 @@ function Box3( min, max ) {
 
 }
 
-Object.assign( Box3.prototype, {
+Box3.prototype = {
 
 	constructor: Box3,
 
@@ -310,10 +310,12 @@ Object.assign( Box3.prototype, {
 
 	intersectsSphere: ( function () {
 
-		var closestPoint = new Vector3();
+		var closestPoint;
 
 		return function intersectsSphere( sphere ) {
 
+			if ( closestPoint === undefined ) closestPoint = new Vector3();
+
 			// Find the point on the AABB closest to the sphere center.
 			this.clampPoint( sphere.center, closestPoint );
 
@@ -481,7 +483,7 @@ Object.assign( Box3.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Box3 };

+ 27 - 27
src/math/Color.js

@@ -4,31 +4,6 @@ import { _Math } from './Math';
  * @author mrdoob / http://mrdoob.com/
  */
 
-var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
-	'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
-	'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
-	'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
-	'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
-	'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
-	'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
-	'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
-	'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
-	'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
-	'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
-	'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
-	'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
-	'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
-	'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
-	'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
-	'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
-	'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
-	'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
-	'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
-	'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
-	'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
-	'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
-	'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
-
 function Color( r, g, b ) {
 
 	if ( g === undefined && b === undefined ) {
@@ -42,7 +17,7 @@ function Color( r, g, b ) {
 
 }
 
-Object.assign( Color.prototype, {
+Color.prototype = {
 
 	constructor: Color,
 
@@ -519,7 +494,32 @@ Object.assign( Color.prototype, {
 
 	}
 
-} );
+};
+
+var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
+'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
+'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
+'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
+'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
+'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
+'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
+'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
+'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
+'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
+'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
+'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
+'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
+'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
+'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
+'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
+'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
+'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
+'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
+'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
+'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
+'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
+'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
 
 
 export { Color };

+ 2 - 2
src/math/Cylindrical.js

@@ -15,7 +15,7 @@ function Cylindrical( radius, theta, y ) {
 
 }
 
-Object.assign( Cylindrical.prototype, {
+Cylindrical.prototype = {
 
 	constructor: Cylindrical,
 
@@ -55,7 +55,7 @@ Object.assign( Cylindrical.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Cylindrical };

+ 5 - 3
src/math/Euler.js

@@ -22,7 +22,7 @@ Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
 
 Euler.DefaultOrder = 'XYZ';
 
-Object.assign( Euler.prototype, {
+Euler.prototype = {
 
 	constructor: Euler,
 
@@ -237,10 +237,12 @@ Object.assign( Euler.prototype, {
 
 	setFromQuaternion: function () {
 
-		var matrix = new Matrix4();
+		var matrix;
 
 		return function setFromQuaternion( q, order, update ) {
 
+			if ( matrix === undefined ) matrix = new Matrix4();
+
 			matrix.makeRotationFromQuaternion( q );
 
 			return this.setFromRotationMatrix( matrix, order, update );
@@ -328,7 +330,7 @@ Object.assign( Euler.prototype, {
 
 	onChangeCallback: function () {}
 
-} );
+};
 
 
 export { Euler };

+ 2 - 2
src/math/Frustum.js

@@ -23,7 +23,7 @@ function Frustum( p0, p1, p2, p3, p4, p5 ) {
 
 }
 
-Object.assign( Frustum.prototype, {
+Frustum.prototype = {
 
 	constructor: Frustum,
 
@@ -198,7 +198,7 @@ Object.assign( Frustum.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Frustum };

+ 19 - 14
src/math/Interpolant.js

@@ -20,7 +20,8 @@
  * @author tschw
  */
 
-function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+function Interpolant(
+		parameterPositions, sampleValues, sampleSize, resultBuffer ) {
 
 	this.parameterPositions = parameterPositions;
 	this._cachedIndex = 0;
@@ -32,16 +33,10 @@ function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer
 
 }
 
-Object.assign( Interpolant.prototype, {
+Interpolant.prototype = {
 
 	constructor: Interpolant,
 
-	beforeStart_: //( 0, t, t0 ), returns this.resultBuffer
-		Interpolant.prototype.copySampleValue_,
-
-	afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer
-		Interpolant.prototype.copySampleValue_,
-
 	evaluate: function( t ) {
 
 		var pp = this.parameterPositions,
@@ -57,10 +52,10 @@ Object.assign( Interpolant.prototype, {
 				var right;
 
 				linear_scan: {
-					//- See http://jsperf.com/comparison-to-undefined/3
-					//- slower code:
-					//-
-					//- 				if ( t >= t1 || t1 === undefined ) {
+//- See http://jsperf.com/comparison-to-undefined/3
+//- slower code:
+//-
+//- 				if ( t >= t1 || t1 === undefined ) {
 					forward_scan: if ( ! ( t < t1 ) ) {
 
 						for ( var giveUpAt = i1 + 2; ;) {
@@ -97,8 +92,8 @@ Object.assign( Interpolant.prototype, {
 
 					}
 
-					//- slower code:
-					//-					if ( t < t0 || t0 === undefined ) {
+//- slower code:
+//-					if ( t < t0 || t0 === undefined ) {
 					if ( ! ( t >= t0 ) ) {
 
 						// looping?
@@ -249,6 +244,16 @@ Object.assign( Interpolant.prototype, {
 
 	}
 
+};
+
+Object.assign( Interpolant.prototype, {
+
+	beforeStart_: //( 0, t, t0 ), returns this.resultBuffer
+		Interpolant.prototype.copySampleValue_,
+
+	afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer
+		Interpolant.prototype.copySampleValue_
+
 } );
 
 

+ 2 - 2
src/math/Line3.js

@@ -12,7 +12,7 @@ function Line3( start, end ) {
 
 }
 
-Object.assign( Line3.prototype, {
+Line3.prototype = {
 
 	constructor: Line3,
 
@@ -126,7 +126,7 @@ Object.assign( Line3.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Line3 };

+ 5 - 3
src/math/Matrix3.js

@@ -25,7 +25,7 @@ function Matrix3() {
 
 }
 
-Object.assign( Matrix3.prototype, {
+Matrix3.prototype = {
 
 	constructor: Matrix3,
 
@@ -97,10 +97,12 @@ Object.assign( Matrix3.prototype, {
 
 	applyToBufferAttribute: function () {
 
-		var v1 = new Vector3();
+		var v1;
 
 		return function applyToBufferAttribute( attribute ) {
 
+			if ( v1 === undefined ) v1 = new Vector3();
+
 			for ( var i = 0, l = attribute.count; i < l; i ++ ) {
 
 				v1.x = attribute.getX( i );
@@ -272,7 +274,7 @@ Object.assign( Matrix3.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Matrix3 };

+ 25 - 9
src/math/Matrix4.js

@@ -33,7 +33,7 @@ function Matrix4() {
 
 }
 
-Object.assign( Matrix4.prototype, {
+Matrix4.prototype = {
 
 	constructor: Matrix4,
 
@@ -119,10 +119,12 @@ Object.assign( Matrix4.prototype, {
 
 	extractRotation: function () {
 
-		var v1 = new Vector3();
+		var v1;
 
 		return function extractRotation( m ) {
 
+			if ( v1 === undefined ) v1 = new Vector3();
+
 			var te = this.elements;
 			var me = m.elements;
 
@@ -315,12 +317,18 @@ Object.assign( Matrix4.prototype, {
 
 	lookAt: function () {
 
-		var x = new Vector3();
-		var y = new Vector3();
-		var z = new Vector3();
+		var x, y, z;
 
 		return function lookAt( eye, target, up ) {
 
+			if ( x === undefined ) {
+
+				x = new Vector3();
+				y = new Vector3();
+				z = new Vector3();
+
+			}
+
 			var te = this.elements;
 
 			z.subVectors( eye, target ).normalize();
@@ -442,10 +450,12 @@ Object.assign( Matrix4.prototype, {
 
 	applyToBufferAttribute: function () {
 
-		var v1 = new Vector3();
+		var v1;
 
 		return function applyToBufferAttribute( attribute ) {
 
+			if ( v1 === undefined ) v1 = new Vector3();
+
 			for ( var i = 0, l = attribute.count; i < l; i ++ ) {
 
 				v1.x = attribute.getX( i );
@@ -762,11 +772,17 @@ Object.assign( Matrix4.prototype, {
 
 	decompose: function () {
 
-		var vector = new Vector3();
-		var matrix = new Matrix4();
+		var vector, matrix;
 
 		return function decompose( position, quaternion, scale ) {
 
+			if ( vector === undefined ) {
+
+				vector = new Vector3();
+				matrix = new Matrix4();
+
+			}
+
 			var te = this.elements;
 
 			var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
@@ -923,7 +939,7 @@ Object.assign( Matrix4.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Matrix4 };

+ 2 - 2
src/math/Plane.js

@@ -12,7 +12,7 @@ function Plane( normal, constant ) {
 
 }
 
-Object.assign( Plane.prototype, {
+Plane.prototype = {
 
 	constructor: Plane,
 
@@ -232,7 +232,7 @@ Object.assign( Plane.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Plane };

+ 76 - 77
src/math/Quaternion.js

@@ -16,80 +16,7 @@ function Quaternion( x, y, z, w ) {
 
 }
 
-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.assign( Quaternion.prototype, {
+Quaternion.prototype = {
 
 	constructor: Quaternion,
 
@@ -332,8 +259,7 @@ Object.assign( Quaternion.prototype, {
 
 		// assumes direction vectors vFrom and vTo are normalized
 
-		var v1 = new Vector3();
-		var r;
+		var v1, r;
 
 		var EPS = 0.000001;
 
@@ -527,7 +453,7 @@ Object.assign( Quaternion.prototype, {
 
 		var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
 		var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
-			ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+		ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
 
 		this._w = ( w * ratioA + this._w * ratioB );
 		this._x = ( x * ratioA + this._x * ratioB );
@@ -585,6 +511,79 @@ Object.assign( Quaternion.prototype, {
 
 	onChangeCallback: function () {}
 
+};
+
+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;
+
+	}
+
 } );
 
 

+ 2 - 2
src/math/Ray.js

@@ -11,7 +11,7 @@ function Ray( origin, direction ) {
 
 }
 
-Object.assign( Ray.prototype, {
+Ray.prototype = {
 
 	constructor: Ray,
 
@@ -534,7 +534,7 @@ Object.assign( Ray.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Ray };

+ 5 - 3
src/math/Sphere.js

@@ -13,7 +13,7 @@ function Sphere( center, radius ) {
 
 }
 
-Object.assign( Sphere.prototype, {
+Sphere.prototype = {
 
 	constructor: Sphere,
 
@@ -28,10 +28,12 @@ Object.assign( Sphere.prototype, {
 
 	setFromPoints: function () {
 
-		var box = new Box3();
+		var box;
 
 		return function setFromPoints( points, optionalCenter ) {
 
+			if ( box === undefined ) box = new Box3(); // see #10547
+
 			var center = this.center;
 
 			if ( optionalCenter !== undefined ) {
@@ -174,7 +176,7 @@ Object.assign( Sphere.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Sphere };

+ 2 - 2
src/math/Spherical.js

@@ -20,7 +20,7 @@ function Spherical( radius, phi, theta ) {
 
 }
 
-Object.assign( Spherical.prototype, {
+Spherical.prototype = {
 
 	constructor: Spherical,
 
@@ -80,7 +80,7 @@ Object.assign( Spherical.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Spherical };

+ 63 - 61
src/math/Triangle.js

@@ -15,94 +15,90 @@ function Triangle( a, b, c ) {
 
 }
 
-Object.assign( Triangle, {
+Triangle.normal = function () {
 
-	normal: function () {
+	var v0 = new Vector3();
 
-		var v0 = new Vector3();
+	return function normal( a, b, c, optionalTarget ) {
 
-		return function normal( a, b, c, optionalTarget ) {
+		var result = optionalTarget || new Vector3();
 
-			var result = optionalTarget || new Vector3();
+		result.subVectors( c, b );
+		v0.subVectors( a, b );
+		result.cross( v0 );
 
-			result.subVectors( c, b );
-			v0.subVectors( a, b );
-			result.cross( v0 );
+		var resultLengthSq = result.lengthSq();
+		if ( resultLengthSq > 0 ) {
 
-			var resultLengthSq = result.lengthSq();
-			if ( resultLengthSq > 0 ) {
+			return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
 
-				return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
+		}
 
-			}
+		return result.set( 0, 0, 0 );
 
-			return result.set( 0, 0, 0 );
+	};
 
-		};
+}();
 
-	}(),
+// static/instance method to calculate barycentric coordinates
+// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+Triangle.barycoordFromPoint = function () {
 
-	// static/instance method to calculate barycentric coordinates
-	// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
-	barycoordFromPoint: function () {
+	var v0 = new Vector3();
+	var v1 = new Vector3();
+	var v2 = new Vector3();
 
-		var v0 = new Vector3();
-		var v1 = new Vector3();
-		var v2 = new Vector3();
-
-		return function barycoordFromPoint( point, a, b, c, optionalTarget ) {
+	return function barycoordFromPoint( point, a, b, c, optionalTarget ) {
 
-			v0.subVectors( c, a );
-			v1.subVectors( b, a );
-			v2.subVectors( point, a );
+		v0.subVectors( c, a );
+		v1.subVectors( b, a );
+		v2.subVectors( point, a );
 
-			var dot00 = v0.dot( v0 );
-			var dot01 = v0.dot( v1 );
-			var dot02 = v0.dot( v2 );
-			var dot11 = v1.dot( v1 );
-			var dot12 = v1.dot( v2 );
+		var dot00 = v0.dot( v0 );
+		var dot01 = v0.dot( v1 );
+		var dot02 = v0.dot( v2 );
+		var dot11 = v1.dot( v1 );
+		var dot12 = v1.dot( v2 );
 
-			var denom = ( dot00 * dot11 - dot01 * dot01 );
-
-			var result = optionalTarget || new Vector3();
+		var denom = ( dot00 * dot11 - dot01 * dot01 );
 
-			// collinear or singular triangle
-			if ( denom === 0 ) {
+		var result = optionalTarget || new Vector3();
 
-				// arbitrary location outside of triangle?
-				// not sure if this is the best idea, maybe should be returning undefined
-				return result.set( - 2, - 1, - 1 );
+		// collinear or singular triangle
+		if ( denom === 0 ) {
 
-			}
+			// arbitrary location outside of triangle?
+			// not sure if this is the best idea, maybe should be returning undefined
+			return result.set( - 2, - 1, - 1 );
 
-			var invDenom = 1 / denom;
-			var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
-			var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+		}
 
-			// barycentric coordinates must always sum to 1
-			return result.set( 1 - u - v, v, u );
+		var invDenom = 1 / denom;
+		var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+		var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
 
-		};
+		// barycentric coordinates must always sum to 1
+		return result.set( 1 - u - v, v, u );
 
-	}(),
+	};
 
-	containsPoint: function () {
+}();
 
-		var v1 = new Vector3();
+Triangle.containsPoint = function () {
 
-		return function containsPoint( point, a, b, c ) {
+	var v1 = new Vector3();
 
-			var result = Triangle.barycoordFromPoint( point, a, b, c, v1 );
+	return function containsPoint( point, a, b, c ) {
 
-			return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
+		var result = Triangle.barycoordFromPoint( point, a, b, c, v1 );
 
-		};
+		return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
 
-	}()
+	};
 
-} );
+}();
 
-Object.assign( Triangle.prototype, {
+Triangle.prototype = {
 
 	constructor: Triangle,
 
@@ -193,13 +189,19 @@ Object.assign( Triangle.prototype, {
 
 	closestPointToPoint: function () {
 
-		var plane = new Plane();
-		var edgeList = [ new Line3(), new Line3(), new Line3() ];
-		var projectedPoint = new Vector3();
-		var closestPoint = new Vector3();
+		var plane, edgeList, projectedPoint, closestPoint;
 
 		return function closestPointToPoint( point, optionalTarget ) {
 
+			if ( plane === undefined ) {
+
+				plane = new Plane();
+				edgeList = [ new Line3(), new Line3(), new Line3() ];
+				projectedPoint = new Vector3();
+				closestPoint = new Vector3();
+
+			}
+
 			var result = optionalTarget || new Vector3();
 			var minDistance = Infinity;
 
@@ -254,7 +256,7 @@ Object.assign( Triangle.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Triangle };

+ 10 - 4
src/math/Vector2.js

@@ -12,7 +12,7 @@ function Vector2( x, y ) {
 
 }
 
-Object.assign( Vector2.prototype, {
+Vector2.prototype = {
 
 	constructor: Vector2,
 
@@ -269,11 +269,17 @@ Object.assign( Vector2.prototype, {
 
 	clampScalar: function () {
 
-		var min = new Vector2();
-		var max = new Vector2();
+		var min, max;
 
 		return function clampScalar( minVal, maxVal ) {
 
+			if ( min === undefined ) {
+
+				min = new Vector2();
+				max = new Vector2();
+
+			}
+
 			min.set( minVal, minVal );
 			max.set( maxVal, maxVal );
 
@@ -476,7 +482,7 @@ Object.assign( Vector2.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Vector2 };

+ 28 - 10
src/math/Vector3.js

@@ -19,7 +19,7 @@ function Vector3( x, y, z ) {
 
 }
 
-Object.assign( Vector3.prototype, {
+Vector3.prototype = {
 
 	constructor: Vector3,
 
@@ -246,7 +246,7 @@ Object.assign( Vector3.prototype, {
 
 	applyEuler: function () {
 
-		var quaternion = new Quaternion();
+		var quaternion;
 
 		return function applyEuler( euler ) {
 
@@ -256,6 +256,8 @@ Object.assign( Vector3.prototype, {
 
 			}
 
+			if ( quaternion === undefined ) quaternion = new Quaternion();
+
 			return this.applyQuaternion( quaternion.setFromEuler( euler ) );
 
 		};
@@ -264,10 +266,12 @@ Object.assign( Vector3.prototype, {
 
 	applyAxisAngle: function () {
 
-		var quaternion = new Quaternion();
+		var quaternion;
 
 		return function applyAxisAngle( axis, angle ) {
 
+			if ( quaternion === undefined ) quaternion = new Quaternion();
+
 			return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
 
 		};
@@ -325,10 +329,12 @@ Object.assign( Vector3.prototype, {
 
 	project: function () {
 
-		var matrix = new Matrix4();
+		var matrix;
 
 		return function project( camera ) {
 
+			if ( matrix === undefined ) matrix = new Matrix4();
+
 			matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
 			return this.applyMatrix4( matrix );
 
@@ -338,10 +344,12 @@ Object.assign( Vector3.prototype, {
 
 	unproject: function () {
 
-		var matrix = new Matrix4();
+		var matrix;
 
 		return function unproject( camera ) {
 
+			if ( matrix === undefined ) matrix = new Matrix4();
+
 			matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
 			return this.applyMatrix4( matrix );
 
@@ -415,11 +423,17 @@ Object.assign( Vector3.prototype, {
 
 	clampScalar: function () {
 
-		var min = new Vector3();
-		var max = new Vector3();
+		var min, max;
 
 		return function clampScalar( minVal, maxVal ) {
 
+			if ( min === undefined ) {
+
+				min = new Vector3();
+				max = new Vector3();
+
+			}
+
 			min.set( minVal, minVal, minVal );
 			max.set( maxVal, maxVal, maxVal );
 
@@ -581,10 +595,12 @@ Object.assign( Vector3.prototype, {
 
 	projectOnPlane: function () {
 
-		var v1 = new Vector3();
+		var v1;
 
 		return function projectOnPlane( planeNormal ) {
 
+			if ( v1 === undefined ) v1 = new Vector3();
+
 			v1.copy( this ).projectOnVector( planeNormal );
 
 			return this.sub( v1 );
@@ -598,10 +614,12 @@ Object.assign( Vector3.prototype, {
 		// reflect incident vector off plane orthogonal to normal
 		// normal is assumed to have unit length
 
-		var v1 = new Vector3();
+		var v1;
 
 		return function reflect( normal ) {
 
+			if ( v1 === undefined ) v1 = new Vector3();
+
 			return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
 
 		};
@@ -742,7 +760,7 @@ Object.assign( Vector3.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Vector3 };

+ 10 - 4
src/math/Vector4.js

@@ -15,7 +15,7 @@ function Vector4( x, y, z, w ) {
 
 }
 
-Object.assign( Vector4.prototype, {
+Vector4.prototype = {
 
 	constructor: Vector4,
 
@@ -450,11 +450,17 @@ Object.assign( Vector4.prototype, {
 
 	clampScalar: function () {
 
-		var min = new Vector4();
-		var max = new Vector4();
+		var min, max;
 
 		return function clampScalar( minVal, maxVal ) {
 
+			if ( min === undefined ) {
+
+				min = new Vector4();
+				max = new Vector4();
+
+			}
+
 			min.set( minVal, minVal, minVal, minVal );
 			max.set( maxVal, maxVal, maxVal, maxVal );
 
@@ -622,7 +628,7 @@ Object.assign( Vector4.prototype, {
 
 	}
 
-} );
+};
 
 
 export { Vector4 };

+ 3 - 2
src/renderers/WebGLRenderTarget.js

@@ -39,7 +39,7 @@ function WebGLRenderTarget( width, height, options ) {
 
 }
 
-Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, {
+WebGLRenderTarget.prototype = {
 
 	constructor: WebGLRenderTarget,
 
@@ -90,7 +90,8 @@ Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
+Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype );
 
 export { WebGLRenderTarget };

+ 3 - 2
src/textures/Texture.js

@@ -59,7 +59,7 @@ function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, ty
 Texture.DEFAULT_IMAGE = undefined;
 Texture.DEFAULT_MAPPING = UVMapping;
 
-Object.assign( Texture.prototype, EventDispatcher.prototype, {
+Texture.prototype = {
 
 	constructor: Texture,
 
@@ -285,7 +285,8 @@ Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
 	}
 
-} );
+};
 
+Object.assign( Texture.prototype, EventDispatcher.prototype );
 
 export { Texture };