2
0
Эх сурвалжийг харах

Merge pull request #17441 from kaisalmen/OBJLoader2_V301

OBJLoader2(Parallel) 3.1.0
Mr.doob 5 жил өмнө
parent
commit
82e3808d92

+ 60 - 66
docs/examples/en/loaders/OBJLoader2.html

@@ -22,183 +22,177 @@
 
 
 		<code>
 		<code>
 		// instantiate the loader
 		// instantiate the loader
-		var loader = new THREE.OBJLoader2();
+		let loader = new THREE.OBJLoader2();
 
 
 		// function called on successful load
 		// function called on successful load
-		var callbackOnLoad = function ( object3d ) {
+		function callbackOnLoad ( object3d ) {
 			scene.add( object3d );
 			scene.add( object3d );
-		};
+		}
 
 
 		// load a resource from provided URL synchronously
 		// load a resource from provided URL synchronously
-		loader.load( 'obj/female02/female02.obj', callbackOnLoad, null, null, null, false );
+		loader.load( 'obj/female02/female02.obj', callbackOnLoad, null, null, null );
 		</code>
 		</code>
 
 
 		[example:webgl_loader_obj2] - Simple example <br>
 		[example:webgl_loader_obj2] - Simple example <br>
-		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse, load and run with instructions (sync and async)<br>
+		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse and load, sync or in parallel to main (see [page:OBJLoader2Parallel]))<br>
 
 
 
 
 		<h2>Constructor</h2>
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( [param:LoadingManager manager], [param:LoaderSupport.ConsoleLogger logger] )</h3>
+		<h3>[name]( [param:LoadingManager manager] )</h3>
 		<p>
 		<p>
 			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
 			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
-			[page:LoaderSupport.ConsoleLogger logger] - logger to be used
 		</p>
 		</p>
 		<p>
 		<p>
-			Use [name] to load OBJ data from files or to parse OBJ data from arraybuffer or text.
+			Creates a new [name]. Use it to load OBJ data from files or to parse OBJ data from arraybuffer or text.
 		</p>
 		</p>
 
 
 
 
+		<h2>Properties</h2>
+		<p>See the base [page:Loader] class for common properties.</p>
+
+
 		<h2>Methods</h2>
 		<h2>Methods</h2>
+		<p>See the base [page:Loader] class for common methods.</p>
+
 
 
 		<h3>[method:Object3D parse]( [param:arraybuffer content]|[param:String content] )</h3>
 		<h3>[method:Object3D parse]( [param:arraybuffer content]|[param:String content] )</h3>
 		<p>
 		<p>
 			[[page:arraybuffer content]|[page:String content]] OBJ data as Uint8Array or String
 			[[page:arraybuffer content]|[page:String content]] OBJ data as Uint8Array or String
 		</p>
 		</p>
 		<p>
 		<p>
-			Parses OBJ data synchronously from arraybuffer or string and returns the [page:Object3D loaderRoorNode].
-		</p>
-
-
-		<h3>[method:Object3D parseAsync]( [param:arraybuffer content], [param:Function onLoad] )</h3>
-		<p>
-			[page:arraybuffer content] - OBJ data as Uint8Array<br>
-			[page:Function onLoad] - Called after worker successfully completed loading<br>
-		</p>
-		<p>
-			Parses OBJ content asynchronously from arraybuffer.
+			Parses OBJ data synchronously from arraybuffer or string and returns the [page:Object3D baseObject3d].
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError], [param:Function onMeshAlter], [param:boolean useAsync] )</h3>
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError], [param:Function onMeshAlter] )</h3>
 		<p>
 		<p>
 			[page:String url] - A string containing the path/URL of the file to be loaded.<br>
 			[page:String url] - A string containing the path/URL of the file to be loaded.<br>
 			[page:Function onLoad] - A function to be called after loading is successfully completed. The function receives loaded [page:Object3D] as an argument.<br>
 			[page:Function onLoad] - A function to be called after loading is successfully completed. The function receives loaded [page:Object3D] as an argument.<br>
 			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
 			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
 			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
 			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
 			[page:Function onMeshAlter] - (optional) A function to be called after a new mesh raw data becomes available for alteration.<br>
 			[page:Function onMeshAlter] - (optional) A function to be called after a new mesh raw data becomes available for alteration.<br>
-			[page:boolean useAsync] - (optional) If true, uses async loading with worker, if false loads data synchronously.
 		</p>
 		</p>
 		<p>
 		<p>
 			Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
 			Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null run]( [param:LoaderSupport.PrepData params], [param:LoaderSupport.WorkerSupport workerSupportExternal] )</h3>
+		<h3>[method:OBJLoader2 setLogging]( [param:Boolean enabled], [param:Boolean debug] )</h3>
 		<p>
 		<p>
-			[page:LoaderSupport.PrepData params] - prepData All parameters and resources required for execution<br>
-			[page:LoaderSupport.WorkerSupport workerSupportExternal] - Use pre-existing WorkerSupport
+			[page:Boolean enabled] True or false.<br>
+			[page:Boolean debug] True or false.
 		</p>
 		</p>
 		<p>
 		<p>
-			Run the loader according the provided instructions.
+			Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setLogging]( [param:Boolean enabled], [param:Boolean debug] )</h3>
+		<h3>[method:OBJLoader2 addMaterialPerSmoothingGroup] ( [param:boolean materialPerSmoothingGroup] )</h3>
 		<p>
 		<p>
-			[page:Boolean enabled] True or false.<br>
-			[page:Boolean debug] True or false.
+			[page:boolean materialPerSmoothingGroup]
 		</p>
 		</p>
 		<p>
 		<p>
-			Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
+			Tells whether a material shall be created per smoothing group.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setModelName] ( [param:String modelName] )</h3>
+		<h3>[method:OBJLoader2 setUseOAsMesh] ( [param:boolean useOAsMesh] )</h3>
 		<p>
 		<p>
-			[page:String modelName]
+			[page:boolean useOAsMesh]
 		</p>
 		</p>
 		<p>
 		<p>
-			Set the name of the model.
+			Usually 'o' is meta-information and does not result in creation of new meshes, but mesh creation on occurrence of "o" can be enforced.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setPath] ( [param:String path] )</h3>
+		<h3>[method:OBJLoader2 setUseIndices]( [param:Boolean useIndices] )</h3>
 		<p>
 		<p>
-			[page:String path] - URL
+			[page:Boolean useIndices]
 		</p>
 		</p>
 		<p>
 		<p>
-			The URL of the base path.
+			Instructs loaders to create indexed [page:BufferGeometry].
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setResourcePath] ( [param:String resourcePath] )</h3>
+		<h3>[method:OBJLoader2 setDisregardNormals]( [param:Boolean disregardNormals] )</h3>
 		<p>
 		<p>
-			[page:String resourcePath] - URL
+			[page:Boolean disregardNormals]
 		</p>
 		</p>
 		<p>
 		<p>
-			Allows to specify resourcePath for dependencies of specified resource.
+			Tells whether normals should be completely disregarded and regenerated.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setStreamMeshesTo] ( [param:Object3D streamMeshesTo] )</h3>
+		<h3>[method:OBJLoader2 setModelName] ( [param:String modelName] )</h3>
+		<p>
+			[page:String modelName]
+		</p>
 		<p>
 		<p>
-			[page:Object3D streamMeshesTo] - Object already attached to scenegraph where new meshes will be attached to
+			Set the name of the model.
+		</p>
+
+
+		<h3>[method:OBJLoader2 setBaseObject3d] ( [param:Object3d baseObject3d] )</h3>
+		<p>
+			[page:Object3D baseObject3d - Object already attached to scenegraph where new meshes will be attached to
 		</p>
 		</p>
 		<p>
 		<p>
 			Set the node where the loaded objects will be attached directly.
 			Set the node where the loaded objects will be attached directly.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setMaterials] ( Array of [param:Material materials] )</h3>
+		<h3>[method:OBJLoader2 setMaterials] ( [param:Object materials] )</h3>
 		<p>
 		<p>
-			Array of [page:Material materials] - Array of [page:Material Materials]
+			[page:Object materials] -  materials Object with named [page:Material Materials]
 		</p>
 		</p>
 		<p>
 		<p>
-			Set materials loaded by MTLLoader or any other supplier of an Array of [page:Material Materials].
+			Add materials as associated array.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setUseIndices]( [param:Boolean useIndices] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnLoad] ( [param:Function onLoad] )</h3>
 		<p>
 		<p>
-			[page:Boolean useIndices]
+			[page:Function onLoad]
 		</p>
 		</p>
 		<p>
 		<p>
-			Instructs loaders to create indexed [page:BufferGeometry].
+			Register a function that is called when parsing was completed.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setDisregardNormals]( [param:Boolean disregardNormals] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnAssetAvailable] ( [param:Function onAssetAvailable] )</h3>
 		<p>
 		<p>
-			[page:Boolean disregardNormals]
+			[page:Function onAssetAvailable]
 		</p>
 		</p>
 		<p>
 		<p>
-			Tells whether normals should be completely disregarded and regenerated.
+			Register a function that is called once an asset (mesh/material) becomes available.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setMaterialPerSmoothingGroup] ( [param:boolean materialPerSmoothingGroup] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnProgress] ( [param:Function onProgress] )</h3>
 		<p>
 		<p>
-			[page:boolean materialPerSmoothingGroup]
+			[page:Function onProgress]
 		</p>
 		</p>
 		<p>
 		<p>
-			Tells whether a material shall be created per smoothing group.
+			Register a function that is used to report overall processing progress.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null onProgress]( [param:String type], [param:String text], [param:Number numericalValue] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnError] ( [param:Function onError] )</h3>
 		<p>
 		<p>
-			[page:String type] - The type of event<br>
-			[page:String text] - Textual description of the event<br>
-			[page:Number numericalValue] - Numerical value describing the progress
+			[page:Function onError]
 		</p>
 		</p>
 		<p>
 		<p>
-			Announce feedback which is give to the registered [page:LoaderSupport.Callbacks].
+			Register an error handler function that is called if errors occur. It can decide to just log or to throw an exception.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null loadMtl]( [param:String url], [param:Object content], [param:Function callbackOnLoad], [param:String crossOrigin], [param:Object materialOptions]) </h3>
+		<h3>[method:OBJLoader2 setCallbackOnMeshAlter] ( [param:Function onMeshAlter] )</h3>
 		<p>
 		<p>
