Browse Source

Updated code to version V1.2.1 and updated docs accordingly.

Code related changes

THREE.OBJLoader2.WWOBJLoader2:
- Function _receiveWorkerMessage now uses a meshDescription that allows to override material or bufferGeometry or to completely disregard the mesh. THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride was introduced for this.
- Allow usage of multiple callbacks per callback type
- THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer and THREE.OBJLoader2.WWOBJLoader2.PrepDataFile require less mandatory parameters. Setters are introduced to handle optional things

THREE.OBJLoader2.WWOBJLoader2Director:
- Added per queue object callbacks
- Global callbacks in prepareWorkers will be specified with new object OBJLoader2.WWOBJLoader2.PrepDataCallbacks. This object is also used in both PrepData objects for defining extra per model callbacks in addition to the global ones
- Callbacks will be reset and reassigned for every run

All:
- Improve code quality, especially, replaced != or == with Boolean() or ! Boolean() where applicable
- Improve logging and comments
Kai Salmen 8 years ago
parent
commit
561febce05

+ 152 - 9
docs/examples/loaders/WWOBJLoader2.html

@@ -16,6 +16,8 @@
 		<h2>Sub-Classes</h2>
 		<h2>Sub-Classes</h2>
 		[page:WWOBJLoader2.PrepDataArrayBuffer]<br>
 		[page:WWOBJLoader2.PrepDataArrayBuffer]<br>
 		[page:WWOBJLoader2.PrepDataFile]<br>
 		[page:WWOBJLoader2.PrepDataFile]<br>
+		[page:WWOBJLoader2.PrepDataCallbacks]<br>
+		[page:WWOBJLoader2.LoadedMeshUserOverride]<br>
 		[page:WWOBJLoader2.WWOBJLoader2Director]
 		[page:WWOBJLoader2.WWOBJLoader2Director]
 
 
 		<h2>Example</h2>
 		<h2>Example</h2>
@@ -131,7 +133,7 @@
 			[page:Function callbackMeshLoaded] — 	Callback function for described functionality
 			[page:Function callbackMeshLoaded] — 	Callback function for described functionality
 		</div>
 		</div>
 		<div>
 		<div>
-			Register callback function that is called every time a mesh was loaded. Return altered [page:Material] or null from callback.
+			Register callback function that is called every time a mesh was loaded. Use [page:WWOBJLoader2.LoadedMeshUserOverride] for alteration instructions (geometry, material or disregard mesh).
 		</div>
 		</div>
 
 
 		<h3>[method:null registerCallbackErrorWhileLoading]( [page:Function callbackErrorWhileLoading] )</h3>
 		<h3>[method:null registerCallbackErrorWhileLoading]( [page:Function callbackErrorWhileLoading] )</h3>
@@ -143,6 +145,12 @@
 		</div>
 		</div>
 
 
 
 
+		<h3>[method:null clearAllCallbacks]()</h3>
+		<div>
+			Clears all registered callbacks.
+		</div>
+
+
 		<h2>Sub-Classes</h2>
 		<h2>Sub-Classes</h2>
 		<br>
 		<br>
 		<a name="PrepDataArrayBuffer"></a><h1>PrepDataArrayBuffer</h1>
 		<a name="PrepDataArrayBuffer"></a><h1>PrepDataArrayBuffer</h1>
@@ -154,13 +162,44 @@
 			[page:Uint8Array objAsArrayBuffer] — OBJ file content as ArrayBuffer<br>
 			[page:Uint8Array objAsArrayBuffer] — OBJ file content as ArrayBuffer<br>
 			[page:String pathTexture] — Path to texture files<br>
 			[page:String pathTexture] — Path to texture files<br>
 			[page:String mtlAsString] — MTL file content as string
 			[page:String mtlAsString] — MTL file content as string
-			[page:Object3D sceneGraphBaseNode] [page:Object3D] where meshes will be attached
-			[page:Boolean streamMeshes] Singles meshes are directly integrated into scene when loaded or later
-			[page:Boolean requestTerminate] Request termination of web worker and free local resources after execution
 		</div>
 		</div>
 		<div>
 		<div>
 			Instruction to configure [page:WWOBJLoader2.prepareRun] to load OBJ from given ArrayBuffer and MTL from given String.
 			Instruction to configure [page:WWOBJLoader2.prepareRun] to load OBJ from given ArrayBuffer and MTL from given String.
 		</div>
 		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:PrepDataCallbacks getCallbacks]()</h3>
+		<div>
+			Returns all callbacks as [page:WWOBJLoader2.PrepDataCallbacks].
+		</div>
+
+
+		<h3>[method:null setRequestTerminate]( [page:Boolean requestTerminate] )</h3>
+		<div>
+			[page:Boolean requestTerminate] — Default is false
+		</div>
+		<div>
+			Request termination of web worker and free local resources after execution.
+		</div>
+
+
+		<h3>[method:null setSceneGraphBaseNode]( [page:THREE.Object3D sceneGraphBaseNode] )</h3>
+		<div>
+			[page:Object3D sceneGraphBaseNode] — Scene graph object
+		</div>
+		<div>
+			[page:Object3D] where meshes will be attached.
+		</div>
+
+
+		<h3>[method:null setStreamMeshes]( [page:Boolean streamMeshes] )</h3>
+		<div>
+			[page:Boolean streamMeshes] — Default is true
+		</div>
+		<div>
+			Singles meshes are directly integrated into scene when loaded or later.
+		</div>
 		<br>
 		<br>
 		<br>
 		<br>
 
 
@@ -175,13 +214,117 @@
 			[page:String fileObj] — OBJ file name<br>
 			[page:String fileObj] — OBJ file name<br>
 			[page:String pathTexture] — Path to texture files<br>
 			[page:String pathTexture] — Path to texture files<br>
 			[page:String fileMtl] — MTL file name
 			[page:String fileMtl] — MTL file name
-			[page:Object3D sceneGraphBaseNode] [page:Object3D] where meshes will be attached
-			[page:Boolean streamMeshes] Singles meshes are directly integrated into scene when loaded or later
-			[page:Boolean requestTerminate] Request termination of web worker and free local resources after execution
 		</div>
 		</div>
 		<div>
 		<div>
 			Instruction to configure [page:WWOBJLoader2.prepareRun] to load OBJ and MTL from files.
 			Instruction to configure [page:WWOBJLoader2.prepareRun] to load OBJ and MTL from files.
 		</div>
 		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:PrepDataCallbacks getCallbacks]()</h3>
+		<div>
+			Returns all callbacks as [page:WWOBJLoader2.PrepDataCallbacks].
+		</div>
+
+
+		<h3>[method:null setRequestTerminate]( [page:Boolean requestTerminate] )</h3>
+		<div>
+			[page:Boolean requestTerminate] — Default is false
+		</div>
+		<div>
+			Request termination of web worker and free local resources after execution.
+		</div>
+
+
+		<h3>[method:null setSceneGraphBaseNode]( [page:THREE.Object3D sceneGraphBaseNode] )</h3>
+		<div>
+			[page:Object3D sceneGraphBaseNode] — Scene graph object
+		</div>
+		<div>
+			[page:Object3D] where meshes will be attached.
+		</div>
+
+
+		<h3>[method:null setStreamMeshes]( [page:Boolean streamMeshes] )</h3>
+		<div>
+			[page:Boolean streamMeshes] — Default is true
+		</div>
+		<div>
+			Singles meshes are directly integrated into scene when loaded or later.
+		</div>
+		<br>
+		<br>
+
+
+		<a name="PrepDataCallbacks"></a><h1>PrepDataCallbacks</h1>
+		<h2>Constructor</h2>
+
+		<h3>PrepDataCallbacks()</h3>
+		<div>
+			Callbacks utilized by functions working with [page:WWOBJLoader2.PrepDataArrayBuffer] or [page:WWOBJLoader2.PrepDataFile].
+		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null registerCallbackCompletedLoading]( [page:Function callbackCompletedLoading] )</h3>
+		<div>
+			[page:Function callbackCompletedLoading] — Callback function for described functionality
+		</div>
+		<div>
+			Register callback function that is called once loading of the complete model is completed.
+		</div>
+
+
+		<h3>[method:null registerCallbackProgress]( [page:Function callbackProgress] )</h3>
+		<div>
+			[page:Function callbackProgress] — Callback function for described functionality
+		</div>
+		<div>
+			Register callback function that is invoked by internal function "_announceProgress" to print feedback.
+		</div>
+
+
+		<h3>[method:null registerCallbackErrorWhileLoading]( [page:Function callbackErrorWhileLoading] )</h3>
+		<div>
+			[page:Function callbackErrorWhileLoading] — Callback function for described functionality
+		</div>
+		<div>
+			Report if an error prevented loading.
+		</div>
+
+
+		<h3>[method:null registerCallbackMaterialsLoaded]( [page:Function callbackMaterialsLoaded] )</h3>
+		<div>
+			[page:Function callbackMaterialsLoaded] — Callback function for described functionality
+		</div>
+		<div>
+			Register callback function that is called once materials have been loaded. It allows to alter and return materials.
+		</div>
+
+
+		<h3>[method:null registerCallbackMeshLoaded]( [page:Function callbackMeshLoaded] )</h3>
+		<div>
+			[page:Function callbackMeshLoaded] — Callback function for described functionality
+		</div>
+		<div>
+			Register callback function that is called every time a mesh was loaded. Use [page:WWOBJLoader2.LoadedMeshUserOverride] for alteration instructions (geometry, material or disregard mesh).
+		</div>
+		<br>
+		<br>
+
+
+		<a name="LoadedMeshUserOverride"></a><h1>LoadedMeshUserOverride</h1>
+		<h2>Constructor</h2>
+
+		<h3>LoadedMeshUserOverride( [page:Boolean disregardMesh], [page:THREE.BufferGeometry bufferGeometry], [page:THREE.Material material] )</h3>
+		<div>
+			[page:Boolean disregardMesh] — Tell [page:WWOBJLoader2] to completely disregard this mesh<br>
+			[page:BufferGeometry bufferGeometry] — The [page:BufferGeometry] to be used<br>
+			[page:Material material] — The [page:Material] to be used
+		</div>
+		<div>
+			Object to return by THREE.OBJLoader2.WWOBJLoader2.callbacks.meshLoaded. Used to adjust bufferGeometry or material or prevent complete loading of mesh.
+		</div>
 		<br>
 		<br>
 		<br>
 		<br>
 
 
