浏览代码

Merge pull request #18886 from kaisalmen/OBJ2LoaderParallel_ModuleWorkers

Module worker support in OBJLoader2Parallel and webgl_loader_obj2_options example
Michael Herzog 5 年之前
父节点
当前提交
4ef42a9b24

+ 1 - 1
examples/jsm/loaders/OBJLoader2.d.ts

@@ -4,7 +4,7 @@ import {
 	Object3D,
 	Object3D,
 } from '../../../src/Three';
 } from '../../../src/Three';
 
 
-import { OBJLoader2Parser } from './obj2/worker/parallel/OBJLoader2Parser';
+import { OBJLoader2Parser } from './obj2/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';
 
 

+ 2 - 2
examples/jsm/loaders/OBJLoader2.js

@@ -9,7 +9,7 @@ import {
 	Loader
 	Loader
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
-import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
+import { OBJLoader2Parser } from "./obj2/OBJLoader2Parser.js";
 import { MeshReceiver } from "./obj2/shared/MeshReceiver.js";
 import { MeshReceiver } from "./obj2/shared/MeshReceiver.js";
 import { MaterialHandler } from "./obj2/shared/MaterialHandler.js";
 import { MaterialHandler } from "./obj2/shared/MaterialHandler.js";
 
 
@@ -43,7 +43,7 @@ const OBJLoader2 = function ( manager ) {
 
 
 };
 };
 
 
-OBJLoader2.OBJLOADER2_VERSION = '3.1.1';
+OBJLoader2.OBJLOADER2_VERSION = '3.2.0';
 console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );
 console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );
 
 
 
 

+ 1 - 1
examples/jsm/loaders/OBJLoader2Parallel.d.ts