-			[page:String url] - URL to the file<br>
-			[page:Object content] - The file content as arraybuffer or text<br>
-			[page:Function onLoad] - Callback to be called after successful load<br>
-			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
-			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
-			[page:String crossOrigin] - (optional) CORS value<br>
-			[page:Function materialOptions] - (optional) Set material loading options for MTLLoader
+			[page:Function onMeshAlter]
 		</p>
 		</p>
 		<p>
 		<p>
-			Utility method for loading an mtl file according resource description. Provide url or content.
+			Register a function that is called once a single mesh is available and it could be altered by the supplied function.
 		</p>
 		</p>
 
 
 
 

+ 109 - 0
docs/examples/en/loaders/OBJLoader2Parallel.html

@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		<h1>[name]</h1>
+
+		<p class="desc">A loader for loading a <em>.obj</em> resource.<br />
+			The <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">OBJ file format</a> is a simple data-format
+			that represents 3D geometry in a human readable format as, the position of each vertex, the UV position of
+			each texture coordinate vertex, vertex normals, and the faces that make each polygon defined as a list of
+			vertices, and texture vertices.
+		</p>
+
+		<h2>Examples</h2>
+
+		<code>
+		// instantiate the loader
+		let objLoader2Parallel = new OBJLoader2Parallel();
+
+		// define where to attach the data
+		let local = new THREE.Object3D();
+
+		// function called on successful completion of parsing
+		function callbackOnLoad( object3d, message ) {
+			local.add( object3d );
+		}
+
+		// load a resource from provided URL in parallel to Main
+		objLoader2Parallel.load( 'models/obj/walt/WaltHead.obj', callbackOnLoad, null, null, null );
+		</code>
+
+		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse and load, sync (see [page:OBJLoader2]) or in parallel to main)<br>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
+		</p>
+		<p>
+			Creates a new [name]. Use it to load OBJ data from files or to parse OBJ data from arraybuffer.
+			It extends [page:OBJLoader2]  with the capability to run the parser in a web worker.
+		</p>
+
+
+		<h2>Properties</h2>
+		<p>See the base [page:OBJLoader2] class for common properties.</p>
+
+
+		<h2>Methods</h2>
+		<p>See the base [page:OBJLoader2] class for common methods. </p>
+
+
+		<h3>[method:Object3D parse]</h3>
+		<p>See [page:OBJLoader2.parse].<br>
+			The callback [page:OBJLoader2.setCallbackOnLoad OBJLoader2.onLoad] needs to be set to be able to receive the content if used in parallel mode.
+			Fallback is possible via [page:OBJLoader2Parallel.setExecuteParallel].
+		</p>
+
+
+		<h3>[method:null load]</h3>
+		<p>See [page:OBJLoader2.load].</p>
+
+
+		<h3>[method:OBJLoader2Parallel setExecuteParallel] ( [param:boolean executeParallel] )</h3>
+		<p>
+			[page:boolean executeParallel] - True or False
+		</p>
+		<p>
+			Execution of parse in parallel via Worker is default, but synchronous [page:OBJLoader2] parsing can be enforced via false here.
+		</p>
+
+
+		<h3>[method:OBJLoader2Parallel setPreferJsmWorker] ( [param:boolean preferJsmWorker] )</h3>
+		<p>
+			[page:boolean preferJsmWorker] - True or False
+		</p>
+		<p>
+			Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
+		</p>
+
+
+		<h3>[method:WorkerExecutionSupport getWorkerExecutionSupport] ()</h3>
+		<p>
+			Allow to get hold of [page:WorkerExecutionSupport] for configuration purposes.
+		</p>
+
+
+		<h3>[method:CodeBuilderInstructions buildWorkerCode] ()</h3>
+		<p>
+			Provide instructions on what is to be contained in the worker.
+		</p>
+
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/OBJLoader2Parallel.js examples/jsm/loaders/OBJLoader2Parallel.js]
+		</p>
+	</body>
+</html>

+ 61 - 67
docs/examples/zh/loaders/OBJLoader2.html

@@ -22,183 +22,177 @@
 
 
 		<code>
 		<code>
 		// instantiate the loader
 		// instantiate the loader
-		var loader = new THREE.OBJLoader2();
+		let loader = new THREE.OBJLoader2();
 
 
 		// function called on successful load
 		// function called on successful load
-		var callbackOnLoad = function ( event ) {
-			scene.add( event.detail.loaderRootNode );
-		};
+		function callbackOnLoad ( object3d ) {
+			scene.add( object3d );
+		}
 
 
 		// load a resource from provided URL synchronously
 		// load a resource from provided URL synchronously
-		loader.load( 'obj/female02/female02.obj', callbackOnLoad, null, null, null, false );
+		loader.load( 'obj/female02/female02.obj', callbackOnLoad, null, null, null );
 		</code>
 		</code>
 
 
 		[example:webgl_loader_obj2] - Simple example <br>
 		[example:webgl_loader_obj2] - Simple example <br>
-		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse, load and run with instructions (sync and async)<br>
+		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse and load, sync or in parallel to main (see [page:OBJLoader2Parallel]))<br>
 
 
 
 
 		<h2>Constructor</h2>
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( [param:LoadingManager manager], [param:LoaderSupport.ConsoleLogger logger] )</h3>
+		<h3>[name]( [param:LoadingManager manager] )</h3>
 		<p>
 		<p>
 			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
 			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
-			[page:LoaderSupport.ConsoleLogger logger] - logger to be used
 		</p>
 		</p>
 		<p>
 		<p>
-			Use [name] to load OBJ data from files or to parse OBJ data from arraybuffer or text.
+			Creates a new [name]. Use it to load OBJ data from files or to parse OBJ data from arraybuffer or text.
 		</p>
 		</p>
 
 
 
 
+		<h2>Properties</h2>
+		<p>See the base [page:Loader] class for common properties.</p>
+
+
 		<h2>Methods</h2>
 		<h2>Methods</h2>
+		<p>See the base [page:Loader] class for common methods.</p>
+
 
 
 		<h3>[method:Object3D parse]( [param:arraybuffer content]|[param:String content] )</h3>
 		<h3>[method:Object3D parse]( [param:arraybuffer content]|[param:String content] )</h3>
 		<p>
 		<p>
 			[[page:arraybuffer content]|[page:String content]] OBJ data as Uint8Array or String
 			[[page:arraybuffer content]|[page:String content]] OBJ data as Uint8Array or String
 		</p>
 		</p>
 		<p>
 		<p>
-			Parses OBJ data synchronously from arraybuffer or string and returns the [page:Object3D loaderRoorNode].
-		</p>
-
-
-		<h3>[method:Object3D parseAsync]( [param:arraybuffer content], [param:Function onLoad] )</h3>
-		<p>
-			[page:arraybuffer content] - OBJ data as Uint8Array<br>
-			[page:Function onLoad] - Called after worker successfully completed loading<br>
-		</p>
-		<p>
-			Parses OBJ content asynchronously from arraybuffer.
+			Parses OBJ data synchronously from arraybuffer or string and returns the [page:Object3D baseObject3d].
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError], [param:Function onMeshAlter], [param:boolean useAsync] )</h3>
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError], [param:Function onMeshAlter] )</h3>
 		<p>
 		<p>
 			[page:String url] - A string containing the path/URL of the file to be loaded.<br>
 			[page:String url] - A string containing the path/URL of the file to be loaded.<br>
 			[page:Function onLoad] - A function to be called after loading is successfully completed. The function receives loaded [page:Object3D] as an argument.<br>
 			[page:Function onLoad] - A function to be called after loading is successfully completed. The function receives loaded [page:Object3D] as an argument.<br>
 			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
 			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
 			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
 			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
 			[page:Function onMeshAlter] - (optional) A function to be called after a new mesh raw data becomes available for alteration.<br>
 			[page:Function onMeshAlter] - (optional) A function to be called after a new mesh raw data becomes available for alteration.<br>
-			[page:boolean useAsync] - (optional) If true, uses async loading with worker, if false loads data synchronously.
 		</p>
 		</p>
 		<p>
 		<p>
 			Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
 			Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null run]( [param:LoaderSupport.PrepData params], [param:LoaderSupport.WorkerSupport workerSupportExternal] )</h3>
+		<h3>[method:OBJLoader2 setLogging]( [param:Boolean enabled], [param:Boolean debug] )</h3>
 		<p>
 		<p>
-			[page:LoaderSupport.PrepData params] - prepData All parameters and resources required for execution<br>
-			[page:LoaderSupport.WorkerSupport workerSupportExternal] - Use pre-existing WorkerSupport
+			[page:Boolean enabled] True or false.<br>
+			[page:Boolean debug] True or false.
 		</p>
 		</p>
 		<p>
 		<p>
-			Run the loader according the provided instructions.
+			Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setLogging]( [param:Boolean enabled], [param:Boolean debug] )</h3>
+		<h3>[method:OBJLoader2 addMaterialPerSmoothingGroup] ( [param:boolean materialPerSmoothingGroup] )</h3>
 		<p>
 		<p>
-			[page:Boolean enabled] True or false.<br>
-			[page:Boolean debug] True or false.
+			[page:boolean materialPerSmoothingGroup]
 		</p>
 		</p>
 		<p>
 		<p>
-			Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
+			Tells whether a material shall be created per smoothing group.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setModelName] ( [param:String modelName] )</h3>
+		<h3>[method:OBJLoader2 setUseOAsMesh] ( [param:boolean useOAsMesh] )</h3>
 		<p>
 		<p>
-			[page:String modelName]
+			[page:boolean useOAsMesh]
 		</p>
 		</p>
 		<p>
 		<p>
-			Set the name of the model.
+			Usually 'o' is meta-information and does not result in creation of new meshes, but mesh creation on occurrence of "o" can be enforced.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setPath] ( [param:String path] )</h3>
+		<h3>[method:OBJLoader2 setUseIndices]( [param:Boolean useIndices] )</h3>
 		<p>
 		<p>
-			[page:String path] - URL
+			[page:Boolean useIndices]
 		</p>
 		</p>
 		<p>
 		<p>
-			The URL of the base path.
+			Instructs loaders to create indexed [page:BufferGeometry].
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setResourcePath] ( [param:String resourcePath] )</h3>
+		<h3>[method:OBJLoader2 setDisregardNormals]( [param:Boolean disregardNormals] )</h3>
 		<p>
 		<p>