@@ -199,9 +342,9 @@
 				deregister
 				deregister
 		</div>
 		</div>
 
 
-		<h3>[method:null prepareWorkers]( Array of [page:Function callbacks], [page:Number maxQueueSize], [page:Number maxWebWorkers] )</h3>
+		<h3>[method:null prepareWorkers]( [page:WWOBJLoader2.PrepDataCallbacks globalCallbacks], [page:Number maxQueueSize], [page:Number maxWebWorkers] )</h3>
 		<div>
 		<div>
-			Array of [page:Function callbacks] — Register callbacks for all web workers: progress, completedLoading, errorWhileLoading, materialsLoaded, meshLoaded<br>
+			[page:WWOBJLoader2.PrepDataCallbacks globalCallbacks] — Register global callbacks used by all web workers<br>
 			[page:Number maxQueueSize] — Set the maximum size of the instruction queue (1-1024)<br>
 			[page:Number maxQueueSize] — Set the maximum size of the instruction queue (1-1024)<br>
 			[page:Number maxWebWorkers] — Set the maximum amount of workers (1-16)
 			[page:Number maxWebWorkers] — Set the maximum amount of workers (1-16)
 		</div>
 		</div>

+ 27 - 33
examples/js/loaders/OBJLoader2.js

@@ -6,18 +6,18 @@
 'use strict';
 'use strict';
 
 
 if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
 if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