@@ -12,7 +12,7 @@ export class OBJLoader2Parallel extends OBJLoader2 {
 	executeParallel: boolean;
 	executeParallel: boolean;
 	workerExecutionSupport: WorkerExecutionSupport;
 	workerExecutionSupport: WorkerExecutionSupport;
 
 
-	setPreferJsmWorker( preferJsmWorker: boolean ): this;
+	setJsmWorker( preferJsmWorker: boolean, jsmWorkerUrl: URL ): this;
 	setExecuteParallel( executeParallel: boolean ): this;
 	setExecuteParallel( executeParallel: boolean ): this;
 	getWorkerExecutionSupport(): object;
 	getWorkerExecutionSupport(): object;
 	buildWorkerCode(): object;
 	buildWorkerCode(): object;

+ 13 - 7
examples/jsm/loaders/OBJLoader2Parallel.js

@@ -15,7 +15,7 @@ import { CodeSerializer } from "./obj2/utils/CodeSerializer.js";
 import { OBJLoader2 } from "./OBJLoader2.js";
 import { OBJLoader2 } from "./OBJLoader2.js";
 
 
 // Imports only related to worker (when standard workers (modules aren't supported) are used)
 // Imports only related to worker (when standard workers (modules aren't supported) are used)
-import { OBJLoader2Parser } from "./obj2/worker/parallel/OBJLoader2Parser.js";
+import { OBJLoader2Parser } from "./obj2/OBJLoader2Parser.js";
 import {
 import {
 	WorkerRunner,
 	WorkerRunner,
 	DefaultWorkerPayloadHandler,
 	DefaultWorkerPayloadHandler,
@@ -34,15 +34,16 @@ const OBJLoader2Parallel = function ( manager ) {
 
 
 	OBJLoader2.call( this, manager );
 	OBJLoader2.call( this, manager );
 	this.preferJsmWorker = false;
 	this.preferJsmWorker = false;
+	this.jsmWorkerUrl = null;
 
 
 	this.executeParallel = true;
 	this.executeParallel = true;
 	this.workerExecutionSupport = new WorkerExecutionSupport();
 	this.workerExecutionSupport = new WorkerExecutionSupport();
 
 
 };
 };
 
 
-OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.1.2';
+OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.2.0';
 console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
 console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
-
+OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH = './jsm/loaders/obj2/worker/parallel/OBJLoader2JsmWorker.js';
 
 
 OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototype ), {
 OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototype ), {
 
 
@@ -51,7 +52,7 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp
 	/**
 	/**
 	 * Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
 	 * Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
 	 *
 	 *
-	 * @param executeParallel True or False
+	 * @param {boolean} executeParallel True or False
 	 * @return {OBJLoader2Parallel}
 	 * @return {OBJLoader2Parallel}
 	 */
 	 */
 	setExecuteParallel: function ( executeParallel ) {
 	setExecuteParallel: function ( executeParallel ) {
@@ -63,12 +64,17 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp
 
 
 	/**
 	/**
 	 * Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
 	 * Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
-	 * @param preferJsmWorker True or False
+	 * @param {boolean} preferJsmWorker True or False
+	 * @param {URL} jsmWorkerUrl Provide complete jsm worker URL otherwise relative path to this module may not be correct
 	 * @return {OBJLoader2Parallel}
 	 * @return {OBJLoader2Parallel}
 	 */
 	 */
-	setPreferJsmWorker: function ( preferJsmWorker ) {
+	setJsmWorker: function ( preferJsmWorker, jsmWorkerUrl ) {
 
 
 		this.preferJsmWorker = preferJsmWorker === true;
 		this.preferJsmWorker = preferJsmWorker === true;
+		if ( jsmWorkerUrl === undefined || jsmWorkerUrl === null ) {
+			throw "The url to the jsm worker is not valid. Aborting..."
+		}
+		this.jsmWorkerUrl = jsmWorkerUrl;
 		return this;
 		return this;
 
 
 	},
 	},
@@ -92,7 +98,7 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp
 		let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
 		let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
 		if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
 		if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
 
 
-			codeBuilderInstructions.setJsmWorkerFile( '../examples/loaders/jsm/obj2/worker/parallel/jsm/OBJLoader2Worker.js' );
+			codeBuilderInstructions.setJsmWorkerUrl( this.jsmWorkerUrl );
 
 
 		}
 		}
 		if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
 		if ( codeBuilderInstructions.isSupportsStandardWorker() ) {

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


+ 0 - 0
examples/jsm/loaders/obj2/worker/parallel/OBJLoader2Parser.js → examples/jsm/loaders/obj2/OBJLoader2Parser.js


+ 1 - 1
examples/jsm/loaders/obj2/worker/main/WorkerExecutionSupport.d.ts

@@ -13,7 +13,7 @@ export class CodeBuilderInstructions {
 	isSupportsStandardWorker(): boolean;
 	isSupportsStandardWorker(): boolean;
 	isSupportsJsmWorker(): boolean;
 	isSupportsJsmWorker(): boolean;
 	isPreferJsmWorker(): boolean;
 	isPreferJsmWorker(): boolean;
-	setJsmWorkerFile( jsmWorkerFile: string ): void;
+	setJsmWorkerUrl( jsmWorkerUrl: string ): void;
 	addStartCode( startCode: string ): void;
 	addStartCode( startCode: string ): void;
 	addCodeFragment( code: string ): void;
 	addCodeFragment( code: string ): void;
 	addLibraryImport( libraryPath: string ): void;
 	addLibraryImport( libraryPath: string ): void;

+ 15 - 9
examples/jsm/loaders/obj2/worker/main/WorkerExecutionSupport.js

@@ -19,7 +19,7 @@ const CodeBuilderInstructions = function ( supportsStandardWorker, supportsJsmWo
 	this.codeFragments = [];
 	this.codeFragments = [];
 	this.importStatements = [];
 	this.importStatements = [];
 
 
-	this.jsmWorkerFile = null;
+	this.jsmWorkerUrl = null;
 	this.defaultGeometryType = 0;
 	this.defaultGeometryType = 0;
 
 
 };
 };
@@ -49,13 +49,13 @@ CodeBuilderInstructions.prototype = {
 	/**
 	/**
 	 * Set the full path to the module that contains the worker code.
 	 * Set the full path to the module that contains the worker code.
 	 *
 	 *
-	 * @param {String} jsmWorkerFile
+	 * @param {String} jsmWorkerUrl
 	 */
 	 */
-	setJsmWorkerFile: function ( jsmWorkerFile ) {
+	setJsmWorkerUrl: function ( jsmWorkerUrl ) {
 
 
-		if ( jsmWorkerFile !== undefined && jsmWorkerFile !== null ) {
+		if ( jsmWorkerUrl !== undefined && jsmWorkerUrl !== null ) {
 
 
-			this.jsmWorkerFile = jsmWorkerFile;
+			this.jsmWorkerUrl = jsmWorkerUrl;
 
 
 		}
 		}
 
 
@@ -127,7 +127,7 @@ const WorkerExecutionSupport = function () {
 	this._reset();
 	this._reset();
 
 
 };
 };
-WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.1.0';
+WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.2.0';
 console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
 console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
 
 
 
 
@@ -289,10 +289,9 @@ WorkerExecutionSupport.prototype = {
 		let workerAvailable = this._buildWorkerCheckPreconditions( true, timeLabel );
 		let workerAvailable = this._buildWorkerCheckPreconditions( true, timeLabel );
 		if ( ! workerAvailable ) {
 		if ( ! workerAvailable ) {
 
 
-			let workerFileUrl = new URL( codeBuilderInstructions.jsmWorkerFile, window.location.href ).href;
 			try {
 			try {
 
 
-				let worker = new Worker( workerFileUrl, { type: "module" } );
+				let worker = new Worker( codeBuilderInstructions.jsmWorkerUrl.href, { type: "module" } );
 				this._configureWorkerCommunication( worker, true, codeBuilderInstructions.defaultGeometryType, timeLabel );
 				this._configureWorkerCommunication( worker, true, codeBuilderInstructions.defaultGeometryType, timeLabel );
 
 
 			} catch ( e ) {
 			} catch ( e ) {
@@ -387,6 +386,7 @@ WorkerExecutionSupport.prototype = {
 
 
 		};
 		};
 		this.worker.native.onmessage = scopedReceiveWorkerMessage;
 		this.worker.native.onmessage = scopedReceiveWorkerMessage;
+		this.worker.native.onerror = scopedReceiveWorkerMessage;
 		if ( defaultGeometryType !== undefined && defaultGeometryType !== null ) {
 		if ( defaultGeometryType !== undefined && defaultGeometryType !== null ) {
 
 
 			this.worker.workerRunner.defaultGeometryType = defaultGeometryType;
 			this.worker.workerRunner.defaultGeometryType = defaultGeometryType;
@@ -418,9 +418,15 @@ WorkerExecutionSupport.prototype = {
 	 */
 	 */
 	_receiveWorkerMessage: function ( event ) {
 	_receiveWorkerMessage: function ( event ) {
 
 
+		// fast-fail in case of error
+		if ( event.type === "error" ) {
+
+			console.error( event );
+			return;
+
+		}
 		let payload = event.data;
 		let payload = event.data;
 		let workerRunnerName = this.worker.workerRunner.name;
 		let workerRunnerName = this.worker.workerRunner.name;
-
 		switch ( payload.cmd ) {
 		switch ( payload.cmd ) {
 
 
 			case 'assetAvailable':
 			case 'assetAvailable':

+ 3 - 2
examples/jsm/loaders/obj2/worker/parallel/jsm/OBJLoader2Worker.js → examples/jsm/loaders/obj2/worker/parallel/OBJLoader2JsmWorker.js

@@ -3,10 +3,11 @@
  * Development repository: https://github.com/kaisalmen/WWOBJLoader
  * Development repository: https://github.com/kaisalmen/WWOBJLoader
  */
  */
 
 
-import { OBJLoader2Parser } from "../OBJLoader2Parser.js";
+import { OBJLoader2Parser } from "../../OBJLoader2Parser.js";
+
 import {
 import {
 	WorkerRunner,
 	WorkerRunner,
 	DefaultWorkerPayloadHandler
 	DefaultWorkerPayloadHandler
-} from "../WorkerRunner.js";
+} from "./WorkerRunner.js";
 
 
 new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );
 new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );

+ 82 - 33
examples/webgl_loader_obj2_options.html

@@ -44,7 +44,7 @@
 
 
 		</div>
 		</div>
 		<div id="info">
 		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - OBJLoader2 usage options
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - OBJLoader2 usage options<br>Use module workers with Chromium based browser (80+)
 			<div id="feedback"></div>
 			<div id="feedback"></div>
 		</div>
 		</div>
 
 
@@ -85,6 +85,8 @@
 
 
 				this.flatShading = false;
 				this.flatShading = false;
 				this.doubleSide = false;
 				this.doubleSide = false;
+				this.useJsmWorker = false;
+				this.loadCount = 6;
 
 
 				this.cube = null;
 				this.cube = null;
 				this.pivot = null;
 				this.pivot = null;
@@ -155,7 +157,8 @@
 								scope.pivot.add( local );
 								scope.pivot.add( local );
 								local.add( objLoader2.parse( content ) );
 								local.add( objLoader2.parse( content ) );
 
 
-								scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
+								scope._reportProgress( { detail: { text: 'Loading of ' + modelName + ' completed: OBJLoader2#pase: Parsing completed' } } );
+								scope.finalize();
 							}
 							}
 						);
 						);
 					}
 					}
@@ -176,7 +179,8 @@
 					let scope = this;
 					let scope = this;
 					function callbackOnLoad( object3d, message ) {
 					function callbackOnLoad( object3d, message ) {
 						local.add( object3d );
 						local.add( object3d );
-						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + ' completed: ' + message } } );
+						scope.finalize();
 					}
 					}
 
 
 					let materials = {
 					let materials = {
@@ -185,6 +189,7 @@
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
 						.setModelName( modelName )
 						.setModelName( modelName )
+						.setJsmWorker( this.useJsmWorker, new URL( OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH, window.location.href ) )
 						.setCallbackOnLoad( callbackOnLoad )
 						.setCallbackOnLoad( callbackOnLoad )
 						.addMaterials( materials, true );
 						.addMaterials( materials, true );
 
 
@@ -214,7 +219,8 @@
 						scope.pivot.add( local );
 						scope.pivot.add( local );
 						local.add( object3d );
 						local.add( object3d );
 
 
-						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + ' completed: ' + message } } );
+						scope.finalize();
 					}
 					}
 
 
 					function onLoadMtl ( mtlParseResult ) {
 					function onLoadMtl ( mtlParseResult ) {
@@ -238,12 +244,14 @@
 					this.pivot.add( local );
 					this.pivot.add( local );
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
-						.setModelName( modelName );
+						.setModelName( modelName )
+						.setJsmWorker( this.useJsmWorker, new URL( OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH, window.location.href ) );
 
 
 					let scope = this;
 					let scope = this;
 					function callbackOnLoad ( object3d, message ) {
 					function callbackOnLoad ( object3d, message ) {
 						local.add( object3d );
 						local.add( object3d );
-						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + 'completed: ' + message } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + modelName + ' completed: ' + message } } );
+						scope.finalize();
 					}
 					}
 					function onLoadMtl ( mtlParseResult ) {
 					function onLoadMtl ( mtlParseResult ) {
 						objLoader2Parallel.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ), true );
 						objLoader2Parallel.addMaterials( MtlObjBridge.addMaterialsFromMtlLoader( mtlParseResult ), true );