-			[page:String resourcePath] - URL
+			[page:Boolean disregardNormals]
 		</p>
 		</p>
 		<p>
 		<p>
-			Allows to specify resourcePath for dependencies of specified resource.
+			Tells whether normals should be completely disregarded and regenerated.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setStreamMeshesTo] ( [param:Object3D streamMeshesTo] )</h3>
+		<h3>[method:OBJLoader2 setModelName] ( [param:String modelName] )</h3>
+		<p>
+			[page:String modelName]
+		</p>
 		<p>
 		<p>
-			[page:Object3D streamMeshesTo] - Object already attached to scenegraph where new meshes will be attached to
+			Set the name of the model.
+		</p>
+
+
+		<h3>[method:OBJLoader2 setBaseObject3d] ( [param:Object3d baseObject3d] )</h3>
+		<p>
+			[page:Object3D baseObject3d - Object already attached to scenegraph where new meshes will be attached to
 		</p>
 		</p>
 		<p>
 		<p>
 			Set the node where the loaded objects will be attached directly.
 			Set the node where the loaded objects will be attached directly.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setMaterials] ( Array of [param:Material materials] )</h3>
+		<h3>[method:OBJLoader2 setMaterials] ( [param:Object materials] )</h3>
 		<p>
 		<p>
-			Array of [page:Material materials] - Array of [page:Material Materials]
+			[page:Object materials] -  materials Object with named [page:Material Materials]
 		</p>
 		</p>
 		<p>
 		<p>
-			Set materials loaded by MTLLoader or any other supplier of an Array of [page:Material Materials].
+			Add materials as associated array.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setUseIndices]( [param:Boolean useIndices] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnLoad] ( [param:Function onLoad] )</h3>
 		<p>
 		<p>
-			[page:Boolean useIndices]
+			[page:Function onLoad]
 		</p>
 		</p>
 		<p>
 		<p>
-			Instructs loaders to create indexed [page:BufferGeometry].
+			Register a function that is called when parsing was completed.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setDisregardNormals]( [param:Boolean disregardNormals] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnAssetAvailable] ( [param:Function onAssetAvailable] )</h3>
 		<p>
 		<p>
-			[page:Boolean disregardNormals]
+			[page:Function onAssetAvailable]
 		</p>
 		</p>
 		<p>
 		<p>
-			Tells whether normals should be completely disregarded and regenerated.
+			Register a function that is called once an asset (mesh/material) becomes available.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null setMaterialPerSmoothingGroup] ( [param:boolean materialPerSmoothingGroup] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnProgress] ( [param:Function onProgress] )</h3>
 		<p>
 		<p>
-			[page:boolean materialPerSmoothingGroup]
+			[page:Function onProgress]
 		</p>
 		</p>
 		<p>
 		<p>
-			Tells whether a material shall be created per smoothing group.
+			Register a function that is used to report overall processing progress.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null onProgress]( [param:String type], [param:String text], [param:Number numericalValue] )</h3>
+		<h3>[method:OBJLoader2 setCallbackOnError] ( [param:Function onError] )</h3>
 		<p>
 		<p>
-			[page:String type] - The type of event<br>
-			[page:String text] - Textual description of the event<br>
-			[page:Number numericalValue] - Numerical value describing the progress
+			[page:Function onError]
 		</p>
 		</p>
 		<p>
 		<p>
-			Announce feedback which is give to the registered [page:LoaderSupport.Callbacks].
+			Register an error handler function that is called if errors occur. It can decide to just log or to throw an exception.
 		</p>
 		</p>
 
 
 
 
-		<h3>[method:null loadMtl]( [param:String url], [param:Object content], [param:Function callbackOnLoad], [param:String crossOrigin], [param:Object materialOptions]) </h3>
+		<h3>[method:OBJLoader2 setCallbackOnMeshAlter] ( [param:Function onMeshAlter] )</h3>
 		<p>
 		<p>
-			[page:String url] - URL to the file<br>
-			[page:Object content] - The file content as arraybuffer or text<br>
-			[page:Function onLoad] - Callback to be called after successful load<br>
-			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
-			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
-			[page:String crossOrigin] - (optional) CORS value<br>
-			[page:Function materialOptions] - (optional) Set material loading options for MTLLoader
+			[page:Function onMeshAlter]
 		</p>
 		</p>
 		<p>
 		<p>
-			Utility method for loading an mtl file according resource description. Provide url or content.
+			Register a function that is called once a single mesh is available and it could be altered by the supplied function.
 		</p>
 		</p>
 
 
 
 

+ 109 - 0
docs/examples/zh/loaders/OBJLoader2Parallel.html

@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		<h1>[name]</h1>
+
+		<p class="desc">A loader for loading a <em>.obj</em> resource.<br />
+			The <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">OBJ file format</a> is a simple data-format
+			that represents 3D geometry in a human readable format as, the position of each vertex, the UV position of
+			each texture coordinate vertex, vertex normals, and the faces that make each polygon defined as a list of
+			vertices, and texture vertices.
+		</p>
+
+		<h2>Examples</h2>
+
+		<code>
+		// instantiate the loader
+		let objLoader2Parallel = new OBJLoader2Parallel();
+
+		// define where to attach the data
+		let local = new THREE.Object3D();
+
+		// function called on successful completion of parsing
+		function callbackOnLoad( object3d, message ) {
+			local.add( object3d );
+		}
+
+		// load a resource from provided URL in parallel to Main
+		objLoader2Parallel.load( 'models/obj/walt/WaltHead.obj', callbackOnLoad, null, null, null );
+		</code>
+
+		[example:webgl_loader_obj2_options] - Example for multiple use-cases (parse and load, sync (see [page:OBJLoader2]) or in parallel to main)<br>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+			[page:LoadingManager manager] - The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br>
+		</p>
+		<p>
+			Creates a new [name]. Use it to load OBJ data from files or to parse OBJ data from arraybuffer.
+			It extends [page:OBJLoader2]  with the capability to run the parser in a web worker.
+		</p>
+
+
+		<h2>Properties</h2>
+		<p>See the base [page:OBJLoader2] class for common properties.</p>
+
+
+		<h2>Methods</h2>
+		<p>See the base [page:OBJLoader2] class for common methods. </p>
+
+
+		<h3>[method:Object3D parse]</h3>
+		<p>See [page:OBJLoader2.parse].<br>
+			The callback [page:OBJLoader2.setCallbackOnLoad OBJLoader2.onLoad] needs to be set to be able to receive the content if used in parallel mode.
+			Fallback is possible via [page:OBJLoader2Parallel.setExecuteParallel].
+		</p>
+
+
+		<h3>[method:null load]</h3>
+		<p>See [page:OBJLoader2.load].</p>
+
+
+		<h3>[method:OBJLoader2Parallel setExecuteParallel] ( [param:boolean executeParallel] )</h3>
+		<p>
+			[page:boolean executeParallel] - True or False
+		</p>
+		<p>
+			Execution of parse in parallel via Worker is default, but synchronous [page:OBJLoader2] parsing can be enforced via false here.
+		</p>
+
+
+		<h3>[method:OBJLoader2Parallel setPreferJsmWorker] ( [param:boolean preferJsmWorker] )</h3>
+		<p>
+			[page:boolean preferJsmWorker] - True or False
+		</p>
+		<p>
+			Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
+		</p>
+
+
+		<h3>[method:WorkerExecutionSupport getWorkerExecutionSupport] ()</h3>
+		<p>
+			Allow to get hold of [page:WorkerExecutionSupport] for configuration purposes.
+		</p>
+
+
+		<h3>[method:CodeBuilderInstructions buildWorkerCode] ()</h3>
+		<p>
+			Provide instructions on what is to be contained in the worker.
+		</p>
+
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/OBJLoader2Parallel.js examples/jsm/loaders/OBJLoader2Parallel.js]
+		</p>
+	</body>
+</html>

+ 1 - 0
docs/list.js

