浏览代码

Merge pull request #16887 from kaisalmen/OBJLoader2_V300_Beta2

OBJLoader2 and OBJLoader2Parallel V3.0.0-beta2
Mr.doob 6 年之前
父节点
当前提交
4b18dbe78b

+ 0 - 2
examples/files.js

@@ -107,9 +107,7 @@ var files = {
 		"webgl_loader_obj",
 		"webgl_loader_obj_mtl",
 		"webgl_loader_obj2",
-		"webgl_loader_obj2_meshspray",
 		"webgl_loader_obj2_options",
-		"webgl_loader_obj2_run_director",
 		"webgl_loader_pcd",
 		"webgl_loader_pdb",
 		"webgl_loader_playcanvas",

+ 0 - 1711
examples/js/loaders/LoaderSupport.js

@@ -1,1711 +0,0 @@
-/**
-  * @author Kai Salmen / https://kaisalmen.de
-  * Development repository: https://github.com/kaisalmen/WWOBJLoader
-  */
-
-'use strict';
-
-if ( THREE.LoaderSupport === undefined ) { THREE.LoaderSupport = {} }
-
-/**
- * Validation functions.
- * @class
- */
-THREE.LoaderSupport.Validator = {
-	/**
-	 * If given input is null or undefined, false is returned otherwise true.
-	 *
-	 * @param input Can be anything
-	 * @returns {boolean}
-	 */
-	isValid: function( input ) {
-		return ( input !== null && input !== undefined );
-	},
-	/**
-	 * If given input is null or undefined, the defaultValue is returned otherwise the given input.
-	 *
-	 * @param input Can be anything
-	 * @param defaultValue Can be anything
-	 * @returns {*}
-	 */
-	verifyInput: function( input, defaultValue ) {
-		return ( input === null || input === undefined ) ? defaultValue : input;
-	}
-};
-
-
-/**
- * Callbacks utilized by loaders and builders.
- * @class
- */
-THREE.LoaderSupport.Callbacks = function () {
-	this.onProgress = null;
-	this.onReportError = null;
-	this.onMeshAlter = null;
-	this.onLoad = null;
-	this.onLoadMaterials = null;
-};
-
-THREE.LoaderSupport.Callbacks.prototype = {
-
-	constructor: THREE.LoaderSupport.Callbacks,
-
-	/**
-	 * Register callback function that is invoked by internal function "announceProgress" to print feedback.
-	 *
-	 * @param {callback} callbackOnProgress Callback function for described functionality
-	 */
-	setCallbackOnProgress: function ( callbackOnProgress ) {
-		this.onProgress = THREE.LoaderSupport.Validator.verifyInput( callbackOnProgress, this.onProgress );
-	},
-
-	/**
-	 * Register callback function that is invoked when an error is reported.
-	 *
-	 * @param {callback} callbackOnReportError Callback function for described functionality
-	 */
-	setCallbackOnReportError: function ( callbackOnReportError ) {
-		this.onReportError = THREE.LoaderSupport.Validator.verifyInput( callbackOnReportError, this.onReportError );
-	},
-
-	/**
-	 * Register callback function that is called every time a mesh was loaded.
-	 * Use {@link THREE.LoaderSupport.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
-	 *
-	 * @param {callback} callbackOnMeshAlter Callback function for described functionality
-	 */
-	setCallbackOnMeshAlter: function ( callbackOnMeshAlter ) {
-		this.onMeshAlter = THREE.LoaderSupport.Validator.verifyInput( callbackOnMeshAlter, this.onMeshAlter );
-	},
-
-	/**
-	 * Register callback function that is called once loading of the complete OBJ file is completed.
-	 *
-	 * @param {callback} callbackOnLoad Callback function for described functionality
-	 */
-	setCallbackOnLoad: function ( callbackOnLoad ) {
-		this.onLoad = THREE.LoaderSupport.Validator.verifyInput( callbackOnLoad, this.onLoad );
-	},
-
-	/**
-	 * Register callback function that is called when materials have been loaded.
-	 *
-	 * @param {callback} callbackOnLoadMaterials Callback function for described functionality
-	 */
-	setCallbackOnLoadMaterials: function ( callbackOnLoadMaterials ) {
-		this.onLoadMaterials = THREE.LoaderSupport.Validator.verifyInput( callbackOnLoadMaterials, this.onLoadMaterials );
-	}
-
-};
-
-
-/**
- * Object to return by callback onMeshAlter. Used to disregard a certain mesh or to return one to many meshes.
- * @class
- *
- * @param {boolean} disregardMesh=false Tell implementation to completely disregard this mesh
- * @param {boolean} disregardMesh=false Tell implementation that mesh(es) have been altered or added
- */
-THREE.LoaderSupport.LoadedMeshUserOverride = function( disregardMesh, alteredMesh ) {
-	this.disregardMesh = disregardMesh === true;
-	this.alteredMesh = alteredMesh === true;
-	this.meshes = [];
-};
-
-THREE.LoaderSupport.LoadedMeshUserOverride.prototype = {
-
-	constructor: THREE.LoaderSupport.LoadedMeshUserOverride,
-
-	/**
-	 * Add a mesh created within callback.
-	 *
-	 * @param {THREE.Mesh} mesh
-	 */
-	addMesh: function ( mesh ) {
-		this.meshes.push( mesh );
-		this.alteredMesh = true;
-	},
-
-	/**
-	 * Answers if mesh shall be disregarded completely.
-	 *
-	 * @returns {boolean}
-	 */
-	isDisregardMesh: function () {
-		return this.disregardMesh;
-	},
-
-	/**
-	 * Answers if new mesh(es) were created.
-	 *
-	 * @returns {boolean}
-	 */
-	providesAlteredMeshes: function () {
-		return this.alteredMesh;
-	}
-
-};
-
-
-/**
- * A resource description used by {@link THREE.LoaderSupport.PrepData} and others.
- * @class
- *
- * @param {string} url URL to the file
- * @param {string} extension The file extension (type)
- */
-THREE.LoaderSupport.ResourceDescriptor = function ( url, extension ) {
-	var urlParts = url.split( '/' );
-
-	this.path;
-	this.resourcePath;
-	this.name = url;
-	this.url = url;
-	if ( urlParts.length >= 2 ) {
-
-		this.path = THREE.LoaderSupport.Validator.verifyInput( urlParts.slice( 0, urlParts.length - 1).join( '/' ) + '/', this.path );
-		this.name = urlParts[ urlParts.length - 1 ];
-		this.url = url;
-
-	}
-	this.name = THREE.LoaderSupport.Validator.verifyInput( this.name, 'Unnamed_Resource' );
-	this.extension = THREE.LoaderSupport.Validator.verifyInput( extension, 'default' );
-	this.extension = this.extension.trim();
-	this.content = null;
-};
-
-THREE.LoaderSupport.ResourceDescriptor.prototype = {
-
-	constructor: THREE.LoaderSupport.ResourceDescriptor,
-
-	/**
-	 * Set the content of this resource
-	 *
-	 * @param {Object} content The file content as arraybuffer or text
-	 */
-	setContent: function ( content ) {
-		this.content = THREE.LoaderSupport.Validator.verifyInput( content, null );
-	},
-
-	/**
-	 * Allows to specify resourcePath for dependencies of specified resource.
-	 * @param {string} resourcePath
-	 */
-	setResourcePath: function ( resourcePath ) {
-		this.resourcePath = THREE.LoaderSupport.Validator.verifyInput( resourcePath, this.resourcePath );
-	}
-};
-
-
-/**
- * Configuration instructions to be used by run method.
- * @class
- */
-THREE.LoaderSupport.PrepData = function ( modelName ) {
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-	this.modelName = THREE.LoaderSupport.Validator.verifyInput( modelName, '' );
-	this.resources = [];
-	this.callbacks = new THREE.LoaderSupport.Callbacks();
-};
-
-THREE.LoaderSupport.PrepData.prototype = {
-
-	constructor: THREE.LoaderSupport.PrepData,
-
-	/**
-	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
-	 *
-	 * @param {boolean} enabled True or false.
-	 * @param {boolean} debug True or false.
-	 */
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-	},
-
-	/**
-	 * Returns all callbacks as {@link THREE.LoaderSupport.Callbacks}
-	 *
-	 * @returns {THREE.LoaderSupport.Callbacks}
-	 */
-	getCallbacks: function () {
-		return this.callbacks;
-	},
-
-	/**
-	 * Add a resource description.
-	 *
-	 * @param {THREE.LoaderSupport.ResourceDescriptor} Adds a {@link THREE.LoaderSupport.ResourceDescriptor}
-	 */
-	addResource: function ( resource ) {
-		this.resources.push( resource );
-	},
-
-	/**
-	 * Clones this object and returns it afterwards. Callbacks and resources are not cloned deep (references!).
-	 *
-	 * @returns {@link THREE.LoaderSupport.PrepData}
-	 */
-	clone: function () {
-		var clone = new THREE.LoaderSupport.PrepData( this.modelName );
-		clone.logging.enabled = this.logging.enabled;
-		clone.logging.debug = this.logging.debug;
-		clone.resources = this.resources;
-		clone.callbacks = this.callbacks;
-
-		var property, value;
-		for ( property in this ) {
-
-			value = this[ property ];
-			if ( ! clone.hasOwnProperty( property ) && typeof this[ property ] !== 'function' ) {
-
-				clone[ property ] = value;
-
-			}
-		}
-
-		return clone;
-	},
-
-	/**
-	 * Identify files or content of interest from an Array of {@link THREE.LoaderSupport.ResourceDescriptor}.
-	 *
-	 * @param {THREE.LoaderSupport.ResourceDescriptor[]} resources Array of {@link THREE.LoaderSupport.ResourceDescriptor}
-	 * @param Object fileDesc Object describing which resources are of interest (ext, type (string or UInt8Array) and ignore (boolean))
-	 * @returns {{}} Object with each "ext" and the corresponding {@link THREE.LoaderSupport.ResourceDescriptor}
-	 */
-	checkResourceDescriptorFiles: function ( resources, fileDesc ) {
-		var resource, triple, i, found;
-		var result = {};
-
-		for ( var index in resources ) {
-
-			resource = resources[ index ];
-			found = false;
-			if ( ! THREE.LoaderSupport.Validator.isValid( resource.name ) ) continue;
-			if ( THREE.LoaderSupport.Validator.isValid( resource.content ) ) {
-
-				for ( i = 0; i < fileDesc.length && !found; i++ ) {
-
-					triple = fileDesc[ i ];
-					if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) {
-
-						if ( triple.ignore ) {
-
-							found = true;
-
-						} else if ( triple.type === "ArrayBuffer" ) {
-
-							// fast-fail on bad type
-							if ( ! ( resource.content instanceof ArrayBuffer || resource.content instanceof Uint8Array ) ) throw 'Provided content is not of type ArrayBuffer! Aborting...';
-							result[ triple.ext ] = resource;
-							found = true;
-
-						} else if ( triple.type === "String" ) {
-
-							if ( ! ( typeof( resource.content ) === 'string' || resource.content instanceof String) ) throw 'Provided  content is not of type String! Aborting...';
-							result[ triple.ext ] = resource;
-							found = true;
-
-						}
-
-					}
-
-				}
-				if ( !found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url;
-
-			} else {
-
-				// fast-fail on bad type
-				if ( ! ( typeof( resource.name ) === 'string' || resource.name instanceof String ) ) throw 'Provided file is not properly defined! Aborting...';
-				for ( i = 0; i < fileDesc.length && !found; i++ ) {
-
-					triple = fileDesc[ i ];
-					if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) {
-
-						if ( ! triple.ignore ) result[ triple.ext ] = resource;
-						found = true;
-
-					}
-
-				}
-				if ( !found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url;
-
-			}
-		}
-
-		return result;
-	}
-};
-
-/**
- * Builds one or many THREE.Mesh from one raw set of Arraybuffers, materialGroup descriptions and further parameters.
- * Supports vertex, vertexColor, normal, uv and index buffers.
- * @class
- */
-THREE.LoaderSupport.MeshBuilder = function() {
-	this.validator = THREE.LoaderSupport.Validator;
-
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-
-	this.callbacks = new THREE.LoaderSupport.Callbacks();
-	this.materials = {};
-};
-THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION = '1.3.1';
-console.info( 'Using THREE.LoaderSupport.MeshBuilder version: ' + THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION );
-
-
-THREE.LoaderSupport.MeshBuilder.prototype = {
-
-	constructor: THREE.LoaderSupport.MeshBuilder,
-
-	/**
-	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
-	 *
-	 * @param {boolean} enabled True or false.
-	 * @param {boolean} debug True or false.
-	 */
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-	},
-
-	/**
-	 * Initializes the MeshBuilder (currently only default material initialisation).
-	 *
-	 */
-	init: function () {
-		var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
-		defaultMaterial.name = 'defaultMaterial';
-
-		var defaultVertexColorMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
-		defaultVertexColorMaterial.name = 'defaultVertexColorMaterial';
-		defaultVertexColorMaterial.vertexColors = THREE.VertexColors;
-
-		var defaultLineMaterial = new THREE.LineBasicMaterial();
-		defaultLineMaterial.name = 'defaultLineMaterial';
-
-		var defaultPointMaterial = new THREE.PointsMaterial( { size: 1 } );
-		defaultPointMaterial.name = 'defaultPointMaterial';
-
-		var runtimeMaterials = {};
-		runtimeMaterials[ defaultMaterial.name ] = defaultMaterial;
-		runtimeMaterials[ defaultVertexColorMaterial.name ] = defaultVertexColorMaterial;
-		runtimeMaterials[ defaultLineMaterial.name ] = defaultLineMaterial;
-		runtimeMaterials[ defaultPointMaterial.name ] = defaultPointMaterial;
-
-		this.updateMaterials(
-			{
-				cmd: 'materialData',
-				materials: {
-					materialCloneInstructions: null,
-					serializedMaterials: null,
-					runtimeMaterials: runtimeMaterials
-				}
-			}
-		);
-	},
-
-	/**
-	 * Set materials loaded by any supplier of an Array of {@link THREE.Material}.
-	 *
-	 * @param {THREE.Material[]} materials Array of {@link THREE.Material}
-	 */
-	setMaterials: function ( materials ) {
-		var payload = {
-			cmd: 'materialData',
-			materials: {
-				materialCloneInstructions: null,
-				serializedMaterials: null,
-				runtimeMaterials: this.validator.isValid( this.callbacks.onLoadMaterials ) ? this.callbacks.onLoadMaterials( materials ) : materials
-			}
-		};
-		this.updateMaterials( payload );
-	},
-
-	_setCallbacks: function ( callbacks ) {
-		if ( this.validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
-		if ( this.validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError );
-		if ( this.validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
-		if ( this.validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
-		if ( this.validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
-	},
-
-	/**
-	 * Delegates processing of the payload (mesh building or material update) to the corresponding functions (BW-compatibility).
-	 *
-	 * @param {Object} payload Raw Mesh or Material descriptions.
-	 * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} or null in case of material update
-	 */
-	processPayload: function ( payload ) {
-		if ( payload.cmd === 'meshData' ) {
-
-			return this.buildMeshes( payload );
-
-		} else if ( payload.cmd === 'materialData' ) {
-
-			this.updateMaterials( payload );
-			return null;
-
-		}
-	},
-
-	/**
-	 * Builds one or multiple meshes from the data described in the payload (buffers, params, material info).
-	 *
-	 * @param {Object} meshPayload Raw mesh description (buffers, params, materials) used to build one to many meshes.
-	 * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh}
-	 */
-	buildMeshes: function ( meshPayload ) {
-		var meshName = meshPayload.params.meshName;
-
-		var bufferGeometry = new THREE.BufferGeometry();
-		bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.vertices ), 3 ) );
-		if ( this.validator.isValid( meshPayload.buffers.indices ) ) {
-
-			bufferGeometry.setIndex( new THREE.BufferAttribute( new Uint32Array( meshPayload.buffers.indices ), 1 ));
-
-		}
-		var haveVertexColors = this.validator.isValid( meshPayload.buffers.colors );
-		if ( haveVertexColors ) {
-
-			bufferGeometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.colors ), 3 ) );
-
-		}
-		if ( this.validator.isValid( meshPayload.buffers.normals ) ) {
-
-			bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.normals ), 3 ) );
-
-		} else {
-
-			bufferGeometry.computeVertexNormals();
-
-		}
-		if ( this.validator.isValid( meshPayload.buffers.uvs ) ) {
-
-			bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.uvs ), 2 ) );
-
-		}
-
-		var material, materialName, key;
-		var materialNames = meshPayload.materials.materialNames;
-		var createMultiMaterial = meshPayload.materials.multiMaterial;
-		var multiMaterials = [];
-		for ( key in materialNames ) {
-
-			materialName = materialNames[ key ];
-			material = this.materials[ materialName ];
-			if ( createMultiMaterial ) multiMaterials.push( material );
-
-		}
-		if ( createMultiMaterial ) {
-
-			material = multiMaterials;
-			var materialGroups = meshPayload.materials.materialGroups;
-			var materialGroup;
-			for ( key in materialGroups ) {
-
-				materialGroup = materialGroups[ key ];
-				bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
-
-			}
-
-		}
-
-		var meshes = [];
-		var mesh;
-		var callbackOnMeshAlter = this.callbacks.onMeshAlter;
-		var callbackOnMeshAlterResult;
-		var useOrgMesh = true;
-		var geometryType = this.validator.verifyInput( meshPayload.geometryType, 0 );
-		if ( this.validator.isValid( callbackOnMeshAlter ) ) {
-
-			callbackOnMeshAlterResult = callbackOnMeshAlter(
-				{
-					detail: {
-						meshName: meshName,
-						bufferGeometry: bufferGeometry,
-						material: material,
-						geometryType: geometryType
-					}
-				}
-			);
-			if ( this.validator.isValid( callbackOnMeshAlterResult ) ) {
-
-				if ( callbackOnMeshAlterResult.isDisregardMesh() ) {
-
-					useOrgMesh = false;
-
-				} else if ( callbackOnMeshAlterResult.providesAlteredMeshes() ) {
-
-					for ( var i in callbackOnMeshAlterResult.meshes ) {
-
-						meshes.push( callbackOnMeshAlterResult.meshes[ i ] );
-
-					}
-					useOrgMesh = false;
-
-				}
-
-			}
-
-		}
-		if ( useOrgMesh ) {
-
-			if ( meshPayload.computeBoundingSphere ) bufferGeometry.computeBoundingSphere();
-			if ( geometryType === 0 ) {
-
-				mesh = new THREE.Mesh( bufferGeometry, material );
-
-			} else if ( geometryType === 1) {
-
-				mesh = new THREE.LineSegments( bufferGeometry, material );
-
-			} else {
-
-				mesh = new THREE.Points( bufferGeometry, material );
-
-			}
-			mesh.name = meshName;
-			meshes.push( mesh );
-
-		}
-
-		var progressMessage;
-		if ( this.validator.isValid( meshes ) && meshes.length > 0 ) {
-
-			var meshNames = [];
-			for ( var i in meshes ) {
-
-				mesh = meshes[ i ];
-				meshNames[ i ] = mesh.name;
-
-			}
-			progressMessage = 'Adding mesh(es) (' + meshNames.length + ': ' + meshNames + ') from input mesh: ' + meshName;
-			progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)';
-
-		} else {
-
-			progressMessage = 'Not adding mesh: ' + meshName;
-			progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)';
-
-		}
-		var callbackOnProgress = this.callbacks.onProgress;
-		if ( this.validator.isValid( callbackOnProgress ) ) {
-
-			var event = new CustomEvent( 'MeshBuilderEvent', {
-				detail: {
-					type: 'progress',
-					modelName: meshPayload.params.meshName,
-					text: progressMessage,
-					numericalValue: meshPayload.progress.numericalValue
-				}
-			} );
-			callbackOnProgress( event );
-
-		}
-
-		return meshes;
-	},
-
-	/**
-	 * Updates the materials with contained material objects (sync) or from alteration instructions (async).
-	 *
-	 * @param {Object} materialPayload Material update instructions
-	 */
-	updateMaterials: function ( materialPayload ) {
-		var material, materialName;
-		var materialCloneInstructions = materialPayload.materials.materialCloneInstructions;
-		if ( this.validator.isValid( materialCloneInstructions ) ) {
-
-			var materialNameOrg = materialCloneInstructions.materialNameOrg;
-			var materialOrg = this.materials[ materialNameOrg ];
-
-			if ( this.validator.isValid( materialNameOrg ) ) {
-
-				material = materialOrg.clone();
-
-				materialName = materialCloneInstructions.materialName;
-				material.name = materialName;
-
-				var materialProperties = materialCloneInstructions.materialProperties;
-				for ( var key in materialProperties ) {
-
-					if ( material.hasOwnProperty( key ) && materialProperties.hasOwnProperty( key ) ) material[ key ] = materialProperties[ key ];
-
-				}
-				this.materials[ materialName ] = material;
-
-			} else {
-
-				console.warn( 'Requested material "' + materialNameOrg + '" is not available!' );
-
-			}
-		}
-
-		var materials = materialPayload.materials.serializedMaterials;
-		if ( this.validator.isValid( materials ) && Object.keys( materials ).length > 0 ) {
-
-			var loader = new THREE.MaterialLoader();
-			var materialJson;
-			for ( materialName in materials ) {
-
-				materialJson = materials[ materialName ];
-				if ( this.validator.isValid( materialJson ) ) {
-
-					material = loader.parse( materialJson );
-					if ( this.logging.enabled ) console.info( 'De-serialized material with name "' + materialName + '" will be added.' );
-					this.materials[ materialName ] = material;
-				}
-
-			}
-
-		}
-
-		materials = materialPayload.materials.runtimeMaterials;
-		if ( this.validator.isValid( materials ) && Object.keys( materials ).length > 0 ) {
-
-			for ( materialName in materials ) {
-
-				material = materials[ materialName ];
-				if ( this.logging.enabled ) console.info( 'Material with name "' + materialName + '" will be added.' );
-				this.materials[ materialName ] = material;
-
-			}
-
-		}
-	},
-
-	/**
-	 * Returns the mapping object of material name and corresponding jsonified material.
-	 *
-	 * @returns {Object} Map of Materials in JSON representation
-	 */
-	getMaterialsJSON: function () {
-		var materialsJSON = {};
-		var material;
-		for ( var materialName in this.materials ) {
-
-			material = this.materials[ materialName ];
-			materialsJSON[ materialName ] = material.toJSON();
-		}
-
-		return materialsJSON;
-	},
-
-	/**
-	 * Returns the mapping object of material name and corresponding material.
-	 *
-	 * @returns {Object} Map of {@link THREE.Material}
-	 */
-	getMaterials: function () {
-		return this.materials;
-	}
-
-};
-
-/**
- * This class provides means to transform existing parser code into a web worker. It defines a simple communication protocol
- * which allows to configure the worker and receive raw mesh data during execution.
- * @class
- */
-THREE.LoaderSupport.WorkerSupport = function () {
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-
-	//Choose implementation of worker based on environment
-	this.loaderWorker = typeof window !== "undefined" ? new THREE.LoaderSupport.WorkerSupport.LoaderWorker() : new THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker();
-};
-
-THREE.LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION = '2.3.0';
-console.info( 'Using THREE.LoaderSupport.WorkerSupport version: ' + THREE.LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION );
-
-
-THREE.LoaderSupport.WorkerSupport.prototype = {
-
-	constructor: THREE.LoaderSupport.WorkerSupport,
-
-	/**
-	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
-	 *
-	 * @param {boolean} enabled True or false.
-	 * @param {boolean} debug True or false.
-	 */
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-		this.loaderWorker.setLogging( this.logging.enabled, this.logging.debug );
-	},
-
-	/**
-	 * Forces all ArrayBuffers to be transferred to worker to be copied.
-	 *
-	 * @param {boolean} forceWorkerDataCopy True or false.
-	 */
-	setForceWorkerDataCopy: function ( forceWorkerDataCopy ) {
-		this.loaderWorker.setForceCopy( forceWorkerDataCopy );
-	},
-
-	/**
-	 * Validate the status of worker code and the derived worker.
-	 *
-	 * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingleton that allows stringification of objects and singletons.
-	 * @param {String} parserName Name of the Parser object
-	 * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath
-	 * @param {String} libPath Base path used for loading libraries
-	 * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here.
-	 */
-	validate: function ( functionCodeBuilder, parserName, libLocations, libPath, runnerImpl ) {
-		if ( THREE.LoaderSupport.Validator.isValid( this.loaderWorker.worker ) ) return;
-
-		if ( this.logging.enabled ) {
-
-			console.info( 'WorkerSupport: Building worker code...' );
-			console.time( 'buildWebWorkerCode' );
-
-		}
-		if ( THREE.LoaderSupport.Validator.isValid( runnerImpl ) ) {
-
-			if ( this.logging.enabled ) console.info( 'WorkerSupport: Using "' + runnerImpl.runnerName + '" as Runner class for worker.' );
-
-		// Browser implementation
-		} else if ( typeof window !== "undefined" ) {
-
-			runnerImpl = THREE.LoaderSupport.WorkerRunnerRefImpl;
-			if ( this.logging.enabled ) console.info( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runner class for worker.' );
-
-		// NodeJS implementation
-		} else {
-
-			runnerImpl = THREE.LoaderSupport.NodeWorkerRunnerRefImpl;
-			if ( this.logging.enabled ) console.info( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.NodeWorkerRunnerRefImpl" as Runner class for worker.' );
-
-		}
-		var userWorkerCode = functionCodeBuilder( THREE.LoaderSupport.WorkerSupport.CodeSerializer );
-		userWorkerCode += 'var Parser = '+ parserName +  ';\n\n';
-		userWorkerCode += THREE.LoaderSupport.WorkerSupport.CodeSerializer.serializeClass( runnerImpl.runnerName, runnerImpl );
-		userWorkerCode += 'new ' + runnerImpl.runnerName + '();\n\n';
-
-		var scope = this;
-		if ( THREE.LoaderSupport.Validator.isValid( libLocations ) && libLocations.length > 0 ) {
-
-			var libsContent = '';
-			var loadAllLibraries = function ( path, locations ) {
-				if ( locations.length === 0 ) {
-
-					scope.loaderWorker.initWorker( libsContent + userWorkerCode, runnerImpl.runnerName );
-					if ( scope.logging.enabled ) console.timeEnd( 'buildWebWorkerCode' );
-
-				} else {
-
-					var loadedLib = function ( contentAsString ) {
-						libsContent += contentAsString;
-						loadAllLibraries( path, locations );
-					};
-
-					var fileLoader = new THREE.FileLoader();
-					fileLoader.setPath( path );
-					fileLoader.setResponseType( 'text' );
-					fileLoader.load( locations[ 0 ], loadedLib );
-					locations.shift();
-
-				}
-			};
-			loadAllLibraries( libPath, libLocations );
-
-		} else {
-
-			this.loaderWorker.initWorker( userWorkerCode, runnerImpl.runnerName );
-			if ( this.logging.enabled ) console.timeEnd( 'buildWebWorkerCode' );
-
-		}
-	},
-
-	/**
-	 * Specify functions that should be build when new raw mesh data becomes available and when the parser is finished.
-	 *
-	 * @param {Function} meshBuilder The mesh builder function. Default is {@link THREE.LoaderSupport.MeshBuilder}.
-	 * @param {Function} onLoad The function that is called when parsing is complete.
-	 */
-	setCallbacks: function ( meshBuilder, onLoad ) {
-		this.loaderWorker.setCallbacks( meshBuilder, onLoad );
-	},
-
-	/**
-	 * Runs the parser with the provided configuration.
-	 *
-	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
-	 */
-	run: function ( payload ) {
-		this.loaderWorker.run( payload );
-	},
-
-	/**
-	 * Request termination of worker once parser is finished.
-	 *
-	 * @param {boolean} terminateRequested True or false.
-	 */
-	setTerminateRequested: function ( terminateRequested ) {
-		this.loaderWorker.setTerminateRequested( terminateRequested );
-	}
-
-};
-
-
-THREE.LoaderSupport.WorkerSupport.LoaderWorker = function () {
-	this._reset();
-};
-
-THREE.LoaderSupport.WorkerSupport.LoaderWorker.prototype = {
-
-	constructor: THREE.LoaderSupport.WorkerSupport.LoaderWorker,
-
-	_reset: function () {
-		this.logging = {
-			enabled: true,
-			debug: false
-		};
-		this.worker = null;
-		this.runnerImplName = null;
-		this.callbacks = {
-			meshBuilder: null,
-			onLoad: null
-		};
-		this.terminateRequested = false;
-		this.queuedMessage = null;
-		this.started = false;
-		this.forceCopy = false;
-	},
-
-	/**
-	 * Check support for Workers and other necessary features returning
-	 * reason if the environment is unsupported
-	 *
-	 * @returns {string|undefined} Returns undefined if supported, or
-	 * string with error if not supported
-	 */
-	checkSupport: function() {
-		if ( window.Worker === undefined ) return "This browser does not support web workers!";
-		if ( window.Blob === undefined  ) return "This browser does not support Blob!";
-		if ( typeof window.URL.createObjectURL !== 'function'  ) return "This browser does not support Object creation from URL!";
-	},
-
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-	},
-
-	setForceCopy: function ( forceCopy ) {
-		this.forceCopy = forceCopy === true;
-	},
-
-	initWorker: function ( code, runnerImplName ) {
-		var supportError = this.checkSupport();
-		if ( supportError ) {
-
-			throw supportError;
-
-		}
-		this.runnerImplName = runnerImplName;
-
-		var blob = new Blob( [ code ], { type: 'application/javascript' } );
-		this.worker = new Worker( window.URL.createObjectURL( blob ) );
-
-		this.worker.onmessage = this._receiveWorkerMessage;
-
-		// set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members
-		this.worker.runtimeRef = this;
-
-		// process stored queuedMessage
-		this._postMessage();
-	},
-
-	/**
-	 * Executed in worker scope
-	 */
-	_receiveWorkerMessage: function ( e ) {
-		var payload = e.data;
-		switch ( payload.cmd ) {
-			case 'meshData':
-			case 'materialData':
-			case 'imageData':
-				this.runtimeRef.callbacks.meshBuilder( payload );
-				break;
-
-			case 'complete':
-				this.runtimeRef.queuedMessage = null;
-				this.started = false;
-				this.runtimeRef.callbacks.onLoad( payload.msg );
-
-				if ( this.runtimeRef.terminateRequested ) {
-
-					if ( this.runtimeRef.logging.enabled ) console.info( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' );
-					this.runtimeRef._terminate();
-
-				}
-				break;
-
-			case 'error':
-				console.error( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg );
-				this.runtimeRef.queuedMessage = null;
-				this.started = false;
-				this.runtimeRef.callbacks.onLoad( payload.msg );
-
-				if ( this.runtimeRef.terminateRequested ) {
-
-					if ( this.runtimeRef.logging.enabled ) console.info( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' );
-					this.runtimeRef._terminate();
-
-				}
-				break;
-
-			default:
-				console.error( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd );
-				break;
-
-		}
-	},
-
-	setCallbacks: function ( meshBuilder, onLoad ) {
-		this.callbacks.meshBuilder = THREE.LoaderSupport.Validator.verifyInput( meshBuilder, this.callbacks.meshBuilder );
-		this.callbacks.onLoad = THREE.LoaderSupport.Validator.verifyInput( onLoad, this.callbacks.onLoad );
-	},
-
-	run: function( payload ) {
-		if ( THREE.LoaderSupport.Validator.isValid( this.queuedMessage ) ) {
-
-			console.warn( 'Already processing message. Rejecting new run instruction' );
-			return;
-
-		} else {
-
-			this.queuedMessage = payload;
-			this.started = true;
-
-		}
-		if ( ! THREE.LoaderSupport.Validator.isValid( this.callbacks.meshBuilder ) ) throw 'Unable to run as no "MeshBuilder" callback is set.';
-		if ( ! THREE.LoaderSupport.Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.';
-		if ( payload.cmd !== 'run' ) payload.cmd = 'run';
-		if ( THREE.LoaderSupport.Validator.isValid( payload.logging ) ) {
-
-			payload.logging.enabled = payload.logging.enabled === true;
-			payload.logging.debug = payload.logging.debug === true;
-
-		} else {
-
-			payload.logging = {
-				enabled: true,
-				debug: false
-			}
-
-		}
-		this._postMessage();
-	},
-
-	_postMessage: function () {
-		if ( THREE.LoaderSupport.Validator.isValid( this.queuedMessage ) && THREE.LoaderSupport.Validator.isValid( this.worker ) ) {
-
-			if ( this.queuedMessage.data.input instanceof ArrayBuffer ) {
-
-				var content;
-				if ( this.forceCopy ) {
-
-					content = this.queuedMessage.data.input.slice( 0 );
-
-				} else {
-
-					content = this.queuedMessage.data.input;
-
-				}
-				this.worker.postMessage( this.queuedMessage, [ content ] );
-
-			} else {
-
-				this.worker.postMessage( this.queuedMessage );
-
-			}
-
-		}
-	},
-
-	setTerminateRequested: function ( terminateRequested ) {
-		this.terminateRequested = terminateRequested === true;
-		if ( this.terminateRequested && THREE.LoaderSupport.Validator.isValid( this.worker ) && ! THREE.LoaderSupport.Validator.isValid( this.queuedMessage ) && this.started ) {
-
-			if ( this.logging.enabled ) console.info( 'Worker is terminated immediately as it is not running!' );
-			this._terminate();
-
-		}
-	},
-
-	_terminate: function () {
-		this.worker.terminate();
-		this._reset();
-	}
-};
-
-
-THREE.LoaderSupport.WorkerSupport.CodeSerializer = {
-
-	/**
-	 *
-	 * @param fullName
-	 * @param object
-	 * @returns {string}
-	 */
-	serializeObject: function ( fullName, object ) {
-		var objectString = fullName + ' = {\n\n';
-		var part;
-		for ( var name in object ) {
-
-			part = object[ name ];
-			if ( typeof( part ) === 'string' || part instanceof String ) {
-
-				part = part.replace( '\n', '\\n' );
-				part = part.replace( '\r', '\\r' );
-				objectString += '\t' + name + ': "' + part + '",\n';
-
-			} else if ( part instanceof Array ) {
-
-				objectString += '\t' + name + ': [' + part + '],\n';
-
-			} else if ( typeof part === 'object' ) {
-
-				// TODO: Short-cut for now. Recursion required?
-				objectString += '\t' + name + ': {},\n';
-
-			} else {
-
-				objectString += '\t' + name + ': ' + part + ',\n';
-
-			}
-
-		}
-		objectString += '}\n\n';
-
-		return objectString;
-	},
-
-	/**
-	 *
-	 * @param fullName
-	 * @param object
-	 * @param basePrototypeName
-	 * @param ignoreFunctions
-	 * @returns {string}
-	 */
-	serializeClass: function ( fullName, object, constructorName, basePrototypeName, ignoreFunctions, includeFunctions, overrideFunctions ) {
-		var valueString, objectPart, constructorString, i, funcOverride;
-		var prototypeFunctions = [];
-		var objectProperties = [];
-		var objectFunctions = [];
-		var isExtended = ( basePrototypeName !== null && basePrototypeName !== undefined );
-
-		if ( ! Array.isArray( ignoreFunctions ) ) ignoreFunctions = [];
-		if ( ! Array.isArray( includeFunctions ) ) includeFunctions = null;
-		if ( ! Array.isArray( overrideFunctions ) ) overrideFunctions = [];
-
-		for ( var name in object.prototype ) {
-
-			objectPart = object.prototype[ name ];
-			valueString = objectPart.toString();
-			if ( name === 'constructor' ) {
-
-				constructorString = fullName + ' = ' + valueString + ';\n\n';
-
-			} else if ( typeof objectPart === 'function' ) {
-
-				if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) {
-
-					funcOverride = overrideFunctions[ name ];
-					if ( funcOverride && funcOverride.fullName === fullName + '.prototype.' + name ) {
-
-						valueString = funcOverride.code;
-
-					}
-					if ( isExtended ) {
-
-						prototypeFunctions.push( fullName + '.prototype.' + name + ' = ' + valueString + ';\n\n' );
-
-					} else {
-
-						prototypeFunctions.push( '\t' + name + ': ' + valueString + ',\n\n' );
-
-					}
-				}
-
-			}
-
-		}
-		for ( var name in object ) {
-
-			objectPart = object[ name ];
-
-			if ( typeof objectPart === 'function' ) {
-
-				if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) {
-
-					funcOverride = overrideFunctions[ name ];
-					if ( funcOverride && funcOverride.fullName === fullName + '.' + name ) {
-
-						valueString = funcOverride.code;
-
-					} else {
-
-						valueString = objectPart.toString();
-
-					}
-					objectFunctions.push( fullName + '.' + name + ' = ' + valueString + ';\n\n' );
-
-				}
-
-			} else {
-
-				if ( typeof( objectPart ) === 'string' || objectPart instanceof String) {
-
-					valueString = '\"' + objectPart.toString() + '\"';
-
-				} else if ( typeof objectPart === 'object' ) {
-
-					// TODO: Short-cut for now. Recursion required?
-					valueString = "{}";
-
-				} else {
-
-					valueString = objectPart;
-
-				}
-				objectProperties.push( fullName + '.' + name + ' = ' + valueString + ';\n' );
-
-			}
-
-		}
-		if ( ( constructorString === undefined || constructorString === null ) && typeof object.prototype.constructor === 'function' ) {
-
-			constructorString = fullName + ' = ' + object.prototype.constructor.toString().replace( constructorName, '' );
-
-		}
-		var objectString = constructorString + '\n\n';
-		if ( isExtended ) {
-
-			objectString += fullName + '.prototype = Object.create( ' + basePrototypeName + '.prototype );\n';
-
-		}
-		objectString += fullName + '.prototype.constructor = ' + fullName + ';\n';
-		objectString += '\n\n';
-
-		for ( i = 0; i < objectProperties.length; i ++ ) objectString += objectProperties[ i ];
-		objectString += '\n\n';
-
-		for ( i = 0; i < objectFunctions.length; i ++ ) objectString += objectFunctions[ i ];
-		objectString += '\n\n';
-
-		if ( isExtended ) {
-
-			for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ];
-
-		} else {
-
-			objectString += fullName + '.prototype = {\n\n';
-			for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ];
-			objectString += '\n};';
-
-		}
-		objectString += '\n\n';
-
-		return objectString;
-	},
-};
-
-/**
- * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker.
- *
- * @class
- */
-THREE.LoaderSupport.WorkerRunnerRefImpl = function () {
-	var scopedRunner = function( event ) {
-		this.processMessage( event.data );
-	};
-	this.getParentScope().addEventListener( 'message', scopedRunner.bind( this ) );
-};
-
-THREE.LoaderSupport.WorkerRunnerRefImpl.runnerName = 'THREE.LoaderSupport.WorkerRunnerRefImpl';
-
-THREE.LoaderSupport.WorkerRunnerRefImpl.prototype = {
-
-	constructor: THREE.LoaderSupport.WorkerRunnerRefImpl,
-
-	/**
-	 * Returns the parent scope that this worker was spawned in.
-	 *
-	 * @returns {WorkerGlobalScope|Object} Returns a references
-	 * to the parent global scope or compatible type.
-	 */
-	getParentScope: function () {
-		return self;
-	},
-
-	/**
-	 * Applies values from parameter object via set functions or via direct assignment.
-	 *
-	 * @param {Object} parser The parser instance
-	 * @param {Object} params The parameter object
-	 */
-	applyProperties: function ( parser, params ) {
-		var property, funcName, values;
-		for ( property in params ) {
-			funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 );
-			values = params[ property ];
-
-			if ( typeof parser[ funcName ] === 'function' ) {
-
-				parser[ funcName ]( values );
-
-			} else if ( parser.hasOwnProperty( property ) ) {
-
-				parser[ property ] = values;
-
-			}
-		}
-	},
-
-	/**
-	 * Configures the Parser implementation according the supplied configuration object.
-	 *
-	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
-	 */
-	processMessage: function ( payload ) {
-		if ( payload.cmd === 'run' ) {
-
-			var self = this.getParentScope();
-			var callbacks = {
-				callbackOnAssetAvailable: function ( payload ) {
-					self.postMessage( payload );
-				},
-				callbackOnProgress: function ( text ) {
-					if ( payload.logging.enabled && payload.logging.debug ) console.debug( 'WorkerRunner: progress: ' + text );
-				}
-			};
-
-			// Parser is expected to be named as such
-			var parser = new Parser();
-			if ( typeof parser[ 'setLogging' ] === 'function' ) parser.setLogging( payload.logging.enabled, payload.logging.debug );
-			this.applyProperties( parser, payload.params );
-			this.applyProperties( parser, payload.materials );
-			this.applyProperties( parser, callbacks );
-			parser.workerScope = self;
-			parser.parse( payload.data.input, payload.data.options );
-
-			if ( payload.logging.enabled ) console.log( 'WorkerRunner: Run complete!' );
-
-			callbacks.callbackOnAssetAvailable( {
-				cmd: 'complete',
-				msg: 'WorkerRunner completed run.'
-			} );
-
-		} else {
-
-			console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd );
-
-		}
-	}
-};
-
-
-/**
- * This class provides the NodeJS implementation of the WorkerRunnerRefImpl
- * @class
- * @extends THREE.LoaderSupport.WorkerRunnerRefImpl
- */
-THREE.LoaderSupport.NodeWorkerRunnerRefImpl = function () {
-	this.runnerName = 'THREE.LoaderSupport.NodeWorkerRunnerRefImpl';
-	// No call to super because super class only binds to processMessage
-	// In NodeJS, there is no addEventListener so use onmessage.
-	// Also, the message object can be passed directly to
-	// processMessage() as it isn't an `Event`, but a plain object
-	// with the data
-	this.getParentScope().onmessage = this.processMessage.bind( this );
-};
-
-THREE.LoaderSupport.NodeWorkerRunnerRefImpl.prototype = Object.create( THREE.LoaderSupport.WorkerRunnerRefImpl.prototype );
-THREE.LoaderSupport.NodeWorkerRunnerRefImpl.prototype.constructor = THREE.LoaderSupport.NodeWorkerRunnerRefImpl;
-THREE.LoaderSupport.NodeWorkerRunnerRefImpl.runnerName = 'THREE.LoaderSupport.NodeWorkerRunnerRefImpl';
-
-THREE.LoaderSupport.NodeWorkerRunnerRefImpl.prototype = {
-
-	getParentScope: function(){
-		// Work around webpack builds failing with NodeJS requires
-		// (placing it outside this function will fail because
-		// this class is passed to the worker as a string!)
-		var _require = eval( 'require' );
-		return _require( 'worker_threads' ).parentPort;
-	}
-};
-
-
-/**
- * This class provides the NodeJS implementation of LoaderWorker
- * @class
- * @extends LoaderWorker
- */
-THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker = function (){
-	THREE.LoaderSupport.WorkerSupport.LoaderWorker.call( this );
-};
-
-THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype = Object.create( THREE.LoaderSupport.WorkerSupport.LoaderWorker.prototype );
-THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype.constructor = THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker;
-
-/**
- * @inheritdoc
-  */
-THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker.checkSupport = function() {
-	try {
-		// Work around webpack builds failing with NodeJS requires
-		var _require = eval( 'require' );
-		_require.resolve( 'worker_threads' );
-	}
-	catch(e) {
-		return 'This version of Node does not support web workers!';
-	}
-};
-
-/**
- * @inheritdoc
- */
-THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype.initWorker = function ( code, runnerImplName ) {
-	var supportError = this.checkSupport();
-	if( supportError ) {
-
-		throw supportError;
-
-	}
-	this.runnerImplName = runnerImplName;
-
-	// Work around webpack builds failing with NodeJS requires
-	var _require = eval( 'require' );
-	var Worker = _require( 'worker_threads' ).Worker;
-	this.worker = new Worker( code, { eval: true } );
-
-	this.worker.onmessage = this._receiveWorkerMessage;
-
-	// set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members
-	this.worker.runtimeRef = this;
-
-	// process stored queuedMessage
-	this._postMessage();
-};
-
-/**
- * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
- * Workflow:
- *   prepareWorkers
- *   enqueueForRun
- *   processQueue
- *   tearDown (to force stop)
- *
- * @class
- *
- * @param {string} classDef Class definition to be used for construction
- */
-THREE.LoaderSupport.WorkerDirector = function ( classDef ) {
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-
-	this.maxQueueSize = THREE.LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE ;
-	this.maxWebWorkers = THREE.LoaderSupport.WorkerDirector.MAX_WEB_WORKER;
-	this.crossOrigin = null;
-
-	if ( ! THREE.LoaderSupport.Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef;
-
-	this.workerDescription = {
-		classDef: classDef,
-		globalCallbacks: {},
-		workerSupports: {},
-		forceWorkerDataCopy: true
-	};
-	this.objectsCompleted = 0;
-	this.instructionQueue = [];
-	this.instructionQueuePointer = 0;
-
-	this.callbackOnFinishedProcessing = null;
-}
-
-
-THREE.LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION = '2.3.0';
-THREE.LoaderSupport.WorkerDirector.MAX_WEB_WORKER = 16;
-THREE.LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE = 2048;
-console.info( 'Using THREE.LoaderSupport.WorkerDirector version: ' + THREE.LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION );
-
-
-THREE.LoaderSupport.WorkerDirector.prototype = {
-
-	constructor: THREE.LoaderSupport.WorkerDirector,
-	/**
-	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
-	 *
-	 * @param {boolean} enabled True or false.
-	 * @param {boolean} debug True or false.
-	 */
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-	},
-
-	/**
-	 * Returns the maximum length of the instruction queue.
-	 *
-	 * @returns {number}
-	 */
-	getMaxQueueSize: function () {
-		return this.maxQueueSize;
-	},
-
-	/**
-	 * Returns the maximum number of workers.
-	 *
-	 * @returns {number}
-	 */
-	getMaxWebWorkers: function () {
-		return this.maxWebWorkers;
-	},
-
-	/**
-	 * Sets the CORS string to be used.
-	 *
-	 * @param {string} crossOrigin CORS value
-	 */
-	setCrossOrigin: function ( crossOrigin ) {
-		this.crossOrigin = crossOrigin;
-	},
-
-	/**
-	 * Forces all ArrayBuffers to be transferred to worker to be copied.
-	 *
-	 * @param {boolean} forceWorkerDataCopy True or false.
-	 */
-	setForceWorkerDataCopy: function ( forceWorkerDataCopy ) {
-		this.workerDescription.forceWorkerDataCopy = forceWorkerDataCopy === true;
-	},
-
-	/**
-	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
-	 *
-	 * @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} maxWebWorkers Set the maximum amount of workers (1-16)
-	 */
-	prepareWorkers: function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
-		if ( THREE.LoaderSupport.Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
-		this.maxQueueSize = Math.min( maxQueueSize, THREE.LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE );
-		this.maxWebWorkers = Math.min( maxWebWorkers, THREE.LoaderSupport.WorkerDirector.MAX_WEB_WORKER );
-		this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize );
-		this.objectsCompleted = 0;
-		this.instructionQueue = [];
-		this.instructionQueuePointer = 0;
-
-		for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) {
-
-			var workerSupport = new THREE.LoaderSupport.WorkerSupport();
-			workerSupport.setLogging( this.logging.enabled, this.logging.debug );
-			workerSupport.setForceWorkerDataCopy( this.workerDescription.forceWorkerDataCopy );
-			this.workerDescription.workerSupports[ instanceNo ] = {
-				instanceNo: instanceNo,
-				inUse: false,
-				terminateRequested: false,
-				workerSupport: workerSupport,
-				loader: null
-			};
-
-		}
-	},
-
-	/**
-	 * Store run instructions in internal instructionQueue.
-	 *
-	 * @param {THREE.LoaderSupport.PrepData} prepData
-	 */
-	enqueueForRun: function ( prepData ) {
-		if ( this.instructionQueue.length < this.maxQueueSize ) {
-			this.instructionQueue.push( prepData );
-		}
-	},
-
-	/**
-	 * Returns if any workers are running.
-	 *
-	 * @returns {boolean}
-	 */
-	isRunning: function () {
-		var wsKeys = Object.keys( this.workerDescription.workerSupports );
-		return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 );
-	},
-
-	/**
-	 * Process the instructionQueue until it is depleted.
-	 */
-	processQueue: function () {
-		var prepData, supportDesc;
-		for ( var instanceNo in this.workerDescription.workerSupports ) {
-
-			supportDesc = this.workerDescription.workerSupports[ instanceNo ];
-			if ( ! supportDesc.inUse ) {
-
-				if ( this.instructionQueuePointer < this.instructionQueue.length ) {
-
-					prepData = this.instructionQueue[ this.instructionQueuePointer ];
-					this._kickWorkerRun( prepData, supportDesc );
-					this.instructionQueuePointer++;
-
-				} else {
-
-					this._deregister( supportDesc );
-
-				}
-
-			}
-
-		}
-
-		if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) {
-
-			this.callbackOnFinishedProcessing();
-			this.callbackOnFinishedProcessing = null;
-
-		}
-	},
-
-	_kickWorkerRun: function( prepData, supportDesc ) {
-		supportDesc.inUse = true;
-		supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested );
-
-		if ( this.logging.enabled ) console.info( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' );
-
-		var validator = THREE.LoaderSupport.Validator;
-		var scope = this;
-		var prepDataCallbacks = prepData.getCallbacks();
-		var globalCallbacks = this.workerDescription.globalCallbacks;
-		var wrapperOnLoad = function ( event ) {
-			if ( validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event );
-			if ( validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event );
-			scope.objectsCompleted++;
-			supportDesc.inUse = false;
-
-			scope.processQueue();
-		};
-
-		var wrapperOnProgress = function ( event ) {
-			if ( validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event );
-			if ( validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event );
-		};
-
-		var wrapperOnMeshAlter = function ( event, override ) {
-			if ( validator.isValid( globalCallbacks.onMeshAlter ) ) override = globalCallbacks.onMeshAlter( event, override );
-			if ( validator.isValid( prepDataCallbacks.onMeshAlter ) ) override = globalCallbacks.onMeshAlter( event, override );
-			return override;
-		};
-
-		var wrapperOnLoadMaterials = function ( materials ) {
-			if ( validator.isValid( globalCallbacks.onLoadMaterials ) ) materials = globalCallbacks.onLoadMaterials( materials );
-			if ( validator.isValid( prepDataCallbacks.onLoadMaterials ) ) materials = prepDataCallbacks.onLoadMaterials( materials );
-			return materials;
-		};
-
-		var wrapperOnReportError = function ( errorMessage ) {
-			var continueProcessing = true;
-			if ( validator.isValid( globalCallbacks.onReportError ) ) continueProcessing = globalCallbacks.onReportError( supportDesc, errorMessage );
-			if ( validator.isValid( prepDataCallbacks.onReportError ) )	continueProcessing = prepDataCallbacks.onReportError( supportDesc, errorMessage );
-
-			if ( ! validator.isValid( globalCallbacks.onReportError ) && ! validator.isValid( prepDataCallbacks.onReportError ) ) {
-
-				console.error( 'Loader reported an error: ' );
-				console.error( errorMessage );
-
-			}
-			if ( continueProcessing ) {
-
-				supportDesc.inUse = false;
-				scope.processQueue();
-
-			}
-		};
-
-		supportDesc.loader = this._buildLoader( supportDesc.instanceNo );
-
-		var updatedCallbacks = new THREE.LoaderSupport.Callbacks();
-		updatedCallbacks.setCallbackOnLoad( wrapperOnLoad );
-		updatedCallbacks.setCallbackOnProgress( wrapperOnProgress );
-		updatedCallbacks.setCallbackOnReportError( wrapperOnReportError );
-		updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter );
-		updatedCallbacks.setCallbackOnLoadMaterials( wrapperOnLoadMaterials );
-		prepData.callbacks = updatedCallbacks;
-
-		supportDesc.loader.run( prepData, supportDesc.workerSupport );
-	},
-
-	_buildLoader: function ( instanceNo ) {
-		var classDef = this.workerDescription.classDef;
-		var loader = Object.create( classDef.prototype );
-		classDef.call( loader, THREE.DefaultLoadingManager );
-
-		// verify that all required functions are implemented
-		if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".';
-		loader.instanceNo = instanceNo;
-
-		if ( ! loader.hasOwnProperty( 'workerSupport' ) ) {
-
-			throw classDef.name + ' has no property "workerSupport".';
-
-		}
-		if ( typeof loader.run !== 'function'  ) throw classDef.name + ' has no function "run".';
-		if ( ! loader.hasOwnProperty( 'callbacks' ) || ! THREE.LoaderSupport.Validator.isValid( loader.callbacks ) ) {
-
-			console.warn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' );
-			loader.callbacks = new THREE.LoaderSupport.Callbacks();
-
-		}
-
-		return loader;
-	},
-
-	_deregister: function ( supportDesc ) {
-		if ( THREE.LoaderSupport.Validator.isValid( supportDesc ) ) {
-
-			supportDesc.workerSupport.setTerminateRequested( true );
-			if ( this.logging.enabled ) console.info( 'Requested termination of worker #' + supportDesc.instanceNo + '.' );
-
-			var loaderCallbacks = supportDesc.loader.callbacks;
-			if ( THREE.LoaderSupport.Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
-			delete this.workerDescription.workerSupports[ supportDesc.instanceNo ];
-
-		}
-	},
-
-	/**
-	 * Terminate all workers.
-	 *
-	 * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing.
-	 */
-	tearDown: function ( callbackOnFinishedProcessing ) {
-		if ( this.logging.enabled ) console.info( 'WorkerDirector received the deregister call. Terminating all workers!' );
-
-		this.instructionQueuePointer = this.instructionQueue.length;
-		this.callbackOnFinishedProcessing = THREE.LoaderSupport.Validator.verifyInput( callbackOnFinishedProcessing, null );
-
-		for ( var name in this.workerDescription.workerSupports ) {
-
-			this.workerDescription.workerSupports[ name ].terminateRequested = true;
-
-		}
-	}
-
-};