@@ -269,7 +277,8 @@
 					let scope = this;
 					let scope = this;
 					function callbackOnLoad ( object3d, message ) {
 					function callbackOnLoad ( object3d, message ) {
 						local.add( object3d );
 						local.add( object3d );
-						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + 'completed: ' + message } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + ' completed: ' + message } } );
+						scope.finalize();
 					}
 					}
 
 
 					objLoader2Parallel.load( 'models/obj/cerberus/Cerberus.obj', callbackOnLoad );
 					objLoader2Parallel.load( 'models/obj/cerberus/Cerberus.obj', callbackOnLoad );
@@ -283,6 +292,7 @@
 
 
 					let objLoader2Parallel = new OBJLoader2Parallel()
 					let objLoader2Parallel = new OBJLoader2Parallel()
 						.setModelName( local.name )
 						.setModelName( local.name )
+						.setJsmWorker( this.useJsmWorker, new URL( OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH, window.location.href ) )
 						.setBaseObject3d( local );
 						.setBaseObject3d( local );
 
 
 					// Configure WorkerExecutionSupport to not disregard worker after execution
 					// Configure WorkerExecutionSupport to not disregard worker after execution
@@ -305,14 +315,20 @@
 
 
 					let scope = this;
 					let scope = this;
 					function callbackOnLoad ( object3d, message ) {
 					function callbackOnLoad ( object3d, message ) {
-						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + 'completed: ' + message } } );
+						scope._reportProgress( { detail: { text: 'Loading of ' + objLoader2Parallel.modelName + ' completed: ' + message } } );
+						scope.finalize();
 					}
 					}
 
 
 					objLoader2Parallel.load( 'models/obj/ninja/ninjaHead_Low.obj', callbackOnLoad );
 					objLoader2Parallel.load( 'models/obj/ninja/ninjaHead_Low.obj', callbackOnLoad );
 				},
 				},
 
 
 				finalize: function () {
 				finalize: function () {
-					this._reportProgress( { detail: { text: '' } } );
+					this.loadCount--;
+					if ( this.loadCount === 0 ) {
+
+						this._reportProgress( { detail: { text: '' } } );
+
+					}
 				},
 				},
 
 
 				_reportProgress: function( event ) {
 				_reportProgress: function( event ) {
@@ -408,15 +424,55 @@
 						this.traversalFunction( object3d.material );
 						this.traversalFunction( object3d.material );
 
 
 					}
 					}
