Bladeren bron

Merge pull request #10627 from TristanVALCKE/FixCircularReferences

Fix circular references and improve closure performance
Mr.doob 8 jaren geleden
bovenliggende
commit
5d90fb7554

+ 2 - 2
src/animation/AnimationAction.js

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

+ 49 - 53
src/animation/AnimationClip.js

@@ -32,55 +32,6 @@ 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 ) {
@@ -98,8 +49,7 @@ Object.assign( AnimationClip, {
 		return new AnimationClip( json.name, json.duration, tracks );
 
 	},
-
-
+	
 	toJSON: function( clip ) {
 
 		var tracks = [],
@@ -122,8 +72,7 @@ Object.assign( AnimationClip, {
 		return json;
 
 	},
-
-
+	
 	CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {
 
 		var numMorphTargets = morphTargetSequence.length;
@@ -359,5 +308,52 @@ 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 };

+ 251 - 258
src/animation/AnimationMixer.js

@@ -27,260 +27,10 @@ function AnimationMixer( root ) {
 
 }
 
-AnimationMixer.prototype = {
+Object.assign( AnimationMixer.prototype, EventDispatcher.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,
@@ -332,8 +82,8 @@ Object.assign( AnimationMixer.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 );
@@ -362,7 +112,7 @@ Object.assign( AnimationMixer.prototype, {
 					actionsForClip = this._actionsByClip[ clipUuid ];
 
 				this._bindAction( action,
-						actionsForClip && actionsForClip.knownActions[ 0 ] );
+					actionsForClip && actionsForClip.knownActions[ 0 ] );
 
 				this._addInactiveAction( action, clipUuid, rootUuid );
 
@@ -710,8 +460,8 @@ Object.assign( AnimationMixer.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;
@@ -739,10 +489,253 @@ Object.assign( AnimationMixer.prototype, {
 
 	},
 
-	_controlInterpolantsResultBuffer: new Float32Array( 1 )
+	_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 );
+
+		}
+
+	}
 
 } );
 
-Object.assign( AnimationMixer.prototype, EventDispatcher.prototype );
 
 export { AnimationMixer };

+ 2 - 3
src/animation/AnimationObjectGroup.js

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

+ 271 - 272
src/animation/PropertyBinding.js

@@ -8,268 +8,226 @@
  * @author tschw
  */
 
-function PropertyBinding( rootNode, path, parsedPath ) {
-
-	this.path = path;
-	this.parsedPath = parsedPath ||
-			PropertyBinding.parseTrackName( path );
+function Composite ( targetGroup, path, optionalParsedPath ) {
 
-	this.node = PropertyBinding.findNode(
-			rootNode, this.parsedPath.nodeName ) || rootNode;
+	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
 
-	this.rootNode = rootNode;
+	this._targetGroup = targetGroup;
+	this._bindings = targetGroup.subscribe_( path, parsedPath );
 
 }
 
-PropertyBinding.prototype = {
+Object.assign( Composite.prototype, {
 
-	constructor: PropertyBinding,
+	constructor: Composite,
 
-	getValue: function getValue_unbound( targetArray, offset ) {
-
-		this.bind();
-		this.getValue( targetArray, offset );
-
-		// 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.
+	getValue: function( array, offset ) {
 
-	},
+		this.bind(); // bind all binding
 
-	setValue: function getValue_unbound( sourceArray, offset ) {
+		var firstValidIndex = this._targetGroup.nCachedObjects_,
+			binding = this._bindings[ firstValidIndex ];
 
-		this.bind();
-		this.setValue( sourceArray, offset );
+		// and only call .getValue on the first
+		if ( binding !== undefined ) binding.getValue( array, offset );
 
 	},
 
-	// create getter / setter pair for a property in the scene graph
-	bind: function() {
-
-		var targetObject = this.node,
-			parsedPath = this.parsedPath,
-
-			objectName = parsedPath.objectName,
-			propertyName = parsedPath.propertyName,
-			propertyIndex = parsedPath.propertyIndex;
-
-		if ( ! targetObject ) {
-
-			targetObject = PropertyBinding.findNode(
-					this.rootNode, parsedPath.nodeName ) || this.rootNode;
-
-			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;
-
-			// special cases were we need to reach deeper into the hierarchy to get the face materials....
-			switch ( objectName ) {
-
-				case 'materials':
+	},
 
-					if ( ! targetObject.material ) {
+	bind: function() {
 
-						console.error( '  can not bind to material as node does not have a material', this );
-						return;
+		var bindings = this._bindings;
 
-					}
+		for ( var i = this._targetGroup.nCachedObjects_,
+				  n = bindings.length; i !== n; ++ i ) {
 
-					if ( ! targetObject.material.materials ) {
+			bindings[ i ].bind();
 
-						console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
-						return;
+		}
 
-					}
+	},
 
-					targetObject = targetObject.material.materials;
+	unbind: function() {
 
-					break;
+		var bindings = this._bindings;
 
-				case 'bones':
+		for ( var i = this._targetGroup.nCachedObjects_,
+				  n = bindings.length; i !== n; ++ i ) {
 
-					if ( ! targetObject.skeleton ) {
+			bindings[ i ].unbind();
 
-						console.error( '  can not bind to bones as node does not have a skeleton', this );
-						return;
+		}
 
-					}
+	}
 
-					// potential future optimization: skip this if propertyIndex is already an integer
-					// and convert the integer string to a true integer.
+} );
 
-					targetObject = targetObject.skeleton.bones;
 
-					// support resolving morphTarget names into indices.
-					for ( var i = 0; i < targetObject.length; i ++ ) {
+function PropertyBinding( rootNode, path, parsedPath ) {
 
-						if ( targetObject[ i ].name === objectIndex ) {
+	this.path = path;
+	this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );
 
-							objectIndex = i;
-							break;
+	this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;
 
-						}
+	this.rootNode = rootNode;
 
-					}
+}
 
-					break;
+Object.assign( PropertyBinding, {
 
-				default:
+	Composite: Composite,
 
-					if ( targetObject[ objectName ] === undefined ) {
+	create: function( root, path, parsedPath ) {
 
-						console.error( '  can not bind to objectName of node, undefined', this );
-						return;
+		if ( ! ( root && root.isAnimationObjectGroup ) ) {
 
-					}
+			return new PropertyBinding( root, path, parsedPath );
 
-					targetObject = targetObject[ objectName ];
+		} else {
 
-			}
+			return new PropertyBinding.Composite( root, path, parsedPath );
 
+		}
 
-			if ( objectIndex !== undefined ) {
+	},
 
-				if ( targetObject[ objectIndex ] === undefined ) {
+	parseTrackName: function( trackName ) {
 
-					console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
-					return;
+		// 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
 
-				}
+		var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
+		var matches = re.exec( trackName );
 
-				targetObject = targetObject[ objectIndex ];
+		if ( ! matches ) {
 
-			}
+			throw new Error( "cannot parse trackName at all: " + trackName );
 
 		}
 
-		// resolve property
-		var nodeProperty = targetObject[ propertyName ];
-
-		if ( nodeProperty === undefined ) {
+		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.
+		};
 
-			var nodeName = parsedPath.nodeName;
+		if ( results.propertyName === null || results.propertyName.length === 0 ) {
 
-			console.error( "  trying to update property for track: " + nodeName +
-					'.' + propertyName + " but it wasn't found.", targetObject );
-			return;
+			throw new Error( "can not parse propertyName from trackName: " + trackName );
 
 		}
 
-		// determine versioning scheme
-		var versioning = this.Versioning.None;
+		return results;
 
-		if ( targetObject.needsUpdate !== undefined ) { // material
+	},
 
-			versioning = this.Versioning.NeedsUpdate;
-			this.targetObject = targetObject;
+	findNode: function( root, nodeName ) {
 
-		} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
+		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
 
-			versioning = this.Versioning.MatrixWorldNeedsUpdate;
-			this.targetObject = targetObject;
+			return root;
 
 		}
 
-		// determine how the property gets bound
-		var bindingType = this.BindingType.Direct;
-
-		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 skeleton bones.
+		if ( root.skeleton ) {
 
-				// support resolving morphTarget names into indices.
-				if ( ! targetObject.geometry ) {
+			var searchSkeleton = function( skeleton ) {
 
-					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
-					return;
+				for( var i = 0; i < skeleton.bones.length; i ++ ) {
 
-				}
+					var bone = skeleton.bones[ i ];
 
-				if ( ! targetObject.geometry.morphTargets ) {
+					if ( bone.name === nodeName ) {
 
-					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
-					return;
+						return bone;
 
+					}
 				}
 
-				for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
+				return null;
 
-					if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
+			};
 
-						propertyIndex = i;
-						break;
+			var bone = searchSkeleton( root.skeleton );
 
-					}
+			if ( bone ) {
 
-				}
+				return bone;
 
 			}
+		}
 
-			bindingType = this.BindingType.ArrayElement;
+		// search into node subtree.
+		if ( root.children ) {
 
-			this.resolvedProperty = nodeProperty;
-			this.propertyIndex = propertyIndex;
+			var searchNodeSubtree = function( children ) {
 
-		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
-			// must use copy for Object3D.Euler/Quaternion
+				for( var i = 0; i < children.length; i ++ ) {
 
-			bindingType = this.BindingType.HasFromToArray;
+					var childNode = children[ i ];
 
-			this.resolvedProperty = nodeProperty;
+					if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
 
-		} else if ( Array.isArray( nodeProperty ) ) {
+						return childNode;
 
-			bindingType = this.BindingType.EntireArray;
+					}
 
-			this.resolvedProperty = nodeProperty;
+					var result = searchNodeSubtree( childNode.children );
 
-		} else {
+					if ( result ) return result;
 
-			this.propertyName = propertyName;
+				}
 
-		}
+				return null;
 
-		// select getter / setter
-		this.getValue = this.GetterByBindingType[ bindingType ];
-		this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
+			};
 
-	},
+			var subTreeNode = searchNodeSubtree( root.children );
 
-	unbind: function() {
+			if ( subTreeNode ) {
 
-		this.node = null;
+				return subTreeNode;
 
-		// 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;
 
 	}
 
-};
+} );
 
 Object.assign( PropertyBinding.prototype, { // prototype, continued
 
+	constructor: PropertyBinding,
+
 	// these are used to "bind" a nonexistent property
 	_getValue_unavailable: function() {},
 	_setValue_unavailable: function() {},
@@ -444,209 +402,250 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 		]
 
-	]
+	],
 
-} );
+	getValue: function getValue_unbound( targetArray, offset ) {
 
-PropertyBinding.Composite =
-		function( targetGroup, path, optionalParsedPath ) {
+		this.bind();
+		this.getValue( targetArray, offset );
 
-	var parsedPath = optionalParsedPath ||
-			PropertyBinding.parseTrackName( path );
+		// 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.
 
-	this._targetGroup = targetGroup;
-	this._bindings = targetGroup.subscribe_( path, parsedPath );
+	},
 
-};
+	setValue: function getValue_unbound( sourceArray, offset ) {
 
-PropertyBinding.Composite.prototype = {
+		this.bind();
+		this.setValue( sourceArray, offset );
 
-	constructor: PropertyBinding.Composite,
+	},
 
-	getValue: function( array, offset ) {
+	// create getter / setter pair for a property in the scene graph
+	bind: function() {
 
-		this.bind(); // bind all binding
+		var targetObject = this.node,
+			parsedPath = this.parsedPath,
 
-		var firstValidIndex = this._targetGroup.nCachedObjects_,
-			binding = this._bindings[ firstValidIndex ];
+			objectName = parsedPath.objectName,
+			propertyName = parsedPath.propertyName,
+			propertyIndex = parsedPath.propertyIndex;
 
-		// and only call .getValue on the first
-		if ( binding !== undefined ) binding.getValue( array, offset );
+		if ( ! targetObject ) {
 
-	},
+			targetObject = PropertyBinding.findNode(
+					this.rootNode, parsedPath.nodeName ) || this.rootNode;
 
-	setValue: function( array, offset ) {
+			this.node = targetObject;
 
-		var bindings = this._bindings;
+		}
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				n = bindings.length; i !== n; ++ i ) {
+		// set fail state so we can just 'return' on error
+		this.getValue = this._getValue_unavailable;
+		this.setValue = this._setValue_unavailable;
 
-			bindings[ i ].setValue( array, offset );
+		// ensure there is a value node
+		if ( ! targetObject ) {
 
-		}
+			console.error( "  trying to update node for track: " + this.path + " but it wasn't found." );
+			return;
 
-	},
+		}
 
-	bind: function() {
+		if ( objectName ) {
 
-		var bindings = this._bindings;
+			var objectIndex = parsedPath.objectIndex;
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				n = bindings.length; i !== n; ++ i ) {
+			// special cases were we need to reach deeper into the hierarchy to get the face materials....
+			switch ( objectName ) {
 
-			bindings[ i ].bind();
+				case 'materials':
 
-		}
+					if ( ! targetObject.material ) {
 
-	},
+						console.error( '  can not bind to material as node does not have a material', this );
+						return;
 
-	unbind: function() {
+					}
 
-		var bindings = this._bindings;
+					if ( ! targetObject.material.materials ) {
 
-		for ( var i = this._targetGroup.nCachedObjects_,
-				n = bindings.length; i !== n; ++ i ) {
+						console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
+						return;
 
-			bindings[ i ].unbind();
+					}
 
-		}
+					targetObject = targetObject.material.materials;
 
-	}
+					break;
 
-};
+				case 'bones':
 
-PropertyBinding.create = function( root, path, parsedPath ) {
+					if ( ! targetObject.skeleton ) {
 
-	if ( ! ( root && root.isAnimationObjectGroup ) ) {
+						console.error( '  can not bind to bones as node does not have a skeleton', this );
+						return;
 
-		return new PropertyBinding( root, path, parsedPath );
+					}
 
-	} else {
+					// potential future optimization: skip this if propertyIndex is already an integer
+					// and convert the integer string to a true integer.
 
-		return new PropertyBinding.Composite( root, path, parsedPath );
+					targetObject = targetObject.skeleton.bones;
 
-	}
+					// support resolving morphTarget names into indices.
+					for ( var i = 0; i < targetObject.length; i ++ ) {
 
-};
+						if ( targetObject[ i ].name === objectIndex ) {
 
-PropertyBinding.parseTrackName = function( trackName ) {
+							objectIndex = i;
+							break;
 
-	// 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
+						}
 
-	var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
-	var matches = re.exec( trackName );
+					}
 
-	if ( ! matches ) {
+					break;
 
-		throw new Error( "cannot parse trackName at all: " + trackName );
+				default:
 
-	}
+					if ( targetObject[ objectName ] === undefined ) {
 
-	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.
-	};
+						console.error( '  can not bind to objectName of node, undefined', this );
+						return;
 
-	if ( results.propertyName === null || results.propertyName.length === 0 ) {
+					}
 
-		throw new Error( "can not parse propertyName from trackName: " + trackName );
+					targetObject = targetObject[ objectName ];
 
-	}
+			}
 
-	return results;
 
-};
+			if ( objectIndex !== undefined ) {
 
-PropertyBinding.findNode = function( root, nodeName ) {
+				if ( targetObject[ objectIndex ] === undefined ) {
 
-	if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+					console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
+					return;
 
-		return root;
+				}
 
-	}
+				targetObject = targetObject[ objectIndex ];
 
-	// search into skeleton bones.
-	if ( root.skeleton ) {
+			}
 
-		var searchSkeleton = function( skeleton ) {
+		}
 
-			for( var i = 0; i < skeleton.bones.length; i ++ ) {
+		// resolve property
+		var nodeProperty = targetObject[ propertyName ];
 
-				var bone = skeleton.bones[ i ];
+		if ( nodeProperty === undefined ) {
 
-				if ( bone.name === nodeName ) {
+			var nodeName = parsedPath.nodeName;
 
-					return bone;
+			console.error( "  trying to update property for track: " + nodeName +
+				'.' + propertyName + " but it wasn't found.", targetObject );
+			return;
 
-				}
-			}
+		}
 
-			return null;
+		// determine versioning scheme
+		var versioning = this.Versioning.None;
 
-		};
+		if ( targetObject.needsUpdate !== undefined ) { // material
 
-		var bone = searchSkeleton( root.skeleton );
+			versioning = this.Versioning.NeedsUpdate;
+			this.targetObject = targetObject;
 
-		if ( bone ) {
+		} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
 
-			return bone;
+			versioning = this.Versioning.MatrixWorldNeedsUpdate;
+			this.targetObject = targetObject;
 
 		}
