diff --git a/examples/jsm/loaders/OBJLoader2Parallel.js b/examples/jsm/loaders/OBJLoader2Parallel.js index 0fbf54f99dc0dd..0517398fc7e3e7 100644 --- a/examples/jsm/loaders/OBJLoader2Parallel.js +++ b/examples/jsm/loaders/OBJLoader2Parallel.js @@ -71,15 +71,10 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp setJsmWorker: function ( preferJsmWorker, jsmWorkerUrl ) { this.preferJsmWorker = preferJsmWorker === true; - if ( jsmWorkerUrl === undefined || jsmWorkerUrl === null ) { - - throw "The url to the jsm worker is not valid. Aborting..."; - + throw "The url to the jsm worker is not valid. Aborting..." } - this.jsmWorkerUrl = jsmWorkerUrl; - return this; }, @@ -101,19 +96,18 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp buildWorkerCode: function () { let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker ); - if ( codeBuilderInstructions.isSupportsJsmWorker() ) { codeBuilderInstructions.setJsmWorkerUrl( this.jsmWorkerUrl ); } - if ( codeBuilderInstructions.isSupportsStandardWorker() ) { let objectManipulator = new ObjectManipulator(); let defaultWorkerPayloadHandler = new DefaultWorkerPayloadHandler( this.parser ); let workerRunner = new WorkerRunner( {} ); - codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( OBJLoader2Parser, this.parser ) ); + + codeBuilderInstructions.addCodeFragment( 'const OBJLoader2Parser = ' + OBJLoader2Parser.toString() + ';\n\n' ); codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( ObjectManipulator, objectManipulator ) ); codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( DefaultWorkerPayloadHandler, defaultWorkerPayloadHandler ) ); codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( WorkerRunner, workerRunner ) ); @@ -122,7 +116,6 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp codeBuilderInstructions.addStartCode( startCode ); } - return codeBuilderInstructions; }, @@ -169,7 +162,6 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp 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 ) ) { @@ -181,7 +173,6 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp scope._onAssetAvailable( payload ); }; - function scopedOnLoad( message ) { scope.parser.callbacks.onLoad( scope.baseObject3d, message ); diff --git a/examples/jsm/loaders/TaskManager.d.ts b/examples/jsm/loaders/TaskManager.d.ts index 228030ef316c80..788e4354e3d2f1 100644 --- a/examples/jsm/loaders/TaskManager.d.ts +++ b/examples/jsm/loaders/TaskManager.d.ts @@ -9,10 +9,10 @@ export class TaskManager { setMaxParallelExecutions(maxParallelExecutions: number): TaskManager; getMaxParallelExecutions(): number; supportsTaskType(taskType: string): boolean; - registerTaskType(taskType: string, initFunction: Function, executeFunction: Function, comRoutingFunction: Function, fallback: boolean, dependencyUrls?: string[]): TaskManager; + registerTaskType(taskType: string, initFunction: Function, executeFunction: Function, comRoutingFunction: Function, fallback: boolean, dependencyDescriptions?: any[]): TaskManager; registerTaskTypeModule(taskType: string, workerModuleUrl: string): TaskManager; - initTaskType(taskType: string, config: object, transferables?: Transferable[]): Promise; - enqueueForExecution(taskType: string, config: object, transferables?: Transferable[]): Promise; + initTaskType(taskType: string, config: object, transferables?: any): Promise; + enqueueForExecution(taskType: string, config: object, assetAvailableFunction: Function, transferables?: any): Promise; _kickExecutions(): void; dispose(): TaskManager; } @@ -35,7 +35,7 @@ declare class WorkerTypeDefinition { code: string; }; dependencies: { - urls: URL[]; + descriptions: any[]; code: string[]; }; workerModuleUrl: URL; @@ -47,23 +47,24 @@ declare class WorkerTypeDefinition { }; getTaskType(): string; setFunctions(initFunction: Function, executeFunction: Function, comRoutingFunction?: Function): void; - setDependencyUrls(dependencyUrls: string[]): void; + setDependencyDescriptions(dependencyDescriptions: any[]): void; setWorkerModule(workerModuleUrl: string): void; isWorkerModule(): boolean; loadDependencies(): Promise; generateWorkerCode(dependencies: ArrayBuffer[]): Promise; createWorkers(code: string): Promise; createWorkerModules(): Promise; - initWorkers(instances: TaskWorker[] | MockedTaskWorker[], config: object, transferables: Transferable[]): Promise; + initWorkers(instances: TaskWorker[] | MockedTaskWorker[], config: object, transferables: any): Promise; getAvailableTask(): TaskWorker | MockedTaskWorker | undefined; hasTask(): boolean; returnAvailableTask(taskWorker: TaskWorker | MockedTaskWorker): void; dispose(): void; } declare class StoredExecution { - constructor(taskType: string, config: object, resolve: Function, reject: Function, transferables?: Transferable[]); + constructor(taskType: string, config: object, assetAvailableFunction: Function, resolve: Function, reject: Function, transferables?: Transferable[]); taskType: string; config: any; + assetAvailableFunction: Function; resolve: Function; reject: Function; transferables: Transferable[]; @@ -82,5 +83,6 @@ declare class MockedTaskWorker { }; getId(): number; postMessage(message: string, transfer?: Transferable[]): void; + terminate(): void; } export {}; diff --git a/examples/jsm/loaders/TaskManager.js b/examples/jsm/loaders/TaskManager.js index f626ed173dbeee..ac2040991604b3 100644 --- a/examples/jsm/loaders/TaskManager.js +++ b/examples/jsm/loaders/TaskManager.js @@ -88,14 +88,14 @@ class TaskManager { * @param {function} executeFunction The function to be called when the worker is executed * @param {function} comRoutingFunction The function that should handle communication, leave undefined for default behavior * @param {boolean} fallback Set to true if execution should be performed in main - * @param {String[]} [dependencyUrls] + * @param {Object[]} [dependencyDescriptions] * @return {TaskManager} */ - registerTaskType ( taskType, initFunction, executeFunction, comRoutingFunction, fallback, dependencyUrls ) { + registerTaskType ( taskType, initFunction, executeFunction, comRoutingFunction, fallback, dependencyDescriptions ) { let workerTypeDefinition = new WorkerTypeDefinition( taskType, this.maxParallelExecutions, fallback, this.verbose ); workerTypeDefinition.setFunctions( initFunction, executeFunction, comRoutingFunction ); - workerTypeDefinition.setDependencyUrls( dependencyUrls ); + workerTypeDefinition.setDependencyDescriptions( dependencyDescriptions ); this.taskTypes.set( taskType, workerTypeDefinition ); return this; @@ -122,7 +122,7 @@ class TaskManager { * * @param {string} taskType The name of the registered task type. * @param {object} config Configuration properties as serializable string. - * @param {Transferable[]} [transferables] Any optional {@link ArrayBuffer}. + * @param {Object} [transferables] Any optional {@link ArrayBuffer} encapsulated in object.. */ async initTaskType ( taskType, config, transferables ) { @@ -149,14 +149,15 @@ class TaskManager { * * @param {string} taskType The name of the registered task type. * @param {object} config Configuration properties as serializable string. - * @param {Transferable[]} [transferables] Any optional {@link ArrayBuffer}. + * @param {Function} assetAvailableFunction Invoke this function if an asset become intermediately available + * @param {Object} [transferables] Any optional {@link ArrayBuffer} encapsulated in object. * @return {Promise} */ - async enqueueForExecution ( taskType, config, transferables ) { + async enqueueForExecution ( taskType, config, assetAvailableFunction, transferables ) { let localPromise = new Promise( ( resolveUser, rejectUser ) => { - this.storedExecutions.push( new StoredExecution( taskType, config, resolveUser, rejectUser, transferables ) ); + this.storedExecutions.push( new StoredExecution( taskType, config, assetAvailableFunction, resolveUser, rejectUser, transferables ) ); } ); this._kickExecutions(); @@ -178,12 +179,30 @@ class TaskManager { this.actualExecutionCount++; let promiseWorker = new Promise( ( resolveWorker, rejectWorker ) => { - taskWorker.onmessage = resolveWorker; + taskWorker.onmessage = function ( e ) { + + // allow intermediate asset provision before flagging execComplete + if ( e.data.cmd === 'assetAvailable' ) { + + if ( storedExecution.assetAvailableFunction instanceof Function ) { + + storedExecution.assetAvailableFunction( e.data ); + + } + + } + else { + + resolveWorker( e ); + + } + + }; taskWorker.onerror = rejectWorker; taskWorker.postMessage( { cmd: "execute", - id: taskWorker.getId(), + workerId: taskWorker.getId(), config: storedExecution.config }, storedExecution.transferables ); @@ -268,8 +287,8 @@ class WorkerTypeDefinition { code: null }, dependencies: { - /** @type {URL[]} */ - urls: [], + /** @type {Object[]} */ + descriptions: [], /** @type {string[]} */ code: [] }, @@ -334,13 +353,13 @@ class WorkerTypeDefinition { /** * Set the url of all dependent libraries (only used in non-module case). * - * @param {String[]} dependencyUrls URLs of code init and execute functions rely on. + * @param {Object[]} dependencyDescriptions URLs of code init and execute functions rely on. */ - setDependencyUrls ( dependencyUrls ) { + setDependencyDescriptions ( dependencyDescriptions ) { - if ( dependencyUrls ) { + if ( dependencyDescriptions ) { - dependencyUrls.forEach( url => { this.functions.dependencies.urls.push( new URL( url, window.location.href ) ) } ); + dependencyDescriptions.forEach( description => { this.functions.dependencies.descriptions.push( description ) } ); } @@ -377,9 +396,20 @@ class WorkerTypeDefinition { let fileLoader = new FileLoader(); fileLoader.setResponseType( 'arraybuffer' ); - for ( let url of this.functions.dependencies.urls ) { + for ( let description of this.functions.dependencies.descriptions ) { - let dep = await fileLoader.loadAsync( url.href, report => { if ( this.verbose ) console.log( report ); } ) + let dep; + if ( description.url ) { + + let url = new URL( description.url, window.location.href ); + dep = await fileLoader.loadAsync( url.href, report => { if ( this.verbose ) console.log( report ); } ) + + } + if ( description.code ) { + + dep = description.code; + + } this.functions.dependencies.code.push( dep ); } @@ -469,7 +499,7 @@ class WorkerTypeDefinition { * * @param {TaskWorker[]|MockedTaskWorker[]} instances * @param {object} config - * @param {Transferable[]} transferables + * @param {Object} transferables * @return {Promise} */ async initWorkers ( instances, config, transferables ) { @@ -481,11 +511,20 @@ class WorkerTypeDefinition { taskWorker.onmessage = resolveWorker; taskWorker.onerror = rejectWorker; + // ensure all transferables are copies to all workers on int! + let transferablesToWorker; + if ( transferables ) { + transferablesToWorker = {}; + for ( let [ key, transferable ] of Object.entries( transferables ) ) { + transferablesToWorker[ key ] = transferable !== null ? transferable.slice( 0 ) : null; + } + } + taskWorker.postMessage( { cmd: "init", - id: taskWorker.getId(), + workerId: taskWorker.getId(), config: config - }, transferables ); + }, transferablesToWorker ); } ); this.workers.available.push( taskWorker ); @@ -553,14 +592,16 @@ class StoredExecution { * * @param {string} taskType * @param {object} config + * @param {Function} assetAvailableFunction * @param {Function} resolve * @param {Function} reject * @param {Transferable[]} [transferables] */ - constructor( taskType, config, resolve, reject, transferables ) { + constructor( taskType, config, assetAvailableFunction, resolve, reject, transferables ) { this.taskType = taskType; this.config = config; + this.assetAvailableFunction = assetAvailableFunction; this.resolve = resolve; this.reject = reject; this.transferables = transferables; diff --git a/examples/jsm/loaders/obj2/OBJLoader2Parser.js b/examples/jsm/loaders/obj2/OBJLoader2Parser.js index 245b3bfc48cce5..a1930671ee4fb4 100644 --- a/examples/jsm/loaders/obj2/OBJLoader2Parser.js +++ b/examples/jsm/loaders/obj2/OBJLoader2Parser.js @@ -15,26 +15,51 @@ const OBJLoader2Parser = function () { let scope = this; this.callbacks = { + + /** + * Announce parse progress feedback which is logged to the console. + * @private + * + * @param {string} text Textual description of the event + */ onProgress: function ( text ) { - scope._onProgress( text ); + let message = text ? text : ''; + if ( scope.logging.enabled && scope.logging.debug ) { - }, - onAssetAvailable: function ( payload ) { + console.log( message ); - scope._onAssetAvailable( payload ); + } }, + + /** + * Announce error feedback which is logged as error message. + * @private + * + * @param {String} errorMessage The event containing the error + */ onError: function ( errorMessage ) { - scope._onError( errorMessage ); + if ( scope.logging.enabled && scope.logging.debug ) { + + console.error( errorMessage ); + + } }, - onLoad: function ( object3d, message ) { + onAssetAvailable: function ( payload ) { - scope._onLoad( object3d, message ); + let errorMessage = 'OBJLoader2Parser does not provide implementation for onAssetAvailable. Aborting...'; + scope.callbacks.onError( errorMessage ); + throw errorMessage; }, + onLoad: function ( object3d, message ) { + + console.log( "You reached parser default onLoad callback: " + message ); + + } }; this.contentRef = null; this.legacyMode = false; @@ -49,6 +74,7 @@ const OBJLoader2Parser = function () { this.colors = []; this.normals = []; this.uvs = []; + this.objectId = 0; this.rawMesh = { objectName: '', @@ -84,13 +110,7 @@ const OBJLoader2Parser = function () { totalBytes: 0 }; -}; - -OBJLoader2Parser.prototype = { - - constructor: OBJLoader2Parser, - - _resetRawMesh: function () { + this._resetRawMesh = function () { // faces are stored according combined index of group, material and smoothingGroup (0 or not) this.rawMesh.subGroups = []; @@ -106,7 +126,7 @@ OBJLoader2Parser.prototype = { this.rawMesh.counts.mtlCount = 0; this.rawMesh.counts.smoothingGroupCount = 0; - }, + } /** * Tells whether a material shall be created per smoothing group. @@ -114,12 +134,12 @@ OBJLoader2Parser.prototype = { * @param {boolean} materialPerSmoothingGroup=false * @return {OBJLoader2Parser} */ - setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) { + this.setMaterialPerSmoothingGroup = function ( materialPerSmoothingGroup ) { this.materialPerSmoothingGroup = materialPerSmoothingGroup === true; return this; - }, + } /** * Usually 'o' is meta-information and does not result in creation of new meshes, but mesh creation on occurrence of "o" can be enforced. @@ -127,12 +147,12 @@ OBJLoader2Parser.prototype = { * @param {boolean} useOAsMesh=false * @return {OBJLoader2Parser} */ - setUseOAsMesh: function ( useOAsMesh ) { + this.setUseOAsMesh = function ( useOAsMesh ) { this.useOAsMesh = useOAsMesh === true; return this; - }, + } /** * Instructs loaders to create indexed {@link BufferGeometry}. @@ -140,12 +160,12 @@ OBJLoader2Parser.prototype = { * @param {boolean} useIndices=false * @return {OBJLoader2Parser} */ - setUseIndices: function ( useIndices ) { + this.setUseIndices = function ( useIndices ) { this.useIndices = useIndices === true; return this; - }, + } /** * Tells whether normals should be completely disregarded and regenerated. @@ -153,23 +173,23 @@ OBJLoader2Parser.prototype = { * @param {boolean} disregardNormals=false * @return {OBJLoader2Parser} */ - setDisregardNormals: function ( disregardNormals ) { + this.setDisregardNormals = function ( disregardNormals ) { this.disregardNormals = disregardNormals === true; return this; - }, + } /** * Clears materials object and sets the new ones. * * @param {Object} materials Object with named materials */ - setMaterials: function ( materials ) { + this.setMaterials = function ( materials ) { this.materials = Object.assign( {}, materials ); - }, + } /** * Register a function that is called once an asset (mesh/material) becomes available. @@ -177,17 +197,16 @@ OBJLoader2Parser.prototype = { * @param onAssetAvailable * @return {OBJLoader2Parser} */ - setCallbackOnAssetAvailable: function ( onAssetAvailable ) { + this.setCallbackOnAssetAvailable = function ( onAssetAvailable ) { if ( onAssetAvailable !== null && onAssetAvailable !== undefined && onAssetAvailable instanceof Function ) { this.callbacks.onAssetAvailable = onAssetAvailable; } - return this; - }, + } /** * Register a function that is used to report overall processing progress. @@ -195,17 +214,16 @@ OBJLoader2Parser.prototype = { * @param {Function} onProgress * @return {OBJLoader2Parser} */ - setCallbackOnProgress: function ( onProgress ) { + this.setCallbackOnProgress = function ( onProgress ) { if ( onProgress !== null && onProgress !== undefined && onProgress instanceof Function ) { this.callbacks.onProgress = onProgress; } - return this; - }, + } /** * Register an error handler function that is called if errors occur. It can decide to just log or to throw an exception. @@ -213,17 +231,16 @@ OBJLoader2Parser.prototype = { * @param {Function} onError * @return {OBJLoader2Parser} */ - setCallbackOnError: function ( onError ) { + this.setCallbackOnError = function ( onError ) { if ( onError !== null && onError !== undefined && onError instanceof Function ) { this.callbacks.onError = onError; } - return this; - }, + } /** * Register a function that is called when parsing was completed. @@ -231,64 +248,16 @@ OBJLoader2Parser.prototype = { * @param {Function} onLoad * @return {OBJLoader2Parser} */ - setCallbackOnLoad: function ( onLoad ) { + this.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. - * @private - * - * @param {string} text Textual description of the event - */ - _onProgress: function ( text ) { - - let message = text ? text : ''; - if ( this.logging.enabled && this.logging.debug ) { - - console.log( message ); - - } - - }, - - /** - * Announce error feedback which is logged as error message. - * @private - * - * @param {String} errorMessage The event containing the error - */ - _onError: function ( errorMessage ) { - - if ( this.logging.enabled && this.logging.debug ) { - - console.error( errorMessage ); - - } - - }, - - _onAssetAvailable: function ( payload ) { - - let errorMessage = 'OBJLoader2Parser does not provide implementation for onAssetAvailable. Aborting...'; - this.callbacks.onError( errorMessage ); - throw errorMessage; - - }, - - _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. @@ -298,15 +267,15 @@ OBJLoader2Parser.prototype = { * * @return {OBJLoader2Parser} */ - setLogging: function ( enabled, debug ) { + this.setLogging = function ( enabled, debug ) { this.logging.enabled = enabled === true; this.logging.debug = debug === true; return this; - }, + } - _configure: function () { + this._configure = function () { this._pushSmoothingGroup( 1 ); if ( this.logging.enabled ) { @@ -326,14 +295,14 @@ OBJLoader2Parser.prototype = { } - }, + } /** * Parse the provided arraybuffer * * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array */ - execute: function ( arrayBuffer ) { + this.execute = function ( arrayBuffer ) { if ( this.logging.enabled ) console.time( 'OBJLoader2Parser.execute' ); this._configure(); @@ -389,14 +358,14 @@ OBJLoader2Parser.prototype = { this._finalizeParsing(); if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2Parser.execute' ); - }, + } /** * Parse the provided text * * @param {string} text OBJ data as string */ - executeLegacy: function ( text ) { + this.executeLegacy = function ( text ) { if ( this.logging.enabled ) console.time( 'OBJLoader2Parser.executeLegacy' ); this._configure(); @@ -447,9 +416,9 @@ OBJLoader2Parser.prototype = { this._finalizeParsing(); if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2Parser.executeLegacy' ); - }, + } - _processLine: function ( buffer, bufferPointer, slashesCount, word, currentByte ) { + this._processLine = function ( buffer, bufferPointer, slashesCount, word, currentByte ) { this.globalCounts.lineByte = this.globalCounts.currentByte; this.globalCounts.currentByte = currentByte; @@ -623,9 +592,9 @@ OBJLoader2Parser.prototype = { } - }, + } - _pushSmoothingGroup: function ( smoothingGroup ) { + this._pushSmoothingGroup = function ( smoothingGroup ) { let smoothingGroupInt = parseInt( smoothingGroup ); if ( isNaN( smoothingGroupInt ) ) { @@ -645,7 +614,7 @@ OBJLoader2Parser.prototype = { } - }, + } /** * Expanded faceTypes include all four face types, both line types and the point type @@ -657,7 +626,7 @@ OBJLoader2Parser.prototype = { * faceType = 5: "l vertex ..." * faceType = 6: "p vertex ..." */ - _checkFaceType: function ( faceType ) { + this._checkFaceType = function ( faceType ) { if ( this.rawMesh.faceType !== faceType ) { @@ -667,9 +636,9 @@ OBJLoader2Parser.prototype = { } - }, + } - _checkSubGroup: function () { + this._checkSubGroup = function () { let index = this.rawMesh.activeMtlName + '|' + this.rawMesh.smoothingGroup.normalized; this.rawMesh.subGroupInUse = this.rawMesh.subGroups[ index ]; @@ -694,9 +663,9 @@ OBJLoader2Parser.prototype = { } - }, + } - _buildFace: function ( faceIndexV, faceIndexU, faceIndexN ) { + this._buildFace = function ( faceIndexV, faceIndexU, faceIndexN ) { let subGroupInUse = this.rawMesh.subGroupInUse; let scope = this; @@ -771,9 +740,9 @@ OBJLoader2Parser.prototype = { this.rawMesh.counts.faceCount ++; - }, + } - _createRawMeshReport: function ( inputObjectCount ) { + this._createRawMeshReport = function ( inputObjectCount ) { return 'Input Object number: ' + inputObjectCount + '\n\tObject name: ' + this.rawMesh.objectName + @@ -786,12 +755,12 @@ OBJLoader2Parser.prototype = { '\n\tMaterial count: ' + this.rawMesh.counts.mtlCount + '\n\tReal MeshOutputGroup count: ' + this.rawMesh.subGroups.length; - }, + } /** * Clear any empty subGroup and calculate absolute vertex, normal and uv counts */ - _finalizeRawMesh: function () { + this._finalizeRawMesh = function () { let meshOutputGroupTemp = []; let meshOutputGroup; @@ -850,9 +819,9 @@ OBJLoader2Parser.prototype = { return result; - }, + } - _processCompletedMesh: function () { + this._processCompletedMesh = function () { let result = this._finalizeRawMesh(); let haveMesh = result !== null; @@ -869,15 +838,20 @@ OBJLoader2Parser.prototype = { this._buildMesh( result ); let progressBytesPercent = this.globalCounts.currentByte / this.globalCounts.totalBytes; - this._onProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '' + + this.callbacks.onProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '' + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%' ); this._resetRawMesh(); } + this.callbacks.onAssetAvailable( + { + cmd: 'execComplete', + type: 'void' + } ); return haveMesh; - }, + } /** * SubGroups are transformed to too intermediate format that is forwarded to the MeshReceiver. @@ -885,7 +859,7 @@ OBJLoader2Parser.prototype = { * * @param result */ - _buildMesh: function ( result ) { + this._buildMesh = function ( result ) { let meshOutputGroups = result.subGroups; @@ -1075,6 +1049,7 @@ OBJLoader2Parser.prototype = { { cmd: 'assetAvailable', type: 'mesh', + id: this.objectId, meshName: result.name, progress: { numericalValue: this.globalCounts.currentByte / this.globalCounts.totalBytes @@ -1103,9 +1078,9 @@ OBJLoader2Parser.prototype = { uvFA !== null ? [ uvFA.buffer ] : null ); - }, + } - _finalizeParsing: function () { + this._finalizeParsing = function () { if ( this.logging.enabled ) console.info( 'Global output object count: ' + this.outputObjectCount ); if ( this._processCompletedMesh() && this.logging.enabled ) { diff --git a/examples/jsm/loaders/obj2/utils/TransferableUtils.d.ts b/examples/jsm/loaders/obj2/utils/TransferableUtils.d.ts index 6b00520f43878a..0f38bec6622ffd 100644 --- a/examples/jsm/loaders/obj2/utils/TransferableUtils.d.ts +++ b/examples/jsm/loaders/obj2/utils/TransferableUtils.d.ts @@ -1,14 +1,15 @@ export class TransferableUtils { static walkMesh(rootNode: Object3D, callback: Function): void; - static packageBufferGeometry(bufferGeometry: BufferGeometry, meshName: string, geometryType: number, materialNames?: string[]): MeshMessageStructure; + static packageBufferGeometry(bufferGeometry: BufferGeometry, id: string, meshName: string, geometryType: number, materialNames?: string[]): MeshMessageStructure; } export class MeshMessageStructure { static cloneMessageStructure(input: object | MeshMessageStructure): MeshMessageStructure; static copyTypedArray(arrayIn: ArrayBuffer, arrayOut: ArrayBuffer): void; - constructor(cmd: string, meshName: string); + constructor(cmd: string, id: string, meshName: string); main: { cmd: string; type: string; + id: string; meshName: string; progress: { numericalValue: number; @@ -42,5 +43,8 @@ export class MeshMessageStructure { }; postMessage(postMessageImpl: object): void; } +export class ObjectManipulator { + static applyProperties(objToAlter: any, params: any, forceCreation: boolean): void; +} import { Object3D } from "../../../../../build/three.module.js"; import { BufferGeometry } from "../../../../../build/three.module.js"; diff --git a/examples/jsm/loaders/obj2/utils/TransferableUtils.js b/examples/jsm/loaders/obj2/utils/TransferableUtils.js index aa69000da1fc1c..432f78f7183093 100644 --- a/examples/jsm/loaders/obj2/utils/TransferableUtils.js +++ b/examples/jsm/loaders/obj2/utils/TransferableUtils.js @@ -17,12 +17,14 @@ class MeshMessageStructure { * Creates a new {@link MeshMessageStructure}. * * @param {string} cmd + * @param {string} id * @param {string} meshName */ - constructor( cmd, meshName ) { + constructor( cmd, id, meshName ) { this.main = { cmd: cmd, type: 'mesh', + id: id, meshName: meshName, progress: { numericalValue: 0 @@ -192,7 +194,7 @@ class TransferableUtils { console.info( 'Walking: ' + object3d.name ); if ( object3d.hasOwnProperty( 'geometry' ) && object3d[ 'geometry' ] instanceof BufferGeometry ) { - let payload = TransferableUtils.packageBufferGeometry( object3d[ 'geometry' ], object3d.name, 0,['TBD'] ); + let payload = TransferableUtils.packageBufferGeometry( object3d[ 'geometry' ], rootNode, object3d.name, 0,['TBD'] ); callback( payload.main, payload.transferables ); } @@ -229,12 +231,13 @@ class TransferableUtils { * Package {@link BufferGeometry} into {@link MeshMessageStructure} * * @param {BufferGeometry} bufferGeometry + * @param {string} id * @param {string} meshName * @param {number} geometryType * @param {string[]} [materialNames] * @return {MeshMessageStructure} */ - static packageBufferGeometry( bufferGeometry, meshName, geometryType, materialNames ) { + static packageBufferGeometry( bufferGeometry, id, meshName, geometryType, materialNames ) { let vertexBA = bufferGeometry.getAttribute( 'position' ); let indexBA = bufferGeometry.getIndex(); let colorBA = bufferGeometry.getAttribute( 'color' ); @@ -251,7 +254,7 @@ class TransferableUtils { let skinWeightFA = (skinWeightBA !== null && skinWeightBA !== undefined) ? skinWeightBA.array : null; - let payload = new MeshMessageStructure( 'exec', meshName ); + let payload = new MeshMessageStructure( 'execComplete', id, meshName ); payload.main.params.geometryType = geometryType; payload.main.materials.materialNames = materialNames; if ( vertexFA !== null ) { @@ -301,4 +304,39 @@ class TransferableUtils { } -export { TransferableUtils, MeshMessageStructure } +class ObjectManipulator { + + /** + * Applies values from parameter object via set functions or via direct assignment. + * + * @param {Object} objToAlter The objToAlter instance + * @param {Object} params The parameter object + * @param {boolean} forceCreation Force the creation of a property + */ + static applyProperties ( objToAlter, params, forceCreation ) { + + // fast-fail + if ( objToAlter === undefined || objToAlter === null || params === undefined || params === null ) return; + + let property, funcName, values; + for ( property in params ) { + + funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 ); + values = params[ property ]; + + if ( typeof objToAlter[ funcName ] === 'function' ) { + + objToAlter[ funcName ]( values ); + + } else if ( objToAlter.hasOwnProperty( property ) || forceCreation ) { + + objToAlter[ property ] = values; + + } + + } + + } +} + +export { TransferableUtils, MeshMessageStructure, ObjectManipulator } diff --git a/examples/jsm/taskmanager/tmDefaultComRouting.js b/examples/jsm/taskmanager/tmDefaultComRouting.js index 659e4eb2326e35..1adb3618c425e7 100644 --- a/examples/jsm/taskmanager/tmDefaultComRouting.js +++ b/examples/jsm/taskmanager/tmDefaultComRouting.js @@ -6,12 +6,12 @@ function comRouting ( message, init, execute ) { let payload = message.data; if ( payload.cmd === 'init' ) { - init( self, payload.id, payload.config ); + init( self, payload.workerId, payload.config ); } else if ( payload.cmd === 'execute' ) { - execute( self, payload.id, payload.config ); + execute( self, payload.workerId, payload.config ); } } diff --git a/examples/jsm/taskmanager/tmModuleExample.js b/examples/jsm/taskmanager/tmModuleExample.js index 02c63b03462be1..f311f77c50d64e 100644 --- a/examples/jsm/taskmanager/tmModuleExample.js +++ b/examples/jsm/taskmanager/tmModuleExample.js @@ -12,16 +12,16 @@ import { comRouting } from "./tmDefaultComRouting.js"; function init ( context, id, config ) { - context.storage = { - whoami: id, - }; + context.storage = { + whoami: id, + }; - context.postMessage( { - cmd: "init", - id: id - } ); + context.postMessage( { + cmd: "init", + id: id + } ); - } +} function execute ( context, id, config ) { @@ -34,7 +34,7 @@ function execute ( context, id, config ) { vertexArray[ i ] = vertexArray[ i ] + 10 * ( Math.random() - 0.5 ); } - let payload = TransferableUtils.packageBufferGeometry( bufferGeometry, 'tmProto' + config.count, 2,[ 'defaultPointMaterial' ] ); + let payload = TransferableUtils.packageBufferGeometry( bufferGeometry, config.id, 'tmProto' + config.id, 2,[ 'defaultPointMaterial' ] ); let randArray = new Uint8Array( 3 ); context.crypto.getRandomValues( randArray ); diff --git a/examples/jsm/taskmanager/tmModuleExampleNoThree.js b/examples/jsm/taskmanager/tmModuleExampleNoThree.js index 4a50f5e22297e6..c5baba5bf817e9 100644 --- a/examples/jsm/taskmanager/tmModuleExampleNoThree.js +++ b/examples/jsm/taskmanager/tmModuleExampleNoThree.js @@ -27,7 +27,8 @@ function execute ( context, id, config ) { vertexArray[ i ] = vertexArray[ i ] + 10 * ( Math.random() - 0.5 ); } - payload.main.meshName = 'tmProto' + config.count; + payload.main.meshName = 'tmProto' + config.id; + payload.main.id = config.id; payload.main.params.geometryType = 1; payload.main.materials.materialNames = [ 'defaultLineMaterial' ]; let randArray = new Uint8Array( 3 ); diff --git a/examples/jsm/taskmanager/tmOBJLoader2.js b/examples/jsm/taskmanager/tmOBJLoader2.js new file mode 100644 index 00000000000000..1cd9e2523b227e --- /dev/null +++ b/examples/jsm/taskmanager/tmOBJLoader2.js @@ -0,0 +1,37 @@ +/** + * @author Kai Salmen / www.kaisalmen.de + */ + +import { OBJLoader2Parser } from "../loaders/obj2/OBJLoader2Parser.js"; +import { ObjectManipulator } from "../loaders/obj2/utils/TransferableUtils.js"; +import { comRouting } from "./tmDefaultComRouting.js"; + + +function init ( context, id, config ) { + + self.objParser = new OBJLoader2Parser(); + self.buffer = config.buffer; + context.postMessage( { + cmd: "init", + id: id + } ); + +} + +function execute ( context, id, config ) { +/* + let callbacks = { + callbackOnAssetAvailable: ( m => { self.postMessage( m ); } ), + callbackOnProgress: ( text => { console.debug( 'WorkerRunner: progress: ' + text ) } ) + }; + ObjectManipulator.applyProperties( objParser, payload.config, false ); + ObjectManipulator.applyProperties( objParser, callbacks, false ); +*/ + self.objParser.setCallbackOnAssetAvailable( m => { self.postMessage( m ); } ); + self.objParser.setCallbackOnProgress( text => { console.debug( 'WorkerRunner: progress: ' + text ) } ); + self.objParser.objectId = config.id; + self.objParser.execute( self.buffer ); + +} + +self.addEventListener( 'message', message => comRouting( message, init, execute ), false ); diff --git a/examples/webgl_loader_taskmanager.html b/examples/webgl_loader_taskmanager.html index fda8775a2f69d6..f99025da5537a5 100644 --- a/examples/webgl_loader_taskmanager.html +++ b/examples/webgl_loader_taskmanager.html @@ -34,74 +34,14 @@ import { TrackballControls } from "./jsm/controls/TrackballControls.js"; import { TaskManager } from "./jsm/loaders/TaskManager.js"; import { MeshReceiver } from "./jsm/loaders/obj2/shared/MeshReceiver.js"; + import { OBJLoader2Parser } from "./jsm/loaders/obj2/OBJLoader2Parser.js"; import { MaterialHandler } from "./jsm/loaders/obj2/shared/MaterialHandler.js"; import { TransferableUtils } from "./jsm/loaders/obj2/utils/TransferableUtils.js"; - - const workerInitFunction = function ( context, id, config ) { - context.storage = { - whoami: id, - }; - - context.postMessage( { - cmd: "init", - id: id - } ); - - }; - - const workerExecFunction = function ( context, id, config ) { - - let bufferGeometry = new THREE.SphereBufferGeometry( 40, 64, 64 ); - - let vertexBA = bufferGeometry.getAttribute( 'position' ); - let indexBA = bufferGeometry.getIndex(); - let colorBA = bufferGeometry.getAttribute( 'color' ); - let normalBA = bufferGeometry.getAttribute( 'normal' ); - let uvBA = bufferGeometry.getAttribute( 'uv' ); - - let vertexArray = vertexBA.array; - for ( let i = 0; i < vertexArray.length; i ++ ) { - - vertexArray[ i ] = vertexArray[ i ] * Math.random() * 0.48; - - } - let vertexFA = vertexArray; - let indexUA = (indexBA !== null && indexBA !== undefined) ? indexBA.array : null; - let colorFA = (colorBA !== null && colorBA !== undefined) ? colorBA.array : null; - let normalFA = (normalBA !== null && normalBA !== undefined) ? normalBA.array : null; - let uvFA = (uvBA !== null && uvBA !== undefined) ? uvBA.array : null; - - context.postMessage( { - cmd: 'exec', - type: 'mesh', - meshName: 'tmProto' + config.count, - params: { - geometryType: 0 - }, - buffers: { - vertices: vertexFA, - indices: indexUA, - colors: colorFA, - normals: normalFA, - uvs: uvFA, - }, - materials: { - materialNames: [ 'meshNormalMaterial' ] - } - }, - [vertexFA.buffer], - indexUA !== null ? [indexUA.buffer] : null, - colorFA !== null ? [colorFA.buffer] : null, - normalFA !== null ? [normalFA.buffer] : null, - uvFA !== null ? [uvFA.buffer] : null - ); - - } + import { FileLoader } from "../src/loaders/FileLoader.js"; class TaskManagerPrototypeExample { constructor ( elementToBindTo ) { - this.renderer = null; this.canvas = elementToBindTo; this.aspectRatio = 1; @@ -119,13 +59,15 @@ this.controls = null; this.taskManager = null; + this.materialHandler = null; this.meshReceiver = null; - this.taskNames = new Map (); + this.taskDescriptions = new Map (); this.tasksToUse = []; this.executions = []; + this.objectsUsed = new Map(); + this.meshesAdded = []; this.meshCount = 0; - this.lastDeletedMesh = 0; this.removeCount = 50; this.numberOfMeshesToKeep = 750; this.overallExecutionCount = 1000000; @@ -142,39 +84,75 @@ this.baseVectorX = new THREE.Vector3( 1, 0, 0 ); this.baseVectorY = new THREE.Vector3( 0, 1, 0 ); this.baseVectorZ = new THREE.Vector3( 0, 0, 1 ); - } resetAppContext () { - this.taskManager = new TaskManager(); this.taskManager.setVerbose( true ); - let materialHandler = new MaterialHandler(); - materialHandler.createDefaultMaterials( true ); + this.materialHandler = new MaterialHandler(); + this.materialHandler.createDefaultMaterials( true ); let meshNormalMaterial = new THREE.MeshNormalMaterial(); meshNormalMaterial.name = 'meshNormalMaterial'; - materialHandler.addMaterials( { meshNormalMaterial: meshNormalMaterial }, true ) - this.meshReceiver = new MeshReceiver( materialHandler ); - - this.taskNames.clear(); - this.taskNames.set( 'tmProtoExample', { use: true, fallback: false } ); - this.taskNames.set( 'tmProtoExampleModule', { use: true, fallback: false } ); - this.taskNames.set( 'tmProtoExampleModuleNoThree', { use: true, fallback: false} ); - this.taskNames.set( 'tmProtoExampleMain', { use: false, fallback: true } ); + this.materialHandler.addMaterials( { meshNormalMaterial: meshNormalMaterial }, true ) + this.meshReceiver = new MeshReceiver( this.materialHandler ); + + this.taskDescriptions.clear(); + this.taskDescriptions.set( 'tmProtoExample', { + name: 'tmProtoExample', + use: true, + fallback: false, + funcInit: WorkerFunctions.workerLegacyInit, + funcExec: WorkerFunctions.workerLegacyExec + } ); + this.taskDescriptions.set( 'tmProtoExampleModule', { + name: 'tmProtoExampleModule', + use: true, + fallback: false, + module: './jsm/taskmanager/tmModuleExample.js' + } ); + this.taskDescriptions.set( 'tmProtoExampleModuleNoThree', { + name: 'tmProtoExampleModuleNoThree', + use: true, + fallback: false, + module: './jsm/taskmanager/tmModuleExampleNoThree.js' + } ); + this.taskDescriptions.set( 'tmProtoExampleMain', { + name: 'tmProtoExampleMain', + use: false, + fallback: true, + funcInit: WorkerFunctions.workerLegacyInit, + funcExec: WorkerFunctions.workerLegacyExec + } ); + this.taskDescriptions.set( 'tmOBJLoader2', { + name: 'tmOBJLoader2', + use: true, + fallback: false, + module: './jsm/taskmanager/tmOBJLoader2.js', + filename: './models/obj/female02/female02.obj' + } ); + this.taskDescriptions.set( 'tmOBJLoader2Legacy', { + name: 'tmOBJLoader2Legacy', + use: true, + fallback: false, + funcInit: WorkerFunctions.workerObj2LegacyInit, + funcExec: WorkerFunctions.workerObj2LegacyExec, + filename: './models/obj/male02/male02.obj' + } ); this.tasksToUse = []; this.executions = []; + this.objectsUsed = new Map(); if ( this.reset !== null ) { - this._deleteMeshRange( this.lastDeletedMesh, this.meshCount ); + this._deleteMeshRange( this.meshesAdded.length ); this.reset(); this.reset = null; } + this.meshesAdded = []; this.meshCount = 0; - this.lastDeletedMesh = 0; this.removeCount = 50; this.numberOfMeshesToKeep = 750; @@ -221,49 +199,70 @@ let helper = new THREE.GridHelper( 1000, 30, 0xFF4444, 0x404040 ); this.scene.add( helper ); - } - initContent () { - + async initContent () { let awaiting = []; this.tasksToUse = []; - let taskName = 'tmProtoExample'; - let valueObj = this.taskNames.get( taskName ); - if ( valueObj.use ) { + let taskDescr = this.taskDescriptions.get( 'tmProtoExample' ); + if ( taskDescr.use ) { - this.tasksToUse.push( taskName ); - this.taskManager.registerTaskType( taskName, workerInitFunction, workerExecFunction, null, false, ["../build/three.js"] ); - awaiting.push( this.taskManager.initTaskType( taskName, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); + this.tasksToUse.push( taskDescr ); + this.taskManager.registerTaskType( taskDescr.name, taskDescr.funcInit, taskDescr.funcExec, null, false, [ { url: "../build/three.js" } ] ); + awaiting.push( this.taskManager.initTaskType( taskDescr.name, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); } - taskName = 'tmProtoExampleModule'; - valueObj = this.taskNames.get( taskName ); - if ( valueObj.use ) { + taskDescr = this.taskDescriptions.get( 'tmProtoExampleModule' ); + if ( taskDescr.use ) { - this.tasksToUse.push( taskName ); - this.taskManager.registerTaskTypeModule( taskName, './jsm/taskmanager/tmModuleExample.js' ); - awaiting.push( this.taskManager.initTaskType( taskName, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); + this.tasksToUse.push( taskDescr ); + this.taskManager.registerTaskTypeModule( taskDescr.name, taskDescr.module ); + awaiting.push( this.taskManager.initTaskType( taskDescr.name, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); } - taskName = 'tmProtoExampleModuleNoThree'; - valueObj = this.taskNames.get( taskName ); - if ( valueObj.use ) { + taskDescr = this.taskDescriptions.get( 'tmProtoExampleModuleNoThree' ); + if ( taskDescr.use ) { let torus = new THREE.TorusBufferGeometry( 25, 8, 16, 100 ); - let torusPayload = TransferableUtils.packageBufferGeometry( torus, 'torus', 0 ); - this.tasksToUse.push( taskName ); - this.taskManager.registerTaskTypeModule( taskName, './jsm/taskmanager/tmModuleExampleNoThree.js' ); - awaiting.push( this.taskManager.initTaskType( taskName, torusPayload, torusPayload.transferables ).catch( e => console.error( e ) ) ); + let torusPayload = TransferableUtils.packageBufferGeometry( torus, '0', 'torus', 0 ); + this.tasksToUse.push( taskDescr ); + this.taskManager.registerTaskTypeModule( taskDescr.name, taskDescr.module ); + awaiting.push( this.taskManager.initTaskType( taskDescr.name, torusPayload, torusPayload.transferables ).catch( e => console.error( e ) ) ); } - taskName = 'tmProtoExampleMain'; - valueObj = this.taskNames.get( taskName ); - if ( valueObj.use ) { + taskDescr = this.taskDescriptions.get( 'tmProtoExampleMain' ); + if ( taskDescr.use ) { + + this.tasksToUse.push( taskDescr ); + this.taskManager.registerTaskType( taskDescr.name, taskDescr.funcInit, taskDescr.funcExec, null, true ); + awaiting.push( this.taskManager.initTaskType( taskDescr.name, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); - this.tasksToUse.push( taskName ); - this.taskManager.registerTaskType( taskName, workerInitFunction, workerExecFunction, null, true ); - awaiting.push( this.taskManager.initTaskType( taskName, { param1: 'param1value' } ).catch( e => console.error( e ) ) ); + } + taskDescr = this.taskDescriptions.get( 'tmOBJLoader2' ); + if ( taskDescr.use ) { + + this.tasksToUse.push( taskDescr ); + this.taskManager.registerTaskTypeModule( taskDescr.name, taskDescr.module ); + await this.loadFile( taskDescr.filename ) + .then( buffer => { + let config = { buffer: buffer } + let transferables = { buffer: buffer }; + awaiting.push( this.taskManager.initTaskType( taskDescr.name, config, transferables ).catch( e => console.error( e ) ) ); + } ); + + } + taskDescr = this.taskDescriptions.get( 'tmOBJLoader2Legacy' ); + if ( taskDescr.use ) { + + this.tasksToUse.push( taskDescr ); + let obj2ParserDep = 'const OBJLoader2Parser = ' + OBJLoader2Parser.toString() + ';\n\n'; + this.taskManager.registerTaskType( taskDescr.name, taskDescr.funcInit, taskDescr.funcExec, null, false, [ { code: obj2ParserDep } ] ); + await this.loadFile( taskDescr.filename ) + .then( buffer => { + let config = { buffer: buffer } + let transferables = { buffer: buffer }; + awaiting.push( this.taskManager.initTaskType( taskDescr.name, config, transferables ).catch( e => console.error( e ) ) ); + } ); } if ( awaiting.length > 0 ) { @@ -276,6 +275,13 @@ return new Promise( ( resolve, reject ) => { reject ( 'No task type has been configured' ) } ) } + } + + async loadFile ( file ) { + + let fileLoader = new FileLoader(); + fileLoader.setResponseType( 'arraybuffer' ); + return fileLoader.loadAsync( file ) } @@ -315,9 +321,14 @@ } - _storeAddTaskPromise( taskNameIndex, globalCount ) { + _storeAddTaskPromise( taskNameIndex, globalCount ) { - this.executions.push( this.taskManager.enqueueForExecution( this.tasksToUse[ taskNameIndex ], { count: globalCount } ).then( data => this._processMessage( data ) ).catch( e => console.error( e ) ) ); + let taskDescr = this.tasksToUse[ taskNameIndex ]; + let promise = this.taskManager.enqueueForExecution( taskDescr.name, { id: globalCount }, + data => this._processMessage( data ) ) + .then( data => this._processMessage( data ) ) + .catch( e => console.error( e ) ) + this.executions.push( promise ); } @@ -327,18 +338,24 @@ console.log( 'Init Completed: ' + payload.id ); break; - case 'exec': - if ( payload.type === 'mesh' ) { + case 'execComplete': + case 'assetAvailable': + switch ( payload.type ) { - let meshes = this.meshReceiver.buildMeshes( payload ); - this._addMesh( meshes[ 0 ], payload.params ); + case 'mesh': + let meshes = this.meshReceiver.buildMeshes( payload ); + this._addMesh( meshes[ 0 ], payload.id, payload.params ); + break; - } - else { + case 'material': + this.materialHandler.addPayloadMaterials( payload ); + + case 'void': + break; - let mesh = new THREE.Mesh( new THREE.SphereBufferGeometry( 5, 32, 32 ) ); - mesh.name = payload.meshName; - this._addMesh( mesh, {} ) + default: + console.error( 'Provided payload.type was neither mesh nor assetAvailable: ' + payload.cmd ); + break; } this._cleanMeshes(); @@ -350,50 +367,73 @@ } } - _addMesh( mesh, params ) { + _addMesh( mesh, id, params ) { + + let storedPos = this.objectsUsed.get( id ); + let pos; + if ( storedPos ) { + + pos = storedPos.pos; + } + else { + + pos = new THREE.Vector3( this.baseFactor * Math.random(), this.baseFactor * Math.random(), this.baseFactor * Math.random() ); + pos.applyAxisAngle( this.baseVectorX, 2 * Math.PI * Math.random() ); + pos.applyAxisAngle( this.baseVectorY, 2 * Math.PI * Math.random() ); + pos.applyAxisAngle( this.baseVectorZ, 2 * Math.PI * Math.random() ); + this.objectsUsed.set( id, { name: mesh.name, pos: pos } ); - let pos = new THREE.Vector3( this.baseFactor * Math.random(), this.baseFactor * Math.random(), this.baseFactor * Math.random() ); - pos.applyAxisAngle( this.baseVectorX, 2 * Math.PI * Math.random() ); - pos.applyAxisAngle( this.baseVectorY, 2 * Math.PI * Math.random() ); - pos.applyAxisAngle( this.baseVectorZ, 2 * Math.PI * Math.random() ); + } + mesh.position.set( pos.x, pos.y, pos.z ); + mesh.name = id + '' + mesh.name; if ( params.color !== undefined ) { mesh.material.color = new THREE.Color( params.color.r, params.color.g, params.color.b ); } - mesh.position.set( pos.x, pos.y, pos.z ); + this.scene.add( mesh ); + this.meshesAdded.push( mesh.name ); this.meshCount ++; } _cleanMeshes() { - if ( this.meshCount > this.numberOfMeshesToKeep && this.meshCount - this.lastDeletedMesh >= this.numberOfMeshesToKeep ) { + if ( this.meshesAdded.length >= this.numberOfMeshesToKeep ) { - let deleteRange = this.lastDeletedMesh + this.removeCount; - this._deleteMeshRange( this.lastDeletedMesh, deleteRange ); - this.lastDeletedMesh = deleteRange; + this._deleteMeshRange( this.removeCount ); } } - _deleteMeshRange ( firstMesh, lastMesh ) { + _deleteMeshRange( deleteRange ) { - for ( let toBeRemoved, i = firstMesh; i < lastMesh; i++ ) { + let toBeRemoved; + let deleteCount = 0; + let i = 0; + while ( deleteCount < deleteRange && i < this.meshesAdded.length ) { - toBeRemoved = this.scene.getObjectByName( 'tmProto' + i ); + let meshName = this.meshesAdded[ i ]; + toBeRemoved = this.scene.getObjectByName( meshName ); if ( toBeRemoved ) { toBeRemoved.geometry.dispose(); - toBeRemoved.material.dispose(); + if ( toBeRemoved.material !== undefined && toBeRemoved.material !== null && toBeRemoved.material.dispose instanceof Function ) { + + toBeRemoved.material.dispose(); + + } this.scene.remove( toBeRemoved ); + this.meshesAdded.splice( i, 1 ); + deleteCount++; } else { - console.log( 'Unable to remove: tmProto' + i ); + i++; + console.log( 'Unable to remove: ' + meshName ); } @@ -435,6 +475,91 @@ } + const WorkerFunctions = { + + workerLegacyInit: function ( context, id, config ) { + context.storage = { + whoami: config.id, + }; + + context.postMessage( { + cmd: "init", + id: id + } ); + + }, + + workerLegacyExec: function ( context, id, config ) { + + let bufferGeometry = new THREE.SphereBufferGeometry( 40, 64, 64 ); + + let vertexBA = bufferGeometry.getAttribute( 'position' ); + let indexBA = bufferGeometry.getIndex(); + let colorBA = bufferGeometry.getAttribute( 'color' ); + let normalBA = bufferGeometry.getAttribute( 'normal' ); + let uvBA = bufferGeometry.getAttribute( 'uv' ); + + let vertexArray = vertexBA.array; + for ( let i = 0; i < vertexArray.length; i ++ ) { + + vertexArray[ i ] = vertexArray[ i ] * Math.random() * 0.48; + + } + let vertexFA = vertexArray; + let indexUA = (indexBA !== null && indexBA !== undefined) ? indexBA.array : null; + let colorFA = (colorBA !== null && colorBA !== undefined) ? colorBA.array : null; + let normalFA = (normalBA !== null && normalBA !== undefined) ? normalBA.array : null; + let uvFA = (uvBA !== null && uvBA !== undefined) ? uvBA.array : null; + + context.postMessage( { + cmd: 'execComplete', + type: 'mesh', + meshName: 'tmProto' + config.id, + id: config.id, + params: { + geometryType: 0 + }, + buffers: { + vertices: vertexFA, + indices: indexUA, + colors: colorFA, + normals: normalFA, + uvs: uvFA, + }, + materials: { + materialNames: ['meshNormalMaterial'] + } + }, + [vertexFA.buffer], + indexUA !== null ? [indexUA.buffer] : null, + colorFA !== null ? [colorFA.buffer] : null, + normalFA !== null ? [normalFA.buffer] : null, + uvFA !== null ? [uvFA.buffer] : null + ); + + }, + + workerObj2LegacyInit: function ( context, id, config ) { + + self.objParser = new OBJLoader2Parser(); + self.buffer = config.buffer; + context.postMessage( { + cmd: "init", + id: id + } ); + + }, + + workerObj2LegacyExec: function ( context, id, config ) { + + self.objParser.setCallbackOnAssetAvailable( m => { self.postMessage( m ); } ); + self.objParser.setCallbackOnProgress( text => { console.debug( 'WorkerRunner: progress: ' + text ) } ); + self.objParser.objectId = config.id; + self.objParser.execute( self.buffer ); + + } + } + let app = new TaskManagerPrototypeExample( document.getElementById( 'example' ) ); app.resetAppContext(); @@ -447,16 +572,20 @@ tmProtoExampleModule: false, tmProtoExampleModuleNoThree: false, tmProtoExampleMain: false, + tmOBJLoader2: false, + tmOBJLoader2Legacy: false, maxParallelExecutions: 0, overallExecutionCount: 0, numberOfMeshesToKeep: 0, maxPerLoop: 0, resetContent () { - this.tmProtoExampleName = app.taskNames.get( 'tmProtoExample' ).use; - this.tmProtoExampleModule = app.taskNames.get( 'tmProtoExampleModule' ).use; - this.tmProtoExampleModuleNoThree = app.taskNames.get( 'tmProtoExampleModuleNoThree' ).use; - this.tmProtoExampleMain = app.taskNames.get( 'tmProtoExampleMain' ).use; + this.tmProtoExampleName = app.taskDescriptions.get( 'tmProtoExample' ).use; + this.tmProtoExampleModule = app.taskDescriptions.get( 'tmProtoExampleModule' ).use; + this.tmProtoExampleModuleNoThree = app.taskDescriptions.get( 'tmProtoExampleModuleNoThree' ).use; + this.tmProtoExampleMain = app.taskDescriptions.get( 'tmProtoExampleMain' ).use; + this.tmOBJLoader2 = app.taskDescriptions.get( 'tmOBJLoader2' ).use; + this.tmOBJLoader2Legacy = app.taskDescriptions.get( 'tmOBJLoader2Legacy' ).use; this.maxParallelExecutions = app.taskManager.getMaxParallelExecutions(); this.overallExecutionCount = app.overallExecutionCount; this.numberOfMeshesToKeep = app.numberOfMeshesToKeep; @@ -537,35 +666,54 @@ menuDiv.appendChild( gui.domElement ); let taskName0 = 'tmProtoExample'; - tmControls.controls[ 0 ] = gui.add( tmControls, taskName0 + 'Name' ).name( 'Worker Legacy + three' ); - tmControls.controls[ 0 ].onChange( value => { app.taskNames.get( taskName0 ).use = value; } ); + let index = 0; + tmControls.controls[ index ] = gui.add( tmControls, taskName0 + 'Name' ).name( 'Worker Legacy + three' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName0 ).use = value; } ); let taskName1 = 'tmProtoExampleModule'; - tmControls.controls[ 1 ] = gui.add( tmControls, taskName1 ).name( 'Worker Module + three' ); - tmControls.controls[ 1 ].onChange( value => { app.taskNames.get( taskName1 ).use = value; } ); + index++; + tmControls.controls[ index ] = gui.add( tmControls, taskName1 ).name( 'Worker Module + three' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName1 ).use = value; } ); let taskName2 = 'tmProtoExampleModuleNoThree'; - tmControls.controls[ 2 ] = gui.add( tmControls, taskName2 ).name( 'Worker Module solo' ); - tmControls.controls[ 2 ].onChange( value => { app.taskNames.get( taskName2 ).use = value; } ); + index++; + tmControls.controls[ index ] = gui.add( tmControls, taskName2 ).name( 'Worker Module solo' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName2 ).use = value; } ); let taskName3 = 'tmProtoExampleMain'; - tmControls.controls[ 3 ] = gui.add( tmControls, taskName3 ).name( 'Worker Legacy Main' ); - tmControls.controls[ 3 ].onChange( value => { app.taskNames.get( taskName3 ).use = value;} ); - - tmControls.controls[ 4 ] = gui.add( tmControls, 'maxParallelExecutions', 1, 16 ).step( 1 ).name( 'Maximum Parallel Executions' ); - tmControls.controls[ 4 ].onChange( value => { app.taskManager.setMaxParallelExecutions( value ) } ); - - tmControls.controls[ 5 ] = gui.add( tmControls, 'overallExecutionCount', 0, 10000000 ).step( 1000 ).name( 'Overall Execution Count' ); - tmControls.controls[ 5 ].onChange( value => { app.overallExecutionCount = value } ); - - tmControls.controls[ 6 ] = gui.add( tmControls, 'maxPerLoop', 0, 10000 ).step( 100 ).name( 'Loop executions' ); - tmControls.controls[ 6 ].onChange( value => { app.maxPerLoop = value } ); - - tmControls.controls[ 7 ] = gui.add( tmControls, 'numberOfMeshesToKeep', 100, 10000 ).step( 25 ).name( 'Keep N Meshes' ); - tmControls.controls[ 7 ].onChange( value => { app.numberOfMeshesToKeep = value } ); - - tmControls.controls[ 8 ] = gui.add( tmControls, 'executeLoading' ).name( 'Engage' ); - tmControls.controls[ 8 ].domElement.id = 'startButton'; + index++; + tmControls.controls[ index ] = gui.add( tmControls, taskName3 ).name( 'Worker Legacy Main' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName3 ).use = value; } ); + + let taskName4 = 'tmOBJLoader2'; + index++; + tmControls.controls[ index ] = gui.add( tmControls, taskName4 ).name( 'OBJLoader2 Module' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName4 ).use = value; } ); + + let taskName5 = 'tmOBJLoader2Legacy'; + index++; + tmControls.controls[ index ] = gui.add( tmControls, taskName5 ).name( 'OBJLoader2 Legacy' ); + tmControls.controls[ index ].onChange( value => { app.taskDescriptions.get( taskName5 ).use = value; } ); + + index++; + tmControls.controls[ index ] = gui.add( tmControls, 'maxParallelExecutions', 1, 16 ).step( 1 ).name( 'Maximum Parallel Executions' ); + tmControls.controls[ index ].onChange( value => { app.taskManager.setMaxParallelExecutions( value ) } ); + + index++; + tmControls.controls[ index ] = gui.add( tmControls, 'overallExecutionCount', 0, 10000000 ).step( 1000 ).name( 'Overall Execution Count' ); + tmControls.controls[ index ].onChange( value => { app.overallExecutionCount = value } ); + + index++; + tmControls.controls[ index ] = gui.add( tmControls, 'maxPerLoop', 0, 10000 ).step( 100 ).name( 'Loop executions' ); + tmControls.controls[ index ].onChange( value => { app.maxPerLoop = value } ); + + index++; + tmControls.controls[ index ] = gui.add( tmControls, 'numberOfMeshesToKeep', 100, 10000 ).step( 25 ).name( 'Keep N Meshes' ); + tmControls.controls[ index ].onChange( value => { app.numberOfMeshesToKeep = value } ); + + index++; + tmControls.controls[ index ] = gui.add( tmControls, 'executeLoading' ).name( 'Engage' ); + tmControls.controls[ index ].domElement.id = 'startButton'; tmControls.controlStop = gui.add( tmControls, 'stopExecution' ).name( 'Stop' ); tmControls.controlReset = gui.add( tmControls, 'resetExecution' ).name( 'Reset' );