@@ -378,6 +378,7 @@ var list = {
 				"MTLLoader": "examples/en/loaders/MTLLoader",
 				"MTLLoader": "examples/en/loaders/MTLLoader",
 				"OBJLoader": "examples/en/loaders/OBJLoader",
 				"OBJLoader": "examples/en/loaders/OBJLoader",
 				"OBJLoader2": "examples/en/loaders/OBJLoader2",
 				"OBJLoader2": "examples/en/loaders/OBJLoader2",
+				"OBJLoader2Parallel": "examples/en/loaders/OBJLoader2Parallel",
 				"PCDLoader": "examples/en/loaders/PCDLoader",
 				"PCDLoader": "examples/en/loaders/PCDLoader",
 				"PDBLoader": "examples/en/loaders/PDBLoader",
 				"PDBLoader": "examples/en/loaders/PDBLoader",
 				"PRWMLoader": "examples/en/loaders/PRWMLoader",
 				"PRWMLoader": "examples/en/loaders/PRWMLoader",

+ 20 - 8
examples/jsm/loaders/OBJLoader2.d.ts

@@ -1,31 +1,43 @@
 import {
 import {
-  LoadingManager,
-  Group,
-  Object3D
+	Loader,
+	LoadingManager,
+	Object3D,
 } from '../../../src/Three';
 } from '../../../src/Three';
 
 
 import { OBJLoader2Parser } from './obj2/worker/parallel/OBJLoader2Parser';
 import { OBJLoader2Parser } from './obj2/worker/parallel/OBJLoader2Parser';
 import { MaterialHandler } from './obj2/shared/MaterialHandler';
 import { MaterialHandler } from './obj2/shared/MaterialHandler';
 import { MeshReceiver} from './obj2/shared/MeshReceiver';
 import { MeshReceiver} from './obj2/shared/MeshReceiver';
 
 
-export class OBJLoader2 extends OBJLoader2Parser {
+export class OBJLoader2 extends Loader {
   constructor(manager?: LoadingManager);
   constructor(manager?: LoadingManager);
-  manager: LoadingManager;
+  parser: OBJLoader2Parser;
   modelName: string;
   modelName: string;
   instanceNo: number;
   instanceNo: number;
   path: string;
   path: string;
   resourcePath: string;
   resourcePath: string;
-  baseObject3d: Group;
+  baseObject3d: Object3D;
   materialHandler: MaterialHandler;
   materialHandler: MaterialHandler;
   meshReceiver: MeshReceiver;
   meshReceiver: MeshReceiver;
 
 
+  setLogging(enabled: boolean, debug: boolean): this;
+  setMaterialPerSmoothingGroup(materialPerSmoothingGroup: boolean): this;
+  setUseOAsMesh(useOAsMesh: boolean): this;
+  setUseIndices(useIndices: boolean): this;
+  setDisregardNormals(disregardNormals: boolean): this;
+
   setModelName(modelName: string): this;
   setModelName(modelName: string): this;
   setPath(path: string): this;
   setPath(path: string): this;
   setResourcePath(path: string): this;
   setResourcePath(path: string): this;
   setBaseObject3d(baseObject3d: Object3D): this;
   setBaseObject3d(baseObject3d: Object3D): this;
   addMaterials(materials: object): this;
   addMaterials(materials: object): this;
+
+  setCallbackOnAssetAvailable(onAssetAvailable: Function): this;
+  setCallbackOnProgress(onProgress: Function): this;
+  setCallbackOnError(onError: Function): this;
+  setCallbackOnLoad(onLoad: Function): this;
   setCallbackOnMeshAlter(onMeshAlter: Function): this;
   setCallbackOnMeshAlter(onMeshAlter: Function): this;
   setCallbackOnLoadMaterials(onLoadMaterials: Function): this;
   setCallbackOnLoadMaterials(onLoadMaterials: Function): this;
-  load(url: string, onLoad: (group: Group) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void, onMeshAlter?: (meshData: object) => void): void;
-  parse(content: ArrayBuffer | string): void;
+
+  load(url: string, onLoad: (object3d: Object3D) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void, onMeshAlter?: (meshData: object) => void): void;
+  parse(content: ArrayBuffer | string): Object3D;
 }
 }

+ 270 - 179
examples/jsm/loaders/OBJLoader2.js

@@ -4,9 +4,10 @@
  */
  */
 
 
 import {
 import {
-	DefaultLoadingManager,
+	LoadingManager,
 	FileLoader,
 	FileLoader,
-	Group
+	Object3D,
+	Loader
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
 import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
 import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
@@ -14,272 +15,362 @@ import { MeshReceiver } from "./obj2/shared/MeshReceiver.js";
 import { MaterialHandler } from "./obj2/shared/MaterialHandler.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
- * @class
+ * Creates a new OBJLoader2. Use it to load OBJ data from files or to parse OBJ data from arraybuffer or text.
  *
  *
- * @param {DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link DefaultLoadingManager}
+ * @param {LoadingManager} [manager] The loadingManager for the loader to use. Default is {@link LoadingManager}
+ * @constructor
  */
  */
 const OBJLoader2 = function ( manager ) {
 const OBJLoader2 = function ( manager ) {
+	Loader.call( this, manager );
 
 
-	OBJLoader2Parser.call( this );
-	this.manager = ( manager !== undefined && manager !== null ) ? manager : DefaultLoadingManager;
+	this.parser = new OBJLoader2Parser();
 
 
 	this.modelName = '';
 	this.modelName = '';
 	this.instanceNo = 0;
 	this.instanceNo = 0;
-	this.path = undefined;
-	this.resourcePath = undefined;
-	this.baseObject3d = new Group();
+	this.baseObject3d = new Object3D();
 
 
 	this.materialHandler = new MaterialHandler();
 	this.materialHandler = new MaterialHandler();
 	this.meshReceiver = new MeshReceiver( this.materialHandler );
 	this.meshReceiver = new MeshReceiver( this.materialHandler );
 
 
+	// as OBJLoader2 is no longer derived from OBJLoader2Parser, we need to override the default onAssetAvailable callback
+	let scope = this;
+	let defaultOnAssetAvailable = function ( payload ) {
+		scope._onAssetAvailable( payload )
+	};
+	this.parser.setCallbackOnAssetAvailable( defaultOnAssetAvailable );
 };
 };
-OBJLoader2.OBJLOADER2_VERSION = '3.0.0';
+
+OBJLoader2.OBJLOADER2_VERSION = '3.1.0';
 console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );
 console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );
 
 
-OBJLoader2.prototype = Object.create( OBJLoader2Parser.prototype );
-OBJLoader2.prototype.constructor = OBJLoader2;
 
 
+OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 
-/**
- * Set the name of the model.
- *
- * @param {string} modelName
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setModelName = function ( modelName ) {
+	constructor: OBJLoader2,
 
 
-	this.modelName = modelName ? modelName : this.modelName;
-	return this;
+	/**
+	 * See {@link OBJLoader2Parser.setLogging}
+	 * @return {OBJLoader2}
+	 */
+	setLogging: function ( enabled, debug ) {
 
 
-};
+		this.parser.setLogging( enabled, debug );
+		return this;
 
 
-/**
- * The URL of the base path.
- *
- * @param {string} path URL
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setPath = function ( path ) {
+	},
 
 
-	this.path = path ? path : this.path;
-	return this;
+	/**
+	 * See {@link OBJLoader2Parser.setMaterialPerSmoothingGroup}
+	 * @return {OBJLoader2}
+	 */
+	setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) {
 
 
-};
+		this.parser.setMaterialPerSmoothingGroup( materialPerSmoothingGroup );
+		return this;
 
 
-/**
- * Allow to specify resourcePath for dependencies of specified resource.
- *
- * @param {string} resourcePath
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setResourcePath = function ( resourcePath ) {
+	},
 
 
-	this.resourcePath = resourcePath ? resourcePath : this.resourcePath;
-	return this;
+	/**
+	 * See {@link OBJLoader2Parser.setUseOAsMesh}
+	 * @return {OBJLoader2}
+	 */
+	setUseOAsMesh: function ( useOAsMesh ) {
 
 
-};
+		this.parser.setUseOAsMesh( useOAsMesh );
+		return this;
 
 
-/**
- * Set the node where the loaded objects will be attached directly.
- *
- * @param {Object3D} baseObject3d Object already attached to scenegraph where new meshes will be attached to
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setBaseObject3d = function ( baseObject3d ) {
+	},
 
 
-	this.baseObject3d = ( baseObject3d === undefined || baseObject3d === null ) ? this.baseObject3d : baseObject3d;
-	return this;
+	/**
+	 * See {@link OBJLoader2Parser.setUseIndices}
+	 * @return {OBJLoader2}
+	 */
+	setUseIndices: function ( useIndices ) {
 
 
-};
+		this.parser.setUseIndices( useIndices );
+		return this;
 
 
-/**
- * Add materials as associated array.
- *
- * @param {Object} materials Object with named {@link Material}
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.addMaterials = function ( materials ) {
+	},
 
 
-	this.materialHandler.addMaterials( materials );
-	return this;
+	/**
+	 * See {@link OBJLoader2Parser.setDisregardNormals}
+	 * @return {OBJLoader2}
+	 */
+	setDisregardNormals: function ( disregardNormals ) {
 
 
-};
+		this.parser.setDisregardNormals( disregardNormals );
+		return this;
 
 
-/**
- * Register a function that is called once a single mesh is available and it could be altered by the supplied function.
- *
- * @param {Function} [onMeshAlter]
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setCallbackOnMeshAlter = function ( onMeshAlter ) {
+	},
 
 
-	this.meshReceiver._setCallbacks( this.callbacks.onProgress, onMeshAlter );
-	return this;
+	/**
+	 * Set the name of the model.
+	 *
+	 * @param {string} modelName
+	 * @return {OBJLoader2}
+	 */
+	setModelName: function ( modelName ) {
 
 
-};
+		this.modelName = modelName ? modelName : this.modelName;
+		return this;
 
 
-/**
- * Register a function that is called once all materials have been loaded and they could be altered by the supplied function.
- *
- * @param {Function} [onLoadMaterials]
- * @return {OBJLoader2}
- */
-OBJLoader2.prototype.setCallbackOnLoadMaterials = function ( onLoadMaterials ) {
+	},
 
 
-	this.materialHandler._setCallbacks( onLoadMaterials );
-	return this;
+	/**
+	 * Set the node where the loaded objects will be attached directly.
+	 *
+	 * @param {Object3D} baseObject3d Object already attached to scenegraph where new meshes will be attached to
+	 * @return {OBJLoader2}
+	 */
+	setBaseObject3d: function ( baseObject3d ) {
 
 
-};
+		this.baseObject3d = (baseObject3d === undefined || baseObject3d === null) ? this.baseObject3d : baseObject3d;
+		return this;
 
 
-/**
- * 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 {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 every single mesh is made available by the parser
- */
-OBJLoader2.prototype.load = function ( url, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
+	},
 
 
-	let scope = this;
-	if ( onLoad === null || onLoad === undefined || ! ( onLoad instanceof Function ) ) {
+	/**
+	 * Add materials as associated array.
+	 *
+	 * @param {Object} materials Object with named {@link Material}
+	 * @return {OBJLoader2}
+	 */
+	addMaterials: function ( materials ) {
 
 
-		let errorMessage = 'onLoad is not a function! Aborting...';
-		scope.callbacks.onError( errorMessage );
-		throw errorMessage
+		this.materialHandler.addMaterials( materials );
+		return this;
 
 
-	}
-	if ( onError === null || onError === undefined || ! ( onError instanceof Function ) ) {
+	},
 
 
-		onError = function ( event ) {
+	/**
+	 * See {@link OBJLoader2Parser.setCallbackOnAssetAvailable}
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnAssetAvailable: function ( onAssetAvailable ) {
 
 
-			let errorMessage = event;
-			if ( event.currentTarget && event.currentTarget.statusText !== null ) {
+		this.parser.setCallbackOnAssetAvailable( onAssetAvailable );
+		return this;
 
 
-				 errorMessage = 'Error occurred while downloading!\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;
+	},
 
 
-			}
-			scope.callbacks.onError( errorMessage );
+	/**
+	 * See {@link OBJLoader2Parser.setCallbackOnProgress}
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnProgress: function ( onProgress ) {
 
 
-		};
+		this.parser.setCallbackOnProgress( onProgress );
+		return this;
 
 
-	}
-	if ( ! url ) {
+	},
 
 
-		onError( 'An invalid url was provided. Unable to continue!' );
+	/**
+	 * See {@link OBJLoader2Parser.setCallbackOnError}
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnError: function ( onError ) {
 
 
-	}
-	let urlFull = new URL( url, window.location.href ).href;
-	let filename = urlFull;
-	let urlParts = urlFull.split( '/' );
-	if ( urlParts.length > 2 ) {
+		this.parser.setCallbackOnError( onError );
+		return this;
 
 
-		filename = urlParts[ urlParts.length - 1 ];
-		let urlPartsPath = urlParts.slice( 0, urlParts.length - 1 ).join( '/' ) + '/';
-		if ( urlPartsPath !== undefined && urlPartsPath !== null ) this.path = urlPartsPath;
+	},
 
 
-	}
-	if ( onFileLoadProgress === null || onFileLoadProgress === undefined || ! ( onFileLoadProgress instanceof Function ) ) {
+	/**
+	 * See {@link OBJLoader2Parser.setCallbackOnLoad}
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnLoad: function ( onLoad ) {
 
 
-		let numericalValueRef = 0;
-		let numericalValue = 0;
-		onFileLoadProgress = function ( event ) {
+		this.parser.setCallbackOnLoad( onLoad );
+		return this;
 
 
-			if ( ! event.lengthComputable ) return;
+	},
 
 
-			numericalValue = event.loaded / event.total;
-			if ( numericalValue > numericalValueRef ) {
+	/**
+	 * Register a function that is called once a single mesh is available and it could be altered by the supplied function.
+	 *
+	 * @param {Function} [onMeshAlter]
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnMeshAlter: function ( onMeshAlter ) {
+
+		this.meshReceiver._setCallbacks( this.parser.callbacks.onProgress, onMeshAlter );
+		return this;
 
 
-				numericalValueRef = numericalValue;
-				let output = 'Download of "' + url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%';
-				scope.callbacks.onProgress( 'progressLoad', output, numericalValue );
+	},
+
+	/**
+	 * Register a function that is called once all materials have been loaded and they could be altered by the supplied function.
+	 *
+	 * @param {Function} [onLoadMaterials]
+	 * @return {OBJLoader2}
+	 */
+	setCallbackOnLoadMaterials: function ( onLoadMaterials ) {
+
+		this.materialHandler._setCallbacks( onLoadMaterials );
+		return this;
 
 
-			}
+	},
 
 
-		};
+	/**
+	 * 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 {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 every single mesh is made available by the parser
+	 */
+	load: function ( url, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
 
 
-	}
+		let scope = this;
+		if ( onLoad === null || onLoad === undefined || !(onLoad instanceof Function) ) {
+
+			let errorMessage = 'onLoad is not a function! Aborting...';
+			scope.parser.callbacks.onError( errorMessage );
+			throw errorMessage
 
 
-	this.setCallbackOnMeshAlter( onMeshAlter );
-	let fileLoaderOnLoad = function ( content ) {
+		}
+		else {
 
 
-		onLoad( scope.parse( content ) );
+			this.parser.setCallbackOnLoad( onLoad );
 
 
-	};
-	let fileLoader = new FileLoader( this.manager );
-	fileLoader.setPath( this.path || this.resourcePath );
-	fileLoader.setResponseType( 'arraybuffer' );
-	fileLoader.load( filename, fileLoaderOnLoad, onFileLoadProgress, onError );
+		}
+		if ( onError === null || onError === undefined || !(onError instanceof Function) ) {
 
 
-};
+			onError = function ( event ) {
 
 
-/**
- * Parses OBJ data synchronously from arraybuffer or string.
- *
- * @param {arraybuffer|string} content OBJ data as Uint8Array or String
- */
-OBJLoader2.prototype.parse = function ( content ) {
+				let errorMessage = event;
+				if ( event.currentTarget && event.currentTarget.statusText !== null ) {
 
 
-	// fast-fail in case of illegal data
-	if ( content === null || content === undefined ) {
+					errorMessage = 'Error occurred while downloading!\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;
 
 
-		throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
+				}
+				scope.parser.callbacks.onError( errorMessage );
 
 
-	}
-	if ( this.logging.enabled ) {
+			};
 
 
-		console.time( 'OBJLoader parse: ' + this.modelName );
+		}
+		if ( !url ) {
 
 
-	}
+			onError( 'An invalid url was provided. Unable to continue!' );
 
 
-	// sync code works directly on the material references
-	this._setMaterials( this.materialHandler.getMaterials() );
+		}
+		let urlFull = new URL( url, window.location.href ).href;
+		let filename = urlFull;
+		let urlParts = urlFull.split( '/' );
+		if ( urlParts.length > 2 ) {
 
 
-	if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) {
+			filename = urlParts[ urlParts.length - 1 ];
+			let urlPartsPath = urlParts.slice( 0, urlParts.length - 1 ).join( '/' ) + '/';
+			if ( urlPartsPath !== undefined && urlPartsPath !== null ) this.path = urlPartsPath;
 
 
-		if ( this.logging.enabled ) console.info( 'Parsing arrayBuffer...' );
-		this.execute( content );
+		}
+		if ( onFileLoadProgress === null || onFileLoadProgress === undefined || !(onFileLoadProgress instanceof Function) ) {
 
 
-	} else if ( typeof ( content ) === 'string' || content instanceof String ) {
+			let numericalValueRef = 0;
+			let numericalValue = 0;
+			onFileLoadProgress = function ( event ) {
 
 
-		if ( this.logging.enabled ) console.info( 'Parsing text...' );
-		this.executeLegacy( content );
+				if ( !event.lengthComputable ) return;
 
 
-	} else {
+				numericalValue = event.loaded / event.total;
+				if ( numericalValue > numericalValueRef ) {
 
 
-		this.callbacks.onError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );
+					numericalValueRef = numericalValue;
+					let output = 'Download of "' + url + '": ' + (numericalValue * 100).toFixed( 2 ) + '%';
+					scope.parser.callbacks.onProgress( 'progressLoad', output, numericalValue );
 
 
-	}
-	if ( this.logging.enabled ) {
+				}
 
 
-		console.timeEnd( 'OBJLoader parse: ' + this.modelName );
+			};
 
 
-	}
-	return this.baseObject3d;
+		}
 
 
-};
+		this.setCallbackOnMeshAlter( onMeshAlter );
+		let fileLoaderOnLoad = function ( content ) {
+
+			scope.parser.callbacks.onLoad( scope.parse( content ), "OBJLoader2#load: Parsing completed" );
 
 
-OBJLoader2.prototype._onAssetAvailable = function ( payload ) {
+		};
+		let fileLoader = new FileLoader( this.manager );
+		fileLoader.setPath( this.path || this.resourcePath );
+		fileLoader.setResponseType( 'arraybuffer' );
+		fileLoader.load( filename, fileLoaderOnLoad, onFileLoadProgress, onError );
 
 
-	if ( payload.cmd !== 'assetAvailable' ) return;
+	},
 
 
-	if ( payload.type === 'mesh' ) {
+	/**
+	 * Parses OBJ data synchronously from arraybuffer or string and returns the {@link Object3D}.
+	 *
+	 * @param {arraybuffer|string} content OBJ data as Uint8Array or String
+	 * @return {Object3D}
+	 */
+	parse: function ( content ) {
 
 
-		let meshes = this.meshReceiver.buildMeshes( payload );
-		for ( let mesh of meshes ) {
+		// fast-fail in case of illegal data
+		if ( content === null || content === undefined ) {
 
 
-			this.baseObject3d.add( mesh );
+			throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
 
 
 		}
 		}
+		if ( this.parser.logging.enabled ) {
+
+			console.time( 'OBJLoader parse: ' + this.modelName );
+
+		}
+
+		// Create default materials beforehand, but do not override previously set materials (e.g. during init)
+		this.materialHandler.createDefaultMaterials( false );
+
+		// code works directly on the material references, parser clear its materials before updating
+		this.parser.setMaterials( this.materialHandler.getMaterials() );
 
 
-	} else if ( payload.type === 'material' ) {
+		if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) {
 
 
-		this.materialHandler.addPayloadMaterials( payload );
+			if ( this.parser.logging.enabled ) console.info( 'Parsing arrayBuffer...' );
+			this.parser.execute( content );
+
+		} else if ( typeof (content) === 'string' || content instanceof String ) {
+
+			if ( this.parser.logging.enabled ) console.info( 'Parsing text...' );
+			this.parser.executeLegacy( content );
+
+		} else {
+
+			this.parser.callbacks.onError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );
+
+		}
+		if ( this.parser.logging.enabled ) {
+
+			console.timeEnd( 'OBJLoader parse: ' + this.modelName );
+
+		}
+		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 );
+
+		}
 
 
 	}
 	}
 
 
