diff --git a/docs/examples/loaders/LoaderSupport.html b/docs/examples/loaders/LoaderSupport.html index 5479a6bb9c5abb41dd6461c8db4a83a61bb8b732..4e3fd7918f1947cca5d622b93b45442efb573c51 100644 --- a/docs/examples/loaders/LoaderSupport.html +++ b/docs/examples/loaders/LoaderSupport.html @@ -156,10 +156,9 @@

Methods

-

[method:null validate] ( [page:Function functionCodeBuilder], [page:Boolean forceWorkerReload], Array of [page:String libLocations], [page:String libPath], [page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] )

+

[method:null validate] ( [page:Function functionCodeBuilder], Array of [page:String libLocations], [page:String libPath], [page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] )

[page:Function functionCodeBuilder] - Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons.
- [page:Boolean forceWorkerReload] - Force re-build of the worker code.
Array of [page:String libLocations] - URL of libraries that shall be added to worker code relative to libPath.
[page:String libPath] - Base path used for loading libraries.
[page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] - The default worker parser wrapper implementation (communication and execution). An extended class could be passed here. @@ -178,12 +177,6 @@
-

[method:null terminateWorker] ()

-
- Terminate the worker and the code. -
- -

[method:null setCallbacks] ( [page:Function builder], [page:Function onLoad] )

[page:Function builder] - The builder function. Default is [page:LoaderSupport.Builder].
@@ -251,7 +244,7 @@ - prepareWorkers
- enqueueForRun
- processQueue
- - deregister + - tearDown
@@ -283,7 +276,10 @@ -

[method:null deregister]()

+

[method:null tearDown]( [page:Function callbackOnFinishedProcessing] )

+
+ [page:Function callbackOnFinishedProcessing] - Function called once all workers finished processing. +
Terminate all workers.
@@ -300,6 +296,11 @@ Returns the maximum number of workers. +

[method:Boolean isRunning]()

+
+ Returns if any workers are running. +
+

[method:null setCrossOrigin]( [page:String crossOrigin] )

@@ -543,7 +544,7 @@

Methods

-

[method:null isValid]( [page:Object input] )

+

[method:Boolean isValid]( [page:Object input] )