+ 0 - 1492
examples/js/loaders/OBJLoader2.js

@@ -1,1492 +0,0 @@
-/**
-  * @author Kai Salmen / https://kaisalmen.de
-  * Development repository: https://github.com/kaisalmen/WWOBJLoader
-  */
-
-'use strict';
-
-if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
-
-if ( THREE.LoaderSupport === undefined ) console.error( '"THREE.LoaderSupport" is not available. "THREE.OBJLoader2" requires it. Please include "LoaderSupport.js" in your HTML.' );
-
-/**
- * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer
- * @class
- *
- * @param {THREE.DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link THREE.DefaultLoadingManager}
- */
-
-THREE.OBJLoader2 = function ( manager ) {
-	this.manager = THREE.LoaderSupport.Validator.verifyInput( manager, THREE.DefaultLoadingManager );
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-
-	this.modelName = '';
-	this.instanceNo = 0;
-	this.path;
-	this.resourcePath;
-	this.useIndices = false;
-	this.disregardNormals = false;
-	this.materialPerSmoothingGroup = false;
-	this.useOAsMesh = false;
-	this.loaderRootNode = new THREE.Group();
-
-	this.meshBuilder = new THREE.LoaderSupport.MeshBuilder();
-	this.callbacks = new THREE.LoaderSupport.Callbacks();
-	this.workerSupport = new THREE.LoaderSupport.WorkerSupport();
-	this.terminateWorkerOnLoad = true;
-};
-THREE.OBJLoader2.OBJLOADER2_VERSION = '2.5.1';
-console.info( 'Using THREE.OBJLoader2 version: ' + THREE.OBJLoader2.OBJLOADER2_VERSION );
-
-
-THREE.OBJLoader2.prototype = {
-
-	constructor: THREE.OBJLoader2,
-
-	/**
-	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
-	 *
-	 * @param {boolean} enabled True or false.
-	 * @param {boolean} debug True or false.
-	 */
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-		this.meshBuilder.setLogging( this.logging.enabled, this.logging.debug );
-	},
-
-	/**
-	 * Set the name of the model.
-	 *
-	 * @param {string} modelName
-	 */
-	setModelName: function ( modelName ) {
-		this.modelName = THREE.LoaderSupport.Validator.verifyInput( modelName, this.modelName );
-	},
-
-	/**
-	 * The URL of the base path.
-	 *
-	 * @param {string} path URL
-	 */
-	setPath: function ( path ) {
-		this.path = THREE.LoaderSupport.Validator.verifyInput( path, this.path );
-	},
-
-	/**
-	 * Allows to specify resourcePath for dependencies of specified resource.
-	 * @param {string} resourcePath
-	 */
-	setResourcePath: function ( resourcePath ) {
-		this.resourcePath = THREE.LoaderSupport.Validator.verifyInput( resourcePath, this.resourcePath );
-	},
-
-	/**
-	 * Set the node where the loaded objects will be attached directly.
-	 *
-	 * @param {THREE.Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to
-	 */
-	setStreamMeshesTo: function ( streamMeshesTo ) {
-		this.loaderRootNode = THREE.LoaderSupport.Validator.verifyInput( streamMeshesTo, this.loaderRootNode );
-	},
-
-	/**
-	 * Set materials loaded by MTLLoader or any other supplier of an Array of {@link THREE.Material}.
-	 *
-	 * @param {THREE.Material[]} materials Array of {@link THREE.Material}
-	 */
-	setMaterials: function ( materials ) {
-		this.meshBuilder.setMaterials( materials );
-	},
-
-	/**
-	 * Instructs loaders to create indexed {@link THREE.BufferGeometry}.
-	 *
-	 * @param {boolean} useIndices=false
-	 */
-	setUseIndices: function ( useIndices ) {
-		this.useIndices = useIndices === true;
-	},
-
-	/**
-	 * Tells whether normals should be completely disregarded and regenerated.
-	 *
-	 * @param {boolean} disregardNormals=false
-	 */
-	setDisregardNormals: function ( disregardNormals ) {
-		this.disregardNormals = disregardNormals === true;
-	},
-
-	/**
-	 * Tells whether a material shall be created per smoothing group.
-	 *
-	 * @param {boolean} materialPerSmoothingGroup=false
-	 */
-	setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) {
-		this.materialPerSmoothingGroup = materialPerSmoothingGroup === true;
-	},
-
-	/**
-	 * Usually 'o' is meta-information and does not result in creation of new meshes, but mesh creation on occurrence of "o" can be enforced.
-	 *
-	 * @param {boolean} useOAsMesh=false
-	 */
-	setUseOAsMesh: function ( useOAsMesh ) {
-		this.useOAsMesh = useOAsMesh === true;
-	},
-
-	_setCallbacks: function ( callbacks ) {
-		if ( THREE.LoaderSupport.Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
-		if ( THREE.LoaderSupport.Validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError );
-		if ( THREE.LoaderSupport.Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
-		if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
-		if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
-
-		this.meshBuilder._setCallbacks( this.callbacks );
-	},
-
-	/**
-	 * Announce feedback which is give to the registered callbacks.
-	 * @private
-	 *
-	 * @param {string} type The type of event
-	 * @param {string} text Textual description of the event
-	 * @param {number} numericalValue Numerical value describing the progress
-	 */
-	onProgress: function ( type, text, numericalValue ) {
-		var content = THREE.LoaderSupport.Validator.isValid( text ) ? text: '';
-		var event = {
-			detail: {
-				type: type,
-				modelName: this.modelName,
-				instanceNo: this.instanceNo,
-				text: content,
-				numericalValue: numericalValue
-			}
-		};
-
-		if ( THREE.LoaderSupport.Validator.isValid( this.callbacks.onProgress ) ) this.callbacks.onProgress( event );
-
-		if ( this.logging.enabled && this.logging.debug ) console.debug( content );
-	},
-
-	_onError: function ( errorMessage ) {
-		if ( this.logging.enabled && this.logging.debug ) {
-
-			console.log( errorMessage );
-
-		}
-		if ( THREE.LoaderSupport.Validator.isValid( this.callbacks.onReportError ) )  {
-
-			this.callbacks.onReportError( errorMessage );
-
-		} else {
-
-			throw errorMessage;
-
-		}
-	},
-
-	/**
-	 * Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
-	 *
-	 * @param {string} url A string containing the path/URL of the file to be loaded.
-	 * @param {callback} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument.
-	 * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes.
-	 * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument.
-	 * @param {callback} [onMeshAlter] A function to be called after a new mesh raw data becomes available for alteration.
-	 * @param {boolean} [useAsync] If true, uses async loading with worker, if false loads data synchronously.
-	 */
-	load: function ( url, onLoad, onProgress, onError, onMeshAlter, useAsync ) {
-		var resource = new THREE.LoaderSupport.ResourceDescriptor( url, 'OBJ' );
-		this._loadObj( resource, onLoad, onProgress, onError, onMeshAlter, useAsync );
-	},
-
-	_loadObj: function ( resource, onLoad, onProgress, onError, onMeshAlter, useAsync ) {
-		var scope = this;
-		if ( ! THREE.LoaderSupport.Validator.isValid( onError ) ) {
-
-			onError = function ( event ) {
-
-				var errorMessage = event;
-				if ( event.currentTarget && event.currentTarget.statusText !== null ) {
-
-					errorMessage = 'Error occurred while downloading!\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;
-
-				}
-				scope._onError( errorMessage );
-
-			};
-		}
-
-		// fast-fail
-		if ( ! THREE.LoaderSupport.Validator.isValid( resource ) ) onError( 'An invalid ResourceDescriptor was provided. Unable to continue!' );
-		var fileLoaderOnLoad = function ( content ) {
-
-			resource.content = content;
-			if ( useAsync ) {
-
-				scope.parseAsync( content, onLoad );
-
-			} else {
-
-				var callbacks = new THREE.LoaderSupport.Callbacks();
-				callbacks.setCallbackOnMeshAlter( onMeshAlter );
-				scope._setCallbacks( callbacks );
-				onLoad(
-					{
-						detail: {
-							loaderRootNode: scope.parse( content ),
-							modelName: scope.modelName,
-							instanceNo: scope.instanceNo
-						}
-					}
-				);
-
-			}
-		};
-		this.setPath( resource.path );
-		this.setResourcePath( resource.resourcePath );
-
-		// fast-fail
-		if ( ! THREE.LoaderSupport.Validator.isValid( resource.url ) || THREE.LoaderSupport.Validator.isValid( resource.content ) ) {
-
-			fileLoaderOnLoad( THREE.LoaderSupport.Validator.isValid( resource.content ) ? resource.content : null );
-
-		} else {
-
-			if ( ! THREE.LoaderSupport.Validator.isValid( onProgress ) ) {
-				var numericalValueRef = 0;
-				var numericalValue = 0;
-				onProgress = function ( event ) {
-					if ( ! event.lengthComputable ) return;
-
-					numericalValue = event.loaded / event.total;
-					if ( numericalValue > numericalValueRef ) {
-
-						numericalValueRef = numericalValue;
-						var output = 'Download of "' + resource.url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%';
-						scope.onProgress( 'progressLoad', output, numericalValue );
-
-					}
-				};
-			}
-
-
-			var fileLoader = new THREE.FileLoader( this.manager );
-			fileLoader.setPath( this.path || this.resourcePath );
-			fileLoader.setResponseType( 'arraybuffer' );
-			fileLoader.load( resource.name, fileLoaderOnLoad, onProgress, onError );
-
-		}
-	},
-
-	/**
-	 * Run the loader according the provided instructions.
-	 *
-	 * @param {THREE.LoaderSupport.PrepData} prepData All parameters and resources required for execution
-	 * @param {THREE.LoaderSupport.WorkerSupport} [workerSupportExternal] Use pre-existing WorkerSupport
-	 */
-	run: function ( prepData, workerSupportExternal ) {
-		this._applyPrepData( prepData );
-		var available = prepData.checkResourceDescriptorFiles( prepData.resources,
-			[
-				{ ext: "obj", type: "ArrayBuffer", ignore: false },
-				{ ext: "mtl", type: "String", ignore: false },
-				{ ext: "zip", type: "String", ignore: true }
-			]
-		);
-		if ( THREE.LoaderSupport.Validator.isValid( workerSupportExternal ) ) {
-
-			this.terminateWorkerOnLoad = false;
-			this.workerSupport = workerSupportExternal;
-			this.logging.enabled = this.workerSupport.logging.enabled;
-			this.logging.debug = this.workerSupport.logging.debug;
-
-		}
-		var scope = this;
-		var onMaterialsLoaded = function ( materials ) {
-			if ( materials !== null ) scope.meshBuilder.setMaterials( materials );
-			scope._loadObj( available.obj, scope.callbacks.onLoad, null, null, scope.callbacks.onMeshAlter, prepData.useAsync );
-
-		};
-		this._loadMtl( available.mtl, onMaterialsLoaded, null, null, prepData.crossOrigin, prepData.materialOptions );
-	},
-
-	_applyPrepData: function ( prepData ) {
-		if ( THREE.LoaderSupport.Validator.isValid( prepData ) ) {
-
-			this.setLogging( prepData.logging.enabled, prepData.logging.debug );
-			this.setModelName( prepData.modelName );
-			this.setStreamMeshesTo( prepData.streamMeshesTo );
-			this.meshBuilder.setMaterials( prepData.materials );
-			this.setUseIndices( prepData.useIndices );
-			this.setDisregardNormals( prepData.disregardNormals );
-			this.setMaterialPerSmoothingGroup( prepData.materialPerSmoothingGroup );
-			this.setUseOAsMesh( prepData.useOAsMesh );
-
-			this._setCallbacks( prepData.getCallbacks() );
-
-		}
-	},
-
-	/**
-	 * Parses OBJ data synchronously from arraybuffer or string.
-	 *
-	 * @param {arraybuffer|string} content OBJ data as Uint8Array or String
-	 */
-	parse: function ( content ) {
-		// fast-fail in case of illegal data
-		if ( content === null || content === undefined ) {
-
-			throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
-
-		}
-		if ( this.logging.enabled ) console.time( 'OBJLoader2 parse: ' + this.modelName );
-		this.meshBuilder.init();
-
-		var parser = new THREE.OBJLoader2.Parser();
-		parser.setLogging( this.logging.enabled, this.logging.debug );
-		parser.setMaterialPerSmoothingGroup( this.materialPerSmoothingGroup );
-		parser.setUseOAsMesh( this.useOAsMesh );
-		parser.setUseIndices( this.useIndices );
-		parser.setDisregardNormals( this.disregardNormals );
-		// sync code works directly on the material references
-		parser.setMaterials( this.meshBuilder.getMaterials() );
-
-		var scope = this;
-		var onMeshLoaded = function ( payload ) {
-			var meshes = scope.meshBuilder.processPayload( payload );
-			var mesh;
-			for ( var i in meshes ) {
-				mesh = meshes[ i ];
-				scope.loaderRootNode.add( mesh );
-			}
-		};
-		parser.setCallbackOnAssetAvailable( onMeshLoaded );
-		var onProgressScoped = function ( text, numericalValue ) {
-			scope.onProgress( 'progressParse', text, numericalValue );
-		};
-		parser.setCallbackOnProgress( onProgressScoped );
-		var onErrorScoped = function ( message ) {
-			scope._onError( message );
-		};
-		parser.setCallbackOnError( onErrorScoped );
-
-		if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) {
-
-			if ( this.logging.enabled ) console.info( 'Parsing arrayBuffer...' );
-			parser.parse( content );
-
-		} else if ( typeof( content ) === 'string' || content instanceof String ) {
-
-			if ( this.logging.enabled ) console.info( 'Parsing text...' );
-			parser.parseText( content );
-
-		} else {
-
-			this._onError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );
-
-		}
-		if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2 parse: ' + this.modelName );
-
-		return this.loaderRootNode;
-	},
-
-	/**
-	 * Parses OBJ content asynchronously from arraybuffer.
-	 *
-	 * @param {arraybuffer} content OBJ data as Uint8Array
-	 * @param {callback} onLoad Called after worker successfully completed loading
-	 */
-	parseAsync: function ( content, onLoad ) {
-		var scope = this;
-		var measureTime = false;
-		var scopedOnLoad = function () {
-			onLoad(
-				{
-					detail: {
-						loaderRootNode: scope.loaderRootNode,
-						modelName: scope.modelName,
-						instanceNo: scope.instanceNo
-					}
-				}
-			);
-			if ( measureTime && scope.logging.enabled ) console.timeEnd( 'OBJLoader2 parseAsync: ' + scope.modelName );
-		};
-		// fast-fail in case of illegal data
-		if ( content === null || content === undefined ) {
-
-			throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
-
-		} else {
-
-			measureTime = true;
-
-		}
-		if ( measureTime && this.logging.enabled ) console.time( 'OBJLoader2 parseAsync: ' + this.modelName );
-		this.meshBuilder.init();
-
-		var scopedOnMeshLoaded = function ( payload ) {
-			var meshes = scope.meshBuilder.processPayload( payload );
-			var mesh;
-			for ( var i in meshes ) {
-				mesh = meshes[ i ];
-				scope.loaderRootNode.add( mesh );
-			}
-		};
-		var buildCode = function ( codeSerializer ) {
-			var workerCode = '';
-			workerCode += '/**\n';
-			workerCode += '  * This code was constructed by OBJLoader2 buildCode.\n';
-			workerCode += '  */\n\n';
-			workerCode += 'THREE = { LoaderSupport: {}, OBJLoader2: {} };\n\n';
-			workerCode += codeSerializer.serializeClass( 'THREE.OBJLoader2.Parser', THREE.OBJLoader2.Parser );
-
-			return workerCode;
-		};
-		this.workerSupport.validate( buildCode, 'THREE.OBJLoader2.Parser' );
-		this.workerSupport.setCallbacks( scopedOnMeshLoaded, scopedOnLoad );
-		if ( scope.terminateWorkerOnLoad ) this.workerSupport.setTerminateRequested( true );
-
-		var materialNames = {};
-		var materials = this.meshBuilder.getMaterials();
-		for ( var materialName in materials ) {
-
-			materialNames[ materialName ] = materialName;
-
-		}
-		this.workerSupport.run(
-			{
-				params: {
-					useAsync: true,
-					materialPerSmoothingGroup: this.materialPerSmoothingGroup,
-					useOAsMesh: this.useOAsMesh,
-					useIndices: this.useIndices,
-					disregardNormals: this.disregardNormals
-				},
-				logging: {
-					enabled: this.logging.enabled,
-					debug: this.logging.debug
-				},
-				materials: {
-					// in async case only material names are supplied to parser
-					materials: materialNames
-				},
-				data: {
-					input: content,
-					options: null
-				}
-			}
-		);
-	},
-
-	/**
-	 * Utility method for loading an mtl file according resource description. Provide url or content.
-	 *
-	 * @param {string} url URL to the file
-	 * @param {Object} content The file content as arraybuffer or text
-	 * @param {function} onLoad Callback to be called after successful load
-	 * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes.
-	 * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument.
-	 * @param {string} [crossOrigin] CORS value
- 	 * @param {Object} [materialOptions] Set material loading options for MTLLoader
-	 */
-	loadMtl: function ( url, content, onLoad, onProgress, onError, crossOrigin, materialOptions ) {
-		var resource = new THREE.LoaderSupport.ResourceDescriptor( url, 'MTL' );
-		resource.setContent( content );
-		this._loadMtl( resource, onLoad, onProgress, onError, crossOrigin, materialOptions );
-	},
-
-	_loadMtl: function ( resource, onLoad, onProgress, onError, crossOrigin, materialOptions ) {
-		if ( THREE.MTLLoader === undefined ) console.error( '"THREE.MTLLoader" is not available. "THREE.OBJLoader2" requires it for loading MTL files.' );
-		if ( THREE.LoaderSupport.Validator.isValid( resource ) && this.logging.enabled ) console.time( 'Loading MTL: ' + resource.name );
-
-		var materials = {};
-		var scope = this;
-		var processMaterials = function ( materialCreator ) {
-			var materialCreatorMaterials = [];
-			if ( THREE.LoaderSupport.Validator.isValid( materialCreator ) ) {
-
-				materialCreator.preload();
-				materialCreatorMaterials = materialCreator.materials;
-				for ( var materialName in materialCreatorMaterials ) {
-
-					if ( materialCreatorMaterials.hasOwnProperty( materialName ) ) {
-
-						materials[ materialName ] = materialCreatorMaterials[ materialName ];
-
-					}
-				}
-			}
-
-			if ( THREE.LoaderSupport.Validator.isValid( resource ) && scope.logging.enabled ) console.timeEnd( 'Loading MTL: ' + resource.name );
-			onLoad( materials, materialCreator );
-		};
-
-		// fast-fail
-		if ( ! THREE.LoaderSupport.Validator.isValid( resource ) || ( ! THREE.LoaderSupport.Validator.isValid( resource.content ) && ! THREE.LoaderSupport.Validator.isValid( resource.url ) ) ) {
-
-			processMaterials();
-
-		} else {
-
-			var mtlLoader = new THREE.MTLLoader( this.manager );
-			crossOrigin = THREE.LoaderSupport.Validator.verifyInput( crossOrigin, 'anonymous' );
-			mtlLoader.setCrossOrigin( crossOrigin );
-			mtlLoader.setResourcePath( resource.resourcePath || resource.path );
-			if ( THREE.LoaderSupport.Validator.isValid( materialOptions ) ) mtlLoader.setMaterialOptions( materialOptions );
-
-			var parseTextWithMtlLoader = function ( content ) {
-				var contentAsText = content;
-				if ( typeof( content ) !== 'string' && ! ( content instanceof String ) ) {
-
-					if ( content.length > 0 || content.byteLength > 0 ) {
-
-						contentAsText = THREE.LoaderUtils.decodeText( content );
-
-					} else {
-
-						scope._onError( 'Unable to parse mtl as it it seems to be neither a String, an Array or an ArrayBuffer!' );
-					}
-
-				}
-				processMaterials( mtlLoader.parse( contentAsText ) );
-			};
-
-			if ( THREE.LoaderSupport.Validator.isValid( resource.content ) ) {
-
-				parseTextWithMtlLoader( resource.content );
-
-			} else if ( THREE.LoaderSupport.Validator.isValid( resource.url ) ) {
-
-				var fileLoader = new THREE.FileLoader( this.manager );
-				if ( ! THREE.LoaderSupport.Validator.isValid( onError ) ) {
-					onError = function ( event ) {
-						scope._onError( event );
-					}
-				}
-				if ( ! THREE.LoaderSupport.Validator.isValid( onProgress ) ) {
-					var numericalValueRef = 0;
-					var numericalValue = 0;
-					onProgress = function ( event ) {
-						if ( ! event.lengthComputable ) return;
-
-						numericalValue = event.loaded / event.total;
-						if ( numericalValue > numericalValueRef ) {
-
-							numericalValueRef = numericalValue;
-							var output = 'Download of "' + resource.url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%';
-							scope.onProgress( 'progressLoad', output, numericalValue );
-
-						}
-					};
-				}
-
-				fileLoader.load( resource.url, parseTextWithMtlLoader, onProgress, onError );
-
-			}
-		}
-	}
-};
-
-
-/**
- * Parse OBJ data either from ArrayBuffer or string
- * @class
- */
-THREE.OBJLoader2.Parser = function () {
-	this.callbacks = {
-		onProgress: null,
-		onAssetAvailable: null,
-		onError: null
-	};
-	this.contentRef = null;
-	this.legacyMode = false;
-
-	this.materials = {};
-	this.useAsync = false;
-	this.materialPerSmoothingGroup = false;
-	this.useOAsMesh = false;
-	this.useIndices = false;
-	this.disregardNormals = false;
-
-	this.vertices = [];
-	this.colors = [];
-	this.normals = [];
-	this.uvs = [];
-
-	this.rawMesh = {
-		objectName: '',
-		groupName: '',
-		activeMtlName: '',
-		mtllibName: '',
-
-		// reset with new mesh
-		faceType: -1,
-		subGroups: [],
-		subGroupInUse: null,
-		smoothingGroup: {
-			splitMaterials: false,
-			normalized: -1,
-			real: -1
-		},
-		counts: {
-			doubleIndicesCount: 0,
-			faceCount: 0,
-			mtlCount: 0,
-			smoothingGroupCount: 0
-		}
-	};
-
-	this.inputObjectCount = 1;
-	this.outputObjectCount = 1;
-	this.globalCounts = {
-		vertices: 0,
-		faces: 0,
-		doubleIndicesCount: 0,
-		lineByte: 0,
-		currentByte: 0,
-		totalBytes: 0
-	};
-
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-};
-
-
-THREE.OBJLoader2.Parser.prototype = {
-
-	constructor: THREE.OBJLoader2.Parser,
-
-	resetRawMesh: function () {
-		// faces are stored according combined index of group, material and smoothingGroup (0 or not)
-		this.rawMesh.subGroups = [];
-		this.rawMesh.subGroupInUse = null;
-		this.rawMesh.smoothingGroup.normalized = -1;
-		this.rawMesh.smoothingGroup.real = -1;
-
-		// this default index is required as it is possible to define faces without 'g' or 'usemtl'
-		this.pushSmoothingGroup( 1 );
-
-		this.rawMesh.counts.doubleIndicesCount = 0;
-		this.rawMesh.counts.faceCount = 0;
-		this.rawMesh.counts.mtlCount = 0;
-		this.rawMesh.counts.smoothingGroupCount = 0;
-	},
-
-	setUseAsync: function ( useAsync ) {
-		this.useAsync = useAsync;
-	},
-
-	setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) {
-		this.materialPerSmoothingGroup = materialPerSmoothingGroup;
-	},
-
-	setUseOAsMesh: function ( useOAsMesh ) {
-		this.useOAsMesh = useOAsMesh;
-	},
-
-	setUseIndices: function ( useIndices ) {
-		this.useIndices = useIndices;
-	},
-
-	setDisregardNormals: function ( disregardNormals ) {
-		this.disregardNormals = disregardNormals;
-	},
-
-	setMaterials: function ( materials ) {
-		if ( materials === undefined || materials === null ) return;
-
-		for ( var materialName in materials ) {
-			if ( materials.hasOwnProperty( materialName ) ) {
-
-				this.materials[ materialName ] = materials[ materialName ];
-
-			}
-		}
-	},
-
-	setCallbackOnAssetAvailable: function ( onAssetAvailable ) {
-		if ( onAssetAvailable !== null && onAssetAvailable !== undefined ) {
-
-			this.callbacks.onAssetAvailable = onAssetAvailable;
-
-		}
-	},
-
-	setCallbackOnProgress: function ( onProgress ) {
-		if ( onProgress !== null && onProgress !== undefined ) {
-
-			this.callbacks.onProgress = onProgress;
-
-		}
-	},
-
-	setCallbackOnError: function ( onError ) {
-		if ( onError !== null && onError !== undefined ) {
-
-			this.callbacks.onError = onError;
-
-		}
-	},
-
-
-	setLogging: function ( enabled, debug ) {
-		this.logging.enabled = enabled === true;
-		this.logging.debug = debug === true;
-	},
-
-	configure: function () {
-		if ( this.callbacks.onAssetAvailable === null ) {
-
-			var errorMessage = 'Unable to run as no callback for building meshes is set.';
-			if ( this.callbacks.onError !== null ) {
-
-				this.callbacks.onError( errorMessage );
-
-			} else {
-
-				throw errorMessage;
-			}
-
-		}
-		this.pushSmoothingGroup( 1 );
-
-		if ( this.logging.enabled ) {
-
-			var matKeys = Object.keys( this.materials );
-			var matNames = ( matKeys.length > 0 ) ? '\n\tmaterialNames:\n\t\t- ' + matKeys.join( '\n\t\t- ' ) : '\n\tmaterialNames: None';
-			var printedConfig = 'OBJLoader2.Parser configuration:'
-				+ matNames
-				+ '\n\tuseAsync: ' + this.useAsync
-				+ '\n\tmaterialPerSmoothingGroup: ' + this.materialPerSmoothingGroup
-				+ '\n\tuseOAsMesh: ' + this.useOAsMesh
-				+ '\n\tuseIndices: ' + this.useIndices
-				+ '\n\tdisregardNormals: ' + this.disregardNormals;
-			if ( this.callbacks.onProgress !== null ) {
-				printedConfig += '\n\tcallbacks.onProgress: ' + this.callbacks.onProgress.name;
-			}
-			if ( this.callbacks.onAssetAvailable !== null ) {
-				printedConfig += '\n\tcallbacks.onAssetAvailable: ' + this.callbacks.onAssetAvailable.name;
-			}
-			if ( this.callbacks.onError !== null ) {
-				printedConfig += '\n\tcallbacks.onError: ' + this.callbacks.onError.name;
-			}
-			console.info( printedConfig );
-		}
-	},
-
-	/**
-	 * Parse the provided arraybuffer
-	 *
-	 * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array
-	 */
-	parse: function ( arrayBuffer ) {
-		if ( this.logging.enabled ) console.time( 'OBJLoader2.Parser.parse' );
-		this.configure();
-
-		var arrayBufferView = new Uint8Array( arrayBuffer );
-		this.contentRef = arrayBufferView;
-		var length = arrayBufferView.byteLength;
-		this.globalCounts.totalBytes = length;
-		var buffer = new Array( 128 );
-
-		for ( var code, word = '', bufferPointer = 0, slashesCount = 0, i = 0; i < length; i++ ) {
-
-			code = arrayBufferView[ i ];
-			switch ( code ) {
-				// space
-				case 32:
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					word = '';
-					break;
-				// slash
-				case 47:
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					slashesCount++;
-					word = '';
-					break;
-
-				// LF
-				case 10:
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					word = '';
-					this.globalCounts.lineByte = this.globalCounts.currentByte;
-					this.globalCounts.currentByte = i;
-					this.processLine( buffer, bufferPointer, slashesCount );
-					bufferPointer = 0;
-					slashesCount = 0;
-					break;
-
-				// CR
-				case 13:
-					break;
-
-				default:
-					word += String.fromCharCode( code );
-					break;
-			}
-		}
-		this.finalizeParsing();
-		if ( this.logging.enabled ) console.timeEnd(  'OBJLoader2.Parser.parse' );
-	},
-
-	/**
-	 * Parse the provided text
-	 *
-	 * @param {string} text OBJ data as string
-	 */
-	parseText: function ( text ) {
-		if ( this.logging.enabled ) console.time(  'OBJLoader2.Parser.parseText' );
-		this.configure();
-		this.legacyMode = true;
-		this.contentRef = text;
-		var length = text.length;
-		this.globalCounts.totalBytes = length;
-		var buffer = new Array( 128 );
-
-		for ( var char, word = '', bufferPointer = 0, slashesCount = 0, i = 0; i < length; i++ ) {
-
-			char = text[ i ];
-			switch ( char ) {
-				case ' ':
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					word = '';
-					break;
-
-				case '/':
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					slashesCount++;
-					word = '';
-					break;
-
-				case '\n':
-					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
-					word = '';
-					this.globalCounts.lineByte = this.globalCounts.currentByte;
-					this.globalCounts.currentByte = i;
-					this.processLine( buffer, bufferPointer, slashesCount );
-					bufferPointer = 0;
-					slashesCount = 0;
-					break;
-
-				case '\r':
-					break;
-
-				default:
-					word += char;
-			}
-		}
-		this.finalizeParsing();
-		if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2.Parser.parseText' );
-	},
-
-	processLine: function ( buffer, bufferPointer, slashesCount ) {
-		if ( bufferPointer < 1 ) return;
-
-		var reconstructString = function ( content, legacyMode, start, stop ) {
-			var line = '';
-			if ( stop > start ) {
-
-				var i;
-				if ( legacyMode ) {
-
-					for ( i = start; i < stop; i++ ) line += content[ i ];
-
-				} else {
-
-
-					for ( i = start; i < stop; i++ ) line += String.fromCharCode( content[ i ] );
-
-				}
-				line = line.trim();
-
-			}
-			return line;
-		};
-
-		var bufferLength, length, i, lineDesignation;
-		lineDesignation = buffer [ 0 ];
-		switch ( lineDesignation ) {
-			case 'v':
-				this.vertices.push( parseFloat( buffer[ 1 ] ) );
-				this.vertices.push( parseFloat( buffer[ 2 ] ) );
-				this.vertices.push( parseFloat( buffer[ 3 ] ) );
-				if ( bufferPointer > 4 ) {
-
-					this.colors.push( parseFloat( buffer[ 4 ] ) );
-					this.colors.push( parseFloat( buffer[ 5 ] ) );
-					this.colors.push( parseFloat( buffer[ 6 ] ) );
-
-				}
-				break;
-
-			case 'vt':
-				this.uvs.push( parseFloat( buffer[ 1 ] ) );
-				this.uvs.push( parseFloat( buffer[ 2 ] ) );
-				break;
-
-			case 'vn':
-				this.normals.push( parseFloat( buffer[ 1 ] ) );
-				this.normals.push( parseFloat( buffer[ 2 ] ) );
-				this.normals.push( parseFloat( buffer[ 3 ] ) );
-				break;
-
-			case 'f':
-				bufferLength = bufferPointer - 1;
-
-				// "f vertex ..."
-				if ( slashesCount === 0 ) {
-
-					this.checkFaceType( 0 );
-					for ( i = 2, length = bufferLength; i < length; i ++ ) {
-
-						this.buildFace( buffer[ 1 ] );
-						this.buildFace( buffer[ i ] );
-						this.buildFace( buffer[ i + 1 ] );
-
-					}
-
-					// "f vertex/uv ..."
-				} else if  ( bufferLength === slashesCount * 2 ) {
-
-					this.checkFaceType( 1 );
-					for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) {
-
-						this.buildFace( buffer[ 1 ], buffer[ 2 ] );
-						this.buildFace( buffer[ i ], buffer[ i + 1 ] );
-						this.buildFace( buffer[ i + 2 ], buffer[ i + 3 ] );
-
-					}
-
-					// "f vertex/uv/normal ..."
-				} else if  ( bufferLength * 2 === slashesCount * 3 ) {
-
-					this.checkFaceType( 2 );
-					for ( i = 4, length = bufferLength - 3; i < length; i += 3 ) {
-
-						this.buildFace( buffer[ 1 ], buffer[ 2 ], buffer[ 3 ] );
-						this.buildFace( buffer[ i ], buffer[ i + 1 ], buffer[ i + 2 ] );
-						this.buildFace( buffer[ i + 3 ], buffer[ i + 4 ], buffer[ i + 5 ] );
-
-					}
-
-					// "f vertex//normal ..."
-				} else {
-
-					this.checkFaceType( 3 );
-					for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) {
-
-						this.buildFace( buffer[ 1 ], undefined, buffer[ 2 ] );
-						this.buildFace( buffer[ i ], undefined, buffer[ i + 1 ] );
-						this.buildFace( buffer[ i + 2 ], undefined, buffer[ i + 3 ] );
-
-					}
-
-				}
-				break;
-
-			case 'l':
-			case 'p':
-				bufferLength = bufferPointer - 1;
-				if ( bufferLength === slashesCount * 2 )  {
-
-					this.checkFaceType( 4 );
-					for ( i = 1, length = bufferLength + 1; i < length; i += 2 ) this.buildFace( buffer[ i ], buffer[ i + 1 ] );
-
-				} else {
-
-					this.checkFaceType( ( lineDesignation === 'l' ) ? 5 : 6  );
-					for ( i = 1, length = bufferLength + 1; i < length; i ++ ) this.buildFace( buffer[ i ] );
-
-				}
-				break;
-
-			case 's':
-				this.pushSmoothingGroup( buffer[ 1 ] );
-				break;
-
-			case 'g':
-				// 'g' leads to creation of mesh if valid data (faces declaration was done before), otherwise only groupName gets set
-				this.processCompletedMesh();
-				this.rawMesh.groupName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 2, this.globalCounts.currentByte );
-				break;
-
-			case 'o':
-				// 'o' is meta-information and usually does not result in creation of new meshes, but can be enforced with "useOAsMesh"
-				if ( this.useOAsMesh ) this.processCompletedMesh();
-				this.rawMesh.objectName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 2, this.globalCounts.currentByte );
-				break;
-
-			case 'mtllib':
-				this.rawMesh.mtllibName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 7, this.globalCounts.currentByte );
-				break;
-
-			case 'usemtl':
-				var mtlName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 7, this.globalCounts.currentByte );
-				if ( mtlName !== '' && this.rawMesh.activeMtlName !== mtlName ) {
-
-					this.rawMesh.activeMtlName = mtlName;
-					this.rawMesh.counts.mtlCount++;
-					this.checkSubGroup();
-
-				}
-				break;
-
-			default:
-				break;
-		}
-	},
-
-	pushSmoothingGroup: function ( smoothingGroup ) {
-		var smoothingGroupInt = parseInt( smoothingGroup );
-		if ( isNaN( smoothingGroupInt ) ) {
-			smoothingGroupInt = smoothingGroup === "off" ? 0 : 1;
-		}
-
-		var smoothCheck = this.rawMesh.smoothingGroup.normalized;
-		this.rawMesh.smoothingGroup.normalized = this.rawMesh.smoothingGroup.splitMaterials ? smoothingGroupInt : ( smoothingGroupInt === 0 ) ? 0 : 1;
-		this.rawMesh.smoothingGroup.real = smoothingGroupInt;
-
-		if ( smoothCheck !== smoothingGroupInt ) {
-
-			this.rawMesh.counts.smoothingGroupCount++;
-			this.checkSubGroup();
-
-		}
-	},
-
-	/**
-	 * Expanded faceTypes include all four face types, both line types and the point type
-	 * faceType = 0: "f vertex ..."
-	 * faceType = 1: "f vertex/uv ..."
-	 * faceType = 2: "f vertex/uv/normal ..."
-	 * faceType = 3: "f vertex//normal ..."
-	 * faceType = 4: "l vertex/uv ..." or "l vertex ..."
-	 * faceType = 5: "l vertex ..."
-	 * faceType = 6: "p vertex ..."
-	 */
-	checkFaceType: function ( faceType ) {
-		if ( this.rawMesh.faceType !== faceType ) {
-
-			this.processCompletedMesh();
-			this.rawMesh.faceType = faceType;
-			this.checkSubGroup();
-
-		}
-	},
-
-	checkSubGroup: function () {
-		var index = this.rawMesh.activeMtlName + '|' + this.rawMesh.smoothingGroup.normalized;
-		this.rawMesh.subGroupInUse = this.rawMesh.subGroups[ index ];
-
-		if ( this.rawMesh.subGroupInUse === undefined || this.rawMesh.subGroupInUse === null ) {
-
-			this.rawMesh.subGroupInUse = {
-				index: index,
-				objectName: this.rawMesh.objectName,
-				groupName: this.rawMesh.groupName,
-				materialName: this.rawMesh.activeMtlName,
-				smoothingGroup: this.rawMesh.smoothingGroup.normalized,
-				vertices: [],
-				indexMappingsCount: 0,
-				indexMappings: [],
-				indices: [],
-				colors: [],
-				uvs: [],
-				normals: []
-			};
-			this.rawMesh.subGroups[ index ] = this.rawMesh.subGroupInUse;
-
-		}
-	},
-
-	buildFace: function ( faceIndexV, faceIndexU, faceIndexN ) {
-		if ( this.disregardNormals ) faceIndexN = undefined;
-		var scope = this;
-		var updateSubGroupInUse = function () {
-
-			var faceIndexVi = parseInt( faceIndexV );
-			var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 );
-			var indexPointerC = scope.colors.length > 0 ? indexPointerV : null;
-
-			var vertices = scope.rawMesh.subGroupInUse.vertices;
-			vertices.push( scope.vertices[ indexPointerV++ ] );
-			vertices.push( scope.vertices[ indexPointerV++ ] );
-			vertices.push( scope.vertices[ indexPointerV ] );
-
-			if ( indexPointerC !== null ) {
-
-				var colors = scope.rawMesh.subGroupInUse.colors;
-				colors.push( scope.colors[ indexPointerC++ ] );
-				colors.push( scope.colors[ indexPointerC++ ] );
-				colors.push( scope.colors[ indexPointerC ] );
-
-			}
-			if ( faceIndexU ) {
-
-				var faceIndexUi = parseInt( faceIndexU );
-				var indexPointerU = 2 * ( faceIndexUi > 0 ? faceIndexUi - 1 : faceIndexUi + scope.uvs.length / 2 );
-				var uvs = scope.rawMesh.subGroupInUse.uvs;
-				uvs.push( scope.uvs[ indexPointerU++ ] );
-				uvs.push( scope.uvs[ indexPointerU ] );
-
-			}
-			if ( faceIndexN ) {
-
-				var faceIndexNi = parseInt( faceIndexN );
-				var indexPointerN = 3 * ( faceIndexNi > 0 ? faceIndexNi - 1 : faceIndexNi + scope.normals.length / 3 );
-				var normals = scope.rawMesh.subGroupInUse.normals;
-				normals.push( scope.normals[ indexPointerN++ ] );
-				normals.push( scope.normals[ indexPointerN++ ] );
-				normals.push( scope.normals[ indexPointerN ] );
-
-			}
-		};
-
-		if ( this.useIndices ) {
-
-			var mappingName = faceIndexV + ( faceIndexU ? '_' + faceIndexU : '_n' ) + ( faceIndexN ? '_' + faceIndexN : '_n' );
-			var indicesPointer = this.rawMesh.subGroupInUse.indexMappings[ mappingName ];
-			if ( indicesPointer === undefined || indicesPointer === null ) {
-
-				indicesPointer = this.rawMesh.subGroupInUse.vertices.length / 3;
-				updateSubGroupInUse();
-				this.rawMesh.subGroupInUse.indexMappings[ mappingName ] = indicesPointer;
-				this.rawMesh.subGroupInUse.indexMappingsCount++;
-
-			} else {
-
-				this.rawMesh.counts.doubleIndicesCount++;
-
-			}
-			this.rawMesh.subGroupInUse.indices.push( indicesPointer );
-
-		} else {
-
-			updateSubGroupInUse();
-
-		}
-		this.rawMesh.counts.faceCount++;
-	},
-
-	createRawMeshReport: function ( inputObjectCount ) {
-		return 'Input Object number: ' + inputObjectCount +
-			'\n\tObject name: ' + this.rawMesh.objectName +
-			'\n\tGroup name: ' + this.rawMesh.groupName +
-			'\n\tMtllib name: ' + this.rawMesh.mtllibName +
-			'\n\tVertex count: ' + this.vertices.length / 3 +
-			'\n\tNormal count: ' + this.normals.length / 3 +
-			'\n\tUV count: ' + this.uvs.length / 2 +
-			'\n\tSmoothingGroup count: ' + this.rawMesh.counts.smoothingGroupCount +
-			'\n\tMaterial count: ' + this.rawMesh.counts.mtlCount +
-			'\n\tReal MeshOutputGroup count: ' + this.rawMesh.subGroups.length;
-	},
-
-	/**
-	 * Clear any empty subGroup and calculate absolute vertex, normal and uv counts
-	 */
-	finalizeRawMesh: function () {
-		var meshOutputGroupTemp = [];
-		var meshOutputGroup;
-		var absoluteVertexCount = 0;
-		var absoluteIndexMappingsCount = 0;
-		var absoluteIndexCount = 0;
-		var absoluteColorCount = 0;
-		var absoluteNormalCount = 0;
-		var absoluteUvCount = 0;
-		var indices;
-		for ( var name in this.rawMesh.subGroups ) {
-
-			meshOutputGroup = this.rawMesh.subGroups[ name ];
-			if ( meshOutputGroup.vertices.length > 0 ) {
-
-				indices = meshOutputGroup.indices;
-				if ( indices.length > 0 && absoluteIndexMappingsCount > 0 ) {
-
-					for ( var i in indices ) indices[ i ] = indices[ i ] + absoluteIndexMappingsCount;
-
-				}
-				meshOutputGroupTemp.push( meshOutputGroup );
-				absoluteVertexCount += meshOutputGroup.vertices.length;
-				absoluteIndexMappingsCount += meshOutputGroup.indexMappingsCount;
-				absoluteIndexCount += meshOutputGroup.indices.length;
-				absoluteColorCount += meshOutputGroup.colors.length;
-				absoluteUvCount += meshOutputGroup.uvs.length;
-				absoluteNormalCount += meshOutputGroup.normals.length;
-
-			}
-		}
-
-		// do not continue if no result
-		var result = null;
-		if ( meshOutputGroupTemp.length > 0 ) {
-
-			result = {
-				name: this.rawMesh.groupName !== '' ? this.rawMesh.groupName : this.rawMesh.objectName,
-				subGroups: meshOutputGroupTemp,
-				absoluteVertexCount: absoluteVertexCount,
-				absoluteIndexCount: absoluteIndexCount,
-				absoluteColorCount: absoluteColorCount,
-				absoluteNormalCount: absoluteNormalCount,
-				absoluteUvCount: absoluteUvCount,
-				faceCount: this.rawMesh.counts.faceCount,
-				doubleIndicesCount: this.rawMesh.counts.doubleIndicesCount
-			};
-
-		}
-		return result;
-	},
-
-	processCompletedMesh: function () {
-		var result = this.finalizeRawMesh();
-		var haveMesh = result !== null;
-		if ( haveMesh ) {
-
-			if ( this.colors.length > 0 && this.colors.length !== this.vertices.length && this.callbacks.onError !== null ) {
-
-				this.callbacks.onError( 'Vertex Colors were detected, but vertex count and color count do not match!' );
-
-			}
-			if ( this.logging.enabled && this.logging.debug ) console.debug( this.createRawMeshReport( this.inputObjectCount ) );
-			this.inputObjectCount++;
-
-			this.buildMesh( result );
-			var progressBytesPercent = this.globalCounts.currentByte / this.globalCounts.totalBytes;
-			if ( this.callbacks.onProgress !== null ) {
-
-				this.callbacks.onProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName +
-					'] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
-
-			}
-			this.resetRawMesh();
-			return true;
-
-		}
-		return haveMesh;
-	},
-
-	/**
-	 * SubGroups are transformed to too intermediate format that is forwarded to the MeshBuilder.
-	 * It is ensured that SubGroups only contain objects with vertices (no need to check).
-	 *
-	 * @param result
-	 */
-	buildMesh: function ( result ) {
-		var meshOutputGroups = result.subGroups;
-
-		var vertexFA = new Float32Array( result.absoluteVertexCount );
-		this.globalCounts.vertices += result.absoluteVertexCount / 3;
-		this.globalCounts.faces += result.faceCount;
-		this.globalCounts.doubleIndicesCount += result.doubleIndicesCount;
-		var indexUA = ( result.absoluteIndexCount > 0 ) ? new Uint32Array( result.absoluteIndexCount ) : null;
-		var colorFA = ( result.absoluteColorCount > 0 ) ? new Float32Array( result.absoluteColorCount ) : null;
-		var normalFA = ( result.absoluteNormalCount > 0 ) ? new Float32Array( result.absoluteNormalCount ) : null;
-		var uvFA = ( result.absoluteUvCount > 0 ) ? new Float32Array( result.absoluteUvCount ) : null;
-		var haveVertexColors = colorFA !== null;
-
-		var meshOutputGroup;
-		var materialNames = [];
-
-		var createMultiMaterial = ( meshOutputGroups.length > 1 );
-		var materialIndex = 0;
-		var materialIndexMapping = [];
-		var selectedMaterialIndex;
-		var materialGroup;
-		var materialGroups = [];
-
-		var vertexFAOffset = 0;
-		var indexUAOffset = 0;
-		var colorFAOffset = 0;
-		var normalFAOffset = 0;
-		var uvFAOffset = 0;
-		var materialGroupOffset = 0;
-		var materialGroupLength = 0;
-
-		var materialOrg, material, materialName, materialNameOrg;
-		// only one specific face type
-		for ( var oodIndex in meshOutputGroups ) {
-
-			if ( ! meshOutputGroups.hasOwnProperty( oodIndex ) ) continue;
-			meshOutputGroup = meshOutputGroups[ oodIndex ];
-
-			materialNameOrg = meshOutputGroup.materialName;
-			if ( this.rawMesh.faceType < 4 ) {
-
-				materialName = materialNameOrg + ( haveVertexColors ? '_vertexColor' : '' ) + ( meshOutputGroup.smoothingGroup === 0 ? '_flat' : '' );
-
-
-			} else {
-
-				materialName = this.rawMesh.faceType === 6 ? 'defaultPointMaterial' : 'defaultLineMaterial';
-
-			}
-			materialOrg = this.materials[ materialNameOrg ];
-			material = this.materials[ materialName ];
-
-			// both original and derived names do not lead to an existing material => need to use a default material
-			if ( ( materialOrg === undefined || materialOrg === null ) && ( material === undefined || material === null ) ) {
-
-				materialName = haveVertexColors ? 'defaultVertexColorMaterial' : 'defaultMaterial';
-				material = this.materials[ materialName ];
-				if ( this.logging.enabled ) {
-
-					console.info( 'object_group "' + meshOutputGroup.objectName + '_' +
-						meshOutputGroup.groupName + '" was defined with unresolvable material "' +
-						materialNameOrg + '"! Assigning "' + materialName + '".' );
-
-				}
-
-			}
-			if ( material === undefined || material === null ) {
-
-				var materialCloneInstructions = {
-					materialNameOrg: materialNameOrg,
-					materialName: materialName,
-					materialProperties: {
-						vertexColors: haveVertexColors ? 2 : 0,
-						flatShading: meshOutputGroup.smoothingGroup === 0
-					}
-				};
-				var payload = {
-					cmd: 'materialData',
-					materials: {
-						materialCloneInstructions: materialCloneInstructions
-					}
-				};
-				this.callbacks.onAssetAvailable( payload );
-
-				// fake entry for async; sync Parser always works on material references (Builder update directly visible here)
-				if ( this.useAsync ) this.materials[ materialName ] = materialCloneInstructions;
-
-			}
-
-			if ( createMultiMaterial ) {
-
-				// re-use material if already used before. Reduces materials array size and eliminates duplicates
-				selectedMaterialIndex = materialIndexMapping[ materialName ];
-				if ( ! selectedMaterialIndex ) {
-
-					selectedMaterialIndex = materialIndex;
-					materialIndexMapping[ materialName ] = materialIndex;
-					materialNames.push( materialName );
-					materialIndex++;
-
-				}
-				materialGroupLength = this.useIndices ? meshOutputGroup.indices.length : meshOutputGroup.vertices.length / 3;
-				materialGroup = {
-					start: materialGroupOffset,
-					count: materialGroupLength,
-					index: selectedMaterialIndex
-				};
-				materialGroups.push( materialGroup );
-				materialGroupOffset += materialGroupLength;
-
-			} else {
-
-				materialNames.push( materialName );
-
-			}
-
-			vertexFA.set( meshOutputGroup.vertices, vertexFAOffset );
-			vertexFAOffset += meshOutputGroup.vertices.length;
-
-			if ( indexUA ) {
-
-				indexUA.set( meshOutputGroup.indices, indexUAOffset );
-				indexUAOffset += meshOutputGroup.indices.length;
-
-			}
-
-			if ( colorFA ) {
-
-				colorFA.set( meshOutputGroup.colors, colorFAOffset );
-				colorFAOffset += meshOutputGroup.colors.length;
-
-			}
-
-			if ( normalFA ) {
-
-				normalFA.set( meshOutputGroup.normals, normalFAOffset );
-				normalFAOffset += meshOutputGroup.normals.length;
-
-			}
-			if ( uvFA ) {
-
-				uvFA.set( meshOutputGroup.uvs, uvFAOffset );
-				uvFAOffset += meshOutputGroup.uvs.length;
-
-			}
-
-			if ( this.logging.enabled && this.logging.debug ) {
-				var materialIndexLine = ( selectedMaterialIndex === undefined || selectedMaterialIndex === null ) ? '' : '\n\t\tmaterialIndex: ' + selectedMaterialIndex;
-				var createdReport = '\tOutput Object no.: ' + this.outputObjectCount +
-					'\n\t\tgroupName: ' + meshOutputGroup.groupName +
-					'\n\t\tIndex: ' + meshOutputGroup.index +
-					'\n\t\tfaceType: ' + this.rawMesh.faceType +
-					'\n\t\tmaterialName: ' + meshOutputGroup.materialName +
-					'\n\t\tsmoothingGroup: ' + meshOutputGroup.smoothingGroup +
-					materialIndexLine +
-					'\n\t\tobjectName: ' + meshOutputGroup.objectName +
-					'\n\t\t#vertices: ' + meshOutputGroup.vertices.length / 3 +
-					'\n\t\t#indices: ' + meshOutputGroup.indices.length +
-					'\n\t\t#colors: ' + meshOutputGroup.colors.length / 3 +
-					'\n\t\t#uvs: ' + meshOutputGroup.uvs.length / 2 +
-					'\n\t\t#normals: ' + meshOutputGroup.normals.length / 3;
-				console.debug( createdReport );
-			}
-
-		}
-
-		this.outputObjectCount++;
-		this.callbacks.onAssetAvailable(
-			{
-				cmd: 'meshData',
-				progress: {
-					numericalValue: this.globalCounts.currentByte / this.globalCounts.totalBytes
-				},
-				params: {
-					meshName: result.name
-				},
-				materials: {
-					multiMaterial: createMultiMaterial,
-					materialNames: materialNames,
-					materialGroups: materialGroups
-				},
-				buffers: {
-					vertices: vertexFA,
-					indices: indexUA,
-					colors: colorFA,
-					normals: normalFA,
-					uvs: uvFA
-				},
-				// 0: mesh, 1: line, 2: point
-				geometryType: this.rawMesh.faceType < 4 ? 0 : ( this.rawMesh.faceType === 6 ) ? 2 : 1
-			},
-			[ vertexFA.buffer ],
-			indexUA !== null ?  [ indexUA.buffer ] : null,
-			colorFA !== null ? [ colorFA.buffer ] : null,
-			normalFA !== null ? [ normalFA.buffer ] : null,
-			uvFA !== null ? [ uvFA.buffer ] : null
-		);
-	},
-
-	finalizeParsing: function () {
-		if ( this.logging.enabled ) console.info( 'Global output object count: ' + this.outputObjectCount );
-		if ( this.processCompletedMesh() && this.logging.enabled ) {
-
-			var parserFinalReport = 'Overall counts: ' +
-				'\n\tVertices: ' + this.globalCounts.vertices +
-				'\n\tFaces: ' + this.globalCounts.faces +
-				'\n\tMultiple definitions: ' + this.globalCounts.doubleIndicesCount;
-			console.info( parserFinalReport );
-
-		}
-	}
-};