-};
+} );
 
 
 export { OBJLoader2 };
 export { OBJLoader2 };

+ 5 - 3
examples/jsm/loaders/OBJLoader2Parallel.d.ts

@@ -8,12 +8,14 @@ import { WorkerExecutionSupport} from './obj2/worker/main/WorkerExecutionSupport
 export class OBJLoader2Parallel extends OBJLoader2 {
 export class OBJLoader2Parallel extends OBJLoader2 {
   constructor(manager?: LoadingManager);
   constructor(manager?: LoadingManager);
   preferJsmWorker: boolean;
   preferJsmWorker: boolean;
-	executeParallel: boolean;
-	workerExecutionSupport: WorkerExecutionSupport;
+  executeParallel: boolean;
+  workerExecutionSupport: WorkerExecutionSupport;
 
 
   setPreferJsmWorker(preferJsmWorker: boolean): this;
   setPreferJsmWorker(preferJsmWorker: boolean): this;
-  setCallbackOnParseComplete(onParseComplete: Function): this;
   setExecuteParallel(executeParallel: boolean): this;
   setExecuteParallel(executeParallel: boolean): this;
   getWorkerExecutionSupport(): object;
   getWorkerExecutionSupport(): object;
   buildWorkerCode(): object;
   buildWorkerCode(): object;
+
+  // @ts-ignore
+  parse(content: ArrayBuffer): void;
 }
 }

+ 120 - 113
examples/jsm/loaders/OBJLoader2Parallel.js

@@ -4,6 +4,9 @@
  */
  */
 
 
 // Imports only related to wrapper
 // Imports only related to wrapper