+				},
+
+				executeLoading: function () {
+					// Load a file with OBJLoader2.parse on main
+					this.useParseMain();
+
+					// Load a file with OBJLoader2Parallel.parse in parallel in worker
+					this.useParseParallel();
+
+					// Load a file with OBJLoader.load on main
+					this.useLoadMain();
+
+					// Load a file with OBJLoader2Parallel.load in parallel in worker
+					this.useLoadParallel();
+
+					// Load a file with OBJLoader2Parallel.load on main with fallback to OBJLoader2.parse
+					this.useLoadMainFallback();
+
+					// Load a file with OBJLoader2Parallel.load in parallel in worker and add normals during onMeshAlter
+					this.useLoadParallelMeshAlter();
 				}
 				}
 
 
 			};
 			};
 
 
 			let app = new WWOBJLoader2Example( document.getElementById( 'example' ) );
 			let app = new WWOBJLoader2Example( document.getElementById( 'example' ) );
 
 
+			let handleExecuteLoading;
 			let wwObjLoader2Control = {
 			let wwObjLoader2Control = {
 				flatShading: app.flatShading,
 				flatShading: app.flatShading,
-				doubleSide: app.doubleSide
+				doubleSide: app.doubleSide,
+				useJsmWorker: app.useJsmWorker,
+				blockEvent: function ( event ) {
+
+					event.stopPropagation();
+
+				},
+				disableElement: function ( elementHandle ) {
+
+					elementHandle.domElement.addEventListener( 'click', this.blockEvent, true );
+					elementHandle.domElement.parentElement.style.pointerEvents = 'none';
+					elementHandle.domElement.parentElement.style.opacity = 0.5;
+
+				},
+				executeLoading: function() {
+
+					app.executeLoading();
+					this.disableElement( handleExecuteLoading );
+
+				},
 			};
 			};
 
 
 			let menuDiv = document.getElementById( 'dat' );
 			let menuDiv = document.getElementById( 'dat' );