+ 26 - 23
examples/jsm/loaders/OBJLoader2.js

@@ -9,9 +9,9 @@ import {
 	Group
 } from "../../../build/three.module.js";
 
-import { Parser } from "./worker/independent/OBJLoader2Parser.js";
-import { MeshReceiver } from "./shared/MeshReceiver.js";
-import { MaterialHandler } from "./shared/MaterialHandler.js";
+import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
+import { MeshReceiver } from "./obj2/shared/MeshReceiver.js";
+import { MaterialHandler } from "./obj2/shared/MaterialHandler.js";
 
 /**
  * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer
@@ -44,10 +44,9 @@ const OBJLoader2 = function ( manager ) {
 	this.materialHandler = new MaterialHandler();
 	this.meshReceiver = new MeshReceiver( this.materialHandler );
 };
-OBJLoader2.OBJLOADER2_VERSION = '3.0.0-beta';
+OBJLoader2.OBJLOADER2_VERSION = '3.0.0-beta2';
 console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );
 
-
 OBJLoader2.prototype = {
 
 	constructor: OBJLoader2,
@@ -320,7 +319,7 @@ OBJLoader2.prototype = {
 			console.time( 'OBJLoader parse: ' + this.modelName );
 
 		}
-		let parser = new Parser();
+		let parser = new OBJLoader2Parser();
 		parser.setLogging( this.logging.enabled, this.logging.debug );
 		parser.setMaterialPerSmoothingGroup( this.materialPerSmoothingGroup );
 		parser.setUseOAsMesh( this.useOAsMesh );
@@ -330,22 +329,8 @@ OBJLoader2.prototype = {
 		parser.setMaterials( this.materialHandler.getMaterials() );
 
 		let scope = this;
-		let onMeshLoaded = function ( payload ) {
-
-			if ( payload.cmd !== 'data' ) return;
-
-			if ( payload.type === 'mesh' ) {
-
-				let meshes = scope.meshReceiver.buildMeshes( payload );
-				for ( let mesh of meshes ) {
-					scope.baseObject3d.add( mesh );
-				}
-
-			} else if ( payload.type === 'material' ) {
-
-				scope.materialHandler.addPayloadMaterials( payload );
-
-			}
+		let scopedOnAssetAvailable = function ( payload ) {
+			scope._onAssetAvailable( payload );
 		};
 		let onProgressScoped = function ( text, numericalValue ) {
 			scope._onProgress( 'progressParse', text, numericalValue );
@@ -353,7 +338,7 @@ OBJLoader2.prototype = {
 		let onErrorScoped = function ( message ) {
 			scope._onError( message );
 		};
-		parser.setCallbackOnAssetAvailable( onMeshLoaded );
+		parser.setCallbackOnAssetAvailable( scopedOnAssetAvailable );
 		parser.setCallbackOnProgress( onProgressScoped );
 		parser.setCallbackOnError( onErrorScoped );
 		if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) {
@@ -377,6 +362,24 @@ OBJLoader2.prototype = {
 
 		}
 		return this.baseObject3d;
+	},
+
+	_onAssetAvailable: function ( payload ) {
+
+		if ( payload.cmd !== 'assetAvailable' ) return;
+
+		if ( payload.type === 'mesh' ) {
+
+			let meshes = this.meshReceiver.buildMeshes( payload );
+			for ( let mesh of meshes ) {
+				this.baseObject3d.add( mesh );
+			}
+
+		} else if ( payload.type === 'material' ) {
+
+			this.materialHandler.addPayloadMaterials( payload );
+
+		}
 	}
 };
 

+ 188 - 0
examples/jsm/loaders/OBJLoader2Parallel.js

@@ -0,0 +1,188 @@
+/**
+ * @author Kai Salmen / https://kaisalmen.de
+ * Development repository: https://github.com/kaisalmen/WWOBJLoader
+ */
+
+// Imports only related to wrapper
+import {
+	CodeBuilderInstructions,
+	WorkerExecutionSupport
+} from "./obj2/worker/main/WorkerExecutionSupport.js";
+import { CodeSerializer } from "./obj2/utils/CodeSerializer.js";
+import { OBJLoader2 } from "./OBJLoader2.js";
+
+// Imports only related to worker (when standard workers (modules aren't supported) are used)
+import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
+import { ObjectManipulator } from "./obj2/utils/ObjectManipulator.js";
+import {
+	WorkerRunner,
+	DefaultWorkerPayloadHandler
+} from "./obj2/worker/parallel/WorkerRunner.js";
+
+/**
+ * Extends {OBJLoader2} with the capability to run the parser {OBJLoader2Parser} in web worker
+ * with help of {WorkerExecutionSupport}.
+ *
+ * @param [LoadingManager] manager
+ * @constructor
+ */
+const OBJLoader2Parallel = function ( manager ) {
+	OBJLoader2.call( this, manager );
+	this.preferJsmWorker = false;
+
+	this.callbacks.onParseComplete = null;
+	this.executeParallel = true;
+	this.workerExecutionSupport = new WorkerExecutionSupport();
+};
+OBJLoader2Parallel.prototype = Object.create( OBJLoader2.prototype );
+OBJLoader2Parallel.prototype.constructor = OBJLoader2Parallel;
+
+OBJLoader2.OBJLOADER2_PARALLEL_VERSION = '3.0.0-beta2';
+console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2.OBJLOADER2_PARALLEL_VERSION );
+
+
+OBJLoader2Parallel.prototype.setPreferJsmWorker = function ( preferJsmWorker ) {
+	this.preferJsmWorker = preferJsmWorker === true;
+	return this;
+};
+
+/**
+ * If this call back is not set, then the completion message from worker will not be received.
+ *
+ * @param {function} onParseComplete
+ * @return {OBJLoader2Parallel}
+ */
+OBJLoader2Parallel.prototype.setCallbackOnParseComplete = function ( onParseComplete ) {
+	if ( onParseComplete !== undefined && onParseComplete !== null ) {
+		this.callbacks.onParseComplete = onParseComplete;
+	}
+	return this;
+};
+
+/**
+ * Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
+ *
+ * @param executeParallel
+ * @return {OBJLoader2Parallel}
+ */
+OBJLoader2Parallel.prototype.setExecuteParallel = function ( executeParallel ) {
+	this.executeParallel = executeParallel === true;
+	return this;
+};
+
+/**
+ * Allow to get hold of {WorkerExecutionSupport} for configuratin purposes
+ *
+ * @return {WorkerExecutionSupport|WorkerExecutionSupport}
+ */
+OBJLoader2Parallel.prototype.getWorkerExecutionSupport = function () {
+	return this.workerExecutionSupport;
+};
+
+/**
+ * Provides instructions on what is to be contained in the worker
+ *
+ * @return {CodeBuilderInstructions}
+ */
+OBJLoader2Parallel.prototype.buildWorkerCode = function () {
+	let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
+	if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
+
+		codeBuilderInstructions.setJsmWorkerFile( '../../src/loaders/worker/parallel/jsm/OBJLoader2Worker.js' );
+
+	}
+	if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
+
+		let codeOBJLoader2Parser = CodeSerializer.serializeClass( 'OBJLoader2Parser', OBJLoader2Parser );
+		let codeObjectManipulator = CodeSerializer.serializeObject( 'ObjectManipulator', ObjectManipulator );
+		let codeParserPayloadHandler = CodeSerializer.serializeClass( 'DefaultWorkerPayloadHandler', DefaultWorkerPayloadHandler );
+		let codeWorkerRunner = CodeSerializer.serializeClass( 'WorkerRunner', WorkerRunner );
+
+		codeBuilderInstructions.addCodeFragment( codeOBJLoader2Parser );
+		codeBuilderInstructions.addCodeFragment( codeObjectManipulator );
+		codeBuilderInstructions.addCodeFragment( codeParserPayloadHandler );
+		codeBuilderInstructions.addCodeFragment( codeWorkerRunner );
+
+		// allows to include full libraries as importScripts
+//		codeBuilderInstructions.addLibraryImport( '../../node_modules/three/build/three.js' );
+		codeBuilderInstructions.addStartCode( 'new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );' );
+
+	}
+	return codeBuilderInstructions;
+};
+
+/**
+ * @private
+ */
+OBJLoader2Parallel.prototype._configure = function () {
+	if ( this.callbacks.onParseComplete === null ) {
+		throw "No callbackOnLoad was provided! Aborting!";
+	}
+	// check if worker is already available and if so, then fast-fail
+	if ( this.workerExecutionSupport.isWorkerLoaded( this.preferJsmWorker ) ) return;
+
+	this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );
+
+	let scope = this;
+	let scopedOnAssetAvailable = function ( payload ) {
+		scope._onAssetAvailable( payload );
+	};
+
+	this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, this.callbacks.onParseComplete );
+};
+
+/**
+ * Load is intercepted from {OBJLoader2}. It replaces the regular onLoad callback as the final worker result will be
+ * returned later by its own callbackOnLoad.
+ *
+ * @param {string}  url A string containing the path/URL of the file to be loaded.
+ * @param {function} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument.
+ * @param {function} [onFileLoadProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes.
+ * @param {function} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument.
+ * @param {function} [onMeshAlter] Called after worker successfully delivered a single mesh
+ */
+OBJLoader2Parallel.prototype.load = function( content, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
+	this.setCallbackOnParseComplete( onLoad );
+
+	OBJLoader2.prototype.load.call( this, content, function () {}, onFileLoadProgress, onError, onMeshAlter );
+};
+
+/**
+ * Parses OBJ data in parallel with web worker.
+ *
+ * @param {arraybuffer} content OBJ data as Uint8Array or String
+ */
+OBJLoader2Parallel.prototype.parse = function( content ) {
+	if ( this.executeParallel ) {
+
+		this._configure();
+
+		this.workerExecutionSupport.executeParallel(
+			{
+				params: {
+					modelName: this.modelName,
+					instanceNo: this.instanceNo,
+					useIndices: this.useIndices,
+					disregardNormals: this.disregardNormals,
+					materialPerSmoothingGroup: this.materialPerSmoothingGroup,
+					useOAsMesh: this.useOAsMesh,
+				},
+				materials: this.materialHandler.getMaterialsJSON(),
+				data: {
+					input: content,
+					options: null
+				},
+				logging: {
+					enabled: this.logging.enabled,
+					debug: this.logging.debug
+				}
+			} );
+
+	} else {
+
+		this.callbacks.onParseComplete( OBJLoader2.prototype.parse.call( this, content ) );
+
+	}
+};
+
+export { OBJLoader2Parallel }