+import {
+	Object3D
+} from "../../../build/three.module.js";
 import {
 import {
 	CodeBuilderInstructions,
 	CodeBuilderInstructions,
 	WorkerExecutionSupport
 	WorkerExecutionSupport
@@ -19,11 +22,12 @@ import {
 	DefaultWorkerPayloadHandler
 	DefaultWorkerPayloadHandler
 } from "./obj2/worker/parallel/WorkerRunner.js";
 } from "./obj2/worker/parallel/WorkerRunner.js";
 
 
+
 /**
 /**
- * Extends {OBJLoader2} with the capability to run the parser {OBJLoader2Parser} in web worker
- * with help of {WorkerExecutionSupport}.
+ * Creates a new OBJLoader2Parallel. Use it to load OBJ data from files or to parse OBJ data from arraybuffer.
+ * It extends {@link OBJLoader2} with the capability to run the parser in a web worker.
  *
  *
- * @param [LoadingManager] manager
+ * @param [LoadingManager] manager The loadingManager for the loader to use. Default is {@link LoadingManager}
  * @constructor
  * @constructor
  */
  */
 const OBJLoader2Parallel = function ( manager ) {
 const OBJLoader2Parallel = function ( manager ) {
@@ -31,162 +35,159 @@ const OBJLoader2Parallel = function ( manager ) {
 	OBJLoader2.call( this, manager );
 	OBJLoader2.call( this, manager );
 	this.preferJsmWorker = false;
 	this.preferJsmWorker = false;
 
 
-	this.callbacks.onParseComplete = null;
 	this.executeParallel = true;
 	this.executeParallel = true;
 	this.workerExecutionSupport = new WorkerExecutionSupport();
 	this.workerExecutionSupport = new WorkerExecutionSupport();
 
 
 };
 };
-OBJLoader2Parallel.prototype = Object.create( OBJLoader2.prototype );
-OBJLoader2Parallel.prototype.constructor = OBJLoader2Parallel;
 
 
-OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.0.0';
+OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.1.0';
 console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
 console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
 
 
 
 
-OBJLoader2Parallel.prototype.setPreferJsmWorker = function ( preferJsmWorker ) {
+OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototype ), {
 
 
-	this.preferJsmWorker = preferJsmWorker === true;
-	return this;
+	constructor: OBJLoader2Parallel,
 
 
-};
+	/**
+	 * Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
+	 *
+	 * @param executeParallel True or False
+	 * @return {OBJLoader2Parallel}
+	 */
+	setExecuteParallel: function ( executeParallel ) {
 
 
-/**
- * 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 ) {
+		this.executeParallel = executeParallel === true;
+		return this;
 
 
-	if ( onParseComplete !== undefined && onParseComplete !== null ) {
+	},
 
 
-		this.callbacks.onParseComplete = onParseComplete;
+	/**
+	 * Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
+	 * @param preferJsmWorker True or False
+	 * @return {OBJLoader2Parallel}
+	 */
+	setPreferJsmWorker: function ( preferJsmWorker ) {
 
 
-	}
-	return this;
+		this.preferJsmWorker = preferJsmWorker === true;
+		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 ) {
+	/**
+	 * Allow to get hold of {@link WorkerExecutionSupport} for configuration purposes.
+	 * @return {WorkerExecutionSupport}
+	 */
+	getWorkerExecutionSupport: function () {
 
 
-	this.executeParallel = executeParallel === true;
-	return this;
+		return this.workerExecutionSupport;
 
 
-};
+	},
 
 
-/**
- * Allow to get hold of {WorkerExecutionSupport} for configuratin purposes
- *
- * @return {WorkerExecutionSupport|WorkerExecutionSupport}
- */
-OBJLoader2Parallel.prototype.getWorkerExecutionSupport = function () {
+	/**
+	 * Provide instructions on what is to be contained in the worker.
+	 * @return {CodeBuilderInstructions}
+	 */
+	buildWorkerCode: function () {
 
 
-	return this.workerExecutionSupport;
+		let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
+		if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
 
 
-};
+			codeBuilderInstructions.setJsmWorkerFile( '../../src/loaders/worker/parallel/jsm/OBJLoader2Worker.js' );
 
 
-/**
- * Provides instructions on what is to be contained in the worker
- *
- * @return {CodeBuilderInstructions}
- */
-OBJLoader2Parallel.prototype.buildWorkerCode = function () {
+		}
+		if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
 
 
-	let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
-	if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
+			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.setJsmWorkerFile( '../../src/loaders/worker/parallel/jsm/OBJLoader2Worker.js' );
+			codeBuilderInstructions.addCodeFragment( codeOBJLoader2Parser );
+			codeBuilderInstructions.addCodeFragment( codeObjectManipulator );
+			codeBuilderInstructions.addCodeFragment( codeParserPayloadHandler );
+			codeBuilderInstructions.addCodeFragment( codeWorkerRunner );
 
 
-	}
-	if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
+			codeBuilderInstructions.addStartCode( 'new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );' );
 
 
-		let codeOBJLoader2Parser = CodeSerializer.serializeClass( 'OBJLoader2Parser', OBJLoader2Parser );
-		let codeObjectManipulator = CodeSerializer.serializeObject( 'ObjectManipulator', ObjectManipulator );
-		let codeParserPayloadHandler = CodeSerializer.serializeClass( 'DefaultWorkerPayloadHandler', DefaultWorkerPayloadHandler );
-		let codeWorkerRunner = CodeSerializer.serializeClass( 'WorkerRunner', WorkerRunner );
+		}
+		return codeBuilderInstructions;
 
 
-		codeBuilderInstructions.addCodeFragment( codeOBJLoader2Parser );
-		codeBuilderInstructions.addCodeFragment( codeObjectManipulator );
-		codeBuilderInstructions.addCodeFragment( codeParserPayloadHandler );
-		codeBuilderInstructions.addCodeFragment( codeWorkerRunner );
+	},
 
 
-		codeBuilderInstructions.addStartCode( 'new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );' );
+	/**
+	 * See {@link OBJLoader2.load}
+	 */
+	load: function ( content, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
 
 
-	}
-	return codeBuilderInstructions;
+ 		let scope = this;
+		function interceptOnLoad ( object3d, message ) {
 
 
-};
+			if ( object3d.name === 'OBJLoader2ParallelDummy' ) {
 
 
-/**
- * @private
- */
-OBJLoader2Parallel.prototype._configure = function () {
+				if ( scope.parser.logging.enabled && scope.parser.logging.debug ) {
 
 
-	if ( this.callbacks.onParseComplete === null ) {
+					console.debug( 'Received dummy answer from OBJLoader2Parallel#parse' );
 
 
-		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;
+			}
+			else {
 
 
-	this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );
+				onLoad( object3d, message );
 
 
-	let scope = this;
-	let scopedOnAssetAvailable = function ( payload ) {
+			}
 
 
-		scope._onAssetAvailable( payload );
+		}
 
 
-	};
+		OBJLoader2.prototype.load.call( this, content, interceptOnLoad, onFileLoadProgress, onError, onMeshAlter );
 
 
-	this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, this.callbacks.onParseComplete );
+	},
 
 
-};
+	/**
+	 * See {@link OBJLoader2.parse}
+	 * The callback onLoad needs to be set to be able to receive the content if used in parallel mode.
+	 * Fallback is possible via {@link OBJLoader2Parallel#setExecuteParallel}.
+	 */
+	parse: function ( content ) {
 
 
-/**
- * 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 ) {
+		if ( this.executeParallel ) {
 
 
-	this.setCallbackOnParseComplete( onLoad );
+			if ( this.parser.callbacks.onLoad === this.parser._onLoad ) {
 
 
-	OBJLoader2.prototype.load.call( this, content, function () {}, onFileLoadProgress, onError, onMeshAlter );
+				throw "No callback other than the default callback was provided! Aborting!";
 
 
-};
+			}
+			// check if worker has been initialize before. If yes, skip init
+			if ( ! this.workerExecutionSupport.isWorkerLoaded( this.preferJsmWorker ) ) {
 
 
-/**
- * Parses OBJ data in parallel with web worker.
- *
- * @param {arraybuffer} content OBJ data as Uint8Array or String
- */
-OBJLoader2Parallel.prototype.parse = function ( content ) {
+				this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );
+
+				let scope = this;
+				let scopedOnAssetAvailable = function ( payload ) {
 
 
-	if ( this.executeParallel ) {
+					scope._onAssetAvailable( payload );
+
+				};
+				function scopedOnLoad( message ) {
+					scope.parser.callbacks.onLoad( scope.baseObject3d, message );
+				}
 
 
-		this._configure();
+				this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, scopedOnLoad );
 
 
-		this.workerExecutionSupport.executeParallel(
+			}
+
+			// Create default materials beforehand, but do not override previously set materials (e.g. during init)
+			this.materialHandler.createDefaultMaterials( false );
+
+			this.workerExecutionSupport.executeParallel(
 			{
 			{
 				params: {
 				params: {
 					modelName: this.modelName,
 					modelName: this.modelName,
 					instanceNo: this.instanceNo,
 					instanceNo: this.instanceNo,
-					useIndices: this.useIndices,
-					disregardNormals: this.disregardNormals,
-					materialPerSmoothingGroup: this.materialPerSmoothingGroup,
-					useOAsMesh: this.useOAsMesh,
+					useIndices: this.parser.useIndices,
+					disregardNormals: this.parser.disregardNormals,
+					materialPerSmoothingGroup: this.parser.materialPerSmoothingGroup,
+					useOAsMesh: this.parser.useOAsMesh,
 				},
 				},
 				materials: this.materialHandler.getMaterialsJSON(),
 				materials: this.materialHandler.getMaterialsJSON(),
 				data: {
 				data: {
@@ -194,17 +195,23 @@ OBJLoader2Parallel.prototype.parse = function ( content ) {
 					options: null
 					options: null
 				},
 				},
 				logging: {
 				logging: {
-					enabled: this.logging.enabled,
-					debug: this.logging.debug
+					enabled: this.parser.logging.enabled,
+					debug: this.parser.logging.debug
 				}
 				}
 			} );
 			} );
 
 
-	} else {
+			let dummy = new Object3D();
+			dummy.name = 'OBJLoader2ParallelDummy';
+			return dummy;
+		}
+		else {
 
 
-		this.callbacks.onParseComplete( OBJLoader2.prototype.parse.call( this, content ) );
+			return OBJLoader2.prototype.parse.call( this, content );
 
 
-	}
+		}
 
 
-};
+	},
+
+} );
 
 
 export { OBJLoader2Parallel };
 export { OBJLoader2Parallel };

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