@@ -426,19 +482,31 @@
 			} );
 			} );
 			menuDiv.appendChild( gui.domElement );
 			menuDiv.appendChild( gui.domElement );
 
 
-			let folderOptions = gui.addFolder( 'WWOBJLoader2 Options' );
-			let controlFlat = folderOptions.add( wwObjLoader2Control, 'flatShading' ).name( 'Flat Shading' );
+			let folderRenderingOptions = gui.addFolder( 'Rendering Options' );
+			let controlFlat = folderRenderingOptions.add( wwObjLoader2Control, 'flatShading' ).name( 'Flat Shading' );
 			controlFlat.onChange( function( value ) {
 			controlFlat.onChange( function( value ) {
 				console.log( 'Setting flatShading to: ' + value );
 				console.log( 'Setting flatShading to: ' + value );
 				app.alterShading();
 				app.alterShading();
 			});
 			});
 
 
-			let controlDouble = folderOptions.add( wwObjLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
+			let controlDouble = folderRenderingOptions.add( wwObjLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
 			controlDouble.onChange( function( value ) {
 			controlDouble.onChange( function( value ) {
 				console.log( 'Setting doubleSide to: ' + value );
 				console.log( 'Setting doubleSide to: ' + value );
 				app.alterDouble();
 				app.alterDouble();
 			});
 			});
-			folderOptions.open();
+
+			let folderObjLoader2ParallelOptions = gui.addFolder( 'OBJLoader2Parallel Options' );
+			let controlJsmWorker = folderObjLoader2ParallelOptions.add( wwObjLoader2Control, 'useJsmWorker' ).name( 'Use Module Workers' );
+			controlJsmWorker.onChange( function( value ) {
+				console.log( 'Setting useJsmWorker to: ' + value );
+				app.useJsmWorker = value;
+			});
+			let folderExecution = gui.addFolder( 'Execution' );
+			handleExecuteLoading = folderExecution.add( wwObjLoader2Control, 'executeLoading' ).name( 'Run' );
+			handleExecuteLoading.domElement.id = 'startButton';
+			folderRenderingOptions.open();
+			folderObjLoader2ParallelOptions.open();
+			folderExecution.open();
 
 
 
 
 			// init three.js example application
 			// init three.js example application
@@ -459,25 +527,6 @@
 			// kick render loop
 			// kick render loop
 			render();
 			render();
 
 
-			// Load a file with OBJLoader2.parse on main
-			app.useParseMain();
-
-			// Load a file with OBJLoader2Parallel.parse in parallel in worker
-			app.useParseParallel();
-
-			// Load a file with OBJLoader.load on main
-			app.useLoadMain();
-
-			// Load a file with OBJLoader2Parallel.load in parallel in worker
-			app.useLoadParallel();
-
-			// 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>
 		</script>
 	</body>
 	</body>
 </html>
 </html>