+ 1 - 1
examples/jsm/loaders/bridge/MtlObjBridge.js → examples/jsm/loaders/obj2/bridge/MtlObjBridge.js

@@ -3,7 +3,7 @@
  * Development repository: https://github.com/kaisalmen/WWOBJLoader
  */
 
-import { MTLLoader } from "../../../jsm/loaders/MTLLoader.js";
+import { MTLLoader } from "../../../../jsm/loaders/MTLLoader.js";
 
 
 const MtlObjBridge = {

+ 4 - 4
examples/jsm/loaders/shared/MaterialHandler.js → examples/jsm/loaders/obj2/shared/MaterialHandler.js

@@ -9,7 +9,7 @@ import {
 	MeshStandardMaterial,
 	PointsMaterial,
 	VertexColors
-} from "../../../../build/three.module.js";
+} from "../../../../../build/three.module.js";
 
 
 const MaterialHandler = function () {
@@ -85,9 +85,9 @@ MaterialHandler.prototype = {
 		if ( materialCloneInstructions !== undefined && materialCloneInstructions !== null ) {
 
 			let materialNameOrg = materialCloneInstructions.materialNameOrg;
-			if ( materialNameOrg !== undefined && materialNameOrg !== null ) {
-
-				let materialOrg = this.materials[ materialNameOrg ];
+			materialNameOrg = (materialNameOrg !== undefined && materialNameOrg !== null) ? materialNameOrg : "";
+			let materialOrg = this.materials[ materialNameOrg ];
+			if ( materialOrg ) {
 				material = materialOrg.clone();
 
 				materialName = materialCloneInstructions.materialName;

+ 1 - 7
examples/jsm/loaders/shared/MeshReceiver.js → examples/jsm/loaders/obj2/shared/MeshReceiver.js

@@ -9,9 +9,7 @@ import {
 	LineSegments,
 	Mesh,
 	Points
-} from "../../../../build/three.module.js";
-
-import { MaterialHandler } from "./MaterialHandler.js";
+} from "../../../../../build/three.module.js";
 
 
 /**
@@ -20,8 +18,6 @@ import { MaterialHandler } from "./MaterialHandler.js";
  * @constructor
  */
 const MeshReceiver = function( materialHandler ) {
-	console.info( 'Using MeshReceiver version: ' + MeshReceiver.MESH_RECEIVER_VERSION );
-
 	this.logging = {
 		enabled: true,
 		debug: false
@@ -33,8 +29,6 @@ const MeshReceiver = function( materialHandler ) {
 	};
 	this.materialHandler = materialHandler;
 };
-MeshReceiver.MESH_RECEIVER_VERSION = '2.0.0-preview';
-
 
 MeshReceiver.prototype = {
 

+ 178 - 0
examples/jsm/loaders/obj2/utils/CodeSerializer.js

@@ -0,0 +1,178 @@
+/**
+ * @author Kai Salmen / https://kaisalmen.de
+ * Development repository: https://github.com/kaisalmen/WWOBJLoader
+ */
+
+const CodeSerializer = {
+
+	/**
+	 *
+	 * @param fullName
+	 * @param object
+	 * @returns {string}
+	 */
+	serializeObject: function ( fullName, object ) {
+		let objectString = fullName + ' = {\n\n';
+		let part;
+		for ( let name in object ) {
+
+			part = object[ name ];
+			if ( typeof( part ) === 'string' || part instanceof String ) {
+
+				part = part.replace( '\n', '\\n' );
+				part = part.replace( '\r', '\\r' );
+				objectString += '\t' + name + ': "' + part + '",\n';
+
+			} else if ( part instanceof Array ) {
+
+				objectString += '\t' + name + ': [' + part + '],\n';
+
+			} else if ( typeof part === 'object' ) {
+
+				// TODO: Short-cut for now. Recursion required?
+				objectString += '\t' + name + ': {},\n';
+
+			} else {
+
+				objectString += '\t' + name + ': ' + part + ',\n';
+
+			}
+
+		}
+		objectString += '}\n\n';
+
+		return objectString;
+	},
+
+	/**
+	 *
+	 * @param fullName
+	 * @param object
+	 * @param basePrototypeName
+	 * @param ignoreFunctions
+	 * @returns {string}
+	 */
+	serializeClass: function ( fullName, object, constructorName, basePrototypeName, ignoreFunctions, includeFunctions, overrideFunctions ) {
+		let valueString, objectPart, constructorString, i, funcOverride;
+		let prototypeFunctions = [];
+		let objectProperties = [];
+		let objectFunctions = [];
+		let isExtended = ( basePrototypeName !== null && basePrototypeName !== undefined );
+
+		if ( ! Array.isArray( ignoreFunctions ) ) ignoreFunctions = [];
+		if ( ! Array.isArray( includeFunctions ) ) includeFunctions = null;
+		if ( ! Array.isArray( overrideFunctions ) ) overrideFunctions = [];
+
+		for ( let name in object.prototype ) {
+
+			objectPart = object.prototype[ name ];
+			valueString = objectPart.toString();
+			if ( name === 'constructor' ) {
+
+				constructorString = fullName + ' = ' + valueString + ';\n\n';
+
+			} else if ( typeof objectPart === 'function' ) {
+
+				if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) {
+
+					funcOverride = overrideFunctions[ name ];
+					if ( funcOverride && funcOverride.fullName === fullName + '.prototype.' + name ) {
+
+						valueString = funcOverride.code;
+
+					}
+					if ( isExtended ) {
+
+						prototypeFunctions.push( fullName + '.prototype.' + name + ' = ' + valueString + ';\n\n' );
+
+					} else {
+
+						prototypeFunctions.push( '\t' + name + ': ' + valueString + ',\n\n' );
+
+					}
+				}
+
+			}
+
+		}
+		for ( let name in object ) {
+
+			objectPart = object[ name ];
+
+			if ( typeof objectPart === 'function' ) {
+
+				if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) {
+
+					funcOverride = overrideFunctions[ name ];
+					if ( funcOverride && funcOverride.fullName === fullName + '.' + name ) {
+
+						valueString = funcOverride.code;
+
+					} else {
+
+						valueString = objectPart.toString();
+
+					}
+					objectFunctions.push( fullName + '.' + name + ' = ' + valueString + ';\n\n' );
+
+				}
+
+			} else {
+
+				if ( typeof( objectPart ) === 'string' || objectPart instanceof String) {
+
+					valueString = '\"' + objectPart.toString() + '\"';
+
+				} else if ( typeof objectPart === 'object' ) {
+
+					// TODO: Short-cut for now. Recursion required?
+					valueString = "{}";
+
+				} else {
+
+					valueString = objectPart;
+
+				}
+				objectProperties.push( fullName + '.' + name + ' = ' + valueString + ';\n' );
+
+			}
+
+		}
+		if ( ( constructorString === undefined || constructorString === null ) && typeof object.prototype.constructor === 'function' ) {
+
+			constructorString = fullName + ' = ' + object.prototype.constructor.toString().replace( constructorName, '' );
+
+		}
+		let objectString = constructorString + '\n\n';
+		if ( isExtended ) {
+
+			objectString += fullName + '.prototype = Object.create( ' + basePrototypeName + '.prototype );\n';
+
+		}
+		objectString += fullName + '.prototype.constructor = ' + fullName + ';\n';
+		objectString += '\n\n';
+
+		for ( i = 0; i < objectProperties.length; i ++ ) objectString += objectProperties[ i ];
+		objectString += '\n\n';
+
+		for ( i = 0; i < objectFunctions.length; i ++ ) objectString += objectFunctions[ i ];
+		objectString += '\n\n';
+
+		if ( isExtended ) {
+
+			for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ];
+
+		} else {
+
+			objectString += fullName + '.prototype = {\n\n';
+			for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ];
+			objectString += '\n};';
+
+		}
+		objectString += '\n\n';
+
+		return objectString;
+	},
+};
+
+export { CodeSerializer }

+ 36 - 0
examples/jsm/loaders/obj2/utils/ObjectManipulator.js

@@ -0,0 +1,36 @@
+/**
+ * @author Kai Salmen / https://kaisalmen.de
+ * Development repository: https://github.com/kaisalmen/WWOBJLoader
+ */
+
+const ObjectManipulator = {
+
+	/**
+	 * Applies values from parameter object via set functions or via direct assignment.
+	 *
+	 * @param {Object} objToAlter The objToAlter instance
+	 * @param {Object} params The parameter object
+	 */
+	applyProperties: function ( objToAlter, params, forceCreation ) {
+		// fast-fail
+		if ( objToAlter === undefined || objToAlter === null || params === undefined || params === null ) return;
+
+		var property, funcName, values;
+		for ( property in params ) {
+			funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 );
+			values = params[ property ];
+
+			if ( typeof objToAlter[ funcName ] === 'function' ) {
+
+				objToAlter[ funcName ]( values );
+
+			} else if ( objToAlter.hasOwnProperty( property ) || forceCreation ) {
+
+				objToAlter[ property ] = values;
+
+			}
+		}
+	}
+};
+
+export { ObjectManipulator }

+ 480 - 0
examples/jsm/loaders/obj2/worker/main/WorkerExecutionSupport.js

@@ -0,0 +1,480 @@
+/**
+ * @author Kai Salmen / https://kaisalmen.de
+ * Development repository: https://github.com/kaisalmen/WWOBJLoader
+ */
+
+/**
+ * These instructions are used by {WorkerExecutionSupport} to build code for the web worker or to assign code
+ *
+ * @param {boolean} supportsStandardWorker
+ * @param {boolean} supportsJsmWorker
+ * @constructor
+ */
+const CodeBuilderInstructions = function ( supportsStandardWorker, supportsJsmWorker, preferJsmWorker ) {
+	this.supportsStandardWorker = supportsStandardWorker;
+	this.supportsJsmWorker = supportsJsmWorker;
+	this.preferJsmWorker = preferJsmWorker;
+	this.startCode = '';
+	this.codeFragments = [];
+	this.importStatements = [];
+
+	this.jsmWorkerFile = null;
+	this.defaultGeometryType = 0;
+};
+
+CodeBuilderInstructions.prototype = {
+
+	constructor: CodeBuilderInstructions,
+
+	isSupportsStandardWorker: function () {
+		return this.supportsStandardWorker;
+	},
+
+	isSupportsJsmWorker: function () {
+		return this.supportsJsmWorker;
+	},
+
+	isPreferJsmWorker: function () {
+		return this.preferJsmWorker;
+	},
+
+	/**
+	 * Set the full path to the module that contains the worker code.
+	 *
+	 * @param {String} jsmWorkerFile
+	 */
+	setJsmWorkerFile: function ( jsmWorkerFile ) {
+		if ( jsmWorkerFile !== undefined && jsmWorkerFile !== null ) {
+			this.jsmWorkerFile = jsmWorkerFile;
+		}
+	},
+
+	/**
+	 * Add code that is contained in addition to fragments and libraries
+	 * @param {String} startCode
+	 */
+	addStartCode: function ( startCode ) {
+		this.startCode = startCode;
+	},
+
+	/**
+	 * Add code fragment that is included in the provided order
+	 * @param {String} code
+	 */
+	addCodeFragment: function ( code ) {
+		this.codeFragments.push( code );
+	},
+
+	/**
+	 * Add full path to a library that is contained at the start of the worker via "importScripts"
+	 * @param {String} libraryPath
+	 */
+	addLibraryImport: function ( libraryPath ) {
+		let libraryUrl = new URL( libraryPath, window.location.href ).href;
+		let code = 'importScripts( "' + libraryUrl + '" );';
+		this.importStatements.push(	code );
+	},
+
+	getImportStatements: function () {
+		return this.importStatements;
+	},
+
+	getCodeFragments: function () {
+		return this.codeFragments;
+	},
+
+	getStartCode: function () {
+		return this.startCode;
+	}
+
+};
+/**
+ * This class provides means to transform existing parser code into a web worker. It defines a simple communication protocol
+ * which allows to configure the worker and receive raw mesh data during execution.
+ * @class
+ */
+const WorkerExecutionSupport = function () {
+	// check worker support first
+	if ( window.Worker === undefined ) throw "This browser does not support web workers!";
+	if ( window.Blob === undefined ) throw "This browser does not support Blob!";
+	if ( typeof window.URL.createObjectURL !== 'function' ) throw "This browser does not support Object creation from URL!";
+
+	this._reset();
+};
+WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.0.0-beta2';
+console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
+
+
+WorkerExecutionSupport.prototype = {
+
+	constructor: WorkerExecutionSupport,
+
+	_reset: function () {
+		this.logging = {
+			enabled: true,
+			debug: false
+		};
+
+		let scope = this;
+		let scopeTerminate = function (  ) {
+			scope._terminate();
+		};
+		this.worker = {
+			native: null,
+			jsmWorker: false,
+			logging: true,
+			workerRunner: {
+				name: 'WorkerRunner',
+				usesMeshDisassembler: false,
+				defaultGeometryType: 0
+			},
+			terminateWorkerOnLoad: true,
+			forceWorkerDataCopy: false,
+			started: false,
+			queuedMessage: null,
+			callbacks: {
+				onAssetAvailable: null,
+				onLoad: null,
+				terminate: scopeTerminate
+			}
+		};
+	},
+
+	/**
+	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
+	 *
+	 * @param {boolean} enabled True or false.
+	 * @param {boolean} debug True or false.
+	 */
+	setLogging: function ( enabled, debug ) {
+		this.logging.enabled = enabled === true;
+		this.logging.debug = debug === true;
+		this.worker.logging = enabled === true;
+		return this;
+	},
+
+	/**
+	 * Forces all ArrayBuffers to be transferred to worker to be copied.
+	 *
+	 * @param {boolean} forceWorkerDataCopy True or false.
+	 */
+	setForceWorkerDataCopy: function ( forceWorkerDataCopy ) {
+		this.worker.forceWorkerDataCopy = forceWorkerDataCopy === true;
+		return this;
+	},
+
+	/**
+	 * Request termination of worker once parser is finished.
+	 *
+	 * @param {boolean} terminateWorkerOnLoad True or false.
+	 */
+	setTerminateWorkerOnLoad: function ( terminateWorkerOnLoad ) {
+		this.worker.terminateWorkerOnLoad = terminateWorkerOnLoad === true;
+		if ( this.worker.terminateWorkerOnLoad && this.isWorkerLoaded( this.worker.jsmWorker ) &&
+				this.worker.queuedMessage === null && this.worker.started ) {
+
+			if ( this.logging.enabled ) {
+
+				console.info( 'Worker is terminated immediately as it is not running!' );
+
+			}
+			this._terminate();
+
+		}
+		return this;
+	},
+
+	/**
+	 * Update all callbacks.
+	 *
+	 * @param {Function} onAssetAvailable The function for processing the data, e.g. {@link MeshReceiver}.
+	 * @param {Function} [onLoad] The function that is called when parsing is complete.
+	 */
+	updateCallbacks: function ( onAssetAvailable, onLoad ) {
+		if ( onAssetAvailable !== undefined && onAssetAvailable !== null ) {
+
+			this.worker.callbacks.onAssetAvailable = onAssetAvailable;
+
+		}
+		if ( onLoad !== undefined && onLoad !== null ) {
+
+			this.worker.callbacks.onLoad = onLoad;
+
+		}
+		this._verifyCallbacks();
+	},
+
+	_verifyCallbacks: function () {
+		if ( this.worker.callbacks.onAssetAvailable === undefined || this.worker.callbacks.onAssetAvailable === null ) {
+
+			throw 'Unable to run as no "onAssetAvailable" callback is set.';
+
+		}
+	},
+
+	/**
+	 * Builds the worker code according the provided Instructions.
+	 * If jsm worker code shall be built, then function may fall back to standard if lag is set
+	 *
+ 	 * @param {CodeBuilderInstructions} codeBuilderInstructions
+	 */
+	buildWorker: function ( codeBuilderInstructions ) {
+		let jsmSuccess = false;
+
+		if ( codeBuilderInstructions.isSupportsJsmWorker() && codeBuilderInstructions.isPreferJsmWorker() ) {
+			jsmSuccess = this._buildWorkerJsm( codeBuilderInstructions );
+		}
+
+		if ( ! jsmSuccess && codeBuilderInstructions.isSupportsStandardWorker() ) {
+
+			this._buildWorkerStandard( codeBuilderInstructions );
+
+		}
+	},
+
+	/**
+	 *
+	 * @param {CodeBuilderInstructions} codeBuilderInstructions
+	 * @return {boolean} Whether loading of jsm worker was successful
+	 * @private
+	 */
+	_buildWorkerJsm: function ( codeBuilderInstructions ) {
+		let jsmSuccess = true;
+		let timeLabel = 'buildWorkerJsm';
+		let workerAvailable = this._buildWorkerCheckPreconditions( true, timeLabel );
+		if ( ! workerAvailable ) {
+
+			let workerFileUrl = new URL( codeBuilderInstructions.jsmWorkerFile, window.location.href ).href;
+			try {
+
+				let worker = new Worker( workerFileUrl, { type: "module" } );
+				this._configureWorkerCommunication( worker, true, codeBuilderInstructions.defaultGeometryType, timeLabel );
+
+			}
+			catch ( e ) {
+
+				jsmSuccess = false;
+				// Chrome throws this exception, but Firefox currently does not complain, but can't execute the worker afterwards
+				if ( e instanceof TypeError || e instanceof SyntaxError ) {
+
+					console.error( "Modules are not supported in workers." );
+
+				}
+			}
+
+		}
+
+		return jsmSuccess;
+	},
+
+	/**
+	 * Validate the status of worker code and the derived worker and specify functions that should be build when new raw mesh data becomes available and when the parser is finished.
+	 *
+	 * @param {CodeBuilderIns} buildWorkerCode The function that is invoked to create the worker code of the parser.
+	 */
+
+	/**
+	 *
+	 * @param {CodeBuilderInstructions} codeBuilderInstructions
+	 * @private
+	 */
+	_buildWorkerStandard: function ( codeBuilderInstructions ) {
+		let timeLabel = 'buildWorkerStandard';
+		let workerAvailable = this._buildWorkerCheckPreconditions( false, timeLabel );
+		if ( ! workerAvailable ) {
+
+			let concatenateCode = '';
+			codeBuilderInstructions.getImportStatements().forEach( function ( element ) {
+				concatenateCode += element + '\n';
+			} );
+			concatenateCode += '\n';
+			codeBuilderInstructions.getCodeFragments().forEach( function ( element ) {
+				concatenateCode += element + '\n';
+			} );
+			concatenateCode += '\n';
+			concatenateCode += codeBuilderInstructions.getStartCode();
+
+			let blob = new Blob( [ concatenateCode ], { type: 'application/javascript' } );
+			let worker = new Worker( window.URL.createObjectURL( blob ) );
+
+			this._configureWorkerCommunication( worker, false, codeBuilderInstructions.defaultGeometryType, timeLabel );
+
+		}
+	},
+
+	_buildWorkerCheckPreconditions: function ( requireJsmWorker, timeLabel ) {
+		let workerAvailable = false;
+		if ( this.isWorkerLoaded( requireJsmWorker ) ) {
+
+			workerAvailable = true;
+
+		} else {
+			if ( this.logging.enabled ) {
+
+				console.info( 'WorkerExecutionSupport: Building ' + ( requireJsmWorker ? 'jsm' : 'standard' ) + ' worker code...' );
+				console.time( timeLabel );
+
+			}
+
+		}
+		return workerAvailable;
+	},
+
+	_configureWorkerCommunication: function ( worker, haveJsmWorker, defaultGeometryType, timeLabel ) {
+		this.worker.native = worker;
+		this.worker.jsmWorker = haveJsmWorker;
+
+		let scope = this;
+		let scopedReceiveWorkerMessage = function ( event ) {
+			scope._receiveWorkerMessage( event );
+		};
+		this.worker.native.onmessage = scopedReceiveWorkerMessage;
+		if ( defaultGeometryType !== undefined && defaultGeometryType !== null ) {
+
+			this.worker.workerRunner.defaultGeometryType = defaultGeometryType;
+		}
+
+		if ( this.logging.enabled ) {
+
+			console.timeEnd( timeLabel );
+
+		}
+	},
+
+	/**
+	 * Returns if Worker code is available and complies with expectation.
+	 * @param {boolean} requireJsmWorker
+	 * @return {boolean|*}
+	 */
+	isWorkerLoaded: function ( requireJsmWorker ) {
+		return this.worker.native !== null &&
+			( ( requireJsmWorker && this.worker.jsmWorker ) || ( ! requireJsmWorker && ! this.worker.jsmWorker ) );
+	},
+
+	/**
+	 * Executed in worker scope
+	 */
+	_receiveWorkerMessage: function ( event ) {
+		let payload = event.data;
+		let workerRunnerName = this.worker.workerRunner.name;
+
+		switch ( payload.cmd ) {
+			case 'assetAvailable':
+				this.worker.callbacks.onAssetAvailable( payload );
+				break;
+
+			case 'completeOverall':
+				this.worker.queuedMessage = null;
+				this.worker.started = false;
+				if ( this.worker.callbacks.onLoad !== null ) {
+
+					this.worker.callbacks.onLoad( payload.msg );
+
+				}
+				if ( this.worker.terminateWorkerOnLoad ) {
+
+					if ( this.worker.logging.enabled ) console.info( 'WorkerSupport [' + workerRunnerName + ']: Run is complete. Terminating application on request!' );
+					this.worker.callbacks.terminate();
+
+				}
+				break;
+
+			case 'error':
+				console.error( 'WorkerSupport [' + workerRunnerName + ']: Reported error: ' + payload.msg );
+				this.worker.queuedMessage = null;
+				this.worker.started = false;
+				if ( this.worker.callbacks.onLoad !== null ) {
+
+					this.worker.callbacks.onLoad( payload.msg );
+
+				}
+				if ( this.worker.terminateWorkerOnLoad ) {
+
+					if ( this.worker.logging.enabled ) console.info( 'WorkerSupport [' + workerRunnerName + ']: Run reported error. Terminating application on request!' );
+					this.worker.callbacks.terminate();
+
+				}
+				break;
+
+			default:
+				console.error( 'WorkerSupport [' + workerRunnerName + ']: Received unknown command: ' + payload.cmd );
+				break;
+
+		}
+	},
+
+	/**
+	 * Runs the parser with the provided configuration.
+	 *
+	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
+	 */
+	executeParallel: function( payload, transferables ) {
+		payload.cmd = 'parse';
+		payload.usesMeshDisassembler = this.worker.workerRunner.usesMeshDisassembler;
+		payload.defaultGeometryType = this.worker.workerRunner.defaultGeometryType;
+		if ( ! this._verifyWorkerIsAvailable( payload, transferables ) ) return;
+
+		this._postMessage();
+	},
+
+	_verifyWorkerIsAvailable: function ( payload, transferables ) {
+		this._verifyCallbacks();
+		let ready = true;
+		if ( this.worker.queuedMessage !== null ) {
+
+			console.warn( 'Already processing message. Rejecting new run instruction' );
+			ready = false;
+
+		} else {
+
+			this.worker.queuedMessage = {
+				payload: payload,
+				transferables: ( transferables === undefined || transferables === null ) ? [] : transferables
+			};
+			this.worker.started = true;
+
+		}
+		return ready;
+	},
+
+	_postMessage: function () {
+		if ( this.worker.queuedMessage !== null ) {
+
+			if ( this.worker.queuedMessage.payload.data.input instanceof ArrayBuffer ) {
+
+				let transferables = [];
+				if ( this.worker.forceWorkerDataCopy ) {
+
+					transferables.push( this.worker.queuedMessage.payload.data.input.slice( 0 ) );
+
+				} else {
+
+					transferables.push( this.worker.queuedMessage.payload.data.input );
+
+				}
+				if ( this.worker.queuedMessage.transferables.length > 0 ) {
+
+					transferables = transferables.concat( this.worker.queuedMessage.transferables );
+
+				}
+				this.worker.native.postMessage( this.worker.queuedMessage.payload, transferables );
+
+			} else {
+
+				this.worker.native.postMessage( this.worker.queuedMessage.payload );
+
+			}
+
+		}
+	},
+
+	_terminate: function () {
+		this.worker.native.terminate();
+		this._reset();
+	}
+};
+
+export {
+	CodeBuilderInstructions,
+	WorkerExecutionSupport
+}

+ 7 - 8
examples/jsm/loaders/worker/independent/OBJLoader2Parser.js → examples/jsm/loaders/obj2/worker/parallel/OBJLoader2Parser.js

@@ -1,13 +1,12 @@
 /**
- * @author Kai Salmen / https://kaisalmen.de
- * Development repository: https://github.com/kaisalmen/WWOBJLoader
+ * @author Kai Salmen / www.kaisalmen.de
  */
 
 /**
  * Parse OBJ data either from ArrayBuffer or string
  * @class
  */
-const Parser = function() {
+const OBJLoader2Parser = function() {
 	this.callbacks = {
 		onProgress: null,
 		onAssetAvailable: null,
@@ -67,9 +66,9 @@ const Parser = function() {
 	};
 };
 
-Parser.prototype = {
+OBJLoader2Parser.prototype = {
 
-	constructor: Parser,
+	constructor: OBJLoader2Parser,
 
 	resetRawMesh: function () {
 		// faces are stored according combined index of group, material and smoothingGroup (0 or not)
@@ -764,7 +763,7 @@ Parser.prototype = {
 					}
 				};
 				let payload = {
-					cmd: 'data',
+					cmd: 'assetAvailable',
 					type: 'material',
 					materials: {
 						materialCloneInstructions: materialCloneInstructions
@@ -863,7 +862,7 @@ Parser.prototype = {
 		this.outputObjectCount ++;
 		this.callbacks.onAssetAvailable(
 			{
-				cmd: 'data',
+				cmd: 'assetAvailable',
 				type: 'mesh',
 				progress: {
 					numericalValue: this.globalCounts.currentByte / this.globalCounts.totalBytes
@@ -908,4 +907,4 @@ Parser.prototype = {
 	}
 };
 
-export { Parser };
+export { OBJLoader2Parser };

+ 128 - 0
examples/jsm/loaders/obj2/worker/parallel/WorkerRunner.js

@@ -0,0 +1,128 @@
+/**
+ * @author Kai Salmen / www.kaisalmen.de
+ */
+
+import { ObjectManipulator } from "../../utils/ObjectManipulator.js";
+
+const DefaultWorkerPayloadHandler = function ( parser ) {
+	this.parser = parser;
+	this.logging = {
+		enabled: false,
+		debug: false
+	};
+};
+
+DefaultWorkerPayloadHandler.prototype = {
+
+	constructor: DefaultWorkerPayloadHandler,
+
+	handlePayload: function ( payload ) {
+		if ( payload.logging ) {
+			this.logging.enabled = payload.logging.enabled === true;
+			this.logging.debug = payload.logging.debug === true;
+		}
+		if ( payload.cmd === 'parse' ) {
+
+			let scope = this;
+			let callbacks = {
+				callbackOnAssetAvailable: function ( payload ) {
+					self.postMessage( payload );
+				},
+				callbackOnProgress: function ( text ) {
+					if ( scope.logging.enabled && scope.logging.debug ) console.debug( 'WorkerRunner: progress: ' + text );
+				}
+			};
+
+			let parser = this.parser;
+			if ( typeof parser[ 'setLogging' ] === 'function' ) {
+
+				parser.setLogging( this.logging.enabled, this.logging.debug );
+
+			}
+			ObjectManipulator.applyProperties( parser, payload.params );
+			ObjectManipulator.applyProperties( parser, payload.materials );
+			ObjectManipulator.applyProperties( parser, callbacks );
+
+			let arraybuffer;
+			if ( payload.params && payload.params.index !== undefined && payload.params.index !== null) {
+
+				arraybuffer = this.resourceDescriptors[ payload.params.index ].content;
+
+			} else {
+
+				arraybuffer = payload.data.input;
+
+			}
+
+			let parseFunctionName = 'parse';
+			if ( typeof parser.getParseFunctionName === 'function' ) parseFunctionName = parser.getParseFunctionName();
+			if ( payload.usesMeshDisassembler ) {
+
+				// TODO: Allow to plug and use generic MeshDisassembler
+
+			} else {
+
+				parser[ parseFunctionName ] ( arraybuffer, payload.data.options );
+
+			}
+			if ( this.logging.enabled ) console.log( 'WorkerRunner: Run complete!' );
+
+			self.postMessage( {
+				cmd: 'completeOverall',
+				msg: 'WorkerRunner completed run.'
+			} );
+
+		} else {
+
+			console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd );
+
+		}
+
+	}
+};
+
+
+/**
+ * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker.
+ * @constructor
+ */
+const WorkerRunner = function ( payloadHandler ) {
+	this.resourceDescriptors = [];
+	this.payloadHandler = payloadHandler;
+
+	let scope = this;
+	let scopedRunner = function( event ) {
+		scope.processMessage( event.data );
+	};
+	self.addEventListener( 'message', scopedRunner, false );
+};
+
+WorkerRunner.prototype = {
+
+	constructor: WorkerRunner,
+
+	/**
+	 * Configures the Parser implementation according the supplied configuration object.
+	 *
+	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
+	 */
+	processMessage: function ( payload ) {
+		if ( payload.data.resourceDescriptors && this.resourceDescriptors.length === 0 ) {
+
+			for ( let name in payload.data.resourceDescriptors ) {
+
+				this.resourceDescriptors.push( payload.data.resourceDescriptors[ name ] );
+
+			}
+
+		}
+
+		this.payloadHandler.handlePayload( payload );
+	}
+
+};
+
+export {
+	WorkerRunner,
+	DefaultWorkerPayloadHandler
+}

+ 11 - 0
examples/jsm/loaders/obj2/worker/parallel/jsm/OBJLoader2Worker.js

@@ -0,0 +1,11 @@
+/**
+ * @author Kai Salmen / www.kaisalmen.de
+ */
+
+import { OBJLoader2Parser } from "../OBJLoader2Parser.js";
+import {
+	WorkerRunner,
+	DefaultWorkerPayloadHandler
+} from "../WorkerRunner.js";
+
+new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );

+ 1 - 1
examples/models/obj/verify/verify.html

@@ -91,7 +91,7 @@
 			import { TrackballControls } from "../../../jsm/controls/TrackballControls.js";
 
 			import { MTLLoader } from "../../../jsm/loaders/MTLLoader.js";
-			import { MtlObjBridge } from "../../../jsm/loaders/bridge/MtlObjBridge.js";
+			import { MtlObjBridge } from "../../../jsm/loaders/obj2/bridge/MtlObjBridge.js";
 			import { OBJLoader } from "../../../jsm/loaders/OBJLoader.js";
 			import { OBJLoader2 } from "../../../jsm/loaders/OBJLoader2.js";
 

+ 57 - 41
examples/webgl_loader_obj2.html

@@ -48,18 +48,28 @@
 			<div id="feedback"></div>
 		</div>
 
-		<script src="../build/three.js"></script>
-		<script src="js/controls/TrackballControls.js"></script>
-		<script src="js/loaders/MTLLoader.js"></script>
-		<script src="js/libs/dat.gui.min.js"></script>
-
-		<script src="js/loaders/LoaderSupport.js"></script>
-		<script src="js/loaders/OBJLoader2.js"></script>
-		<script>
+		<script type="module">
 
 			'use strict';
 
-			var OBJLoader2Example = function ( elementToBindTo ) {
+			import {
+				AmbientLight,
+				DirectionalLight,
+				GridHelper,
+				PerspectiveCamera,
+				Scene,
+				Vector3,
+				WebGLRenderer
+			} from "../build/three.module.js";
+
+			import { TrackballControls } from "./jsm/controls/TrackballControls.js";
+
+			import { MTLLoader } from "./jsm/loaders/MTLLoader.js";
+			import { OBJLoader2 } from "./jsm/loaders/OBJLoader2.js";
+			import { MtlObjBridge } from "./jsm/loaders/obj2/bridge/MtlObjBridge.js";
+
+
+			const OBJLoader2Example = function ( elementToBindTo ) {
 				this.renderer = null;
 				this.canvas = elementToBindTo;
 				this.aspectRatio = 1;
@@ -67,8 +77,8 @@
 
 				this.scene = null;
 				this.cameraDefaults = {
-					posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
-					posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
+					posCamera: new Vector3( 0.0, 175.0, 500.0 ),
+					posCameraTarget: new Vector3( 0, 0, 0 ),
 					near: 0.1,
 					far: 10000,
 					fov: 45
@@ -84,57 +94,64 @@
 				constructor: OBJLoader2Example,
 
 				initGL: function () {
-					this.renderer = new THREE.WebGLRenderer( {
+					this.renderer = new WebGLRenderer( {
 						canvas: this.canvas,
 						antialias: true,
 						autoClear: true
 					} );
 					this.renderer.setClearColor( 0x050505 );
 
-					this.scene = new THREE.Scene();
+					this.scene = new Scene();
 
-					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
+					this.camera = new PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
 					this.resetCamera();
-					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
+					this.controls = new TrackballControls( this.camera, this.renderer.domElement );
 
-					var ambientLight = new THREE.AmbientLight( 0x404040 );
-					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
-					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
+					let ambientLight = new AmbientLight( 0x404040 );
+					let directionalLight1 = new DirectionalLight( 0xC0C090 );
+					let directionalLight2 = new DirectionalLight( 0xC0C090 );
 
-					directionalLight1.position.set( -100, -50, 100 );
-					directionalLight2.position.set( 100, 50, -100 );
+					directionalLight1.position.set( - 100, - 50, 100 );
+					directionalLight2.position.set( 100, 50, - 100 );
 
 					this.scene.add( directionalLight1 );
 					this.scene.add( directionalLight2 );
 					this.scene.add( ambientLight );
 
-					var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
+					let helper = new GridHelper( 1200, 60, 0xFF4444, 0x404040 );
 					this.scene.add( helper );
 				},
 
 				initContent: function () {
-					var modelName = 'female02';
+					let modelName = 'female02';
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
-					var scope = this;
-					var objLoader = new THREE.OBJLoader2();
-					var callbackOnLoad = function ( event ) {
-						scope.scene.add( event.detail.loaderRootNode );
-						console.log( 'Loading complete: ' + event.detail.modelName );
+
+					let scope = this;
+					let objLoader2 = new OBJLoader2();
+					let callbackOnLoad = function ( object3d ) {
+						scope.scene.add( object3d );
+						console.log( 'Loading complete: ' + modelName );
 						scope._reportProgress( { detail: { text: '' } } );
 					};
 
-					var onLoadMtl = function ( materials ) {
-						objLoader.setModelName( modelName );
-						objLoader.setMaterials( materials );
-						objLoader.setLogging( true, true );
-						objLoader.load( 'models/obj/female02/female02.obj', callbackOnLoad, null, null, null, false );
+					let onLoadMtl = function ( mtlParseResult ) {
+						objLoader2.setModelName( modelName );
+						objLoader2.setLogging( true, true );
+						objLoader2.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
+						objLoader2.load( 'models/obj/female02/female02.obj', callbackOnLoad, null, null, null );
 					};
-					objLoader.loadMtl( 'models/obj/female02/female02.mtl', null, onLoadMtl );
+					let mtlLoader = new MTLLoader();
+					mtlLoader.load( 'models/obj/female02/female02.mtl', onLoadMtl );
 				},
 
-				_reportProgress: function( event ) {
-					var output = THREE.LoaderSupport.Validator.verifyInput( event.detail.text, '' );
+				_reportProgress: function ( event ) {
+					let output = '';
+					if ( event.detail !== null && event.detail !== undefined && event.detail.text ) {
+
+						output = event.detail.text;
+
+					}
 					console.log( 'Progress: ' + output );
 					document.getElementById( 'feedback' ).innerHTML = output;
 				},
@@ -149,7 +166,7 @@
 				},
 
 				recalcAspectRatio: function () {
-					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
+					this.aspectRatio = (this.canvas.offsetHeight === 0) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
 				},
 
 				resetCamera: function () {
@@ -166,20 +183,19 @@
 				},
 
 				render: function () {
-					if ( ! this.renderer.autoClear ) this.renderer.clear();
+					if ( !this.renderer.autoClear ) this.renderer.clear();
 					this.controls.update();
 					this.renderer.render( this.scene, this.camera );
 				}
-
 			};
 
-			var app = new OBJLoader2Example( document.getElementById( 'example' ) );
+			let app = new OBJLoader2Example( document.getElementById( 'example' ) );
 
-			var resizeWindow = function () {
+			let resizeWindow = function () {
 				app.resizeDisplayGL();
 			};
 
-			var render = function () {
+			let render = function () {
 				requestAnimationFrame( render );
 				app.render();
 			};

+ 0 - 548
examples/webgl_loader_obj2_meshspray.html

@@ -1,548 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - Mesh Spray</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<link type="text/css" rel="stylesheet" href="main.css">
-		<style>
-			#glFullscreen {
-				width: 100%;
-				height: 100vh;
-				min-width: 640px;
-				min-height: 360px;
-				position: relative;
-				overflow: hidden;
-				z-index: 0;
-			}
-			#example {
-				width: 100%;
-				height: 100%;
-				top: 0;
-				left: 0;
-				background-color: #000000;
-			}
-			#feedback {
-				color: darkorange;
-			}
-			#dat {
-				user-select: none;
-				position: absolute;
-				left: 0;
-				top: 0;
-				z-Index: 200;
-			}
-		</style>
-
-	</head>
-
-	<body>
-	<div id="glFullscreen">
-		<canvas id="example"></canvas>
-	</div>
-	<div id="dat">
-
-	</div>
-	<div id="info">
-		<a href="http://threejs.org" target="_blank">three.js</a> - Mesh Spray
-		<div id="feedback"></div>
-	</div>
-
-		<script src="../build/three.js"></script>
-		<script src="js/controls/TrackballControls.js"></script>
-		<script src="js/libs/dat.gui.min.js"></script>
-
-		<script src="js/loaders/LoaderSupport.js"></script>
-
-		<script>
-
-			'use strict';
-
-			var MeshSpray = {};
-
-			MeshSpray.Loader = function ( manager ) {
-				this.manager = THREE.LoaderSupport.Validator.verifyInput( manager, THREE.DefaultLoadingManager );
-				this.logging = {
-					enabled: true,
-					debug: false
-				};
-
-				this.instanceNo = 0;
-				this.loaderRootNode = new THREE.Group();
-
-				this.meshBuilder = new THREE.LoaderSupport.MeshBuilder();
-				this.callbacks = new THREE.LoaderSupport.Callbacks();
-				this.workerSupport = null;
-			};
-
-			MeshSpray.Loader.prototype = {
-
-				constructor: MeshSpray.Loader,
-
-				setLogging: function ( enabled, debug ) {
-					this.logging.enabled = enabled === true;
-					this.logging.debug = debug === true;
-					this.meshBuilder.setLogging( this.logging.enabled, this.logging.debug );
-				},
-
-				setStreamMeshesTo: function ( streamMeshesTo ) {
-					this.loaderRootNode = THREE.LoaderSupport.Validator.verifyInput( streamMeshesTo, this.loaderRootNode );
-				},
-
-				setForceWorkerDataCopy: function ( forceWorkerDataCopy ) {
-					// nothing to do here
-				},
-
-				run: function ( prepData, workerSupportExternal ) {
-
-					if ( THREE.LoaderSupport.Validator.isValid( workerSupportExternal ) ) {
-
-						this.workerSupport = workerSupportExternal;
-						this.logging.enabled = this.workerSupport.logging.enabled;
-						this.logging.debug = this.workerSupport.logging.debug;
-
-					} else {
-
-						this.workerSupport = THREE.LoaderSupport.Validator.verifyInput( this.workerSupport, new THREE.LoaderSupport.WorkerSupport() );
-
-					}
-					if ( this.logging.enabled ) console.time( 'MeshSpray' + this.instanceNo );
-
-					this._applyPrepData( prepData );
-
-					this.meshBuilder.init();
-
-					var scope = this;
-					var scopeBuilderFunc = function ( payload ) {
-						var meshes = scope.meshBuilder.processPayload( payload );
-						var mesh;
-						for ( var i in meshes ) {
-							mesh = meshes[ i ];
-							scope.loaderRootNode.add( mesh );
-						}
-					};
-					var scopeFuncComplete = function ( message ) {
-						var callback = scope.callbacks.onLoad;
-						if ( THREE.LoaderSupport.Validator.isValid( callback ) ) callback(
-							{
-								detail: {
-									loaderRootNode: scope.loaderRootNode,
-									modelName: scope.modelName,
-									instanceNo: scope.instanceNo
-								}
-							}
-						);
-						if ( scope.logging.enabled ) console.timeEnd( 'MeshSpray' + scope.instanceNo );
-					};
-
-					var buildCode = function ( codeSerializer ) {
-						var workerCode = '';
-						workerCode += '/**\n';
-						workerCode += '  * This code was constructed by MeshSpray buildCode.\n';
-						workerCode += '  */\n\n';
-						workerCode += 'THREE.LoaderSupport = {};\n\n';
-						workerCode += 'MeshSpray = {};\n\n';
-						workerCode += codeSerializer.serializeObject( 'THREE.LoaderSupport.Validator', THREE.LoaderSupport.Validator );
-						workerCode += codeSerializer.serializeClass( 'MeshSpray.Parser', MeshSpray.Parser );
-
-						return workerCode;
-					};
-					var libs2Load = [ 'build/three.min.js' ];
-					this.workerSupport.validate( buildCode, 'MeshSpray.Parser', libs2Load, '../../' );
-					this.workerSupport.setCallbacks( scopeBuilderFunc, scopeFuncComplete );
-					this.workerSupport.run(
-						{
-							params: {
-								dimension: prepData.dimension,
-								quantity: prepData.quantity,
-								globalObjectCount: prepData.globalObjectCount
-							},
-							materials: {
-								serializedMaterials: this.meshBuilder.getMaterialsJSON()
-							},
-							logging: {
-								enabled: this.logging.enabled,
-								debug: this.logging.debug
-							},
-							data: {
-								input: null,
-								options: null
-							}
-						}
-					);
-				},
-
-				_applyPrepData: function ( prepData ) {
-					if ( THREE.LoaderSupport.Validator.isValid( prepData ) ) {
-
-						this.setLogging( prepData.logging.enabled, prepData.logging.debug );
-						this.setStreamMeshesTo( prepData.streamMeshesTo );
-						this.meshBuilder.setMaterials( prepData.materials );
-						this._setCallbacks( prepData.getCallbacks() );
-
-					}
-				},
-
-				_setCallbacks: function ( callbacks ) {
-					if ( THREE.LoaderSupport.Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
-					if ( THREE.LoaderSupport.Validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError );
-					if ( THREE.LoaderSupport.Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
-					if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
-					if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
-
-					this.meshBuilder._setCallbacks( this.callbacks );
-				}
-			};
-
-
-			MeshSpray.Parser = function () {
-				this.sizeFactor = 0.5;
-				this.localOffsetFactor = 1.0;
-				this.globalObjectCount = 0;
-				this.debug = false;
-				this.dimension = 200;
-				this.quantity = 1;
-				this.callbacks = {
-					onAssetAvailable: null
-				};
-				this.serializedMaterials = null;
-				this.logging = {
-					enabled: true,
-					debug: false
-				};
-			};
-
-			MeshSpray.Parser.prototype = {
-
-				constructor: MeshSpray.Parser,
-
-				setCallbackOnAssetAvailable: function ( onAssetAvailable ) {
-					if ( onAssetAvailable !== null && onAssetAvailable !== undefined ) {
-
-						this.callbacks.onAssetAvailable = onAssetAvailable;
-
-					}
-				},
-
-				setLogging: function ( enabled, debug ) {
-					this.logging.enabled = enabled === true;
-					this.logging.debug = debug === true;
-				},
-
-				parse: function () {
-					var baseTriangle = [ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, -1.0, 1.0 ];
-					var vertices = [];
-					var colors = [];
-					var normals = [];
-					var uvs = [];
-
-					var dimensionHalf = this.dimension / 2;
-					var fixedOffsetX;
-					var fixedOffsetY;
-					var fixedOffsetZ;
-					var s, t;
-					// complete triangle
-					var sizeVaring = this.sizeFactor * Math.random();
-					// local coords offset
-					var localOffsetFactor = this.localOffsetFactor;
-
-					for ( var i = 0; i < this.quantity; i++ ) {
-						sizeVaring = this.sizeFactor * Math.random();
-
-						s = 2 * Math.PI * Math.random();
-						t = Math.PI * Math.random();
-
-						fixedOffsetX = dimensionHalf * Math.random() * Math.cos( s ) * Math.sin( t );
-						fixedOffsetY = dimensionHalf * Math.random() * Math.sin( s ) * Math.sin( t );
-						fixedOffsetZ = dimensionHalf * Math.random() * Math.cos( t );
-						for ( var j = 0; j < baseTriangle.length; j += 3 ) {
-							vertices.push( baseTriangle[ j ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetX );
-							vertices.push( baseTriangle[ j + 1 ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetY );
-							vertices.push( baseTriangle[ j + 2 ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetZ );
-							colors.push( Math.random() );
-							colors.push( Math.random() );
-							colors.push( Math.random() );
-						}
-					}
-
-					var absoluteVertexCount = vertices.length;
-					var absoluteColorCount = colors.length;
-					var absoluteNormalCount = 0;
-					var absoluteUvCount = 0;
-
-					var vertexFA = new Float32Array( absoluteVertexCount );
-					var colorFA = ( absoluteColorCount > 0 ) ? new Float32Array( absoluteColorCount ) : null;
-					var normalFA = ( absoluteNormalCount > 0 ) ? new Float32Array( absoluteNormalCount ) : null;
-					var uvFA = ( absoluteUvCount > 0 ) ? new Float32Array( absoluteUvCount ) : null;
-
-					vertexFA.set( vertices, 0 );
-					if ( colorFA ) {
-
-						colorFA.set( colors, 0 );
-
-					}
-
-					if ( normalFA ) {
-
-						normalFA.set( normals, 0 );
-
-					}
-					if ( uvFA ) {
-
-						uvFA.set( uvs, 0 );
-
-					}
-
-					/*
-					 * This demonstrates the usage of embedded three.js in the worker blob and
-					 * the serialization of materials back to the Builder outside the worker.
-					 *
-					 * This is not the most effective way, but outlining possibilities
-					 */
-					var materialName = 'defaultVertexColorMaterial_double';
-					var defaultVertexColorMaterialJson = this.serializedMaterials[ 'defaultVertexColorMaterial' ];
-					var loader = new THREE.MaterialLoader();
-
-					var defaultVertexColorMaterialDouble = loader.parse( defaultVertexColorMaterialJson );
-					defaultVertexColorMaterialDouble.name = materialName;
-					defaultVertexColorMaterialDouble.side = THREE.DoubleSide;
-
-					var newSerializedMaterials = {};
-					newSerializedMaterials[ materialName ] = defaultVertexColorMaterialDouble.toJSON();
-					var payload = {
-						cmd: 'materialData',
-						materials: {
-							serializedMaterials: newSerializedMaterials
-						}
-					};
-					this.callbacks.onAssetAvailable( payload );
-
-					this.globalObjectCount++;
-					this.callbacks.onAssetAvailable(
-						{
-							cmd: 'meshData',
-							progress: {
-								numericalValue: 1.0
-							},
-							params: {
-								meshName: 'Gen' + this.globalObjectCount
-							},
-							materials: {
-								multiMaterial: false,
-								materialNames: [ materialName ],
-								materialGroups: []
-							},
-							buffers: {
-								vertices: vertexFA,
-								colors: colorFA,
-								normals: normalFA,
-								uvs: uvFA
-							}
-						},
-						[ vertexFA.buffer ],
-						colorFA !== null ? [ colorFA.buffer ] : null,
-						normalFA !== null ? [ normalFA.buffer ] : null,
-						uvFA !== null ? [ uvFA.buffer ] : null
-					);
-
-					if ( this.logging.enabled ) console.info( 'Global output object count: ' + this.globalObjectCount );
-				},
-
-				setSerializedMaterials: function ( serializedMaterials ) {
-					if ( THREE.LoaderSupport.Validator.isValid( serializedMaterials ) ) {
-
-						this.serializedMaterials = serializedMaterials;
-
-					}
-				}
-			};
-
-			var MeshSprayApp = function ( elementToBindTo ) {
-				this.renderer = null;
-				this.canvas = elementToBindTo;
-				this.aspectRatio = 1;
-				this.recalcAspectRatio();
-
-				this.scene = null;
-				this.cameraDefaults = {
-					posCamera: new THREE.Vector3( 500.0, 500.0, 1000.0 ),
-					posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
-					near: 0.1,
-					far: 10000,
-					fov: 45
-				};
-				this.camera = null;
-				this.cameraTarget = this.cameraDefaults.posCameraTarget;
-
-				this.controls = null;
-
-				this.cube = null;
-				this.pivot = null;
-			};
-
-			MeshSprayApp.prototype = {
-
-				constructor: MeshSprayApp,
-
-				initGL: function () {
-					this.renderer = new THREE.WebGLRenderer( {
-						canvas: this.canvas,
-						antialias: true,
-						autoClear: true
-					} );
-					this.renderer.setClearColor( 0x050505 );
-
-					this.scene = new THREE.Scene();
-
-					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
-					this.resetCamera();
-					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
-
-					var ambientLight = new THREE.AmbientLight( 0x404040 );
-					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
-					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
-
-					directionalLight1.position.set( -100, -50, 100 );
-					directionalLight2.position.set( 100, 50, -100 );
-
-					this.scene.add( directionalLight1 );
-					this.scene.add( directionalLight2 );
-					this.scene.add( ambientLight );
-
-					var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
-					this.scene.add( helper );
-
-					var geometry = new THREE.BoxBufferGeometry( 10, 10, 10 );
-					var material = new THREE.MeshNormalMaterial();
-					this.cube = new THREE.Mesh( geometry, material );
-					this.cube.position.set( 0, 0, 0 );
-					this.scene.add( this.cube );
-
-					this.pivot = new THREE.Object3D();
-					this.pivot.name = 'Pivot';
-					this.scene.add( this.pivot );
-				},
-
-				initContent: function () {
-					var maxQueueSize = 1024;
-					var maxWebWorkers = 4;
-					var radius = 640;
-					var workerDirector = new THREE.LoaderSupport.WorkerDirector( MeshSpray.Loader );
-					workerDirector.setLogging( false, false );
-					workerDirector.setCrossOrigin( 'anonymous' );
-
-					var callbackOnLoad = function ( event ) {
-						console.info( 'Worker #' + event.detail.instanceNo + ': Completed loading. (#' + workerDirector.objectsCompleted + ')' );
-					};
-					var reportProgress = function( event ) {
-						document.getElementById( 'feedback' ).innerHTML = event.detail.text;
-						console.info( event.detail.text );
-					};
-					var callbackMeshAlter = function ( event ) {
-						var override = new THREE.LoaderSupport.LoadedMeshUserOverride( false, true );
-
-						event.detail.side = THREE.DoubleSide;
-						var mesh = new THREE.Mesh( event.detail.bufferGeometry, event.detail.material );
-						mesh.name = event.detail.meshName;
-						override.addMesh( mesh );
-
-						return override;
-					};
-
-
-					var callbacks = new THREE.LoaderSupport.Callbacks();
-					callbacks.setCallbackOnMeshAlter( callbackMeshAlter );
-					callbacks.setCallbackOnLoad( callbackOnLoad );
-					callbacks.setCallbackOnProgress( reportProgress );
-					workerDirector.prepareWorkers( callbacks, maxQueueSize, maxWebWorkers );
-
-					var prepData;
-					var pivot;
-					var s, t, r, x, y, z;
-					var globalObjectCount = 0;
-					for ( var i = 0; i < maxQueueSize; i++ ) {
-						prepData = new THREE.LoaderSupport.PrepData( 'Triangles_' + i );
-
-						pivot = new THREE.Object3D();
-						s = 2 * Math.PI * Math.random();
-						t = Math.PI * Math.random();
-						r = radius * Math.random();
-						x = r * Math.cos( s ) * Math.sin( t );
-						y = r * Math.sin( s ) * Math.sin( t );
-						z = r * Math.cos( t );
-						pivot.position.set( x, y, z );
-						this.scene.add( pivot );
-						prepData.streamMeshesTo = pivot;
-						prepData.setLogging( false, false );
-
-						prepData.quantity = 8192;
-						prepData.dimension = Math.max( Math.random() * 500, 100 );
-						prepData.globalObjectCount = globalObjectCount++;
-
-						workerDirector.enqueueForRun( prepData );
-					}
-					workerDirector.processQueue();
-				},
-
-				resizeDisplayGL: function () {
-					this.controls.handleResize();
-
-					this.recalcAspectRatio();
-					this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
-
-					this.updateCamera();
-				},
-
-				recalcAspectRatio: function () {
-					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
-				},
-
-				resetCamera: function () {
-					this.camera.position.copy( this.cameraDefaults.posCamera );
-					this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
-
-					this.updateCamera();
-				},
-
-				updateCamera: function () {
-					this.camera.aspect = this.aspectRatio;
-					this.camera.lookAt( this.cameraTarget );
-					this.camera.updateProjectionMatrix();
-				},
-
-				render: function () {
-					if ( ! this.renderer.autoClear ) this.renderer.clear();
-
-					this.controls.update();
-
-					this.cube.rotation.x += 0.05;
-					this.cube.rotation.y += 0.05;
-
-					this.renderer.render( this.scene, this.camera );
-				}
-			};
-
-			var app = new MeshSprayApp( document.getElementById( 'example' ) );
-
-			// init three.js example application
-			var resizeWindow = function () {
-				app.resizeDisplayGL();
-			};
-
-			var render = function () {
-				requestAnimationFrame( render );
-				app.render();
-			};
-
-			window.addEventListener( 'resize', resizeWindow, false );
-
-			console.log( 'Starting initialisation phase...' );
-			app.initGL();
-			app.resizeDisplayGL();
-			app.initContent();
-
-			render();
-
-		</script>
-	</body>
-</html>

+ 191 - 180
examples/webgl_loader_obj2_options.html

@@ -48,18 +48,38 @@
 			<div id="feedback"></div>
 		</div>
 
-		<script src="../build/three.js"></script>
-		<script src="js/controls/TrackballControls.js"></script>
-		<script src="js/loaders/MTLLoader.js"></script>
-		<script src="js/libs/dat.gui.min.js"></script>
-
-		<script src="js/loaders/LoaderSupport.js"></script>
-		<script src="js/loaders/OBJLoader2.js"></script>
-		<script>
+		<script type="module">
 
 			'use strict';
 
-			var WWOBJLoader2Example = function ( elementToBindTo ) {
+			import {
+				AmbientLight,
+				DirectionalLight,
+				GridHelper,
+				PerspectiveCamera,
+				Scene,
+				Vector3,
+				BoxBufferGeometry,
+				WebGLRenderer,
+				Object3D,
+				MeshNormalMaterial,
+				Mesh,
+				VertexNormalsHelper,
+				FileLoader,
+				DoubleSide,
+				FrontSide
+			} from "../build/three.module.js";
+			import { GUI } from './jsm/libs/dat.gui.module.js';
+
+			import { TrackballControls } from "./jsm/controls/TrackballControls.js";
+
+			import { MTLLoader } from "./jsm/loaders/MTLLoader.js";
+			import { MtlObjBridge } from "./jsm/loaders/obj2/bridge/MtlObjBridge.js";
+			import { OBJLoader2 } from "./jsm/loaders/OBJLoader2.js";
+			import { OBJLoader2Parallel } from "./jsm/loaders/OBJLoader2Parallel.js";
+			import { LoadedMeshUserOverride } from "./jsm/loaders/obj2/shared/MeshReceiver.js";
+
+			const WWOBJLoader2Example = function ( elementToBindTo ) {
 				this.renderer = null;
 				this.canvas = elementToBindTo;
 				this.aspectRatio = 1;
@@ -67,8 +87,8 @@
 
 				this.scene = null;
 				this.cameraDefaults = {
-					posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
-					posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
+					posCamera: new Vector3( 0.0, 175.0, 500.0 ),
+					posCameraTarget: new Vector3( 0, 0, 0 ),
 					near: 0.1,
 					far: 10000,
 					fov: 45
@@ -90,22 +110,22 @@
 				constructor: WWOBJLoader2Example,
 
 				initGL: function () {
-					this.renderer = new THREE.WebGLRenderer( {
+					this.renderer = new WebGLRenderer( {
 						canvas: this.canvas,
 						antialias: true,
 						autoClear: true
 					} );
 					this.renderer.setClearColor( 0x050505 );
 
-					this.scene = new THREE.Scene();
+					this.scene = new Scene();
 
-					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
+					this.camera = new PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
 					this.resetCamera();
-					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
+					this.controls = new TrackballControls( this.camera, this.renderer.domElement );
 
-					var ambientLight = new THREE.AmbientLight( 0x404040 );
-					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
-					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
+					let ambientLight = new AmbientLight( 0x404040 );
+					let directionalLight1 = new DirectionalLight( 0xC0C090 );
+					let directionalLight2 = new DirectionalLight( 0xC0C090 );
 
 					directionalLight1.position.set( -100, -50, 100 );
 					directionalLight2.position.set( 100, 50, -100 );
@@ -114,193 +134,182 @@
 					this.scene.add( directionalLight2 );
 					this.scene.add( ambientLight );
 
-					var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
+					let helper = new GridHelper( 1200, 60, 0xFF4444, 0x404040 );
 					this.scene.add( helper );
 
-					var geometry = new THREE.BoxBufferGeometry( 10, 10, 10 );
-					var material = new THREE.MeshNormalMaterial();
-					this.cube = new THREE.Mesh( geometry, material );
+					let geometry = new BoxBufferGeometry( 10, 10, 10 );
+					let material = new MeshNormalMaterial();
+					this.cube = new Mesh( geometry, material );
 					this.cube.position.set( 0, 0, 0 );
 					this.scene.add( this.cube );
 
-					this.pivot = new THREE.Object3D();
+					this.pivot = new Object3D();
 					this.pivot.name = 'Pivot';
 					this.scene.add( this.pivot );
 				},
 
-				useParseSync: function () {
-					var modelName = 'female02';
+				useParseMain: function () {
+					let modelName = 'female02';
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
-					var scope = this;
-					var objLoader = new THREE.OBJLoader2();
-					var onLoadMtl = function ( materials ) {
-						objLoader.setModelName( modelName );
-						objLoader.setMaterials( materials );
+					let objLoader2 = new OBJLoader2()
+						.setModelName( modelName );
+
+					let scope = this;
+					function onLoadMtl ( mtlParseResult ) {
+						objLoader2.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
 
-						var fileLoader = new THREE.FileLoader();
+						let fileLoader = new FileLoader();
 						fileLoader.setPath( '' );
 						fileLoader.setResponseType( 'arraybuffer' );
 						fileLoader.load( 'models/obj/female02/female02.obj',
 							function ( content ) {
-								var local = new THREE.Object3D();
+								let local = new Object3D();
 								local.name = 'Pivot_female02';
 								local.position.set( 75, 0, 0 );
 								scope.pivot.add( local );
-								local.add( objLoader.parse( content ) );
+								local.add( objLoader2.parse( content ) );
 
 								scope._reportProgress( { detail: { text: 'Loading complete: ' + modelName } } );
 							}
 						);
-					};
-					var onError = function ( event ) {
-						console.error( 'Error occurred: ' + event );
-					};
-					objLoader.loadMtl( 'models/obj/female02/female02.mtl', null, onLoadMtl, null, onError );
+					}
+
+					let mtlLoader = new MTLLoader();
+					mtlLoader.load( 'models/obj/female02/female02.mtl', onLoadMtl );
 				},
 
-				useParseAsync: function () {
-					var modelName = 'female02_vertex' ;
+				useParseParallel: function () {
+					let modelName = 'female02_vertex' ;
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
-					var callbackOnLoad = function ( event ) {
-						var local = new THREE.Object3D();
-						local.name = 'Pivot_female02_vertex';
-						local.position.set( -75, 0, 0 );
-						scope.pivot.add( local );
-						local.add( event.detail.loaderRootNode );
+					let scope = this;
 
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + event.detail.modelName } } );
-					};
+					let objLoader2Parallel = new OBJLoader2Parallel();
+					objLoader2Parallel.setModelName( modelName );
 
-					var scope = this;
-					var objLoader = new THREE.OBJLoader2();
-					objLoader.setModelName( modelName );
-
-					var fileLoader = new THREE.FileLoader();
-					fileLoader.setPath( '' );
-					fileLoader.setResponseType( 'arraybuffer' );
-					var filename = 'models/obj/female02/female02_vertex_colors.obj';
-					fileLoader.load( filename,
-						function ( content ) {
-							objLoader.parseAsync( content, callbackOnLoad );
-							scope._reportProgress( { detail: { text: 'File loading complete: ' + filename } } );
-						}
-					);
+					function callbackOnLoad ( message ) {
+						scope.scene.add( objLoader2Parallel.baseObject3d  );
+						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					}
+
+					let filename = 'models/obj/female02/female02_vertex_colors.obj';
+					objLoader2Parallel.load( filename, callbackOnLoad );
 				},
 
-				useLoadSync: function () {
-					var modelName = 'male02';
+				useLoadMain: function () {
+					let modelName = 'male02';
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
-					var scope = this;
-					var objLoader = new THREE.OBJLoader2();
-					var callbackOnLoad = function ( event ) {
-						var local = new THREE.Object3D();
+					let objLoader2 = new OBJLoader2()
+					.setModelName( modelName )
+					.setUseIndices( true );
+
+					let scope = this;
+					function callbackOnLoad ( object3d ) {
+						let local = new Object3D();
 						local.name = 'Pivot_male02';
 						local.position.set( 0, 0, -75 );
 						scope.pivot.add( local );
-						local.add( event.detail.loaderRootNode );
+						local.add( object3d );
 
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + event.detail.modelName } } );
-					};
+						scope._reportProgress( { detail: { text: 'Loading complete: ' + objLoader2.modelName } } );
+					}
 
-					var onLoadMtl = function ( materials ) {
-						objLoader.setModelName( modelName );
-						objLoader.setMaterials( materials );
-						objLoader.setUseIndices( true );
-						objLoader.load( 'models/obj/male02/male02.obj', callbackOnLoad, null, null, null, false );
-					};
-					objLoader.loadMtl( 'models/obj/male02/male02.mtl', null, onLoadMtl );
+					function onLoadMtl ( mtlParseResult ) {
+						objLoader2.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
+						objLoader2.load( 'models/obj/male02/male02.obj', callbackOnLoad, null, null, null );
+					}
+
+					let mtlLoader = new MTLLoader();
+					mtlLoader.load( 'models/obj/male02/male02.mtl', onLoadMtl );
 				},
 
-				useLoadAsync: function () {
-					var modelName = 'WaltHead';
+				useLoadParallel: function () {
+					let modelName = 'WaltHead';
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
-					var scope = this;
-					var objLoader = new THREE.OBJLoader2();
-					var callbackOnLoad = function ( event ) {
-						objLoader.workerSupport.setTerminateRequested( true );
-
-						var local = new THREE.Object3D();
-						local.name = 'Pivot_WaltHead';
-						local.position.set( -125, 50, 0 );
-						var scale = 0.5;
-						local.scale.set( scale, scale, scale );
-						scope.pivot.add( local );
-						local.add( event.detail.loaderRootNode );
+					let local = new Object3D();
+					local.name = 'Pivot_WaltHead';
+					local.position.set( -125, 50, 0 );
+					let scale = 0.5;
+					local.scale.set( scale, scale, scale );
+					this.pivot.add( local );
 
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + event.detail.modelName } } );
-					};
+					let objLoader2Parallel = new OBJLoader2Parallel()
+					.setModelName( modelName )
+					.setBaseObject3d( local );
 
-					var onLoadMtl = function ( materials ) {
-						objLoader.setModelName( modelName );
-						objLoader.setMaterials( materials );
-						objLoader.terminateWorkerOnLoad = false;
-						objLoader.load( 'models/obj/walt/WaltHead.obj', callbackOnLoad, null, null, null, true );
+					let scope = this;
+					function callbackOnLoad ( message ) {
+						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					}
+					function onLoadMtl ( mtlParseResult ) {
+						objLoader2Parallel.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
+						objLoader2Parallel.load( 'models/obj/walt/WaltHead.obj', callbackOnLoad );
+					}
 
-					};
-					objLoader.loadMtl( 'models/obj/walt/WaltHead.mtl', null, onLoadMtl );
+					let mtlLoader = new MTLLoader();
+					mtlLoader.load( 'models/obj/walt/WaltHead.mtl', onLoadMtl );
 				},
 
-				useRunSync: function () {
-					var scope = this;
-					var callbackOnLoad = function ( event ) {
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + event.detail.modelName } } );
-					};
-
-					var prepData = new THREE.LoaderSupport.PrepData( 'cerberus' );
-					var local = new THREE.Object3D();
+				useLoadMainFallback: function () {
+					let local = new Object3D();
+					local.name = 'Pivot_Cerberus';
 					local.position.set( 0, 0, 100 );
-					local.scale.set( 50.0, 50.0, 50.0 );
+					let scale = 50;
+					local.scale.set( scale, scale, scale );
 					this.pivot.add( local );
-					prepData.streamMeshesTo = local;
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/cerberus/Cerberus.obj', 'OBJ' ) );
-					var callbacks = prepData.getCallbacks();
-					callbacks.setCallbackOnProgress( this._reportProgress );
-					callbacks.setCallbackOnLoad( callbackOnLoad );
-
-					var objLoader = new THREE.OBJLoader2();
-					objLoader.run( prepData );
-				},
 
-				useRunAsyncMeshAlter: function () {
-					var scope = this;
-					var callbackOnLoad = function ( event ) {
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + event.detail.modelName } } );
-					};
+					let objLoader2Parallel = new OBJLoader2Parallel()
+					.setModelName( local.name )
+					.setExecuteParallel( false );
+
+					let scope = this;
+					function callbackOnLoad ( object3d ) {
+						local.add( object3d );
+						scope._reportProgress( { detail: { text: 'Loading complete: ' + local.name } } );
+					}
 
-					var prepData = new THREE.LoaderSupport.PrepData( 'vive-controller' );
-					var local = new THREE.Object3D();
+					objLoader2Parallel.load( 'models/obj/cerberus/Cerberus.obj', callbackOnLoad );
+				},
+
+				useLoadParallelMeshAlter: function () {
+					let local = new Object3D();
 					local.position.set( 125, 50, 0 );
 					local.name = 'Pivot_vive-controller';
 					this.pivot.add( local );
-					prepData.streamMeshesTo = local;
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/vive-controller/vr_controller_vive_1_5.obj', 'OBJ' ) );
-					prepData.useAsync = true;
-					var callbacks = prepData.getCallbacks();
-					var callbackMeshAlter = function ( event ) {
-						var override = new THREE.LoaderSupport.LoadedMeshUserOverride( false, true );
-
-						var mesh = new THREE.Mesh( event.detail.bufferGeometry, event.detail.material );
-						var scale = 200.0;
+
+					let objLoader2Parallel = new OBJLoader2Parallel()
+					.setModelName( local.name )
+					.setBaseObject3d( local );
+
+					// Configure WorkerExecutionSupport to not disregard worker after execution
+					objLoader2Parallel.getWorkerExecutionSupport().setTerminateWorkerOnLoad( false );
+
+					function callbackMeshAlter ( event ) {
+						let override = new LoadedMeshUserOverride( false, true );
+
+						let mesh = new Mesh( event.detail.bufferGeometry, event.detail.material );
+						let scale = 200.0;
 						mesh.scale.set( scale, scale, scale );
 						mesh.name = event.detail.meshName;
-						var helper = new THREE.VertexNormalsHelper( mesh, 2, 0x00ff00, 1 );
+						let helper = new VertexNormalsHelper( mesh, 2, 0x00ff00, 1 );
 						helper.name = 'VertexNormalsHelper';
 
 						override.addMesh( mesh );
 						override.addMesh( helper );
 
 						return override;
-					};
-					callbacks.setCallbackOnMeshAlter( callbackMeshAlter );
-					callbacks.setCallbackOnProgress( this._reportProgress );
-					callbacks.setCallbackOnLoad( callbackOnLoad );
+					}
+					objLoader2Parallel._setCallbacks( null, callbackMeshAlter );
 
-					var objLoader = new THREE.OBJLoader2();
-					objLoader.run( prepData );
+					let scope = this;
+					function callbackOnLoad ( message ) {
+						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					}
+
+					objLoader2Parallel.load( 'models/obj/vive-controller/vr_controller_vive_1_5.obj', callbackOnLoad );
 				},
 
 				finalize: function () {
@@ -308,7 +317,12 @@
 				},
 
 				_reportProgress: function( event ) {
-					var output = THREE.LoaderSupport.Validator.verifyInput( event.detail.text, '' );
+					let output = '';
+					if ( event.detail !== null && event.detail !== undefined && event.detail.text ) {
+
+						output = event.detail.text;
+
+					}
 					console.log( 'Progress: ' + output );
 					document.getElementById( 'feedback' ).innerHTML = output;
 				},
@@ -351,7 +365,7 @@
 				},
 
 				alterShading: function () {
-					var scope = this;
+					let scope = this;
 					scope.flatShading = ! scope.flatShading;
 					console.log( scope.flatShading ? 'Enabling flat shading' : 'Enabling smooth shading');
 
@@ -359,32 +373,32 @@
 						material.flatShading = scope.flatShading;
 						material.needsUpdate = true;
 					};
-					var scopeTraverse = function ( object3d ) {
+					function scopeTraverse ( object3d ) {
 						scope.traverseScene( object3d );
-					};
+					}
 					scope.pivot.traverse( scopeTraverse );
 				},
 
 				alterDouble: function () {
-					var scope = this;
+					let scope = this;
 					scope.doubleSide = ! scope.doubleSide;
-					console.log( scope.doubleSide ? 'Enabling DoubleSide materials' : 'Enabling FrontSide materials' );
+					console.log( scope.doubleSide ? 'Enabling DoubleSide materials' : 'Enabling FrontSide materials');
 
 					scope.traversalFunction = function ( material ) {
-						material.side = scope.doubleSide ? THREE.DoubleSide : THREE.FrontSide;
+						material.side = scope.doubleSide ? DoubleSide : FrontSide;
 					};
 
-					var scopeTraverse = function ( object3d ) {
+					function scopeTraverse ( object3d ) {
 						scope.traverseScene( object3d );
-					};
+					}
 					scope.pivot.traverse( scopeTraverse );
 				},
 
 				traverseScene: function ( object3d ) {
-					if ( object3d.material instanceof THREE.MultiMaterial ) {
+					if ( Array.isArray( object3d.material ) ) {
 
-						var materials = object3d.material.materials;
-						for ( var name in materials ) {
+						let materials = object3d.material.materials;
+						for ( let name in materials ) {
 
 							if ( materials.hasOwnProperty( name ) )	this.traversalFunction( materials[ name ] );
 
@@ -399,29 +413,28 @@
 
 			};
 
+			let app = new WWOBJLoader2Example( document.getElementById( 'example' ) );
 
-			var app = new WWOBJLoader2Example( document.getElementById( 'example' ) );
-
-			var wwObjLoader2Control = {
+			let wwObjLoader2Control = {
 				flatShading: app.flatShading,
 				doubleSide: app.doubleSide
 			};
 
-			var menuDiv = document.getElementById( 'dat' );
-			var gui = new dat.GUI( {
+			let menuDiv = document.getElementById( 'dat' );
+			let gui = new GUI( {
 				autoPlace: false,
 				width: 320
 			} );
 			menuDiv.appendChild( gui.domElement );
 
-			var folderOptions = gui.addFolder( 'WWOBJLoader2 Options' );
-			var controlFlat = folderOptions.add( wwObjLoader2Control, 'flatShading' ).name( 'Flat Shading' );
+			let folderOptions = gui.addFolder( 'WWOBJLoader2 Options' );
+			let controlFlat = folderOptions.add( wwObjLoader2Control, 'flatShading' ).name( 'Flat Shading' );
 			controlFlat.onChange( function( value ) {
 				console.log( 'Setting flatShading to: ' + value );
 				app.alterShading();
 			});
 
-			var controlDouble = folderOptions.add( wwObjLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
+			let controlDouble = folderOptions.add( wwObjLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
 			controlDouble.onChange( function( value ) {
 				console.log( 'Setting doubleSide to: ' + value );
 				app.alterDouble();
@@ -430,14 +443,14 @@
 
 
 			// init three.js example application
-			var resizeWindow = function () {
+			function resizeWindow () {
 				app.resizeDisplayGL();
-			};
+			}
 
-			var render = function () {
+			function render () {
 				requestAnimationFrame( render );
 				app.render();
-			};
+			}
 			window.addEventListener( 'resize', resizeWindow, false );
 
 			console.log( 'Starting initialisation phase...' );
@@ -447,25 +460,23 @@
 			// kick render loop
 			render();
 
+			// Load a file with OBJLoader2.parse on main
+			app.useParseMain();
 
-			// Load a file with OBJLoader.parse synchronously
-			app.useParseSync();
-
-			// Load a file with OBJLoader.parseAsync asynchronously using a worker
-			app.useParseAsync();
-
-			// Load a file with OBJLoader.load synchronously
-			app.useLoadSync();
+			// Load a file with OBJLoader2Parallel.parse in parallel in worker
+			app.useParseParallel();
 
-			// Load a file with OBJLoader.load asynchronously
-			app.useLoadAsync();
+			// Load a file with OBJLoader.load on main
+			app.useLoadMain();
 
-			// Load a file with OBJLoader.run synchronously
-			app.useRunSync();
+			// Load a file with OBJLoader2Parallel.load in parallel in worker
+			app.useLoadParallel();
 
-			// Load a file with OBJLoader.run asynchronously and add normals during onMeshAlter
-			app.useRunAsyncMeshAlter();
+			// Load a file with OBJLoader2Parallel.load on main with fallback to OBJLoader2.parse
+			app.useLoadMainFallback();
 
+			// Load a file with OBJLoader2Parallel.load in parallel in worker and add normals during onMeshAlter
+			app.useLoadParallelMeshAlter();
 			app.finalize();
 
 		</script>

+ 0 - 458
examples/webgl_loader_obj2_run_director.html

@@ -1,458 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - Web Worker Parallel Demo</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<link type="text/css" rel="stylesheet" href="main.css">
-		<style>
-			#glFullscreen {
-				width: 100%;
-				height: 100vh;
-				min-width: 640px;
-				min-height: 360px;
-				position: relative;
-				overflow: hidden;
-				z-index: 0;
-			}
-			#example {
-				width: 100%;
-				height: 100%;
-				top: 0;
-				left: 0;
-				background-color: #000000;
-			}
-			#feedback {
-			    position: absolute;
-			    color: darkorange;
-			    text-align: left;
-			    bottom: 0%;
-			    left: 0%;
-			    width: auto;
-			    padding: 0px 0px 4px 4px;
-			}
-			#dat {
-				user-select: none;
-				position: absolute;
-				left: 0;
-				top: 0;
-				z-Index: 200;
-			}
-		</style>
-	</head>
-
-	<body>
-		<div id="glFullscreen">
-			<canvas id="example"></canvas>
-		</div>
-		<div id="dat">
-
-		</div>
-		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Web Worker LoaderDirector Parallels Demo
-		</div>
-		<div id="feedback">
-		</div>
-
-		<script src="../build/three.js"></script>
-		<script src="js/controls/TrackballControls.js"></script>
-		<script src="js/loaders/MTLLoader.js"></script>
-		<script src="js/libs/dat.gui.min.js"></script>
-
-		<script src="js/loaders/LoaderSupport.js"></script>
-		<script src="js/loaders/OBJLoader2.js"></script>
-		<script>
-
-			'use strict';
-
-			var WWParallels = function ( elementToBindTo ) {
-				this.renderer = null;
-				this.canvas = elementToBindTo;
-				this.aspectRatio = 1;
-				this.recalcAspectRatio();
-
-				this.scene = null;
-				this.cameraDefaults = {
-					posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
-					posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
-					near: 0.1,
-					far: 10000,
-					fov: 45
-				};
-				this.camera = null;
-				this.cameraTarget = this.cameraDefaults.posCameraTarget;
-
-				this.workerDirector = new THREE.LoaderSupport.WorkerDirector( THREE.OBJLoader2 );
-				this.logging = {
-					enabled: false,
-					debug: false
-				};
-				this.workerDirector.setLogging( this.logging.enabled, this.logging.debug );
-				this.workerDirector.setCrossOrigin( 'anonymous' );
-				this.workerDirector.setForceWorkerDataCopy( true );
-
-				this.controls = null;
-				this.cube = null;
-
-				this.allAssets = [];
-				this.feedbackArray = null;
-
-				this.running = false;
-			};
-
-			WWParallels.prototype = {
-
-				constructor: WWParallels,
-
-				initGL: function () {
-					this.renderer = new THREE.WebGLRenderer( {
-						canvas: this.canvas,
-						antialias: true,
-						autoClear: true
-					} );
-					this.renderer.setClearColor( 0x050505 );
-
-					this.scene = new THREE.Scene();
-
-					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
-					this.resetCamera();
-					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
-
-					var ambientLight = new THREE.AmbientLight( 0x404040 );
-					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
-					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
-
-					directionalLight1.position.set( -100, -50, 100 );
-					directionalLight2.position.set( 100, 50, -100 );
-
-					this.scene.add( directionalLight1 );
-					this.scene.add( directionalLight2 );
-					this.scene.add( ambientLight );
-
-					var geometry = new THREE.BoxBufferGeometry( 10, 10, 10 );
-					var material = new THREE.MeshNormalMaterial();
-					this.cube = new THREE.Mesh( geometry, material );
-					this.cube.position.set( 0, 0, 0 );
-					this.scene.add( this.cube );
-				},
-
-				resizeDisplayGL: function () {
-					this.controls.handleResize();
-
-					this.recalcAspectRatio();
-					this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
-
-					this.updateCamera();
-				},
-
-				recalcAspectRatio: function () {
-					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
-				},
-
-				resetCamera: function () {
-					this.camera.position.copy( this.cameraDefaults.posCamera );
-					this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
-
-					this.updateCamera();
-				},
-
-				updateCamera: function () {
-					this.camera.aspect = this.aspectRatio;
-					this.camera.lookAt( this.cameraTarget );
-					this.camera.updateProjectionMatrix();
-				},
-
-				render: function () {
-					if ( ! this.renderer.autoClear ) this.renderer.clear();
-
-					this.controls.update();
-
-					this.cube.rotation.x += 0.05;
-					this.cube.rotation.y += 0.05;
-
-					this.renderer.render( this.scene, this.camera );
-				},
-
-				_reportProgress: function( content ) {
-					var output = content;
-					if ( THREE.LoaderSupport.Validator.isValid( content ) && THREE.LoaderSupport.Validator.isValid( content.detail ) ) output = content.detail.text;
-
-					output = THREE.LoaderSupport.Validator.verifyInput( output, '' );
-					if ( this.logging.enabled ) console.info( 'Progress:\n\t' + output.replace(/\<br\>/g, '\n\t' ) );
-					document.getElementById( 'feedback' ).innerHTML = output;
-				},
-
-				enqueueAllAssests: function ( maxQueueSize, maxWebWorkers, streamMeshes ) {
-					if ( this.running ) {
-
-						return;
-
-					} else {
-
-						this.running = true;
-
-					}
-
-					var scope = this;
-					scope.workerDirector.objectsCompleted = 0;
-					scope.feedbackArray = [];
-					scope.reportDonwload = [];
-
-					var i;
-					for ( i = 0; i < maxWebWorkers; i++ ) {
-
-						scope.feedbackArray[ i ] = 'Worker #' + i + ': Awaiting feedback';
-						scope.reportDonwload[ i ] = true;
-
-					}
-					scope._reportProgress( scope.feedbackArray.join( '\<br\>' ) );
-
-					var callbackOnLoad = function ( event ) {
-						var instanceNo = event.detail.instanceNo;
-						scope.reportDonwload[ instanceNo ] = false;
-						scope.allAssets.push( event.detail.loaderRootNode );
-
-						var msg = 'Worker #' + instanceNo + ': Completed loading: ' + event.detail.modelName + ' (#' + scope.workerDirector.objectsCompleted + ')';
-						if ( scope.logging.enabled ) console.info( msg );
-						scope.feedbackArray[ instanceNo ] = msg;
-						scope._reportProgress( scope.feedbackArray.join( '\<br\>' ) );
-
-						if ( scope.workerDirector.objectsCompleted + 1 === maxQueueSize ) scope.running = false;
-					};
-
-					var callbackReportProgress = function ( event ) {
-						var	instanceNo = event.detail.instanceNo;
-						var text = event.detail.text;
-
-						if ( scope.reportDonwload[ instanceNo ] ) {
-							var msg = 'Worker #' + instanceNo + ': ' + text;
-							if ( scope.logging.enabled ) console.info( msg );
-
-							scope.feedbackArray[ instanceNo ] = msg;
-							scope._reportProgress( scope.feedbackArray.join( '\<br\>' ) );
-						}
-					};
-
-					var callbackReportError = function ( supportDesc, errorMessage ) {
-
-						console.error( 'LoaderWorkerDirector reported an error: ' );
-						console.error( errorMessage );
-						return true;
-
-					};
-
-					var callbackMeshAlter = function ( event, override ) {
-						if ( ! THREE.LoaderSupport.Validator.isValid( override ) ) override = new THREE.LoaderSupport.LoadedMeshUserOverride( false, false );
-
-						var material = event.detail.material;
-						var meshName = event.detail.meshName;
-						if ( THREE.LoaderSupport.Validator.isValid( material ) && material.name === 'defaultMaterial' || meshName === 'Mesh_Mesh_head_geo.001_lambert2SG.001' ) {
-
-							var materialOverride = material;
-							materialOverride.color = new THREE.Color( Math.random(), Math.random(), Math.random() );
-							var mesh = new THREE.Mesh( event.detail.bufferGeometry, material );
-							mesh.name = meshName;
-
-							override.addMesh( mesh );
-							override.alteredMesh = true;
-
-						}
-						return override;
-					};
-
-					var callbackOnLoadMaterials = function ( materials ) {
-						console.log( 'Materials loaded' );
-						return materials;
-					};
-
-					var callbacks = new THREE.LoaderSupport.Callbacks();
-					callbacks.setCallbackOnProgress( callbackReportProgress );
-					callbacks.setCallbackOnReportError( callbackReportError );
-					callbacks.setCallbackOnLoad( callbackOnLoad );
-					callbacks.setCallbackOnMeshAlter( callbackMeshAlter );
-					callbacks.setCallbackOnLoadMaterials( callbackOnLoadMaterials );
-
-					this.workerDirector.prepareWorkers( callbacks, maxQueueSize, maxWebWorkers );
-					if ( this.logging.enabled ) console.info( 'Configuring WWManager with queue size ' + this.workerDirector.getMaxQueueSize() + ' and ' + this.workerDirector.getMaxWebWorkers() + ' workers.' );
-
-					var prepData;
-					var modelPrepDatas = [];
-					prepData = new THREE.LoaderSupport.PrepData( 'male02' );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/male02/male02.obj', 'OBJ ' ) );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/male02/male02.mtl', 'MTL' ) );
-					prepData.setLogging( false, false );
-					modelPrepDatas.push( prepData );
-
-					prepData = new THREE.LoaderSupport.PrepData( 'female02' );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/female02/female02.obj', 'OBJ' ) );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/female02/female02.mtl', 'MTL' ) );
-					prepData.setLogging( false, false );
-					modelPrepDatas.push( prepData );
-
-					prepData = new THREE.LoaderSupport.PrepData( 'viveController' );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/vive-controller/vr_controller_vive_1_5.obj', 'OBJ' ) );
-					prepData.setLogging( false, false );
-					prepData.scale = 400.0;
-					modelPrepDatas.push( prepData );
-
-					prepData = new THREE.LoaderSupport.PrepData( 'cerberus' );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/cerberus/Cerberus.obj', 'OBJ' ) );
-					prepData.setLogging( false, false );
-					prepData.scale = 50.0;
-					modelPrepDatas.push( prepData );
-
-					prepData = new THREE.LoaderSupport.PrepData( 'WaltHead' );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/walt/WaltHead.obj', 'OBJ' ) );
-					prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/walt/WaltHead.mtl', 'MTL' ) );
-					prepData.setLogging( false, false );
-					modelPrepDatas.push( prepData );
-
-					var pivot;
-					var distributionBase = -500;
-					var distributionMax = 1000;
-					var modelPrepDataIndex = 0;
-					var modelPrepData;
-					var scale;
-					for ( i = 0; i < maxQueueSize; i++ ) {
-
-						modelPrepDataIndex = Math.floor( Math.random() * modelPrepDatas.length );
-
-						modelPrepData = modelPrepDatas[ modelPrepDataIndex ];
-						modelPrepData.useAsync = true;
-						scale = THREE.LoaderSupport.Validator.verifyInput( modelPrepData.scale, 0 );
-						modelPrepData = modelPrepData.clone();
-
-						pivot = new THREE.Object3D();
-						pivot.position.set(
-							distributionBase + distributionMax * Math.random(),
-							distributionBase + distributionMax * Math.random(),
-							distributionBase + distributionMax * Math.random()
-						);
-						if ( scale > 0 ) pivot.scale.set( scale, scale, scale );
-						this.scene.add( pivot );
-						modelPrepData.streamMeshesTo = pivot;
-
-						this.workerDirector.enqueueForRun( modelPrepData );
-					}
-					this.workerDirector.processQueue();
-				},
-
-				clearAllAssests: function () {
-					var storedObject3d;
-					for ( var asset in this.allAssets ) {
-
-						storedObject3d = this.allAssets[ asset ];
-						var scope = this;
-						var remover = function ( object3d ) {
-
-							if ( storedObject3d === object3d ) return;
-
-							if ( scope.logging.enabled ) console.info( 'Removing ' + object3d.name );
-							scope.scene.remove( object3d );
-
-							if ( object3d.hasOwnProperty( 'geometry' ) ) object3d.geometry.dispose();
-							if ( object3d.hasOwnProperty( 'material' ) ) {
-
-								var mat = object3d.material;
-								if ( mat.hasOwnProperty( 'materials' ) ) {
-
-									var materials = mat.materials;
-									for ( var name in materials ) {
-
-										if ( materials.hasOwnProperty( name ) ) materials[ name ].dispose();
-
-									}
-								}
-							}
-							if ( object3d.hasOwnProperty( 'texture' ) )	object3d.texture.dispose();
-						};
-						if ( THREE.LoaderSupport.Validator.isValid( storedObject3d ) ) {
-
-							if ( this.pivot !== storedObject3d ) scope.scene.remove( storedObject3d );
-							storedObject3d.traverse( remover );
-							storedObject3d = null;
-
-						}
-					}
-					this.allAssets = [];
-				},
-
-				terminateManager: function () {
-					this.workerDirector.tearDown();
-					this.running = false;
-				},
-
-				terminateManagerAndClearScene: function () {
-					var scope = this;
-					var scopedClearAllAssests = function (){
-						scope.clearAllAssests();
-					};
-					if ( this.workerDirector.isRunning() ) {
-
-						this.workerDirector.tearDown( scopedClearAllAssests );
-
-					} else {
-
-						scopedClearAllAssests();
-					}
-
-					this.running = false;
-				}
-
-			};
-
-
-			var app = new WWParallels( document.getElementById( 'example' ) );
-
-			var wwParallelsControl = {
-				queueLength: 128,
-				workerCount: 4,
-				streamMeshes: true,
-				run: function () {
-					app.enqueueAllAssests( this.queueLength, this.workerCount, this.streamMeshes );
-				},
-				terminate: function () {
-					app.terminateManager();
-				},
-				clearAllAssests: function () {
-					app.terminateManagerAndClearScene();
-				}
-			};
-			var gui = new dat.GUI( {
-				autoPlace: false,
-				width: 320
-			} );
-
-			var menuDiv = document.getElementById( 'dat' );
-			menuDiv.appendChild(gui.domElement);
-			var folderQueue = gui.addFolder( 'Web Worker Director Queue Control' );
-			folderQueue.add( wwParallelsControl, 'queueLength' ).min( 1 ).max( 1024 ).step( 1 );
-			folderQueue.add( wwParallelsControl, 'workerCount' ).min( 1 ).max( 16 ).step( 1 );
-			folderQueue.add( wwParallelsControl, 'streamMeshes' );
-			folderQueue.add( wwParallelsControl, 'run' ).name( 'Run Queue' );
-			folderQueue.open();
-
-			var folderWWControl = gui.addFolder( 'Resource Management' );
-			folderWWControl.add( wwParallelsControl, 'terminate' ).name( 'Terminate WWManager' );
-			folderWWControl.add( wwParallelsControl, 'clearAllAssests' ).name( 'Clear Scene' );
-
-			var resizeWindow = function () {
-				app.resizeDisplayGL();
-			};
-
-			var render = function () {
-				requestAnimationFrame( render );
-				app.render();
-			};
-
-			window.addEventListener( 'resize', resizeWindow, false );
-
-			console.log( 'Starting initialisation phase...' );
-			app.initGL();
-			app.resizeDisplayGL();
-
-			render();
-
-		</script>
-	</body>
-</html>