@@ -17,7 +17,7 @@ const MtlObjBridge = {
 
 
 		if ( typeof assetLoader.addMaterials === 'function' ) {
 		if ( typeof assetLoader.addMaterials === 'function' ) {
 
 
-			assetLoader.addMaterials( this.addMaterialsFromMtlLoader( processResult ) );
+			assetLoader.addMaterials( this.addMaterialsFromMtlLoader( processResult ), true );
 
 
 		}
 		}
 
 

+ 3 - 1
examples/jsm/loaders/obj2/shared/MaterialHandler.d.ts

@@ -13,10 +13,12 @@ export class MaterialHandler {
   };
   };
   materials: object;
   materials: object;
 
 
-  addMaterials(materials: object, newMaterials: object): object;
+  createDefaultMaterials(overrideExisting: boolean): void;
+  addMaterials(materials: object, overrideExisting: boolean, newMaterials: object): object;
   addPayloadMaterials(materialPayload: object): object;
   addPayloadMaterials(materialPayload: object): object;
   setLogging(enabled: boolean, debug: boolean): void;
   setLogging(enabled: boolean, debug: boolean): void;
   getMaterials(): object;
   getMaterials(): object;
   getMaterial(materialName: string): Material;
   getMaterial(materialName: string): Material;
   getMaterialsJSON(): object;
   getMaterialsJSON(): object;
+  clearMaterials(): void;
 }
 }

+ 49 - 20
examples/jsm/loaders/obj2/shared/MaterialHandler.js

@@ -15,7 +15,7 @@ import {
 const MaterialHandler = function () {
 const MaterialHandler = function () {
 
 
 	this.logging = {
 	this.logging = {
-		enabled: true,
+		enabled: false,
 		debug: false
 		debug: false
 	};
 	};
 
 
@@ -23,7 +23,6 @@ const MaterialHandler = function () {
 		onLoadMaterials: null
 		onLoadMaterials: null
 	};
 	};
 	this.materials = {};
 	this.materials = {};
-	this._createDefaultMaterials();
 
 
 };
 };
 
 
@@ -54,7 +53,12 @@ MaterialHandler.prototype = {
 
 
 	},
 	},
 
 
-	_createDefaultMaterials: function () {
+	/**
+	 * Creates default materials and adds them to the materials object.
+	 *
+	 * @param overrideExisting boolean Override existing material
+	 */
+	createDefaultMaterials: function ( overrideExisting ) {
 
 
 		let defaultMaterial = new MeshStandardMaterial( { color: 0xDCF1FF } );
 		let defaultMaterial = new MeshStandardMaterial( { color: 0xDCF1FF } );
 		defaultMaterial.name = 'defaultMaterial';
 		defaultMaterial.name = 'defaultMaterial';
@@ -75,7 +79,7 @@ MaterialHandler.prototype = {
 		runtimeMaterials[ defaultLineMaterial.name ] = defaultLineMaterial;
 		runtimeMaterials[ defaultLineMaterial.name ] = defaultLineMaterial;
 		runtimeMaterials[ defaultPointMaterial.name ] = defaultPointMaterial;
 		runtimeMaterials[ defaultPointMaterial.name ] = defaultPointMaterial;
 
 
-		this.addMaterials( runtimeMaterials );
+		this.addMaterials( runtimeMaterials, overrideExisting );
 
 
 	},
 	},
 
 
@@ -103,22 +107,18 @@ MaterialHandler.prototype = {
 				materialName = materialCloneInstructions.materialName;
 				materialName = materialCloneInstructions.materialName;
 				material.name = materialName;
 				material.name = materialName;
 
 
-				let materialProperties = materialCloneInstructions.materialProperties;
-				for ( let key in materialProperties ) {
-
-					if ( material.hasOwnProperty( key ) && materialProperties.hasOwnProperty( key ) ) {
+				Object.assign( material, materialCloneInstructions.materialProperties );
 
 
-						material[ key ] = materialProperties[ key ];
-
-					}
-
-				}
 				this.materials[ materialName ] = material;
 				this.materials[ materialName ] = material;
 				newMaterials[ materialName ] = material;
 				newMaterials[ materialName ] = material;
 
 
 			} else {
 			} else {
 
 
-				console.info( 'Requested material "' + materialNameOrg + '" is not available!' );
+				if ( this.logging.enabled) {
+
+					console.info( 'Requested material "' + materialNameOrg + '" is not available!' );
+
+				}
 
 
 			}
 			}
 
 