-	}
 
-	// search into node subtree.
-	if ( root.children ) {
+		// determine how the property gets bound
+		var bindingType = this.BindingType.Direct;
+
+		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.
 
-		var searchNodeSubtree = function( children ) {
+				// support resolving morphTarget names into indices.
+				if ( ! targetObject.geometry ) {
 
-			for( var i = 0; i < children.length; i ++ ) {
+					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
+					return;
 
-				var childNode = children[ i ];
+				}
 
-				if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
+				if ( ! targetObject.geometry.morphTargets ) {
 
-					return childNode;
+					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
+					return;
 
 				}
 
-				var result = searchNodeSubtree( childNode.children );
+				for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
+
+					if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
+
+						propertyIndex = i;
+						break;
+
+					}
 
-				if ( result ) return result;
+				}
 
 			}
 
-			return null;
+			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;
+
+		} else if ( Array.isArray( nodeProperty ) ) {
+      
+			bindingType = this.BindingType.EntireArray;
 
-		var subTreeNode = searchNodeSubtree( root.children );
+			this.resolvedProperty = nodeProperty;
 
-		if ( subTreeNode ) {
+		} else {
 
-			return subTreeNode;
+			this.propertyName = propertyName;
 
 		}
 
-	}
+		// 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 ) {
 
 }
 