-THREE.OBJLoader2.version = '1.1.0';
+THREE.OBJLoader2.version = '1.2.1';
 
 
 /**
 /**
  * Use this class to load OBJ data from files or to parse OBJ data from arraybuffer or text
  * Use this class to load OBJ data from files or to parse OBJ data from arraybuffer or text
  * @class
  * @class
  *
  *
- * @param {THREE.DefaultLoadingManager} [manager] Extension of {@link THREE.DefaultLoadingManager}
+ * @param {THREE.DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link THREE.DefaultLoadingManager}
  */
  */
 THREE.OBJLoader2 = (function () {
 THREE.OBJLoader2 = (function () {
 
 
 	function OBJLoader2( manager ) {
 	function OBJLoader2( manager ) {
-		this.manager = ( manager == null ) ? THREE.DefaultLoadingManager : manager;
+		this.manager = ! Boolean( manager ) ? THREE.DefaultLoadingManager : manager;
 
 
 		this.path = '';
 		this.path = '';
 		this.fileLoader = new THREE.FileLoader( this.manager );
 		this.fileLoader = new THREE.FileLoader( this.manager );
@@ -29,17 +29,17 @@ THREE.OBJLoader2 = (function () {
 	}
 	}
 
 
 	/**
 	/**
-	 * Base path to use
+	 * Base path to use.
 	 * @memberOf THREE.OBJLoader2
 	 * @memberOf THREE.OBJLoader2
 	 *
 	 *
 	 * @param {string} path The basepath
 	 * @param {string} path The basepath
 	 */
 	 */
 	OBJLoader2.prototype.setPath = function ( path ) {
 	OBJLoader2.prototype.setPath = function ( path ) {
-		this.path = ( path == null ) ? this.path : path;
+		this.path = Boolean( path ) ? path : this.path;
 	};
 	};
 
 
 	/**
 	/**
-	 * Set the node where the loaded objects will be attached
+	 * Set the node where the loaded objects will be attached.
 	 * @memberOf THREE.OBJLoader2
 	 * @memberOf THREE.OBJLoader2
 	 *
 	 *
 	 * @param {THREE.Object3D} sceneGraphBaseNode Scenegraph object where meshes will be attached
 	 * @param {THREE.Object3D} sceneGraphBaseNode Scenegraph object where meshes will be attached
@@ -49,21 +49,21 @@ THREE.OBJLoader2 = (function () {
 	};
 	};
 
 
 	/**
 	/**
-	 * Set materials loaded by MTLLoader
+	 * Set materials loaded by MTLLoader or any other supplier of an Array of {@link THREE.Material}.
 	 * @memberOf THREE.OBJLoader2
 	 * @memberOf THREE.OBJLoader2
 	 *
 	 *
-	 * @param {THREE.MTLLoader.MaterialCreator.materials[]} materials {@link THREE.MTLLoader.MaterialCreator.materials}
+	 * @param {THREE.Material[]} materials  Array of {@link THREE.Material} from MTLLoader
 	 */
 	 */
 	OBJLoader2.prototype.setMaterials = function ( materials ) {
 	OBJLoader2.prototype.setMaterials = function ( materials ) {
 		this.meshCreator.setMaterials( materials );
 		this.meshCreator.setMaterials( materials );
 	};
 	};
 
 
 	/**
 	/**
-	 * Allows to set debug mode for the parser and the meshCreator
+	 * Allows to set debug mode for the parser and the meshCreator.
 	 * @memberOf THREE.OBJLoader2
 	 * @memberOf THREE.OBJLoader2
 	 *
 	 *
-	 * @param {boolean} parserDebug {@link Parser} will produce debug output
-	 * @param {boolean} meshCreatorDebug {@link THREE.OBJLoader2.MeshCreator} will produce debug output
+	 * @param {boolean} parserDebug Internal Parser will produce debug output
+	 * @param {boolean} meshCreatorDebug Internal MeshCreator will produce debug output
 	 */
 	 */
 	OBJLoader2.prototype.setDebug = function ( parserDebug, meshCreatorDebug ) {
 	OBJLoader2.prototype.setDebug = function ( parserDebug, meshCreatorDebug ) {
 		this.parser.setDebug( parserDebug );
 		this.parser.setDebug( parserDebug );
@@ -76,20 +76,20 @@ THREE.OBJLoader2 = (function () {
 	 *
 	 *
 	 * @param {string} url URL of the file to load
 	 * @param {string} url URL of the file to load
 	 * @param {callback} onLoad Called after loading was successfully completed
 	 * @param {callback} onLoad Called after loading was successfully completed
-	 * @param {callback} onProgress Called to report progress of loading
+	 * @param {callback} onProgress Called to report progress of loading. The argument will be the XmlHttpRequest instance, that contain {integer total} and {integer loaded} bytes.
 	 * @param {callback} onError Called after an error occurred during loading
 	 * @param {callback} onError Called after an error occurred during loading
 	 * @param {boolean} [useArrayBuffer=true] Set this to false to force string based parsing
 	 * @param {boolean} [useArrayBuffer=true] Set this to false to force string based parsing
 	 */
 	 */
 	OBJLoader2.prototype.load = function ( url, onLoad, onProgress, onError, useArrayBuffer ) {
 	OBJLoader2.prototype.load = function ( url, onLoad, onProgress, onError, useArrayBuffer ) {
 		this._validate();
 		this._validate();
 		this.fileLoader.setPath( this.path );
 		this.fileLoader.setPath( this.path );
-		this.fileLoader.setResponseType( ( useArrayBuffer || useArrayBuffer == null ) ? 'arraybuffer' : 'text' );
+		this.fileLoader.setResponseType( ( useArrayBuffer || useArrayBuffer === null || useArrayBuffer === undefined ) ? 'arraybuffer' : 'text' );
 
 
 		var scope = this;
 		var scope = this;
 		scope.fileLoader.load( url, function ( content ) {
 		scope.fileLoader.load( url, function ( content ) {
 
 
 			// only use parseText if useArrayBuffer is explicitly set to false
 			// only use parseText if useArrayBuffer is explicitly set to false
-			onLoad( ( useArrayBuffer || useArrayBuffer == null ) ? scope.parse( content ) : scope.parseText( content ) );
+			onLoad( ( useArrayBuffer || useArrayBuffer === null || useArrayBuffer === undefined ) ? scope.parse( content ) : scope.parseText( content ) );
 
 
 		}, onProgress, onError );
 		}, onProgress, onError );
 	};
 	};
@@ -147,8 +147,7 @@ THREE.OBJLoader2 = (function () {
 	OBJLoader2.prototype._validate = function () {
 	OBJLoader2.prototype._validate = function () {
 		if ( this.validated ) return;
 		if ( this.validated ) return;
 
 
-		this.fileLoader = ( this.fileLoader == null ) ? new THREE.FileLoader( this.manager ) : this.fileLoader;
-		this.setPath();
+		this.fileLoader = Boolean( this.fileLoader ) ? this.fileLoader : new THREE.FileLoader( this.manager );
 		this.parser.validate();
 		this.parser.validate();
 		this.meshCreator.validate();
 		this.meshCreator.validate();
 
 
@@ -221,7 +220,7 @@ THREE.OBJLoader2 = (function () {
 		}
 		}
 
 
 		Parser.prototype.setDebug = function ( debug ) {
 		Parser.prototype.setDebug = function ( debug ) {
-			this.debug = ( debug == null ) ? this.debug : debug;
+			this.debug = Boolean( debug ) ? debug : this.debug;
 		};
 		};
 
 
 		Parser.prototype.validate = function () {
 		Parser.prototype.validate = function () {
@@ -487,9 +486,9 @@ THREE.OBJLoader2 = (function () {
 			this.uvs = [];
 			this.uvs = [];
 
 
 			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
 			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
-			this.mtllibName = ( mtllibName != null ) ? mtllibName : 'none';
-			this.objectName = ( objectName != null ) ? objectName : 'none';
-			this.groupName = ( groupName != null ) ? groupName : 'none';
+			this.mtllibName = Boolean( mtllibName ) ? mtllibName : 'none';
+			this.objectName = Boolean( objectName ) ? objectName : 'none';
+			this.groupName = Boolean( groupName ) ? groupName : 'none';
 			this.activeMtlName = 'none';
 			this.activeMtlName = 'none';
 			this.activeSmoothingGroup = 1;
 			this.activeSmoothingGroup = 1;
 
 
@@ -563,7 +562,7 @@ THREE.OBJLoader2 = (function () {
 		};
 		};
 
 
 		RawObject.prototype.pushUsemtl = function ( mtlName ) {
 		RawObject.prototype.pushUsemtl = function ( mtlName ) {
-			if ( this.activeMtlName === mtlName || mtlName == null ) return;
+			if ( this.activeMtlName === mtlName || mtlName === null || mtlName === undefined ) return;
 			this.activeMtlName = mtlName;
 			this.activeMtlName = mtlName;
 			this.mtlCount++;
 			this.mtlCount++;
 
 
@@ -581,16 +580,11 @@ THREE.OBJLoader2 = (function () {
 
 
 		RawObject.prototype.verifyIndex = function () {
 		RawObject.prototype.verifyIndex = function () {
 			var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 );
 			var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 );
-			if ( this.rawObjectDescriptions[ index ] == null ) {
+			this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ];
+			if ( ! Boolean( this.rawObjectDescriptionInUse ) ) {
 
 
-				this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ] =
-					new RawObjectDescription(
-						this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup
-					);
-
-			} else {
-
-				this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ];
+				this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup );
+				this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse;
 
 
 			}
 			}
 		};
 		};
@@ -815,15 +809,15 @@ THREE.OBJLoader2 = (function () {
 		}
 		}
 
 
 		MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) {
 		MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) {
-			this.sceneGraphBaseNode = ( sceneGraphBaseNode == null ) ? ( this.sceneGraphBaseNode == null ? new THREE.Group() : this.sceneGraphBaseNode ) : sceneGraphBaseNode;
+			this.sceneGraphBaseNode = Boolean( sceneGraphBaseNode ) ? sceneGraphBaseNode : ( Boolean( this.sceneGraphBaseNode ) ? this.sceneGraphBaseNode : new THREE.Group() );
 		};
 		};
 
 
 		MeshCreator.prototype.setMaterials = function ( materials ) {
 		MeshCreator.prototype.setMaterials = function ( materials ) {
-			this.materials = ( materials == null ) ? ( this.materials == null ? { materials: [] } : this.materials ) : materials;
+			this.materials = Boolean( materials ) ? materials : ( Boolean( this.materials ) ? this.materials : { materials: [] } );
 		};
 		};
 
 
 		MeshCreator.prototype.setDebug = function ( debug ) {
 		MeshCreator.prototype.setDebug = function ( debug ) {
-			this.debug = ( debug == null ) ? this.debug : debug;
+			this.debug = Boolean( debug ) ? debug : this.debug;
 		};
 		};
 
 
 		MeshCreator.prototype.validate = function () {
 		MeshCreator.prototype.validate = function () {

+ 378 - 160
examples/js/loaders/WWOBJLoader2.js

@@ -6,7 +6,7 @@
 'use strict';
 'use strict';
 
 
 if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
 if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
-THREE.OBJLoader2.version = '1.1.0';
+THREE.OBJLoader2.version = '1.2.1';
 
 
 /**
 /**
  * OBJ data will be loaded by dynamically created web worker.
  * OBJ data will be loaded by dynamically created web worker.
@@ -39,17 +39,7 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 		this.running = false;
 		this.running = false;
 		this.requestTerminate = false;
 		this.requestTerminate = false;
 
 
-		this.callbacks = {
-			progress: null,
-			completedLoading: null,
-			errorWhileLoading: null,
-			materialsLoaded: null,
-			meshLoaded: null,
-			director: {
-				completedLoading: null,
-				errorWhileLoading: null
-			}
-		};
+		this.clearAllCallbacks();
 
 
 		this.manager = THREE.DefaultLoadingManager;
 		this.manager = THREE.DefaultLoadingManager;
 		this.fileLoader = new THREE.FileLoader( this.manager );
 		this.fileLoader = new THREE.FileLoader( this.manager );
@@ -70,10 +60,10 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 	};
 	};
 
 
 	/**
 	/**
-	 * Set enable or disable debug logging
+	 * Enable or disable debug logging.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
-	 * @param {boolean} enabled
+	 * @param {boolean} enabled True or false
 	 */
 	 */
 	WWOBJLoader2.prototype.setDebug = function ( enabled ) {
 	WWOBJLoader2.prototype.setDebug = function ( enabled ) {
 		this.debug = enabled;
 		this.debug = enabled;
@@ -90,68 +80,83 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 	};
 	};
 
 
 	/**
 	/**
-	 * Register callback function that is invoked by internal function "_announceProgress" to print feedback
+	 * Register callback function that is invoked by internal function "_announceProgress" to print feedback.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
 	 * @param {callback} callbackProgress Callback function for described functionality
 	 * @param {callback} callbackProgress Callback function for described functionality
 	 */
 	 */
 	WWOBJLoader2.prototype.registerCallbackProgress = function ( callbackProgress ) {
 	WWOBJLoader2.prototype.registerCallbackProgress = function ( callbackProgress ) {
-		if ( callbackProgress != null ) this.callbacks.progress = callbackProgress;
+		if ( Boolean( callbackProgress ) ) this.callbacks.progress.push( callbackProgress );
 	};
 	};
 
 
 	/**
 	/**
-	 * Register callback function that is called once loading of the complete model is completed
+	 * Register callback function that is called once loading of the complete model is completed.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
 	 * @param {callback} callbackCompletedLoading Callback function for described functionality
 	 * @param {callback} callbackCompletedLoading Callback function for described functionality
 	 */
 	 */
 	WWOBJLoader2.prototype.registerCallbackCompletedLoading = function ( callbackCompletedLoading ) {
 	WWOBJLoader2.prototype.registerCallbackCompletedLoading = function ( callbackCompletedLoading ) {
-		if ( callbackCompletedLoading != null ) this.callbacks.completedLoading = callbackCompletedLoading;
+		if ( Boolean( callbackCompletedLoading ) ) this.callbacks.completedLoading.push( callbackCompletedLoading );
 	};
 	};
 
 
 	/**
 	/**
-	 * Register callback function that is called once materials have been loaded. It allows to alter and return materials
+	 * Register callback function that is called once materials have been loaded. It allows to alter and return materials.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
 	 * @param {callback} callbackMaterialsLoaded Callback function for described functionality
 	 * @param {callback} callbackMaterialsLoaded Callback function for described functionality
 	 */
 	 */
 	WWOBJLoader2.prototype.registerCallbackMaterialsLoaded = function ( callbackMaterialsLoaded ) {
 	WWOBJLoader2.prototype.registerCallbackMaterialsLoaded = function ( callbackMaterialsLoaded ) {
-		if ( callbackMaterialsLoaded != null ) this.callbacks.materialsLoaded = callbackMaterialsLoaded;
+		if ( Boolean( callbackMaterialsLoaded ) ) this.callbacks.materialsLoaded.push( callbackMaterialsLoaded );
 	};
 	};
 
 
 	/**
 	/**
-	 * Register callback function that is called every time a mesh was loaded
+	 * Register callback function that is called every time a mesh was loaded.
+	 * Use {@link THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
 	 * @param {callback} callbackMeshLoaded Callback function for described functionality
 	 * @param {callback} callbackMeshLoaded Callback function for described functionality
 	 */
 	 */
 	WWOBJLoader2.prototype.registerCallbackMeshLoaded = function ( callbackMeshLoaded ) {
 	WWOBJLoader2.prototype.registerCallbackMeshLoaded = function ( callbackMeshLoaded ) {
-		if ( callbackMeshLoaded != null ) this.callbacks.meshLoaded = callbackMeshLoaded;
+		if ( Boolean( callbackMeshLoaded ) ) this.callbacks.meshLoaded.push( callbackMeshLoaded );
 	};
 	};
 
 
 	/**
 	/**
-	 * Report if an error prevented loading
+	 * Register callback function that is called to report an error that prevented loading.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
 	 * @param {callback} callbackErrorWhileLoading Callback function for described functionality
 	 * @param {callback} callbackErrorWhileLoading Callback function for described functionality
 	 */
 	 */
 	WWOBJLoader2.prototype.registerCallbackErrorWhileLoading = function ( callbackErrorWhileLoading ) {
 	WWOBJLoader2.prototype.registerCallbackErrorWhileLoading = function ( callbackErrorWhileLoading ) {
-		if ( callbackErrorWhileLoading != null ) this.callbacks.errorWhileLoading = callbackErrorWhileLoading;
+		if ( Boolean( callbackErrorWhileLoading ) ) this.callbacks.errorWhileLoading.push( callbackErrorWhileLoading );
+	};
+
+	/**
+	 * Clears all registered callbacks.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 */
+	WWOBJLoader2.prototype.clearAllCallbacks = function () {
+		this.callbacks = {
+			progress: [],
+			completedLoading: [],
+			errorWhileLoading: [],
+			materialsLoaded: [],
+			meshLoaded: []
+		};
 	};
 	};
 
 
 	/**
 	/**
-	 * Call requestTerminate to terminate the web worker and free local resource after execution
+	 * Call requestTerminate to terminate the web worker and free local resource after execution.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
 	 *
 	 *
-	 * @param {boolean} requestTerminate
+	 * @param {boolean} requestTerminate True or false
 	 */
 	 */
 	WWOBJLoader2.prototype.setRequestTerminate = function ( requestTerminate ) {
 	WWOBJLoader2.prototype.setRequestTerminate = function ( requestTerminate ) {
-		this.requestTerminate = ( requestTerminate != null && requestTerminate ) ? true : false;
+		this.requestTerminate = Boolean( requestTerminate );
 	};
 	};
 
 
 	WWOBJLoader2.prototype._validate = function () {
 	WWOBJLoader2.prototype._validate = function () {
 		if ( this.validated ) return;
 		if ( this.validated ) return;
-		if ( this.worker == null ) {
+		if ( ! Boolean( this.worker ) ) {
 
 
 			this._buildWebWorkerCode();
 			this._buildWebWorkerCode();
 			var blob = new Blob( [ this.workerCode ], { type: 'text/plain' } );
 			var blob = new Blob( [ this.workerCode ], { type: 'text/plain' } );
@@ -173,9 +178,9 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 		this.running = true;
 		this.running = true;
 		this.requestTerminate = false;
 		this.requestTerminate = false;
 
 
-		this.fileLoader = ( this.fileLoader == null ) ? new THREE.FileLoader( this.manager ) : this.fileLoader;
-		this.mtlLoader = ( this.mtlLoader == null ) ?  new THREE.MTLLoader() : this.mtlLoader;
-		if ( this.crossOrigin != null ) this.mtlLoader.setCrossOrigin( this.crossOrigin );
+		this.fileLoader = Boolean( this.fileLoader ) ? this.fileLoader : new THREE.FileLoader( this.manager );
+		this.mtlLoader = Boolean( this.mtlLoader ) ? this.mtlLoader : new THREE.MTLLoader();
+		if ( Boolean( this.crossOrigin ) ) this.mtlLoader.setCrossOrigin( this.crossOrigin );
 
 
 		this.dataAvailable = false;
 		this.dataAvailable = false;
 		this.fileObj = null;
 		this.fileObj = null;
@@ -253,7 +258,7 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 		var processLoadedMaterials = function ( materialCreator ) {
 		var processLoadedMaterials = function ( materialCreator ) {
 			var materialCreatorMaterials = [];
 			var materialCreatorMaterials = [];
 			var materialNames = [];
 			var materialNames = [];
-			if ( materialCreator != null ) {
+			if ( Boolean( materialCreator ) ) {
 
 
 				materialCreator.preload();
 				materialCreator.preload();
 				materialCreatorMaterials = materialCreator.materials;
 				materialCreatorMaterials = materialCreator.materials;
@@ -274,13 +279,15 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 				materialNames: materialNames
 				materialNames: materialNames
 			} );
 			} );
 
 
-			if ( scope.callbacks.materialsLoaded != null ) {
+			var materialsFromCallback;
+			var callbackMaterialsLoaded;
+			for ( var index in scope.callbacks.materialsLoaded ) {
 
 
-				var materialsCallback = scope.callbacks.materialsLoaded( scope.materials );
-				if ( materialsCallback != null ) scope.materials = materialsCallback;
+				callbackMaterialsLoaded = scope.callbacks.materialsLoaded[ index ];
+				materialsFromCallback = callbackMaterialsLoaded( scope.materials );
+				if ( Boolean( materialsFromCallback ) ) scope.materials = materialsFromCallback;
 
 
 			}
 			}
-
 			if ( scope.dataAvailable && scope.objAsArrayBuffer ) {
 			if ( scope.dataAvailable && scope.objAsArrayBuffer ) {
 
 
 				scope.worker.postMessage({
 				scope.worker.postMessage({
@@ -337,17 +344,24 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 		this.mtlLoader.setPath( this.pathTexture );
 		this.mtlLoader.setPath( this.pathTexture );
 		if ( this.dataAvailable ) {
 		if ( this.dataAvailable ) {
 
 
-			processLoadedMaterials( ( this.mtlAsString != null ) ? this.mtlLoader.parse( this.mtlAsString ) : null );
+			processLoadedMaterials( Boolean( this.mtlAsString ) ? this.mtlLoader.parse( this.mtlAsString ) : null );
 
 
 		} else {
 		} else {
 
 
-			if ( this.fileMtl == null ) {
+			if ( Boolean( this.fileMtl ) ) {
 
 
-				processLoadedMaterials();
+				var onError = function ( event ) {
+					output = 'Error occurred while downloading "' + scope.fileMtl + '"';
+					console.error( output + ': ' + event );
+					scope._announceProgress( output );
+					scope._finalize( 'error' );
+				};
+
+				this.mtlLoader.load( this.fileMtl, processLoadedMaterials, undefined, onError );
 
 
 			} else {
 			} else {
 
 
-				this.mtlLoader.load( this.fileMtl, processLoadedMaterials );
+				processLoadedMaterials();
 
 
 			}
 			}
 
 
@@ -360,9 +374,10 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 		switch ( payload.cmd ) {
 		switch ( payload.cmd ) {
 			case 'objData':
 			case 'objData':
 
 
-				this.counter ++;
-				var bufferGeometry = new THREE.BufferGeometry();
+				this.counter++;
+				var meshName = payload.meshName;
 
 
+				var bufferGeometry = new THREE.BufferGeometry();
 				bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( payload.vertices ), 3 ) );
 				bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( payload.vertices ), 3 ) );
 				if ( payload.normals !== null ) {
 				if ( payload.normals !== null ) {
 
 
@@ -386,7 +401,8 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 				var createMultiMaterial = payload.multiMaterial;
 				var createMultiMaterial = payload.multiMaterial;
 				var multiMaterials = [];
 				var multiMaterials = [];
 
 
-				for ( var key in materialDescriptions ) {
+				var key;
+				for ( key in materialDescriptions ) {
 
 
 					materialDescription = materialDescriptions[ key ];
 					materialDescription = materialDescriptions[ key ];
 					material = this.materials[ materialDescription.name ];
 					material = this.materials[ materialDescription.name ];
@@ -421,7 +437,7 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 					material = multiMaterials;
 					material = multiMaterials;
 					var materialGroups = payload.materialGroups;
 					var materialGroups = payload.materialGroups;
 					var materialGroup;
 					var materialGroup;
-					for ( var key in materialGroups ) {
+					for ( key in materialGroups ) {
 
 
 						materialGroup = materialGroups[ key ];
 						materialGroup = materialGroups[ key ];
 						bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
 						bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
@@ -429,41 +445,68 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 					}
 					}
 
 
 				}
 				}
-				if ( this.callbacks.meshLoaded !== null ) {
 
 
-					var materialOverride = this.callbacks.meshLoaded( payload.meshName, material );
-					if ( materialOverride != null ) material = materialOverride;
+				var callbackMeshLoaded;
+				var callbackMeshLoadedResult;
+				var disregardMesh = false;
+				for ( var index in this.callbacks.meshLoaded ) {
+
+					callbackMeshLoaded = this.callbacks.meshLoaded[ index ];
+					callbackMeshLoadedResult = callbackMeshLoaded( meshName, bufferGeometry, material );
+
+					if ( Boolean( callbackMeshLoadedResult ) ) {
+
+						if ( callbackMeshLoadedResult.disregardMesh ) {
+
+							// if one callback disregards the mesh, then processing stops
+							disregardMesh = true;
+							break;
+
+						}
+						if ( callbackMeshLoadedResult.replaceBufferGeometry ) bufferGeometry = callbackMeshLoadedResult.bufferGeometry;
+						if ( callbackMeshLoadedResult.replaceMaterial ) material = callbackMeshLoadedResult.material;
+
+					}
 
 
 				}
 				}
-				var mesh = new THREE.Mesh( bufferGeometry, material );
-				mesh.name = payload.meshName;
-				if ( this.streamMeshes ) {
 
 
-					this.sceneGraphBaseNode.add( mesh );
+				if ( !disregardMesh ) {
+
+					var mesh = new THREE.Mesh( bufferGeometry, material );
+					mesh.name = meshName;
+
+					if ( this.streamMeshes ) {
+
+						this.sceneGraphBaseNode.add( mesh );
+
+					} else {
+
+						this.meshStore.push( mesh );
+
+					}
+					this._announceProgress( 'Adding mesh (' + this.counter + '):', meshName );
 
 
 				} else {
 				} else {
 
 
-					this.meshStore.push( mesh );
+					this._announceProgress( 'Removing mesh:', meshName );
 
 
 				}
 				}
-				var output = '(' + this.counter + '): ' + payload.meshName;
-				this._announceProgress( 'Adding mesh', output );
 				break;
 				break;
 
 
 			case 'complete':
 			case 'complete':
 
 
 				if ( ! this.streamMeshes ) {
 				if ( ! this.streamMeshes ) {
 
 
-					for ( var key in this.meshStore ) {
+					for ( var meshStoreKey in this.meshStore ) {
 
 
-						this.sceneGraphBaseNode.add( this.meshStore[ key ] );
+						if ( this.meshStore.hasOwnProperty( meshStoreKey ) ) this.sceneGraphBaseNode.add( this.meshStore[ meshStoreKey ] );
 
 
 					}
 					}
 
 
 				}
 				}
 
 
 				console.timeEnd( 'WWOBJLoader2' );
 				console.timeEnd( 'WWOBJLoader2' );
-				if ( payload.msg != null ) {
+				if ( Boolean( payload.msg ) ) {
 
 
 					this._announceProgress( payload.msg );
 					this._announceProgress( payload.msg );
 
 
@@ -488,7 +531,7 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 	};
 	};
 
 
 	WWOBJLoader2.prototype._terminate = function () {
 	WWOBJLoader2.prototype._terminate = function () {
-		if ( this.worker != null ) {
+		if ( Boolean( this.worker ) ) {
 
 
 			if ( this.running ) throw 'Unable to gracefully terminate worker as it is currently running!';
 			if ( this.running ) throw 'Unable to gracefully terminate worker as it is currently running!';
 
 
@@ -504,15 +547,26 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 
 
 	WWOBJLoader2.prototype._finalize = function ( reason, requestTerminate ) {
 	WWOBJLoader2.prototype._finalize = function ( reason, requestTerminate ) {
 		this.running = false;
 		this.running = false;
+		var index;
+		var callback;
+
 		if ( reason === 'complete' ) {
 		if ( reason === 'complete' ) {
 
 
-			if ( this.callbacks.completedLoading != null ) this.callbacks.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
-			if ( this.callbacks.director.completedLoading != null ) this.callbacks.director.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
+			for ( index in this.callbacks.completedLoading ) {
+
+				callback = this.callbacks.completedLoading[ index ];
+				callback( this.modelName, this.instanceNo, this.requestTerminate );
+
+			}
 
 
 		} else if ( reason === 'error' ) {
 		} else if ( reason === 'error' ) {
 
 
-			if ( this.callbacks.errorWhileLoading != null ) this.callbacks.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
-			if ( this.callbacks.director.errorWhileLoading != null ) this.callbacks.director.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
+			for ( index in this.callbacks.errorWhileLoading ) {
+
+				callback = this.callbacks.errorWhileLoading[ index ];
+				callback( this.modelName, this.instanceNo, this.requestTerminate );
+
+			}
 
 
 		}
 		}
 		this.validated = false;
 		this.validated = false;
@@ -525,32 +579,23 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 	};
 	};
 
 
 	WWOBJLoader2.prototype._announceProgress = function ( baseText, text ) {
 	WWOBJLoader2.prototype._announceProgress = function ( baseText, text ) {
-		var output = "";
-		if ( baseText !== null && baseText !== undefined ) {
+		var output = ( Boolean( baseText ) ) ? baseText: "";
+		output = ( Boolean( text ) ) ? output + " " + text : output;
 
 
-			output = baseText;
-
-		}
-		if ( text !== null && text !== undefined ) {
-
-			output = output + " " + text;
-
-		}
-		if ( this.callbacks.progress !== null ) {
+		var callbackProgress;
+		for ( var index in this.callbacks.progress ) {
 
 
-			this.callbacks.progress( output );
+			callbackProgress = this.callbacks.progress[ index ];
+			callbackProgress( output );
 
 
 		}
 		}
-		if ( this.debug ) {
 
 
-			console.log( output );
-
-		}
+		if ( this.debug ) console.log( output );
 	};
 	};
 
 
 	WWOBJLoader2.prototype._buildWebWorkerCode = function ( existingWorkerCode ) {
 	WWOBJLoader2.prototype._buildWebWorkerCode = function ( existingWorkerCode ) {
-		if ( existingWorkerCode != null ) this.workerCode = existingWorkerCode;
-		if ( this.workerCode == null ) {
+		if ( Boolean( existingWorkerCode ) ) this.workerCode = existingWorkerCode;
+		if ( ! Boolean( this.workerCode ) ) {
 
 
 			console.time( 'buildWebWorkerCode' );
 			console.time( 'buildWebWorkerCode' );
 			var wwDef = (function () {
 			var wwDef = (function () {
@@ -645,11 +690,11 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 				}
 				}
 
 
 				WWMeshCreator.prototype.setMaterials = function ( materials ) {
 				WWMeshCreator.prototype.setMaterials = function ( materials ) {
-					this.materials = ( materials == null ) ? ( this.materials == null ? { materials: [] } : this.materials ) : materials;
+					this.materials = Boolean( materials ) ? materials : ( Boolean( this.materials ) ? this.materials : { materials: [] } );
 				};
 				};
 
 
 				WWMeshCreator.prototype.setDebug = function ( debug ) {
 				WWMeshCreator.prototype.setDebug = function ( debug ) {
-					this.debug = ( debug == null ) ? this.debug : debug;
+					this.debug = Boolean( debug ) ? debug : this.debug;
 				};
 				};
 
 
 				WWMeshCreator.prototype.validate = function () {
 				WWMeshCreator.prototype.validate = function () {
@@ -700,6 +745,7 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 					var uvOffset = 0;
 					var uvOffset = 0;
 
 
 					for ( var oodIndex in rawObjectDescriptions ) {
 					for ( var oodIndex in rawObjectDescriptions ) {
+						if ( ! rawObjectDescriptions.hasOwnProperty( oodIndex ) ) continue;
 						rawObjectDescription = rawObjectDescriptions[ oodIndex ];
 						rawObjectDescription = rawObjectDescriptions[ oodIndex ];
 
 
 						materialDescription = { name: rawObjectDescription.materialName, flat: false, default: false };
 						materialDescription = { name: rawObjectDescription.materialName, flat: false, default: false };
@@ -898,69 +944,228 @@ THREE.OBJLoader2.WWOBJLoader2 = (function () {
 })();
 })();
 
 
 /**
 /**
- * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ from given ArrayBuffer and MTL from given String
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ from given ArrayBuffer and MTL from given String.
  *
  *
  * @param {string} modelName Overall name of the model
  * @param {string} modelName Overall name of the model
  * @param {Uint8Array} objAsArrayBuffer OBJ file content as ArrayBuffer
  * @param {Uint8Array} objAsArrayBuffer OBJ file content as ArrayBuffer
  * @param {string} pathTexture Path to texture files
  * @param {string} pathTexture Path to texture files
  * @param {string} mtlAsString MTL file content as string
  * @param {string} mtlAsString MTL file content as string
- * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
- * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
- * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
  *
  *
  * @returns {{modelName: string, dataAvailable: boolean, objAsArrayBuffer: null, pathTexture: null, mtlAsString: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
  * @returns {{modelName: string, dataAvailable: boolean, objAsArrayBuffer: null, pathTexture: null, mtlAsString: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
  * @constructor
  * @constructor
  */
  */
-THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer = function ( modelName, objAsArrayBuffer, pathTexture, mtlAsString, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
-
-	var data = {
-		modelName: ( modelName == null ) ? 'none' : modelName,
+THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer = function ( modelName, objAsArrayBuffer, pathTexture, mtlAsString ) {
+	return {
+
+		/**
+		 * {@link THREE.Object3D} where meshes will be attached.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
+		 *
+		 * @param {THREE.Object3D} sceneGraphBaseNode Scene graph object
+		 */
+		setSceneGraphBaseNode: function ( sceneGraphBaseNode ) {
+			this.sceneGraphBaseNode = Boolean( sceneGraphBaseNode ) ? sceneGraphBaseNode : null;
+		},
+
+		/**
+		 * Singles meshes are directly integrated into scene when loaded or later.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
+		 *
+		 * @param {boolean} streamMeshes=true Default is true
+		 */
+		setStreamMeshes: function ( streamMeshes ) {
+			this.streamMeshes = ( streamMeshes === null || streamMeshes === undefined ) ? true : streamMeshes;
+		},
+
+		/**
+		 * Request termination of web worker and free local resources after execution.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
+		 *
+		 * @param {boolean} requestTerminate=false Default is false
+		 */
+		setRequestTerminate: function ( requestTerminate ) {
+			this.requestTerminate = Boolean( requestTerminate );
+		},
+
+		/**
+		 * Returns all callbacks as {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
+		 *
+		 * @returns {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
+		 */
+		getCallbacks: function () {
+			return this.callbacks;
+		},
+		modelName: Boolean( modelName ) ? modelName : 'none',
 		dataAvailable: true,
 		dataAvailable: true,
-		objAsArrayBuffer: ( objAsArrayBuffer == null ) ? null : objAsArrayBuffer,
-		pathTexture: ( pathTexture == null ) ? null : pathTexture,
-		mtlAsString: ( mtlAsString == null ) ? null : mtlAsString,
-		sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
-		streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
-		requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
+		objAsArrayBuffer: Boolean( objAsArrayBuffer ) ? objAsArrayBuffer : null,
+		pathTexture: Boolean( pathTexture ) ? pathTexture : null,
+		mtlAsString: Boolean( mtlAsString ) ? mtlAsString : null,
+		sceneGraphBaseNode: null,
+		streamMeshes: true,
+		requestTerminate: false,
+		callbacks: new THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks()
 	};
 	};
-
-	return data;
 };
 };
 
 
 /**
 /**
- * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ and MTL from files
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ and MTL from files.
  *
  *
  * @param {string} modelName Overall name of the model
  * @param {string} modelName Overall name of the model
  * @param {string} pathObj Path to OBJ file
  * @param {string} pathObj Path to OBJ file
  * @param {string} fileObj OBJ file name
  * @param {string} fileObj OBJ file name
  * @param {string} pathTexture Path to texture files
  * @param {string} pathTexture Path to texture files
  * @param {string} fileMtl MTL file name
  * @param {string} fileMtl MTL file name
- * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
- * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
- * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
  *
  *
  * @returns {{modelName: string, dataAvailable: boolean, pathObj: null, fileObj: null, pathTexture: null, fileMtl: null, sceneGraphBaseNode: null, streamMeshes: boolean,  requestTerminate: boolean}}
  * @returns {{modelName: string, dataAvailable: boolean, pathObj: null, fileObj: null, pathTexture: null, fileMtl: null, sceneGraphBaseNode: null, streamMeshes: boolean,  requestTerminate: boolean}}
  * @constructor
  * @constructor
  */
  */
-THREE.OBJLoader2.WWOBJLoader2.PrepDataFile = function ( modelName, pathObj, fileObj, pathTexture, fileMtl, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
-
-	var data = {
-		modelName: ( modelName == null ) ? 'none' : modelName,
+THREE.OBJLoader2.WWOBJLoader2.PrepDataFile = function ( modelName, pathObj, fileObj, pathTexture, fileMtl ) {
+	return {
+
+		/**
+		 * {@link THREE.Object3D} where meshes will be attached.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
+		 *
+		 * @param {THREE.Object3D} sceneGraphBaseNode Scene graph object
+		 */
+		setSceneGraphBaseNode: function ( sceneGraphBaseNode ) {
+			this.sceneGraphBaseNode = Boolean( sceneGraphBaseNode ) ? sceneGraphBaseNode : null;
+		},
+
+		/**
+		 * Singles meshes are directly integrated into scene when loaded or later.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
+		 *
+		 * @param {boolean} streamMeshes=true Default is true
+		 */
+		setStreamMeshes: function ( streamMeshes ) {
+			this.streamMeshes = ( streamMeshes === null || streamMeshes === undefined ) ? true : streamMeshes;
+		},
+
+		/**
+		 * Request termination of web worker and free local resources after execution.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
+		 *
+		 * @param {boolean} requestTerminate=false Default is false
+		 */
+		setRequestTerminate: function ( requestTerminate ) {
+			this.requestTerminate = Boolean( requestTerminate );
+		},
+
+		/**
+		 * Returns all callbacks as {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
+		 *
+		 * @returns {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
+		 */
+		getCallbacks: function () {
+			return this.callbacks;
+		},
+		modelName: Boolean( modelName ) ? modelName : 'none',
 		dataAvailable: false,
 		dataAvailable: false,
-		pathObj: ( pathObj == null ) ? null : pathObj,
-		fileObj: ( fileObj == null ) ? null : fileObj,
-		pathTexture: ( pathTexture == null ) ? null : pathTexture,
-		fileMtl: ( fileMtl == null ) ? null : fileMtl,
-		sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
-		streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
-		requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
+		pathObj: Boolean( pathObj ) ? pathObj : null,
+		fileObj: Boolean( fileObj ) ? fileObj : null,
+		pathTexture: Boolean( pathTexture ) ? pathTexture : null,
+		fileMtl: Boolean( fileMtl ) ? fileMtl : null,
+		sceneGraphBaseNode: null,
+		streamMeshes: true,
+		requestTerminate: false,
+		callbacks: new THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks()
 	};
 	};
+};
 
 
-	return data;
-};
+/**
+ * Callbacks utilized by functions working with {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
+ *
+ * @returns {{registerCallbackProgress: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackProgress, registerCallbackCompletedLoading: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackCompletedLoading, registerCallbackMaterialsLoaded: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackMaterialsLoaded, registerCallbackMeshLoaded: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackMeshLoaded, registerCallbackErrorWhileLoading: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackErrorWhileLoading, progress: null, completedLoading: null, errorWhileLoading: null, materialsLoaded: null, meshLoaded: null}}
+ * @constructor
+ */
+THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks = function () {
+	return {
+		/**
+		 * Register callback function that is invoked by internal function "_announceProgress" to print feedback.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
+		 *
+		 * @param {callback} callbackProgress Callback function for described functionality
+		 */
+		registerCallbackProgress: function ( callbackProgress ) {
+			if ( Boolean( callbackProgress ) ) this.progress = callbackProgress;
+		},
+
+		/**
+		 * Register callback function that is called once loading of the complete model is completed.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
+		 *
+		 * @param {callback} callbackCompletedLoading Callback function for described functionality
+		 */
+		registerCallbackCompletedLoading: function ( callbackCompletedLoading ) {
+			if ( Boolean( callbackCompletedLoading ) ) this.completedLoading = callbackCompletedLoading;
+		},
+
+		/**
+		 * Register callback function that is called once materials have been loaded. It allows to alter and return materials.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
+		 *
+		 * @param {callback} callbackMaterialsLoaded Callback function for described functionality
+		 */
+		registerCallbackMaterialsLoaded: function ( callbackMaterialsLoaded ) {
+			if ( Boolean( callbackMaterialsLoaded ) ) this.materialsLoaded = callbackMaterialsLoaded;
+		},
+
+		/**
+		 * Register callback function that is called every time a mesh was loaded.
+		 * Use {@link THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
+		 *
+		 * @param {callback} callbackMeshLoaded Callback function for described functionality
+		 */
+		registerCallbackMeshLoaded: function ( callbackMeshLoaded ) {
+			if ( Boolean( callbackMeshLoaded ) ) this.meshLoaded = callbackMeshLoaded;
+		},
+
+		/**
+		 * Report if an error prevented loading.
+		 * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
+		 *
+		 * @param {callback} callbackErrorWhileLoading Callback function for described functionality
+		 */
+		registerCallbackErrorWhileLoading: function ( callbackErrorWhileLoading ) {
+			if ( Boolean( callbackErrorWhileLoading ) ) this.errorWhileLoading = callbackErrorWhileLoading;
+		},
+
+		progress: null,
+		completedLoading: null,
+		errorWhileLoading: null,
+		materialsLoaded: null,
+		meshLoaded: null
+	};
+};
+
+
+/**
+ * Object to return by {@link THREE.OBJLoader2.WWOBJLoader2}.callbacks.meshLoaded. Used to adjust bufferGeometry or material or prevent complete loading of mesh
+ *
+ * @param {boolean} disregardMesh=false Tell WWOBJLoader2 to completely disregard this mesh
+ * @param {THREE.BufferGeometry} bufferGeometry The {@link THREE.BufferGeometry} to be used
+ * @param {THREE.Material} material The {@link THREE.Material} to be used
+ *
+ * @returns {{ disregardMesh: boolean, replaceBufferGeometry: boolean, bufferGeometry: THREE.BufferGeometry, replaceMaterial: boolean, material: THREE.Material}}
+ * @constructor
+ */
+THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride = function ( disregardMesh, bufferGeometry, material ) {
+	return {
+		disregardMesh: Boolean( disregardMesh ),
+		replaceBufferGeometry: Boolean( bufferGeometry ),
+		bufferGeometry: Boolean( bufferGeometry ) ? bufferGeometry : null,
+		replaceMaterial: Boolean( material ),
+		material: Boolean( material ) ? material : null
+	};
+};
+
 /**
 /**
  * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
  * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
- * Use:
+ * Workflow:
  *   prepareWorkers
  *   prepareWorkers
  *   enqueueForRun
  *   enqueueForRun
  *   processQueue
  *   processQueue
@@ -980,7 +1185,7 @@ THREE.OBJLoader2.WWOBJLoader2Director = (function () {
 
 
 		this.workerDescription = {
 		this.workerDescription = {
 			prototypeDef: THREE.OBJLoader2.WWOBJLoader2.prototype,
 			prototypeDef: THREE.OBJLoader2.WWOBJLoader2.prototype,
-			callbacks: {},
+			globalCallbacks: {},
 			webWorkers: [],
 			webWorkers: [],
 			codeBuffer: null
 			codeBuffer: null
 		};
 		};
@@ -1022,22 +1227,12 @@ THREE.OBJLoader2.WWOBJLoader2Director = (function () {
 	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
 	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 *
 	 *
-	 * @param {callback[]} callbacks Register callbacks for all web workers:
-	 * 		{ progress: null, completedLoading: null, errorWhileLoading: null, materialsLoaded: null, meshLoaded: null }
+	 * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks  Register global callbacks used by all web workers
 	 * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
 	 * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
 	 * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
 	 * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
 	 */
 	 */
-	WWOBJLoader2Director.prototype.prepareWorkers = function ( callbacks, maxQueueSize, maxWebWorkers ) {
-		if ( callbacks != null ) {
-
-			for ( var key in callbacks ) {
-
-				if ( callbacks.hasOwnProperty( key ) ) this.workerDescription.callbacks[ key ] = callbacks[ key ];
-
-			}
-
-		}
-
+	WWOBJLoader2Director.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
+		if ( Boolean( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
 		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
 		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
 		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
 		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
 		this.objectsCompleted = 0;
 		this.objectsCompleted = 0;
@@ -1068,10 +1263,10 @@ THREE.OBJLoader2.WWOBJLoader2Director = (function () {
 	};
 	};
 
 
 	/**
 	/**
-	 * Store run instructions in internal instructionQueue
+	 * Store run instructions in internal instructionQueue.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 *
 	 *
-	 * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
+	 * @param {Object} runParams Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
 	 */
 	 */
 	WWOBJLoader2Director.prototype.enqueueForRun = function ( runParams ) {
 	WWOBJLoader2Director.prototype.enqueueForRun = function ( runParams ) {
 		if ( this.instructionQueue.length < this.maxQueueSize ) {
 		if ( this.instructionQueue.length < this.maxQueueSize ) {
@@ -1080,76 +1275,99 @@ THREE.OBJLoader2.WWOBJLoader2Director = (function () {
 	};
 	};
 
 
 	/**
 	/**
-	 * Process the instructionQueue until it is depleted
+	 * Process the instructionQueue until it is depleted.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 */
 	 */
 	WWOBJLoader2Director.prototype.processQueue = function () {
 	WWOBJLoader2Director.prototype.processQueue = function () {
 		if ( this.instructionQueue.length === 0 ) return;
 		if ( this.instructionQueue.length === 0 ) return;
 
 
-		var webWorker;
-		var runParams;
 		var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
 		var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
 		for ( var i = 0; i < length; i++ ) {
 		for ( var i = 0; i < length; i++ ) {
 
 
-			webWorker = this.workerDescription.webWorkers[ i ];
-			runParams = this.instructionQueue[ 0 ];
-			webWorker.prepareRun( runParams );
-			webWorker.run();
+			this._kickWebWorkerRun( this.workerDescription.webWorkers[ i ], this.instructionQueue[ 0 ] );
 			this.instructionQueue.shift();
 			this.instructionQueue.shift();
 
 
 		}
 		}
 	};
 	};
 
 
-	WWOBJLoader2Director.prototype._buildWebWorker = function () {
-		var webWorker = Object.create( this.workerDescription.prototypeDef );
-		webWorker._init();
-		if ( this.crossOrigin != null )	webWorker.setCrossOrigin( this.crossOrigin );
-
-		// Ensure code string is built once and then it is just passed on to every new instance
-		if ( this.workerDescription.codeBuffer == null ) {
+	WWOBJLoader2Director.prototype._kickWebWorkerRun = function( worker, runParams ) {
+		worker.clearAllCallbacks();
+		var key;
+		var globalCallbacks = this.workerDescription.globalCallbacks;
+		var workerCallbacks = worker.callbacks;
+		var selectedGlobalCallback;
+		for ( key in globalCallbacks ) {
 
 
-			this.workerDescription.codeBuffer = webWorker._buildWebWorkerCode();
+			if ( workerCallbacks.hasOwnProperty( key ) && globalCallbacks.hasOwnProperty( key ) ) {
 
 
-		} else {
+				selectedGlobalCallback = globalCallbacks[ key ];
+				if ( Boolean( selectedGlobalCallback ) ) workerCallbacks[ key ].push( selectedGlobalCallback );
 
 
-			webWorker._buildWebWorkerCode( this.workerDescription.codeBuffer );
+			}
 
 
 		}
 		}
-		for ( var key in this.workerDescription.callbacks ) {
+		// register per object callbacks
+		var runCallbacks = runParams.callbacks;
+		if ( Boolean( runCallbacks ) ) {
 
 
-			if ( webWorker.callbacks.hasOwnProperty( key ) && this.workerDescription.callbacks.hasOwnProperty( key ) ) {
+			for ( key in runCallbacks ) {
 
 
-				webWorker.callbacks[ key ] = this.workerDescription.callbacks[ key ];
+				if ( workerCallbacks.hasOwnProperty( key ) && runCallbacks.hasOwnProperty( key ) && Boolean( runCallbacks[ key ] ) ) {
+
+					workerCallbacks[ key ].push( runCallbacks[ key ] );
+
+				}
 
 
 			}
 			}
 
 
 		}
 		}
+
 		var scope = this;
 		var scope = this;
-		var managerCompletedLoading = function ( modelName, instanceNo, requestTerminate ) {
+		var directorCompletedLoading = function ( modelName, instanceNo, requestTerminate ) {
 			scope.objectsCompleted++;
 			scope.objectsCompleted++;
 			if ( ! requestTerminate ) {
 			if ( ! requestTerminate ) {
 
 
-				var rekick = scope.workerDescription.webWorkers[ instanceNo ];
+				var worker = scope.workerDescription.webWorkers[ instanceNo ];
 				var runParams = scope.instructionQueue[ 0 ];
 				var runParams = scope.instructionQueue[ 0 ];
-				if ( runParams != null ) {
+				if ( Boolean( runParams ) ) {
 
 
-					rekick.prepareRun( runParams );
-					rekick.run();
+					console.log( '\nAssigning next item from queue to worker (queue length: ' + scope.instructionQueue.length + ')\n\n' );
+					scope._kickWebWorkerRun( worker, runParams );
 					scope.instructionQueue.shift();
 					scope.instructionQueue.shift();
 
 
 				}
 				}
 
 
 			}
 			}
 		};
 		};
+		worker.registerCallbackCompletedLoading( directorCompletedLoading );
+
+		worker.prepareRun( runParams );
+		worker.run();
+	};
+
+	WWOBJLoader2Director.prototype._buildWebWorker = function () {
+		var webWorker = Object.create( this.workerDescription.prototypeDef );
+		webWorker._init();
+		if ( Boolean( this.crossOrigin ) ) webWorker.setCrossOrigin( this.crossOrigin );
+
+		// Ensure code string is built once and then it is just passed on to every new instance
+		if ( Boolean( this.workerDescription.codeBuffer ) ) {
+
+			webWorker._buildWebWorkerCode( this.workerDescription.codeBuffer );
+
+		} else {
+
+			this.workerDescription.codeBuffer = webWorker._buildWebWorkerCode();
+
+		}
 
 
-		webWorker.callbacks.director[ 'completedLoading' ] = managerCompletedLoading;
 		webWorker.instanceNo = this.workerDescription.webWorkers.length;
 		webWorker.instanceNo = this.workerDescription.webWorkers.length;
 		this.workerDescription.webWorkers.push( webWorker );
 		this.workerDescription.webWorkers.push( webWorker );
 		return webWorker;
 		return webWorker;
 	};
 	};
 
 
 	/**
 	/**
-	 * Terminate all workers
+	 * Terminate all workers.
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
 	 */
 	 */
 	WWOBJLoader2Director.prototype.deregister = function () {
 	WWOBJLoader2Director.prototype.deregister = function () {
@@ -1160,7 +1378,7 @@ THREE.OBJLoader2.WWOBJLoader2Director = (function () {
 			webWorker.setRequestTerminate( true );
 			webWorker.setRequestTerminate( true );
 
 
 		}
 		}
-		this.workerDescription.callbacks = {};
+		this.workerDescription.globalCallbacks = {};
 		this.workerDescription.webWorkers = [];
 		this.workerDescription.webWorkers = [];
 		this.workerDescription.codeBuffer = null;
 		this.workerDescription.codeBuffer = null;
 	};
 	};

+ 3 - 2
examples/webgl_loader_obj2.html

@@ -264,9 +264,10 @@
 
 
 					if ( object3d.material instanceof THREE.MultiMaterial ) {
 					if ( object3d.material instanceof THREE.MultiMaterial ) {
 
 
-						for ( var matName in object3d.material.materials ) {
+						var materials = object3d.material.materials;
+						for ( var name in materials ) {
 
 
-							this.traversalFunction( object3d.material.materials[ matName ] );
+							if ( materials.hasOwnProperty( name ) )	this.traversalFunction( materials[ name ] );
 
 
 						}
 						}
 
 

+ 19 - 13
examples/webgl_loader_obj2_ww.html

@@ -181,26 +181,26 @@
 						console.log( 'Progress: ' + content );
 						console.log( 'Progress: ' + content );
 					};
 					};
 					var materialsLoaded = function ( materials ) {
 					var materialsLoaded = function ( materials ) {
-						var count = 0;
-						console.log( 'The following materials have been loaded:' );
-						for ( var mat in materials ) {
-							count++;
-						}
+						var count = Boolean( materials ) ? materials.length : 0;
 						console.log( 'Loaded #' + count + ' materials.' );
 						console.log( 'Loaded #' + count + ' materials.' );
 					};
 					};
+					var meshLoaded = function ( name, bufferGeometry, material ) {
+						console.log( 'Loaded mesh: ' + name + ' Material name: ' + material.name );
+					};
 					var completedLoading = function () {
 					var completedLoading = function () {
 						console.log( 'Loading complete!' );
 						console.log( 'Loading complete!' );
 					};
 					};
 					this.wwObjLoader2.registerCallbackProgress( reportProgress );
 					this.wwObjLoader2.registerCallbackProgress( reportProgress );
 					this.wwObjLoader2.registerCallbackCompletedLoading( completedLoading );
 					this.wwObjLoader2.registerCallbackCompletedLoading( completedLoading );
 					this.wwObjLoader2.registerCallbackMaterialsLoaded( materialsLoaded );
 					this.wwObjLoader2.registerCallbackMaterialsLoaded( materialsLoaded );
+					this.wwObjLoader2.registerCallbackMeshLoaded( meshLoaded );
 
 
 					return true;
 					return true;
 				};
 				};
 
 
 				WWOBJLoader2Example.prototype.loadFiles = function ( prepData ) {
 				WWOBJLoader2Example.prototype.loadFiles = function ( prepData ) {
-					prepData.sceneGraphBaseNode = this.pivot;
-					prepData.streamMeshes = this.streamMeshes;
+					prepData.setSceneGraphBaseNode( this.pivot );
+					prepData.setStreamMeshes( this.streamMeshes );
 					this.wwObjLoader2.prepareRun( prepData );
 					this.wwObjLoader2.prepareRun( prepData );
 					this.wwObjLoader2.run();
 					this.wwObjLoader2.run();
 				};
 				};
@@ -222,7 +222,7 @@
 
 
 					}
 					}
 
 
-					if ( fileObj == null ) {
+					if ( ! Boolean( fileObj ) ) {
 						alert( 'Unable to load OBJ file from given files.' );
 						alert( 'Unable to load OBJ file from given files.' );
 					}
 					}
 
 
@@ -261,8 +261,10 @@
 
 
 				WWOBJLoader2Example.prototype.loadFilesUser = function ( objDef ) {
 				WWOBJLoader2Example.prototype.loadFilesUser = function ( objDef ) {
 					var prepData = new THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer(
 					var prepData = new THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer(
-						objDef.name, objDef.objAsArrayBuffer, objDef.pathTexture, objDef.mtlAsString, this.pivot, this.streamMeshes
+						objDef.name, objDef.objAsArrayBuffer, objDef.pathTexture, objDef.mtlAsString
 					);
 					);
+					prepData.setSceneGraphBaseNode( this.pivot );
+					prepData.setStreamMeshes( this.streamMeshes );
 					this.wwObjLoader2.prepareRun( prepData );
 					this.wwObjLoader2.prepareRun( prepData );
 					this.wwObjLoader2.run();
 					this.wwObjLoader2.run();
 				};
 				};
@@ -340,9 +342,10 @@
 
 
 					if ( object3d.material instanceof THREE.MultiMaterial ) {
 					if ( object3d.material instanceof THREE.MultiMaterial ) {
 
 
-						for ( var matName in object3d.material.materials ) {
+						var materials = object3d.material.materials;
+						for ( var name in materials ) {
 
 
-							this.traversalFunction( object3d.material.materials[ matName ] );
+							if ( materials.hasOwnProperty( name ) )	this.traversalFunction( materials[ name ] );
 
 
 						}
 						}
 
 
@@ -372,8 +375,11 @@
 							var mat = object3d.material;
 							var mat = object3d.material;
 							if ( mat.hasOwnProperty( 'materials' ) ) {
 							if ( mat.hasOwnProperty( 'materials' ) ) {
 
 
-								for ( var mmat in mat.materials ) {
-									mat.materials[ mmat ].dispose();
+								var materials = mat.materials;
+								for ( var name in materials ) {
+
+									if ( materials.hasOwnProperty( name ) ) materials[ name ].dispose();
+
 								}
 								}
 							}
 							}
 						}
 						}

+ 45 - 35
examples/webgl_loader_obj2_ww_parallels.html

@@ -58,9 +58,9 @@
 			#dat {
 			#dat {
 				user-select: none;
 				user-select: none;
 				position: absolute;
 				position: absolute;
-				left: 0px;
-				top: 0px;
-				z-Index: 2;
+				left: 0;
+				top: 0;
+				z-Index: 200;
 			}
 			}
 		</style>
 		</style>
 	</head>
 	</head>
@@ -195,8 +195,12 @@
 					var scope = this;
 					var scope = this;
 					scope.wwDirector.objectsCompleted = 0;
 					scope.wwDirector.objectsCompleted = 0;
 					scope.feedbackArray = new Array( maxWebWorkers );
 					scope.feedbackArray = new Array( maxWebWorkers );
-					for ( var i = 0; i < maxWebWorkers; i++ ) {
+
+					var i;
+					for ( i = 0; i < maxWebWorkers; i++ ) {
+
 						scope.feedbackArray[ i ] = 'Worker #' + i + ': Awaiting feedback';
 						scope.feedbackArray[ i ] = 'Worker #' + i + ': Awaiting feedback';
+
 					}
 					}
 					scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
 					scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
 
 
@@ -206,27 +210,30 @@
 						scope.feedbackArray[ instanceNo ] = msg;
 						scope.feedbackArray[ instanceNo ] = msg;
 						scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
 						scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
 					};
 					};
-					var callbackMeshLoaded = function ( meshName, material ) {
-						var replacedMaterial = null;
 
 
-						if ( material != null && material.name === 'defaultMaterial' || meshName === 'Mesh_Mesh_head_geo.001' ) {
-							replacedMaterial = material;
-							replacedMaterial.color = new THREE.Color( Math.random(), Math.random(), Math.random() );
+					var callbackMeshLoaded = function ( name, bufferGeometry, material ) {
+						var materialOverride;
+
+						if ( Boolean( material ) && material.name === 'defaultMaterial' || name === 'Mesh_Mesh_head_geo.001' ) {
+
+							materialOverride = material;
+							materialOverride.color = new THREE.Color( Math.random(), Math.random(), Math.random() );
+
 						}
 						}
 
 
-						return replacedMaterial;
+						return new THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride( false, undefined, materialOverride );
 					};
 					};
 
 
-					this.wwDirector.prepareWorkers(
-						{
-							completedLoading: callbackCompletedLoading,
-							meshLoaded: callbackMeshLoaded
-						},
-						maxQueueSize,
-						maxWebWorkers
-					);
+					var globalCallbacks = new THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks();
+					globalCallbacks.registerCallbackCompletedLoading( callbackCompletedLoading );
+					globalCallbacks.registerCallbackMeshLoaded( callbackMeshLoaded );
+					this.wwDirector.prepareWorkers( globalCallbacks, maxQueueSize, maxWebWorkers );
 					console.log( 'Configuring WWManager with queue size ' + this.wwDirector.getMaxQueueSize() + ' and ' + this.wwDirector.getMaxWebWorkers() + ' workers.' );
 					console.log( 'Configuring WWManager with queue size ' + this.wwDirector.getMaxQueueSize() + ' and ' + this.wwDirector.getMaxWebWorkers() + ' workers.' );
 
 
+					var callbackCompletedLoadingWalt = function () {
+						console.log( 'Callback check: WALT was loaded (#' + scope.wwDirector.objectsCompleted + ')' );
+					};
+
 					var models = [];
 					var models = [];
 					models.push( {
 					models.push( {
 						modelName: 'male02',
 						modelName: 'male02',
@@ -262,7 +269,7 @@
 						scale: 50.0
 						scale: 50.0
 					} );
 					} );
 					models.push( {
 					models.push( {
-						modelName:'WaltHead',
+						modelName: 'WaltHead',
 						dataAvailable: false,
 						dataAvailable: false,
 						pathObj: 'obj/walt/',
 						pathObj: 'obj/walt/',
 						fileObj: 'WaltHead.obj',
 						fileObj: 'WaltHead.obj',
@@ -276,9 +283,9 @@
 					var modelIndex = 0;
 					var modelIndex = 0;
 					var model;
 					var model;
 					var runParams;
 					var runParams;
-					for ( var i = 0; i < maxQueueSize; i++ ) {
+					for ( i = 0; i < maxQueueSize; i++ ) {
 
 
-						modelIndex = Math.floor( Math.random() * 5 );
+						modelIndex = Math.floor( Math.random() * models.length );
 						model = models[ modelIndex ];
 						model = models[ modelIndex ];
 
 
 						pivot = new THREE.Object3D();
 						pivot = new THREE.Object3D();
@@ -287,15 +294,21 @@
 							distributionBase + distributionMax * Math.random(),
 							distributionBase + distributionMax * Math.random(),
 							distributionBase + distributionMax * Math.random()
 							distributionBase + distributionMax * Math.random()
 						);
 						);
-						if ( model.scale != null ) pivot.scale.set( model.scale, model.scale, model.scale );
+						if ( Boolean( model.scale ) ) pivot.scale.set( model.scale, model.scale, model.scale );
 
 
 						this.scene.add( pivot );
 						this.scene.add( pivot );
 
 
 						model.sceneGraphBaseNode = pivot;
 						model.sceneGraphBaseNode = pivot;
 
 
 						runParams = new THREE.OBJLoader2.WWOBJLoader2.PrepDataFile(
 						runParams = new THREE.OBJLoader2.WWOBJLoader2.PrepDataFile(
-							model.modelName, model.pathObj, model.fileObj, model.pathTexture, model.fileMtl, model.sceneGraphBaseNode, streamMeshes
+							model.modelName, model.pathObj, model.fileObj, model.pathTexture, model.fileMtl
 						);
 						);
+						runParams.setSceneGraphBaseNode( model.sceneGraphBaseNode );
+						runParams.setStreamMeshes( streamMeshes );
+						if ( model.modelName === 'WaltHead' ) {
+							runParams.getCallbacks().registerCallbackCompletedLoading( callbackCompletedLoadingWalt );
+						}
+
 						this.wwDirector.enqueueForRun( runParams );
 						this.wwDirector.enqueueForRun( runParams );
 						this.allAssets.push( runParams );
 						this.allAssets.push( runParams );
 					}
 					}
@@ -308,32 +321,29 @@
 					var scope = this;
 					var scope = this;
 
 
 					for ( var asset in this.allAssets ) {
 					for ( var asset in this.allAssets ) {
-						ref = this.allAssets[asset];
+						ref = this.allAssets[ asset ];
 
 
 						var remover = function ( object3d ) {
 						var remover = function ( object3d ) {
 
 
-							if ( object3d === ref.sceneGraphBaseNode ) {
-								return;
-							}
+							if ( object3d === ref.sceneGraphBaseNode ) return;
 							console.log( 'Removing ' + object3d.name );
 							console.log( 'Removing ' + object3d.name );
 							scope.scene.remove( object3d );
 							scope.scene.remove( object3d );
 
 
-							if ( object3d.hasOwnProperty( 'geometry' ) ) {
-								object3d.geometry.dispose();
-							}
+							if ( object3d.hasOwnProperty( 'geometry' ) ) object3d.geometry.dispose();
 							if ( object3d.hasOwnProperty( 'material' ) ) {
 							if ( object3d.hasOwnProperty( 'material' ) ) {
 
 
 								var mat = object3d.material;
 								var mat = object3d.material;
 								if ( mat.hasOwnProperty( 'materials' ) ) {
 								if ( mat.hasOwnProperty( 'materials' ) ) {
 
 
-									for ( var mmat in mat.materials ) {
-										mat.materials[mmat].dispose();
+									var materials = mat.materials;
+									for ( var name in materials ) {
+
+										if ( materials.hasOwnProperty( name ) ) materials[ name ].dispose();
+
 									}
 									}
 								}
 								}
 							}
 							}
-							if ( object3d.hasOwnProperty( 'texture' ) ) {
-								object3d.texture.dispose();
-							}
+							if ( object3d.hasOwnProperty( 'texture' ) ) object3d.texture.dispose();
 						};
 						};
 						scope.scene.remove( ref.sceneGraphBaseNode );
 						scope.scene.remove( ref.sceneGraphBaseNode );
 						ref.sceneGraphBaseNode.traverse( remover );
 						ref.sceneGraphBaseNode.traverse( remover );