[page:Object input] - Can be anything
diff --git a/examples/js/loaders/LoaderSupport.js b/examples/js/loaders/LoaderSupport.js index d9effa2a49b5e23828a2673281640de4e809b09f..d3bd7051608ac2345adffc0561a24c653cc73bbc 100644 --- a/examples/js/loaders/LoaderSupport.js +++ b/examples/js/loaders/LoaderSupport.js @@ -941,13 +941,8 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. */ WorkerRunnerRefImpl.prototype.processMessage = function ( payload ) { - var logger = new ConsoleLogger(); - if ( Validator.isValid( payload.logger ) ) { - - logger.setEnabled( payload.logger.enabled ); - logger.setDebug( payload.logger.debug ); - - } + var logEnabled = payload.logger.enabled; + var logDebug = payload.logger.enabled; if ( payload.cmd === 'run' ) { var callbacks = { @@ -955,19 +950,20 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { self.postMessage( payload ); }, callbackProgress: function ( text ) { - logger.logDebug( 'WorkerRunner: progress: ' + text ); + if ( logEnabled && logDebug ) console.debug( 'WorkerRunner: progress: ' + text ); } }; // Parser is expected to be named as such - var parser = new Parser( logger ); + var parser = new Parser(); + if ( typeof parser[ 'setLogConfig' ] === 'function' ) parser.setLogConfig( logEnabled, logDebug ); this.applyProperties( parser, payload.params ); this.applyProperties( parser, payload.materials ); this.applyProperties( parser, callbacks ); parser.workerScope = self; parser.parse( payload.data.input, payload.data.options ); - logger.logInfo( 'WorkerRunner: Run complete!' ); + if ( logEnabled ) console.log( 'WorkerRunner: Run complete!' ); callbacks.callbackBuilder( { cmd: 'complete', @@ -976,7 +972,7 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { } else { - logger.logError( 'WorkerRunner: Received unknown command: ' + payload.cmd ); + console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd ); } }; @@ -993,170 +989,226 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { */ THREE.LoaderSupport.WorkerSupport = (function () { - var WORKER_SUPPORT_VERSION = '1.1.1'; + var WORKER_SUPPORT_VERSION = '2.0.0'; var Validator = THREE.LoaderSupport.Validator; - function WorkerSupport( logger ) { - this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); - this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION ); + var LoaderWorker = (function () { - // check worker support first - if ( window.Worker === undefined ) throw "This browser does not support web workers!"; - if ( window.Blob === undefined ) throw "This browser does not support Blob!"; - if ( typeof window.URL.createObjectURL !== 'function' ) throw "This browser does not support Object creation from URL!"; + function LoaderWorker( logger ) { + this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); + this._reset(); + } + + LoaderWorker.prototype._reset = function () { + this.worker = null; + this.runnerImplName = null; + this.callbacks = { + builder: null, + onLoad: null + }; + this.terminateRequested = false; + this.queuedMessage = null; + this.started = false; + }; + + LoaderWorker.prototype.initWorker = function ( code, runnerImplName ) { + this.runnerImplName = runnerImplName; + var blob = new Blob( [ code ], { type: 'application/javascript' } ); + this.worker = new Worker( window.URL.createObjectURL( blob ) ); + this.worker.onmessage = this._receiveWorkerMessage; - this.worker = null; - this.workerCode = null; - this.loading = true; - this.queuedMessage = null; - this.running = false; - this.terminateRequested = false; + // set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members + this.worker.runtimeRef = this; - this.callbacks = { - builder: null, - onLoad: null + // process stored queuedMessage + this._postMessage(); }; - } - /** - * Validate the status of worker code and the derived worker. - * @memberOf THREE.LoaderSupport.WorkerSupport - * - * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons. - * @param {boolean} forceWorkerReload Force re-build of the worker code. - * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath - * @param {String} libPath Base path used for loading libraries - * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here. - */ - WorkerSupport.prototype.validate = function ( functionCodeBuilder, forceWorkerReload, libLocations, libPath, runnerImpl ) { - this.running = false; - if ( forceWorkerReload ) { + /** + * Executed in worker scope + */ + LoaderWorker.prototype._receiveWorkerMessage = function ( e ) { + var payload = e.data; + switch ( payload.cmd ) { + case 'meshData': + case 'materialData': + case 'imageData': + this.runtimeRef.callbacks.builder( payload ); + break; - this.worker = null; - this.workerCode = null; - this.loading = true; - this.queuedMessage = null; - this.callbacks.builder = null; - this.callbacks.onLoad = null; + case 'complete': + this.runtimeRef.queuedMessage = null; + this.started = false; + this.runtimeRef.callbacks.onLoad( payload.msg ); - } + if ( this.runtimeRef.terminateRequested ) { + + this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' ); + this.runtimeRef._terminate(); + + } + break; + + case 'error': + this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg ); + this.runtimeRef.queuedMessage = null; + this.started = false; + this.runtimeRef.callbacks.onLoad( payload.msg ); - if ( ! Validator.isValid( this.worker ) ) { + if ( this.runtimeRef.terminateRequested ) { - this.logger.logInfo( 'WorkerSupport: Building worker code...' ); - this.logger.logTimeStart( 'buildWebWorkerCode' ); + this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' ); + this.runtimeRef._terminate(); - var workerRunner; - if ( Validator.isValid( runnerImpl ) ) { + } + break; - this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runncer class for worker.' ); - workerRunner = runnerImpl; + default: + this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd ); + break; + + } + }; + + LoaderWorker.prototype.setCallbacks = function ( builder, onLoad ) { + this.callbacks.builder = Validator.verifyInput( builder, this.callbacks.builder ); + this.callbacks.onLoad = Validator.verifyInput( onLoad, this.callbacks.onLoad ); + }; + + LoaderWorker.prototype.run = function( payload ) { + if ( Validator.isValid( this.queuedMessage ) ) { + + console.warn( 'Already processing message. Rejecting new run instruction' ); + return; } else { - this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runncer class for worker.' ); - workerRunner = THREE.LoaderSupport.WorkerRunnerRefImpl; + this.queuedMessage = payload; + this.started = true; } + if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.'; + if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.'; + if ( payload.cmd !== 'run' ) payload.cmd = 'run'; + if ( Validator.isValid( payload.logger ) ) { - var scope = this; - var buildWorkerCode = function ( baseWorkerCode ) { - scope.workerCode = baseWorkerCode; - if ( workerRunner == THREE.LoaderSupport.WorkerRunnerRefImpl ) { + payload.logger.enabled = Validator.verifyInput( payload.logger.enabled, true ); + payload.logger.debug = Validator.verifyInput( payload.logger.debug, false ); - scope.workerCode += buildObject( 'Validator', THREE.LoaderSupport.Validator ); - scope.workerCode += buildSingelton( 'ConsoleLogger', 'ConsoleLogger', THREE.LoaderSupport.ConsoleLogger ); + } else { + payload.logger = { + enabled: true, + debug: false } - scope.workerCode += functionCodeBuilder( buildObject, buildSingelton ); - scope.workerCode += buildSingelton( workerRunner.name, workerRunner.name, workerRunner ); - scope.workerCode += 'new ' + workerRunner.name + '();\n\n'; - var blob = new Blob( [ scope.workerCode ], { type: 'application/javascript' } ); - scope.worker = new Worker( window.URL.createObjectURL( blob ) ); - scope.logger.logTimeEnd( 'buildWebWorkerCode' ); + } + this._postMessage(); + }; - var receiveWorkerMessage = function ( e ) { - var payload = e.data; + LoaderWorker.prototype._postMessage = function () { + if ( Validator.isValid( this.queuedMessage ) && Validator.isValid( this.worker ) ) { - switch ( payload.cmd ) { - case 'meshData': - case 'materialData': - case 'imageData': - scope.callbacks.builder( payload ); - break; + this.worker.postMessage( this.queuedMessage ); - case 'complete': - scope.callbacks.onLoad( payload.msg ); - scope.running = false; + } + }; - if ( scope.terminateRequested ) { + LoaderWorker.prototype.setTerminateRequested = function ( terminateRequested ) { + this.terminateRequested = terminateRequested === true; + if ( this.terminateRequested && Validator.isValid( this.worker ) && ! Validator.isValid( this.queuedMessage ) && this.started ) { - scope.logger.logInfo( 'WorkerSupport [' + workerRunner + ']: Run is complete. Terminating application on request!' ); - scope.terminateWorker(); + this.logger.logInfo( 'Worker is terminated immediately as it is not running!' ); + this._terminate(); - } - break; + } + }; - case 'error': - scope.logger.logError( 'WorkerSupport [' + workerRunner + ']: Reported error: ' + payload.msg ); - break; + LoaderWorker.prototype._terminate = function () { + this.worker.terminate(); + this._reset(); + }; - default: - scope.logger.logError( 'WorkerSupport [' + workerRunner + ']: Received unknown command: ' + payload.cmd ); - break; + return LoaderWorker; - } - }; - scope.worker.addEventListener( 'message', receiveWorkerMessage, false ); - scope.loading = false; - scope._postMessage(); - }; + })(); - if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) { + function WorkerSupport( logger ) { + this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); + this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION ); - var libsContent = ''; - var loadAllLibraries = function ( path, locations ) { - if ( locations.length === 0 ) { + // check worker support first + if ( window.Worker === undefined ) throw "This browser does not support web workers!"; + if ( window.Blob === undefined ) throw "This browser does not support Blob!"; + if ( typeof window.URL.createObjectURL !== 'function' ) throw "This browser does not support Object creation from URL!"; - buildWorkerCode( libsContent ); + this.loaderWorker = new LoaderWorker( this.logger ); + } - } else { + /** + * Validate the status of worker code and the derived worker. + * @memberOf THREE.LoaderSupport.WorkerSupport + * + * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons. + * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath + * @param {String} libPath Base path used for loading libraries + * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here. + */ + WorkerSupport.prototype.validate = function ( functionCodeBuilder, libLocations, libPath, runnerImpl ) { + if ( Validator.isValid( this.loaderWorker.worker ) ) return; - var loadedLib = function ( contentAsString ) { - libsContent += contentAsString; - loadAllLibraries( path, locations ); - }; + this.logger.logInfo( 'WorkerSupport: Building worker code...' ); + this.logger.logTimeStart( 'buildWebWorkerCode' ); - var fileLoader = new THREE.FileLoader(); - fileLoader.setPath( path ); - fileLoader.setResponseType( 'text' ); - fileLoader.load( locations[ 0 ], loadedLib ); - locations.shift(); + if ( Validator.isValid( runnerImpl ) ) { - } - }; - loadAllLibraries( libPath, libLocations ); + this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runncer class for worker.' ); - } else { + } else { - buildWorkerCode( '' ); + runnerImpl = THREE.LoaderSupport.WorkerRunnerRefImpl; + this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runncer class for worker.' ); - } } - }; - /** - * Terminate the worker and the code. - * @memberOf THREE.LoaderSupport.WorkerSupport - */ - WorkerSupport.prototype.terminateWorker = function () { - if ( Validator.isValid( this.worker ) ) { - this.worker.terminate(); + var userWorkerCode = functionCodeBuilder( buildObject, buildSingelton ); + userWorkerCode += buildSingelton( runnerImpl.name, runnerImpl.name, runnerImpl ); + userWorkerCode += 'new ' + runnerImpl.name + '();\n\n'; + + var scope = this; + if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) { + + var libsContent = ''; + var loadAllLibraries = function ( path, locations ) { + if ( locations.length === 0 ) { + + scope.loaderWorker.initWorker( libsContent + userWorkerCode, scope.logger, runnerImpl.name ); + scope.logger.logTimeEnd( 'buildWebWorkerCode' ); + + } else { + + var loadedLib = function ( contentAsString ) { + libsContent += contentAsString; + loadAllLibraries( path, locations ); + }; + + var fileLoader = new THREE.FileLoader(); + fileLoader.setPath( path ); + fileLoader.setResponseType( 'text' ); + fileLoader.load( locations[ 0 ], loadedLib ); + locations.shift(); + + } + }; + loadAllLibraries( libPath, libLocations ); + + } else { + + this.loaderWorker.initWorker( userWorkerCode, this.logger, runnerImpl.name ); + this.logger.logTimeEnd( 'buildWebWorkerCode' ); + } - this.worker = null; - this.workerCode = null; }; /** @@ -1167,10 +1219,27 @@ THREE.LoaderSupport.WorkerSupport = (function () { * @param {Function} onLoad The function that is called when parsing is complete. */ WorkerSupport.prototype.setCallbacks = function ( builder, onLoad ) { - this.callbacks = { - builder: builder, - onLoad: onLoad - }; + this.loaderWorker.setCallbacks( builder, onLoad ); + }; + + /** + * Runs the parser with the provided configuration. + * @memberOf THREE.LoaderSupport.WorkerSupport + * + * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. + */ + WorkerSupport.prototype.run = function ( payload ) { + this.loaderWorker.run( payload ); + }; + + /** + * Request termination of worker once parser is finished. + * @memberOf THREE.LoaderSupport.WorkerSupport + * + * @param {boolean} terminateRequested True or false. + */ + WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) { + this.loaderWorker.setTerminateRequested( terminateRequested ); }; var buildObject = function ( fullName, object ) { @@ -1229,41 +1298,8 @@ THREE.LoaderSupport.WorkerSupport = (function () { return objectString; }; - /** - * Request termination of worker once parser is finished. - * @memberOf THREE.LoaderSupport.WorkerSupport - * - * @param {boolean} terminateRequested True or false. - */ - WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) { - this.terminateRequested = terminateRequested === true; - }; - - /** - * Runs the parser with the provided configuration. - * @memberOf THREE.LoaderSupport.WorkerSupport - * - * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. - */ - WorkerSupport.prototype.run = function ( payload ) { - if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.'; - if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.'; - if ( Validator.isValid( this.worker ) || this.loading ) { - if ( payload.cmd !== 'run' ) payload.cmd = 'run'; - this.queuedMessage = payload; - this.running = true; - this._postMessage(); - - } - }; - - WorkerSupport.prototype._postMessage = function () { - if ( ! this.loading && Validator.isValid( this.queuedMessage ) ) { - this.worker.postMessage( this.queuedMessage ); - } - }; - return WorkerSupport; + })(); /** @@ -1272,7 +1308,7 @@ THREE.LoaderSupport.WorkerSupport = (function () { * prepareWorkers * enqueueForRun * processQueue - * deregister + * tearDown (to force stop) * * @class * @@ -1281,7 +1317,7 @@ THREE.LoaderSupport.WorkerSupport = (function () { */ THREE.LoaderSupport.WorkerDirector = (function () { - var LOADER_WORKER_DIRECTOR_VERSION = '2.0.0'; + var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0'; var Validator = THREE.LoaderSupport.Validator; @@ -1301,10 +1337,13 @@ THREE.LoaderSupport.WorkerDirector = (function () { this.workerDescription = { classDef: classDef, globalCallbacks: {}, - workerSupports: [] + workerSupports: {} }; this.objectsCompleted = 0; this.instructionQueue = []; + this.instructionQueuePointer = 0; + + this.callbackOnFinishedProcessing = null; } /** @@ -1349,30 +1388,21 @@ THREE.LoaderSupport.WorkerDirector = (function () { if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks; this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE ); this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER ); + this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize ); this.objectsCompleted = 0; this.instructionQueue = []; + this.instructionQueuePointer = 0; - var start = this.workerDescription.workerSupports.length; - var i; - if ( start < this.maxWebWorkers ) { - - for ( i = start; i < this.maxWebWorkers; i++ ) { - - this.workerDescription.workerSupports[ i ] = { - workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ), - loader: null - }; - - } - - } else { + for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) { - for ( i = start - 1; i >= this.maxWebWorkers; i-- ) { + this.workerDescription.workerSupports[ instanceNo ] = { + instanceNo: instanceNo, + inUse: false, + terminateRequested: false, + workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ), + loader: null + }; - this.workerDescription.workerSupports[ i ].workerSupport.setRequestTerminate( true ); - this.workerDescription.workerSupports.pop(); - - } } }; @@ -1388,47 +1418,68 @@ THREE.LoaderSupport.WorkerDirector = (function () { } }; + /** + * Returns if any workers are running. + * + * @memberOf THREE.LoaderSupport.WorkerDirector + * @returns {boolean} + */ + WorkerDirector.prototype.isRunning = function () { + var wsKeys = Object.keys( this.workerDescription.workerSupports ); + return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 ); + }; + /** * Process the instructionQueue until it is depleted. * @memberOf THREE.LoaderSupport.WorkerDirector */ WorkerDirector.prototype.processQueue = function () { - if ( this.instructionQueue.length === 0 ) return; + var prepData, supportDesc; + for ( var instanceNo in this.workerDescription.workerSupports ) { - var length = Math.min( this.maxWebWorkers, this.instructionQueue.length ); - for ( var i = 0; i < length; i++ ) { + supportDesc = this.workerDescription.workerSupports[ instanceNo ]; + if ( ! supportDesc.inUse ) { - this._kickWorkerRun( this.instructionQueue[ 0 ], i ); - this.instructionQueue.shift(); + if ( this.instructionQueuePointer < this.instructionQueue.length ) { - } - }; + prepData = this.instructionQueue[ this.instructionQueuePointer ]; + this._kickWorkerRun( prepData, supportDesc ); + this.instructionQueuePointer++; - WorkerDirector.prototype._kickWorkerRun = function( prepData, workerInstanceNo ) { - var scope = this; - var directorOnLoad = function ( event ) { - scope.objectsCompleted++; + } else { - var nextPrepData = scope.instructionQueue[ 0 ]; - if ( Validator.isValid( nextPrepData ) ) { + this._deregister( supportDesc ); - scope.instructionQueue.shift(); - scope.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + scope.instructionQueue.length + ')\n\n' ); - scope._kickWorkerRun( nextPrepData, event.detail.instanceNo ); + } - } else if ( scope.instructionQueue.length === 0 ) { + } - scope.deregister(); + } - } - }; + if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) { + + this.callbackOnFinishedProcessing(); + this.callbackOnFinishedProcessing = null; + } + }; + + WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) { + supportDesc.inUse = true; + supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested ); + + this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' ); + + var scope = this; var prepDataCallbacks = prepData.getCallbacks(); var globalCallbacks = this.workerDescription.globalCallbacks; var wrapperOnLoad = function ( event ) { if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event ); if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event ); - directorOnLoad( event ); + scope.objectsCompleted++; + supportDesc.inUse = false; + + scope.processQueue(); }; var wrapperOnProgress = function ( event ) { @@ -1441,8 +1492,7 @@ THREE.LoaderSupport.WorkerDirector = (function () { if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event ); }; - var supportTuple = this.workerDescription.workerSupports[ workerInstanceNo ]; - supportTuple.loader = this._buildLoader( workerInstanceNo ); + supportDesc.loader = this._buildLoader( supportDesc.instanceNo ); var updatedCallbacks = new THREE.LoaderSupport.Callbacks(); updatedCallbacks.setCallbackOnLoad( wrapperOnLoad ); @@ -1450,13 +1500,13 @@ THREE.LoaderSupport.WorkerDirector = (function () { updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter ); prepData.callbacks = updatedCallbacks; - supportTuple.loader.run( prepData, supportTuple.workerSupport ); + supportDesc.loader.run( prepData, supportDesc.workerSupport ); }; WorkerDirector.prototype._buildLoader = function ( instanceNo ) { var classDef = this.workerDescription.classDef; var loader = Object.create( classDef.prototype ); - this.workerDescription.classDef.call( loader, null, this.logger ); + this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger ); // verify that all required functions are implemented if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".'; @@ -1466,36 +1516,47 @@ THREE.LoaderSupport.WorkerDirector = (function () { throw classDef.name + ' has no property "workerSupport".'; - } else if ( ! classDef.workerSupport instanceof THREE.LoaderSupport.WorkerSupport ) { - - throw classDef.name + '.workerSupport is not of type "THREE.LoaderSupport.WorkerSupport".'; - } if ( typeof loader.run !== 'function' ) throw classDef.name + ' has no function "run".'; + if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) { + + this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' ); + loader.callbacks = new THREE.LoaderSupport.Callbacks(); + } return loader; }; + WorkerDirector.prototype._deregister = function ( supportDesc ) { + if ( Validator.isValid( supportDesc ) ) { + + supportDesc.workerSupport.setTerminateRequested( true ); + this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' ); + + var loaderCallbacks = supportDesc.loader.callbacks; + if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } ); + delete this.workerDescription.workerSupports[ supportDesc.instanceNo ]; + + } + }; + /** * Terminate all workers. * @memberOf THREE.LoaderSupport.WorkerDirector + * + * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing. */ - WorkerDirector.prototype.deregister = function () { + WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) { this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' ); - for ( var i = 0, length = this.workerDescription.workerSupports.length; i < length; i++ ) { + this.instructionQueuePointer = this.instructionQueue.length; + this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null ); - var supportTuple = this.workerDescription.workerSupports[ i ]; - supportTuple.workerSupport.setTerminateRequested( true ); - this.logger.logInfo( 'Requested termination of worker.' ); + for ( var name in this.workerDescription.workerSupports ) { - var loaderCallbacks = supportTuple.loader.callbacks; - if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } ); + this.workerDescription.workerSupports[ name ].terminateRequested = true; } - - this.workerDescription.workerSupports = []; - this.instructionQueue = []; }; return WorkerDirector; diff --git a/examples/js/loaders/OBJLoader2.js b/examples/js/loaders/OBJLoader2.js index 322e3e1ca7dd223293dcdd5127b996721e8523a2..f3b71ef569ade8d5782b5b2335cfe928b4db5141 100644 --- a/examples/js/loaders/OBJLoader2.js +++ b/examples/js/loaders/OBJLoader2.js @@ -7,6 +7,8 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} } +if ( THREE.LoaderSupport === undefined ) console.error( '"THREE.LoaderSupport" is not available. "THREE.OBJLoader2" requires it. Please include "LoaderSupport.js" in your HTML.' ); + /** * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer * @class @@ -16,9 +18,10 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} } */ THREE.OBJLoader2 = (function () { - var OBJLOADER2_VERSION = '2.1.2'; + var OBJLOADER2_VERSION = '2.2.0'; var LoaderBase = THREE.LoaderSupport.LoaderBase; var Validator = THREE.LoaderSupport.Validator; + var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; OBJLoader2.prototype = Object.create( THREE.LoaderSupport.LoaderBase.prototype ); OBJLoader2.prototype.constructor = OBJLoader2; @@ -126,10 +129,6 @@ THREE.OBJLoader2 = (function () { this.workerSupport = workerSupportExternal; this.logger = workerSupportExternal.logger; - } else { - - this.terminateWorkerOnLoad = true; - } var scope = this; var onMaterialsLoaded = function ( materials ) { @@ -176,7 +175,8 @@ THREE.OBJLoader2 = (function () { OBJLoader2.prototype.parse = function ( content ) { this.logger.logTimeStart( 'OBJLoader2 parse: ' + this.modelName ); - var parser = new Parser( this.logger ); + var parser = new Parser(); + parser.setLogConfig( this.logger.enabled, this.logger.debug ); parser.setMaterialPerSmoothingGroup( this.materialPerSmoothingGroup ); parser.setUseIndices( this.useIndices ); parser.setDisregardNormals( this.disregardNormals ); @@ -239,7 +239,6 @@ THREE.OBJLoader2 = (function () { } } ); - if ( scope.terminateWorkerOnLoad ) scope.workerSupport.terminateWorker(); scope.logger.logTimeEnd( 'OBJLoader2 parseAsync: ' + scope.modelName ); }; var scopedOnMeshLoaded = function ( payload ) { @@ -257,6 +256,8 @@ THREE.OBJLoader2 = (function () { workerCode += '/**\n'; workerCode += ' * This code was constructed by OBJLoader2 buildCode.\n'; workerCode += ' */\n\n'; + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'ConsoleLogger', 'ConsoleLogger', ConsoleLogger ); workerCode += funcBuildSingelton( 'LoaderBase', 'LoaderBase', LoaderBase ); workerCode += funcBuildObject( 'Consts', Consts ); workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); @@ -267,6 +268,7 @@ THREE.OBJLoader2 = (function () { }; this.workerSupport.validate( buildCode, false ); this.workerSupport.setCallbacks( scopedOnMeshLoaded, scopedOnLoad ); + if ( scope.terminateWorkerOnLoad ) this.workerSupport.setTerminateRequested( true ); var materialNames = {}; var materials = this.builder.getMaterials(); @@ -330,7 +332,9 @@ THREE.OBJLoader2 = (function () { */ var Parser = (function () { - function Parser( logger ) { + var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; + + function Parser() { this.callbackProgress = null; this.callbackBuilder = null; @@ -348,7 +352,7 @@ THREE.OBJLoader2 = (function () { faces: 0, doubleIndicesCount: 0 }; - this.logger = logger; + this.logger = new ConsoleLogger(); this.totalBytes = 0; this.reachedFaces = false; }; @@ -383,6 +387,11 @@ THREE.OBJLoader2 = (function () { this.callbackProgress = callbackProgress; }; + Parser.prototype.setLogConfig = function ( enabled, debug ) { + this.logger.setEnabled( enabled ); + this.logger.setDebug( debug ); + }; + Parser.prototype.configure = function () { this.rawMesh = new RawMesh( this.materialPerSmoothingGroup, this.useIndices, this.disregardNormals ); @@ -1359,6 +1368,7 @@ THREE.OBJLoader2 = (function () { * @param {string} [crossOrigin] CORS value */ OBJLoader2.prototype._loadMtl = function ( resource, callbackOnLoad, crossOrigin ) { + if ( THREE.MTLLoader === undefined ) console.error( '"THREE.MTLLoader" is not available. "THREE.OBJLoader2" requires it for loading MTL files.' ); if ( Validator.isValid( resource ) ) this.logger.logTimeStart( 'Loading MTL: ' + resource.name ); var materials = []; diff --git a/examples/webgl_loader_obj2_meshspray.html b/examples/webgl_loader_obj2_meshspray.html index 83bc557e81dfd6970868fc346d6a58a8018294e6..9aabc86e00e9fa81b4f9c1a0d3bccb16259209c1 100644 --- a/examples/webgl_loader_obj2_meshspray.html +++ b/examples/webgl_loader_obj2_meshspray.html @@ -142,12 +142,14 @@ workerCode += '/**\n'; workerCode += ' * This code was constructed by MeshSpray buildCode.\n'; workerCode += ' */\n\n'; + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'ConsoleLogger', 'ConsoleLogger', ConsoleLogger ); workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); return workerCode; }; var libs2Load = [ 'build/three.min.js' ]; - this.workerSupport.validate( buildCode, false, libs2Load, '../' ); + this.workerSupport.validate( buildCode, libs2Load, '../' ); this.workerSupport.setCallbacks( scopeBuilderFunc, scopeFuncComplete ); this.workerSupport.run( { @@ -173,7 +175,9 @@ var Parser = ( function () { - function Parser( logger ) { + var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; + + function Parser() { this.sizeFactor = 0.5; this.localOffsetFactor = 1.0; this.globalObjectCount = 0; @@ -181,10 +185,16 @@ this.dimension = 200; this.quantity = 1; this.callbackBuilder = null; - this.logger = logger; + this.callbackProgress = null; + this.logger = new ConsoleLogger(); this.serializedMaterials = null; }; + Parser.prototype.setLogConfig = function ( enabled, debug ) { + this.logger.setEnabled( enabled ); + this.logger.setDebug( debug ); + }; + Parser.prototype.parse = function () { var baseTriangle = [ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, -1.0, 1.0 ]; var vertices = []; @@ -389,12 +399,11 @@ var maxWebWorkers = 4; var radius = 640; var logger = new THREE.LoaderSupport.ConsoleLogger( false ); - this.workerDirector = new THREE.LoaderSupport.WorkerDirector( MeshSpray, logger ); - this.workerDirector.setCrossOrigin( 'anonymous' ); + var workerDirector = new THREE.LoaderSupport.WorkerDirector( MeshSpray, logger ); + workerDirector.setCrossOrigin( 'anonymous' ); - var scope = this; var callbackOnLoad = function ( event ) { - logger.logInfo( 'Worker #' + event.detail.instanceNo + ': Completed loading. (#' + scope.workerDirector.objectsCompleted + ')' ); + logger.logInfo( 'Worker #' + event.detail.instanceNo + ': Completed loading. (#' + workerDirector.objectsCompleted + ')' ); }; var reportProgress = function( event ) { document.getElementById( 'feedback' ).innerHTML = event.detail.text; @@ -416,7 +425,7 @@ callbacks.setCallbackOnMeshAlter( callbackMeshAlter ); callbacks.setCallbackOnLoad( callbackOnLoad ); callbacks.setCallbackOnProgress( reportProgress ); - this.workerDirector.prepareWorkers( callbacks, maxQueueSize, maxWebWorkers ); + workerDirector.prepareWorkers( callbacks, maxQueueSize, maxWebWorkers ); var prepData; var pivot; @@ -440,9 +449,9 @@ prepData.dimension = Math.max( Math.random() * 500, 100 ); prepData.globalObjectCount = globalObjectCount++; - this.workerDirector.enqueueForRun( prepData ); + workerDirector.enqueueForRun( prepData ); } - this.workerDirector.processQueue(); + workerDirector.processQueue(); }; MeshSprayApp.prototype.resizeDisplayGL = function () { diff --git a/examples/webgl_loader_obj2_options.html b/examples/webgl_loader_obj2_options.html index 7d801ebbf44f1243791d288cdea28e7ed3fa7949..c57df9814202c5863b578868c70b782636ad1f49 100644 --- a/examples/webgl_loader_obj2_options.html +++ b/examples/webgl_loader_obj2_options.html @@ -1,7 +1,7 @@ - three.js webgl - WWOBJLoader2 + three.js webgl - OBJLoader2 usage options @@ -243,6 +243,8 @@ var scope = this; var objLoader = new THREE.OBJLoader2(); var callbackOnLoad = function ( event ) { + objLoader.workerSupport.setTerminateRequested( true ); + var local = new THREE.Object3D(); local.name = 'Pivot_WaltHead'; local.position.set( -125, 50, 0 ); @@ -257,6 +259,7 @@ var onLoadMtl = function ( materials ) { objLoader.setModelName( modelName ); objLoader.setMaterials( materials ); + objLoader.terminateWorkerOnLoad = false; objLoader.load( 'obj/walt/WaltHead.obj', callbackOnLoad, null, null, null, true ); }; objLoader.loadMtl( 'obj/walt//WaltHead.mtl', 'WaltHead.mtl', null, onLoadMtl ); diff --git a/examples/webgl_loader_obj2_run_director.html b/examples/webgl_loader_obj2_run_director.html index c046b0bae14d5af74d4fddf2cdd9df380dcca422..86a4a2465578ddff88b9383a79a7a755a7d4bd1d 100644 --- a/examples/webgl_loader_obj2_run_director.html +++ b/examples/webgl_loader_obj2_run_director.html @@ -113,7 +113,7 @@ this.logger = new THREE.LoaderSupport.ConsoleLogger(); this.logger.setEnabled( false ); - this.workerDirector = new THREE.LoaderSupport.WorkerDirector( THREE.OBJLoader2, this.logger ); + this.workerDirector = new THREE.LoaderSupport.WorkerDirector( THREE.OBJLoader2, this.logger ); this.workerDirector.setCrossOrigin( 'anonymous' ); this.controls = null; @@ -380,7 +380,24 @@ }; WWParallels.prototype.terminateManager = function () { - this.workerDirector.deregister(); + this.workerDirector.tearDown(); + this.running = false; + }; + + WWParallels.prototype.terminateManagerAndClearScene = function () { + var scope = this; + var scopedClearAllAssests = function (){ + scope.clearAllAssests(); + }; + if ( this.workerDirector.isRunning() ) { + + this.workerDirector.tearDown( scopedClearAllAssests ); + + } else { + + scopedClearAllAssests(); + } + this.running = false; }; @@ -402,8 +419,7 @@ app.terminateManager(); }, clearAllAssests: function () { - app.terminateManager(); - app.clearAllAssests(); + app.terminateManagerAndClearScene(); } }; var gui = new dat.GUI( {