@@ -135,7 +135,11 @@ MaterialHandler.prototype = {
 				if ( materialJson !== undefined && materialJson !== null ) {
 				if ( materialJson !== undefined && materialJson !== null ) {
 
 
 					material = loader.parse( materialJson );
 					material = loader.parse( materialJson );
-					if ( this.logging.enabled ) console.info( 'De-serialized material with name "' + materialName + '" will be added.' );
+					if ( this.logging.enabled ) {
+
+						console.info( 'De-serialized material with name "' + materialName + '" will be added.' );
+
+					}
 					this.materials[ materialName ] = material;
 					this.materials[ materialName ] = material;
 					newMaterials[ materialName ] = material;
 					newMaterials[ materialName ] = material;
 
 
@@ -145,7 +149,7 @@ MaterialHandler.prototype = {
 
 
 		}
 		}
 		materials = materialPayload.materials.runtimeMaterials;
 		materials = materialPayload.materials.runtimeMaterials;
-		newMaterials = this.addMaterials( materials, newMaterials );
+		newMaterials = this.addMaterials( materials, true, newMaterials );
 
 
 		return newMaterials;
 		return newMaterials;
 
 
@@ -155,9 +159,10 @@ MaterialHandler.prototype = {
 	 * Set materials loaded by any supplier of an Array of {@link Material}.
 	 * Set materials loaded by any supplier of an Array of {@link Material}.
 	 *
 	 *
 	 * @param materials Object with named {@link Material}
 	 * @param materials Object with named {@link Material}
+	 * @param overrideExisting boolean Override existing material
 	 * @param newMaterials [Object] with named {@link Material}
 	 * @param newMaterials [Object] with named {@link Material}
 	 */
 	 */
-	addMaterials: function ( materials, newMaterials ) {
+	addMaterials: function ( materials, overrideExisting, newMaterials ) {
 
 
 		if ( newMaterials === undefined || newMaterials === null ) {
 		if ( newMaterials === undefined || newMaterials === null ) {
 
 
@@ -167,12 +172,29 @@ MaterialHandler.prototype = {
 		if ( materials !== undefined && materials !== null && Object.keys( materials ).length > 0 ) {
 		if ( materials !== undefined && materials !== null && Object.keys( materials ).length > 0 ) {
 
 
 			let material;
 			let material;
+			let existingMaterial;
+			let add;
 			for ( let materialName in materials ) {
 			for ( let materialName in materials ) {
 
 
 				material = materials[ materialName ];
 				material = materials[ materialName ];
-				this.materials[ materialName ] = material;
-				newMaterials[ materialName ] = material;
-				if ( this.logging.enabled ) console.info( 'Material with name "' + materialName + '" was added.' );
+				add = overrideExisting === true;
+				if ( ! add ) {
+
+					existingMaterial = this.materials[ materialName ];
+					add = ( existingMaterial === null || existingMaterial === undefined );
+
+				}
+				if ( add ) {
+
+					this.materials[ materialName ] = material;
+					newMaterials[ materialName ] = material;
+
+				}
+				if ( this.logging.enabled && this.logging.debug ) {
+
+					console.info( 'Material with name "' + materialName + '" was added.' );
+
+				}
 
 
 			}
 			}
 
 
@@ -227,6 +249,13 @@ MaterialHandler.prototype = {
 
 
 		return materialsJSON;
 		return materialsJSON;
 
 
+	},
+
+	/**
+	 * Removes all materials
+	 */
+	clearMaterials: function () {
+		this.materials = {};
 	}
 	}
 
 
 };
 };

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

@@ -20,7 +20,7 @@ import {
 const MeshReceiver = function ( materialHandler ) {
 const MeshReceiver = function ( materialHandler ) {
 
 
 	this.logging = {
 	this.logging = {
-		enabled: true,
+		enabled: false,
 		debug: false
 		debug: false
 	};
 	};
 
 

+ 12 - 4
examples/jsm/loaders/obj2/worker/main/WorkerExecutionSupport.js

@@ -127,7 +127,7 @@ const WorkerExecutionSupport = function () {
 	this._reset();
 	this._reset();
 
 
 };
 };
-WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.0.0';
+WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.1.0';
 console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
 console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
 
 
 
 
@@ -138,7 +138,7 @@ WorkerExecutionSupport.prototype = {
 	_reset: function () {
 	_reset: function () {
 
 
 		this.logging = {
 		this.logging = {
-			enabled: true,
+			enabled: false,
 			debug: false
 			debug: false
 		};
 		};
 
 
@@ -437,7 +437,11 @@ WorkerExecutionSupport.prototype = {
 				}
 				}
 				if ( this.worker.terminateWorkerOnLoad ) {
 				if ( this.worker.terminateWorkerOnLoad ) {
 
 
-					if ( this.worker.logging.enabled ) console.info( 'WorkerSupport [' + workerRunnerName + ']: Run is complete. Terminating application on request!' );
+					if ( this.worker.logging.enabled ) {
+
+						console.info( 'WorkerSupport [' + workerRunnerName + ']: Run is complete. Terminating application on request!' );
+
+					}
 					this.worker.callbacks.terminate();
 					this.worker.callbacks.terminate();
 
 
 				}
 				}
@@ -454,7 +458,11 @@ WorkerExecutionSupport.prototype = {
 				}
 				}
 				if ( this.worker.terminateWorkerOnLoad ) {
 				if ( this.worker.terminateWorkerOnLoad ) {
 
 
-					if ( this.worker.logging.enabled ) console.info( 'WorkerSupport [' + workerRunnerName + ']: Run reported error. Terminating application on request!' );
+					if ( this.worker.logging.enabled ) {
+
+						console.info( 'WorkerSupport [' + workerRunnerName + ']: Run reported error. Terminating application on request!' );
+
+					}
 					this.worker.callbacks.terminate();
 					this.worker.callbacks.terminate();
 
 
 				}
 				}

+ 3 - 0
examples/jsm/loaders/obj2/worker/parallel/OBJLoader2Parser.d.ts

@@ -4,6 +4,7 @@ export class OBJLoader2Parser {
     onProgress: Function;
     onProgress: Function;
     onAssetAvailable: Function;
     onAssetAvailable: Function;
     onError: Function;
     onError: Function;
+    onLoad: Function;
   };
   };
   contentRef: Uint8Array;
   contentRef: Uint8Array;
   legacyMode: boolean;
   legacyMode: boolean;
@@ -63,7 +64,9 @@ export class OBJLoader2Parser {
   setCallbackOnAssetAvailable(onAssetAvailable: Function): this;
   setCallbackOnAssetAvailable(onAssetAvailable: Function): this;
   setCallbackOnProgress(onProgress: Function): this;
   setCallbackOnProgress(onProgress: Function): this;
   setCallbackOnError(onError: Function): this;
   setCallbackOnError(onError: Function): this;
+  setCallbackOnLoad(onLoad: Function): this;
   setLogging(enabled: boolean, debug: boolean): this;
   setLogging(enabled: boolean, debug: boolean): this;
+  setMaterials(materials: Object): void;
   execute(arrayBuffer: Uint8Array): void;
   execute(arrayBuffer: Uint8Array): void;
   executeLegacy(text: string): void;
   executeLegacy(text: string): void;
 }
 }

+ 39 - 18
examples/jsm/loaders/obj2/worker/parallel/OBJLoader2Parser.js

@@ -8,6 +8,11 @@
  */
  */
 const OBJLoader2Parser = function () {
 const OBJLoader2Parser = function () {
 
 
+	this.logging = {
+		enabled: false,
+		debug: false
+	};
+
 	let scope = this;
 	let scope = this;
 	this.callbacks = {
 	this.callbacks = {
 		onProgress: function ( text ) {
 		onProgress: function ( text ) {
@@ -18,7 +23,10 @@ const OBJLoader2Parser = function () {
 		},
 		},
 		onError: function ( errorMessage ) {
 		onError: function ( errorMessage ) {
 			scope._onError( errorMessage )
 			scope._onError( errorMessage )
-		}
+		},
+		onLoad: function ( object3d, message ) {
+			scope._onLoad( object3d, message )
+		},
 	};
 	};
 	this.contentRef = null;
 	this.contentRef = null;
 	this.legacyMode = false;
 	this.legacyMode = false;
@@ -68,11 +76,6 @@ const OBJLoader2Parser = function () {
 		totalBytes: 0
 		totalBytes: 0
 	};
 	};
 
 
-	this.logging = {
-		enabled: true,
-		debug: false
-	};
-
 };
 };
 
 
 OBJLoader2Parser.prototype = {
 OBJLoader2Parser.prototype = {
@@ -149,19 +152,14 @@ OBJLoader2Parser.prototype = {
 
 
 	},
 	},
 
 
-	_setMaterials: function ( materials ) {
-
-		if ( materials === undefined || materials === null ) return;
-
-		for ( let materialName in materials ) {
-
-			if ( materials.hasOwnProperty( materialName ) ) {
-
-				this.materials[ materialName ] = materials[ materialName ];
-
-			}
+	/**
+	 * Clears materials object and sets the new ones.
+	 *
+	 * @param {Object} materials Object with named materials
+	 */
+	setMaterials: function ( materials ) {
 
 
-		}
+ 		this.materials = Object.assign( {}, materials );
 
 
 	},
 	},
 
 
@@ -216,6 +214,23 @@ OBJLoader2Parser.prototype = {
 
 
 	},
 	},
 
 
+	/**
+	 * Register a function that is called when parsing was completed.
+	 *
+	 * @param {Function} onLoad
+	 * @return {OBJLoader2Parser}
+	 */
+	setCallbackOnLoad: function ( onLoad ) {
+
+		if ( onLoad !== null && onLoad !== undefined && onLoad instanceof Function ) {
+
+			this.callbacks.onLoad = onLoad;
+
+		}
+		return this;
+
+	},
+
 	/**
 	/**
 	 * Announce parse progress feedback which is logged to the console.
 	 * Announce parse progress feedback which is logged to the console.
 	 * @private
 	 * @private
@@ -257,6 +272,12 @@ OBJLoader2Parser.prototype = {
 
 
 	},
 	},
 
 
+	_onLoad: function ( object3d, message ) {
+
+		console.log( "You reached parser default onLoad callback: " + message );
+
+	},
+
 	/**
 	/**
 	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
 	 * Enable or disable logging in general (except warn and error), plus enable or disable debug logging.
 	 *
 	 *

+ 31 - 25
examples/webgl_loader_obj2_options.html

@@ -147,14 +147,14 @@
 						fileLoader.setPath( '' );
 						fileLoader.setPath( '' );
 						fileLoader.setResponseType( 'arraybuffer' );
 						fileLoader.setResponseType( 'arraybuffer' );
 						fileLoader.load( 'models/obj/female02/female02.obj',
 						fileLoader.load( 'models/obj/female02/female02.obj',
-							function ( content ) {
+							function ( content, message ) {
 								let local = new THREE.Object3D();
 								let local = new THREE.Object3D();
 								local.name = 'Pivot_female02';
 								local.name = 'Pivot_female02';
 								local.position.set( 75, 0, 0 );
 								local.position.set( 75, 0, 0 );
 								scope.pivot.add( local );
 								scope.pivot.add( local );
 								local.add( objLoader2.parse( content ) );
 								local.add( objLoader2.parse( content ) );
 
 
-								scope._reportProgress( { detail: { text: 'Loading complete: ' + modelName } } );
+								scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
 							}
 							}
 						);
 						);
 					}
 					}
@@ -168,17 +168,23 @@
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 					this._reportProgress( { detail: { text: 'Loading: ' + modelName } } );
 
 
 					let scope = this;
 					let scope = this;
-
-					let objLoader2Parallel = new OBJLoader2Parallel();
-					objLoader2Parallel.setModelName( modelName );
-
-					function callbackOnLoad ( message ) {
-						scope.scene.add( objLoader2Parallel.baseObject3d  );
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					function callbackOnLoad( object3d, message ) {
+						scope.scene.add( object3d );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
 					}
 					}
 
 
-					let filename = 'models/obj/female02/female02_vertex_colors.obj';
-					objLoader2Parallel.load( filename, callbackOnLoad );
+					let objLoader2Parallel = new OBJLoader2Parallel()
+						.setModelName( modelName )
+						.setCallbackOnLoad( callbackOnLoad );
+
+					let fileLoader = new THREE.FileLoader();
+					fileLoader.setPath( '' );
+					fileLoader.setResponseType( 'arraybuffer' );
+					fileLoader.load( 'models/obj/female02/female02_vertex_colors.obj',
+						function ( content ) {
+							objLoader2Parallel.parse( content );
+						}
+					);
 				},
 				},
 
 
 				useLoadMain: function () {
 				useLoadMain: function () {
@@ -190,14 +196,14 @@
 					.setUseIndices( true );
 					.setUseIndices( true );
 
 
 					let scope = this;
 					let scope = this;
-					function callbackOnLoad ( object3d ) {
+					function callbackOnLoad ( object3d, message ) {
 						let local = new THREE.Object3D();
 						let local = new THREE.Object3D();
 						local.name = 'Pivot_male02';
 						local.name = 'Pivot_male02';
 						local.position.set( 0, 0, -75 );
 						local.position.set( 0, 0, -75 );
 						scope.pivot.add( local );
 						scope.pivot.add( local );
 						local.add( object3d );
 						local.add( object3d );
 
 
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + objLoader2.modelName } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
 					}
 					}
 
 
 					function onLoadMtl ( mtlParseResult ) {
 					function onLoadMtl ( mtlParseResult ) {
@@ -221,12 +227,12 @@
 					this.pivot.add( local );
 					this.pivot.add( local );
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
-					.setModelName( modelName )
-					.setBaseObject3d( local );
+						.setModelName( modelName );
 
 
 					let scope = this;
 					let scope = this;
-					function callbackOnLoad ( message ) {
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					function callbackOnLoad ( object3d, message ) {
+						local.add( object3d );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
 					}
 					}
 					function onLoadMtl ( mtlParseResult ) {
 					function onLoadMtl ( mtlParseResult ) {
 						objLoader2Parallel.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
 						objLoader2Parallel.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ) );
@@ -246,13 +252,13 @@
 					this.pivot.add( local );
 					this.pivot.add( local );
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
-					.setModelName( local.name )
-					.setExecuteParallel( false );
+						.setModelName( local.name )
+						.setExecuteParallel( false );
 
 
 					let scope = this;
 					let scope = this;
-					function callbackOnLoad ( object3d ) {
+					function callbackOnLoad ( object3d, message ) {
 						local.add( object3d );
 						local.add( object3d );
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + local.name } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + 'completed: ' + message } } );
 					}
 					}
 
 
 					objLoader2Parallel.load( 'models/obj/cerberus/Cerberus.obj', callbackOnLoad );
 					objLoader2Parallel.load( 'models/obj/cerberus/Cerberus.obj', callbackOnLoad );
@@ -265,8 +271,8 @@
 					this.pivot.add( local );
 					this.pivot.add( local );
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
-					.setModelName( local.name )
-					.setBaseObject3d( local );
+						.setModelName( local.name )
+						.setBaseObject3d( local );
 
 
 					// Configure WorkerExecutionSupport to not disregard worker after execution
 					// Configure WorkerExecutionSupport to not disregard worker after execution
 					objLoader2Parallel.getWorkerExecutionSupport().setTerminateWorkerOnLoad( false );
 					objLoader2Parallel.getWorkerExecutionSupport().setTerminateWorkerOnLoad( false );
@@ -289,8 +295,8 @@
 					objLoader2Parallel.setCallbackOnMeshAlter( callbackMeshAlter );
 					objLoader2Parallel.setCallbackOnMeshAlter( callbackMeshAlter );
 
 
 					let scope = this;
 					let scope = this;
-					function callbackOnLoad ( message ) {
-						scope._reportProgress( { detail: { text: 'Loading complete: ' + message } } );
+					function callbackOnLoad ( object3d, message ) {
+						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + 'completed: ' + message } } );
 					}
 					}
 
 
 					objLoader2Parallel.load( 'models/obj/vive-controller/vr_controller_vive_1_5.obj', callbackOnLoad );
 					objLoader2Parallel.load( 'models/obj/vive-controller/vr_controller_vive_1_5.obj', callbackOnLoad );