-PropertyMixer.prototype = {
+Object.assign( PropertyMixer.prototype, {
 
 	constructor: PropertyMixer,
 
@@ -118,7 +118,7 @@ PropertyMixer.prototype = {
 			var originalValueOffset = stride * 3;
 
 			this._mixBufferRegion(
-					buffer, offset, originalValueOffset, 1 - weight, stride );
+				buffer, offset, originalValueOffset, 1 - weight, stride );
 
 		}
 
@@ -188,7 +188,7 @@ 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 @@ PropertyMixer.prototype = {
 
 	}
 
-};
+} );
 
 
 export { PropertyMixer };

+ 2 - 2
src/core/BufferAttribute.js

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

+ 23 - 36
src/core/BufferGeometry.js

@@ -39,7 +39,9 @@ function BufferGeometry() {
 
 }
 
-BufferGeometry.prototype = {
+BufferGeometry.MaxIndex = 65535;
+
+Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 	constructor: BufferGeometry,
 
@@ -173,12 +175,10 @@ BufferGeometry.prototype = {
 
 		// rotate geometry around world x-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateX( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationX( angle );
 
 			this.applyMatrix( m1 );
@@ -193,12 +193,10 @@ BufferGeometry.prototype = {
 
 		// rotate geometry around world y-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateY( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationY( angle );
 
 			this.applyMatrix( m1 );
@@ -213,12 +211,10 @@ BufferGeometry.prototype = {
 
 		// rotate geometry around world z-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateZ( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationZ( angle );
 
 			this.applyMatrix( m1 );
@@ -233,12 +229,10 @@ BufferGeometry.prototype = {
 
 		// translate geometry
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function translate( x, y, z ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeTranslation( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -253,12 +247,10 @@ BufferGeometry.prototype = {
 
 		// scale geometry
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function scale( x, y, z ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeScale( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -271,12 +263,10 @@ BufferGeometry.prototype = {
 
 	lookAt: function () {
 
-		var obj;
+		var obj = new Object3D();
 
 		return function lookAt( vector ) {
 
-			if ( obj === undefined ) obj = new Object3D();
-
 			obj.lookAt( vector );
 
 			obj.updateMatrix();
@@ -979,28 +969,28 @@ BufferGeometry.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 );
 
@@ -1109,10 +1099,7 @@ BufferGeometry.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 ) {
 
 }
 
-Clock.prototype = {
+Object.assign( Clock.prototype, {
 
 	constructor: Clock,
 
@@ -67,7 +67,7 @@ Clock.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Clock };

+ 2 - 2
src/core/EventDispatcher.js

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

+ 2 - 2
src/core/Face3.js

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

+ 32 - 45
src/core/Geometry.js

@@ -59,7 +59,7 @@ function Geometry() {
 
 }
 
-Geometry.prototype = {
+Object.assign( Geometry.prototype, EventDispatcher.prototype, {
 
 	constructor: Geometry,
 
@@ -112,12 +112,10 @@ Geometry.prototype = {
 
 		// rotate geometry around world x-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateX( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationX( angle );
 
 			this.applyMatrix( m1 );
@@ -132,12 +130,10 @@ Geometry.prototype = {
 
 		// rotate geometry around world y-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateY( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationY( angle );
 
 			this.applyMatrix( m1 );
@@ -152,12 +148,10 @@ Geometry.prototype = {
 
 		// rotate geometry around world z-axis
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function rotateZ( angle ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeRotationZ( angle );
 
 			this.applyMatrix( m1 );
@@ -172,12 +166,10 @@ Geometry.prototype = {
 
 		// translate geometry
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function translate( x, y, z ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeTranslation( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -192,12 +184,10 @@ Geometry.prototype = {
 
 		// scale geometry
 
-		var m1;
+		var m1 = new Matrix4();
 
 		return function scale( x, y, z ) {
 
-			if ( m1 === undefined ) m1 = new Matrix4();
-
 			m1.makeScale( x, y, z );
 
 			this.applyMatrix( m1 );
@@ -210,12 +200,10 @@ Geometry.prototype = {
 
 	lookAt: function () {
 
-		var obj;
+		var obj = new Object3D();
 
 		return function lookAt( vector ) {
 
-			if ( obj === undefined ) obj = new Object3D();
-
 			obj.lookAt( vector );
 
 			obj.updateMatrix();
@@ -714,15 +702,15 @@ Geometry.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;
 
@@ -759,8 +747,8 @@ Geometry.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 );
@@ -1171,28 +1159,28 @@ Geometry.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 );
 
@@ -1438,8 +1426,7 @@ Geometry.prototype = {
 
 	}
 
-};
+} );
 
-Object.assign( Geometry.prototype, EventDispatcher.prototype );
 
 export { GeometryIdCount, Geometry };

+ 2 - 2
src/core/InterleavedBuffer.js

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

+ 8 - 13
src/core/InterleavedBufferAttribute.js

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

+ 2 - 2
src/core/Layers.js

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

+ 2 - 3
src/core/Object3D.js

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

+ 2 - 4
src/core/Raycaster.js

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

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

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

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

@@ -1,12 +1,11 @@
-/**
- * @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';
 
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
+ **/
 
 function ShapePath() {
 
@@ -15,7 +14,7 @@ function ShapePath() {
 
 }
 
-ShapePath.prototype = {
+Object.assign( ShapePath.prototype, {
 
 	moveTo: function ( x, y ) {
 
@@ -276,6 +275,7 @@ ShapePath.prototype = {
 
 	}
 
-};
+} );
+
 
 export { ShapePath };

+ 38 - 40
src/loaders/Loader.js

@@ -32,7 +32,40 @@ function Loader() {
 
 }
 
-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;
+
+	}
+
+};
+
+Object.assign( Loader.prototype, {
 
 	constructor: Loader,
 
@@ -75,14 +108,12 @@ Loader.prototype = {
 			CustomBlending: CustomBlending
 		};
 
-		var color, textureLoader, materialLoader;
+		var color = new Color();
+		var textureLoader = new TextureLoader();
+		var materialLoader = new 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 = {};
@@ -320,40 +351,7 @@ 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 };

+ 2 - 3
src/materials/Material.js

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

+ 2 - 2
src/materials/MultiMaterial.js

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

+ 2 - 2
src/math/Box2.js

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

+ 3 - 5
src/math/Box3.js

@@ -13,7 +13,7 @@ function Box3( min, max ) {
 
 }
 
-Box3.prototype = {
+Object.assign( Box3.prototype, {
 
 	constructor: Box3,
 
@@ -310,12 +310,10 @@ Box3.prototype = {
 
 	intersectsSphere: ( function () {
 
-		var closestPoint;
+		var closestPoint = new Vector3();
 
 		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 );
 
@@ -483,7 +481,7 @@ Box3.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Box3 };

+ 27 - 27
src/math/Color.js

@@ -4,6 +4,31 @@ 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 ) {
@@ -17,7 +42,7 @@ function Color( r, g, b ) {
 
 }
 
-Color.prototype = {
+Object.assign( Color.prototype, {
 
 	constructor: Color,
 
@@ -494,32 +519,7 @@ 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 ) {
 
 }
 
-Cylindrical.prototype = {
+Object.assign( Cylindrical.prototype, {
 
 	constructor: Cylindrical,
 
@@ -55,7 +55,7 @@ Cylindrical.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Cylindrical };

+ 3 - 5
src/math/Euler.js

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

+ 2 - 2
src/math/Frustum.js

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

+ 14 - 19
src/math/Interpolant.js

@@ -20,8 +20,7 @@
  * @author tschw
  */
 
-function Interpolant(
-		parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
 
 	this.parameterPositions = parameterPositions;
 	this._cachedIndex = 0;
@@ -33,10 +32,16 @@ function Interpolant(
 
 }
 
-Interpolant.prototype = {
+Object.assign( 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,
@@ -52,10 +57,10 @@ 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; ;) {
@@ -92,8 +97,8 @@ Interpolant.prototype = {
 
 					}
 
-//- slower code:
-//-					if ( t < t0 || t0 === undefined ) {
+					//- slower code:
+					//-					if ( t < t0 || t0 === undefined ) {
 					if ( ! ( t >= t0 ) ) {
 
 						// looping?
@@ -244,16 +249,6 @@ 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 ) {
 
 }
 
-Line3.prototype = {
+Object.assign( Line3.prototype, {
 
 	constructor: Line3,
 
@@ -126,7 +126,7 @@ Line3.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Line3 };

+ 3 - 5
src/math/Matrix3.js

@@ -25,7 +25,7 @@ function Matrix3() {
 
 }
 
-Matrix3.prototype = {
+Object.assign( Matrix3.prototype, {
 
 	constructor: Matrix3,
 
@@ -97,12 +97,10 @@ Matrix3.prototype = {
 
 	applyToBufferAttribute: function () {
 
-		var v1;
+		var v1 = new Vector3();
 
 		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 );
@@ -274,7 +272,7 @@ Matrix3.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Matrix3 };

+ 9 - 25
src/math/Matrix4.js

@@ -33,7 +33,7 @@ function Matrix4() {
 
 }
 
-Matrix4.prototype = {
+Object.assign( Matrix4.prototype, {
 
 	constructor: Matrix4,
 
@@ -119,12 +119,10 @@ Matrix4.prototype = {
 
 	extractRotation: function () {
 
-		var v1;
+		var v1 = new Vector3();
 
 		return function extractRotation( m ) {
 
-			if ( v1 === undefined ) v1 = new Vector3();
-
 			var te = this.elements;
 			var me = m.elements;
 
@@ -317,18 +315,12 @@ Matrix4.prototype = {
 
 	lookAt: function () {
 
-		var x, y, z;
+		var x = new Vector3();
+		var y = new Vector3();
+		var z = new Vector3();
 
 		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();
@@ -450,12 +442,10 @@ Matrix4.prototype = {
 
 	applyToBufferAttribute: function () {
 
-		var v1;
+		var v1 = new Vector3();
 
 		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 );
@@ -772,17 +762,11 @@ Matrix4.prototype = {
 
 	decompose: function () {
 
-		var vector, matrix;
+		var vector = new Vector3();
+		var matrix = new Matrix4();
 
 		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();
@@ -939,7 +923,7 @@ Matrix4.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Matrix4 };

+ 2 - 2
src/math/Plane.js

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

+ 77 - 76
src/math/Quaternion.js

@@ -16,7 +16,80 @@ function Quaternion( x, y, z, w ) {
 
 }
 
-Quaternion.prototype = {
+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, {
 
 	constructor: Quaternion,
 
@@ -259,7 +332,8 @@ Quaternion.prototype = {
 
 		// assumes direction vectors vFrom and vTo are normalized
 
-		var v1, r;
+		var v1 = new Vector3();
+		var r;
 
 		var EPS = 0.000001;
 
@@ -453,7 +527,7 @@ 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 );
@@ -511,79 +585,6 @@ 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 ) {
 
 }
 
-Ray.prototype = {
+Object.assign( Ray.prototype, {
 
 	constructor: Ray,
 
@@ -534,7 +534,7 @@ Ray.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Ray };

+ 3 - 5
src/math/Sphere.js

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

+ 2 - 2
src/math/Spherical.js

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

+ 61 - 63
src/math/Triangle.js

@@ -15,90 +15,94 @@ function Triangle( a, b, c ) {
 
 }
 
-Triangle.normal = function () {
+Object.assign( Triangle, {
 
-	var v0 = new Vector3();
+	normal: function () {
 
-	return function normal( a, b, c, optionalTarget ) {
+		var v0 = new Vector3();
 
-		var result = optionalTarget || new Vector3();
+		return function normal( a, b, c, optionalTarget ) {
 
-		result.subVectors( c, b );
-		v0.subVectors( a, b );
-		result.cross( v0 );
+			var result = optionalTarget || new Vector3();
 
-		var resultLengthSq = result.lengthSq();
-		if ( resultLengthSq > 0 ) {
+			result.subVectors( c, b );
+			v0.subVectors( a, b );
+			result.cross( v0 );
 
-			return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
+			var resultLengthSq = result.lengthSq();
+			if ( resultLengthSq > 0 ) {
 
-		}
+				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 () {
+	}(),
 
-	var v0 = new Vector3();
-	var v1 = new Vector3();
-	var v2 = new Vector3();
+	// static/instance method to calculate barycentric coordinates
+	// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+	barycoordFromPoint: function () {
 
-	return function barycoordFromPoint( point, a, b, c, optionalTarget ) {
+		var v0 = new Vector3();
+		var v1 = new Vector3();
+		var v2 = new Vector3();
 
-		v0.subVectors( c, a );
-		v1.subVectors( b, a );
-		v2.subVectors( point, a );
+		return function barycoordFromPoint( point, a, b, c, optionalTarget ) {
 
-		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 );
+			v0.subVectors( c, a );
+			v1.subVectors( b, a );
+			v2.subVectors( point, a );
 
-		var denom = ( dot00 * dot11 - dot01 * dot01 );
+			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 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 );
 
-}();
+		};
+
+	}(),
 
-Triangle.containsPoint = function () {
+	containsPoint: function () {
+
+		var v1 = new Vector3();
 
-	var v1 = new Vector3();
+		return function containsPoint( point, a, b, c ) {
 
-	return function containsPoint( point, a, b, c ) {
+			var result = Triangle.barycoordFromPoint( point, a, b, c, v1 );
 
-		var result = Triangle.barycoordFromPoint( point, a, b, c, v1 );
+			return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
 
-		return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
+		};
 
-	};
+	}()
 
-}();
+} );
 
-Triangle.prototype = {
+Object.assign( Triangle.prototype, {
 
 	constructor: Triangle,
 
@@ -189,19 +193,13 @@ Triangle.prototype = {
 
 	closestPointToPoint: function () {
 
-		var plane, edgeList, projectedPoint, closestPoint;
+		var plane = new Plane();
+		var edgeList = [ new Line3(), new Line3(), new Line3() ];
+		var projectedPoint = new Vector3();
+		var closestPoint = new Vector3();
 
 		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;
 
@@ -256,7 +254,7 @@ Triangle.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Triangle };

+ 4 - 10
src/math/Vector2.js

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

+ 10 - 28
src/math/Vector3.js

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

+ 4 - 10
src/math/Vector4.js

@@ -15,7 +15,7 @@ function Vector4( x, y, z, w ) {
 
 }
 
-Vector4.prototype = {
+Object.assign( Vector4.prototype, {
 
 	constructor: Vector4,
 
@@ -450,17 +450,11 @@ Vector4.prototype = {
 
 	clampScalar: function () {
 
-		var min, max;
+		var min = new Vector4();
+		var max = new Vector4();
 
 		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 );
 
@@ -628,7 +622,7 @@ Vector4.prototype = {
 
 	}
 
-};
+} );
 
 
 export { Vector4 };

+ 2 - 3
src/renderers/WebGLRenderTarget.js

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

+ 2 - 3
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;
 
-Texture.prototype = {
+Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
 	constructor: Texture,
 
@@ -285,8 +285,7 @@ Texture.prototype = {
 
 	}
 
-};
+} );
 
-Object.assign( Texture.prototype, EventDispatcher.prototype );
 
 export { Texture };