|
@@ -0,0 +1,1171 @@
|
|
|
+/**
|
|
|
+ * @author Kai Salmen / https://kaisalmen.de
|
|
|
+ * Development repository: https://github.com/kaisalmen/WWOBJLoader
|
|
|
+ */
|
|
|
+
|
|
|
+'use strict';
|
|
|
+
|
|
|
+if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
|
|
|
+THREE.OBJLoader2.version = '1.0.6';
|
|
|
+
|
|
|
+/**
|
|
|
+ * OBJ data will be loaded by dynamically created web worker.
|
|
|
+ * First feed instructions with: prepareRun
|
|
|
+ * Then: Execute with: run
|
|
|
+ * @class
|
|
|
+ */
|
|
|
+THREE.OBJLoader2.WWOBJLoader2 = (function () {
|
|
|
+
|
|
|
+ function WWOBJLoader2() {
|
|
|
+ this._init();
|
|
|
+ }
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._init = function () {
|
|
|
+ // check worker support first
|
|
|
+ if ( window.Worker === undefined ) throw "This browser does not support web workers!";
|
|
|
+ if ( window.Blob === undefined ) throw "This browser does not support Blob!";
|
|
|
+ if ( ! typeof window.URL.createObjectURL === 'function' ) throw "This browser does not support Object creation from URL!";
|
|
|
+
|
|
|
+ this.instanceNo = 0;
|
|
|
+ this.worker = null;
|
|
|
+ this.workerCode = null;
|
|
|
+ this.debug = false;
|
|
|
+
|
|
|
+ this.sceneGraphBaseNode = null;
|
|
|
+ this.streamMeshes = true;
|
|
|
+ this.meshStore = null;
|
|
|
+ this.modelName = 'none';
|
|
|
+ this.validated = false;
|
|
|
+ this.running = false;
|
|
|
+ this.requestTerminate = false;
|
|
|
+
|
|
|
+ this.callbacks = {
|
|
|
+ progress: null,
|
|
|
+ completedLoading: null,
|
|
|
+ errorWhileLoading: null,
|
|
|
+ materialsLoaded: null,
|
|
|
+ meshLoaded: null,
|
|
|
+ director: {
|
|
|
+ completedLoading: null,
|
|
|
+ errorWhileLoading: null
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ this.manager = THREE.DefaultLoadingManager;
|
|
|
+ this.fileLoader = new THREE.FileLoader( this.manager );
|
|
|
+ this.mtlLoader = null;
|
|
|
+ this.crossOrigin = null;
|
|
|
+
|
|
|
+ this.dataAvailable = false;
|
|
|
+ this.objAsArrayBuffer = null;
|
|
|
+ this.fileObj = null;
|
|
|
+ this.pathObj = null;
|
|
|
+
|
|
|
+ this.fileMtl = null;
|
|
|
+ this.mtlAsString = null;
|
|
|
+ this.texturePath = null;
|
|
|
+
|
|
|
+ this.materials = [];
|
|
|
+ this.counter = 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set enable or disable debug logging
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {boolean} enabled
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.setDebug = function ( enabled ) {
|
|
|
+ this.debug = enabled;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the CORS string to be used.
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {string} crossOrigin CORS value
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.setCrossOrigin = function ( crossOrigin ) {
|
|
|
+ this.crossOrigin = crossOrigin;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Register callback function that is invoked by internal function "_announceProgress" to print feedback
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {callback} callbackProgress Callback function for described functionality
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.registerCallbackProgress = function ( callbackProgress ) {
|
|
|
+ if ( callbackProgress != null ) this.callbacks.progress = callbackProgress;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Register callback function that is called once loading of the complete model is completed
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {callback} callbackCompletedLoading Callback function for described functionality
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.registerCallbackCompletedLoading = function ( callbackCompletedLoading ) {
|
|
|
+ if ( callbackCompletedLoading != null ) this.callbacks.completedLoading = callbackCompletedLoading;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Register callback function that is called once materials have been loaded. It allows to alter and return materials
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {callback} callbackMaterialsLoaded Callback function for described functionality
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.registerCallbackMaterialsLoaded = function ( callbackMaterialsLoaded ) {
|
|
|
+ if ( callbackMaterialsLoaded != null ) this.callbacks.materialsLoaded = callbackMaterialsLoaded;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Register callback function that is called every time a mesh was loaded
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {callback} callbackMeshLoaded Callback function for described functionality
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.registerCallbackMeshLoaded = function ( callbackMeshLoaded ) {
|
|
|
+ if ( callbackMeshLoaded != null ) this.callbacks.meshLoaded = callbackMeshLoaded;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Report if an error prevented loading
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {callback} callbackErrorWhileLoading Callback function for described functionality
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.registerCallbackErrorWhileLoading = function ( callbackErrorWhileLoading ) {
|
|
|
+ if ( callbackErrorWhileLoading != null ) this.callbacks.errorWhileLoading = callbackErrorWhileLoading;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Call requestTerminate to terminate the web worker and free local resource after execution
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {boolean} requestTerminate
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.setRequestTerminate = function ( requestTerminate ) {
|
|
|
+ this.requestTerminate = ( requestTerminate != null && requestTerminate ) ? true : false;
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._validate = function () {
|
|
|
+ if ( this.validated ) return;
|
|
|
+ if ( this.worker == null ) {
|
|
|
+
|
|
|
+ this._buildWebWorkerCode();
|
|
|
+ var blob = new Blob( [ this.workerCode ], { type: 'text/plain' } );
|
|
|
+ this.worker = new Worker( window.URL.createObjectURL( blob ) );
|
|
|
+
|
|
|
+ var scope = this;
|
|
|
+ var scopeFunction = function ( e ) {
|
|
|
+ scope._receiveWorkerMessage( e );
|
|
|
+ };
|
|
|
+ this.worker.addEventListener( 'message', scopeFunction, false );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.sceneGraphBaseNode = null;
|
|
|
+ this.streamMeshes = true;
|
|
|
+ this.meshStore = [];
|
|
|
+ this.modelName = 'none';
|
|
|
+ this.validated = true;
|
|
|
+ this.running = true;
|
|
|
+ this.requestTerminate = false;
|
|
|
+
|
|
|
+ this.fileLoader = ( this.fileLoader == null ) ? new THREE.FileLoader( this.manager ) : this.fileLoader;
|
|
|
+ this.mtlLoader = ( this.mtlLoader == null ) ? new THREE.MTLLoader() : this.mtlLoader;
|
|
|
+ if ( this.crossOrigin != null ) this.mtlLoader.setCrossOrigin( this.crossOrigin );
|
|
|
+
|
|
|
+ this.dataAvailable = false;
|
|
|
+ this.fileObj = null;
|
|
|
+ this.pathObj = null;
|
|
|
+ this.fileMtl = null;
|
|
|
+ this.texturePath = null;
|
|
|
+
|
|
|
+ this.objAsArrayBuffer = null;
|
|
|
+ this.mtlAsString = null;
|
|
|
+
|
|
|
+ this.materials = [];
|
|
|
+ var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
|
|
|
+ defaultMaterial.name = 'defaultMaterial';
|
|
|
+ this.materials[ defaultMaterial.name ] = defaultMaterial;
|
|
|
+
|
|
|
+ this.counter = 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set all parameters for required for execution of "run".
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ *
|
|
|
+ * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.prepareRun = function ( params ) {
|
|
|
+ this._validate();
|
|
|
+ this.dataAvailable = params.dataAvailable;
|
|
|
+ this.modelName = params.modelName;
|
|
|
+ console.time( 'WWOBJLoader2' );
|
|
|
+ if ( this.dataAvailable ) {
|
|
|
+
|
|
|
+ // fast-fail on bad type
|
|
|
+ if ( ! params.objAsArrayBuffer instanceof Uint8Array ) {
|
|
|
+ throw 'Provided input is not of type arraybuffer! Aborting...';
|
|
|
+ }
|
|
|
+
|
|
|
+ this.worker.postMessage( {
|
|
|
+ cmd: 'init',
|
|
|
+ debug: this.debug
|
|
|
+ } );
|
|
|
+
|
|
|
+ this.objAsArrayBuffer = params.objAsArrayBuffer;
|
|
|
+ this.mtlAsString = params.mtlAsString;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // fast-fail on bad type
|
|
|
+ if ( ! ( typeof( params.fileObj ) === 'string' || params.fileObj instanceof String ) ) {
|
|
|
+ throw 'Provided file is not properly defined! Aborting...';
|
|
|
+ }
|
|
|
+
|
|
|
+ this.worker.postMessage( {
|
|
|
+ cmd: 'init',
|
|
|
+ debug: this.debug
|
|
|
+ } );
|
|
|
+
|
|
|
+ this.fileObj = params.fileObj;
|
|
|
+ this.pathObj = params.pathObj;
|
|
|
+ this.fileMtl = params.fileMtl;
|
|
|
+
|
|
|
+ }
|
|
|
+ this.setRequestTerminate( params.requestTerminate );
|
|
|
+ this.pathTexture = params.pathTexture;
|
|
|
+ this.sceneGraphBaseNode = params.sceneGraphBaseNode;
|
|
|
+ this.streamMeshes = params.streamMeshes;
|
|
|
+ if ( ! this.streamMeshes ) this.meshStore = [];
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Run the loader according the preparation instruction provided in "prepareRun".
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2
|
|
|
+ */
|
|
|
+ WWOBJLoader2.prototype.run = function () {
|
|
|
+ var scope = this;
|
|
|
+ var processLoadedMaterials = function ( materialCreator ) {
|
|
|
+ var materialCreatorMaterials = [];
|
|
|
+ var materialNames = [];
|
|
|
+ if ( materialCreator != null ) {
|
|
|
+
|
|
|
+ materialCreator.preload();
|
|
|
+ materialCreatorMaterials = materialCreator.materials;
|
|
|
+ for ( var materialName in materialCreatorMaterials ) {
|
|
|
+
|
|
|
+ if ( materialCreatorMaterials.hasOwnProperty( materialName ) ) {
|
|
|
+
|
|
|
+ materialNames.push( materialName );
|
|
|
+ scope.materials[ materialName ] = materialCreatorMaterials[ materialName ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ scope.worker.postMessage( {
|
|
|
+ cmd: 'setMaterials',
|
|
|
+ materialNames: materialNames
|
|
|
+ } );
|
|
|
+
|
|
|
+ if ( scope.callbacks.materialsLoaded != null ) {
|
|
|
+
|
|
|
+ var materialsCallback = scope.callbacks.materialsLoaded( scope.materials );
|
|
|
+ if ( materialsCallback != null ) scope.materials = materialsCallback;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( scope.dataAvailable && scope.objAsArrayBuffer ) {
|
|
|
+
|
|
|
+ scope.worker.postMessage({
|
|
|
+ cmd: 'run',
|
|
|
+ objAsArrayBuffer: scope.objAsArrayBuffer
|
|
|
+ }, [ scope.objAsArrayBuffer.buffer ] );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var refPercentComplete = 0;
|
|
|
+ var percentComplete = 0;
|
|
|
+ var output;
|
|
|
+ var onLoad = function ( objAsArrayBuffer ) {
|
|
|
+
|
|
|
+ scope._announceProgress( 'Running web worker!' );
|
|
|
+ scope.objAsArrayBuffer = new Uint8Array( objAsArrayBuffer );
|
|
|
+ scope.worker.postMessage( {
|
|
|
+ cmd: 'run',
|
|
|
+ objAsArrayBuffer: scope.objAsArrayBuffer
|
|
|
+ }, [ scope.objAsArrayBuffer.buffer ] );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ var onProgress = function ( event ) {
|
|
|
+ if ( ! event.lengthComputable ) return;
|
|
|
+
|
|
|
+ percentComplete = Math.round( event.loaded / event.total * 100 );
|
|
|
+ if ( percentComplete > refPercentComplete ) {
|
|
|
+
|
|
|
+ refPercentComplete = percentComplete;
|
|
|
+ output = 'Download of "' + scope.fileObj + '": ' + percentComplete + '%';
|
|
|
+ console.log( output );
|
|
|
+ scope._announceProgress( output );
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var onError = function ( event ) {
|
|
|
+ output = 'Error occurred while downloading "' + scope.fileObj + '"';
|
|
|
+ console.error( output + ': ' + event );
|
|
|
+ scope._announceProgress( output );
|
|
|
+ scope._finalize( 'error' );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ scope.fileLoader.setPath( scope.pathObj );
|
|
|
+ scope.fileLoader.setResponseType( 'arraybuffer' );
|
|
|
+ scope.fileLoader.load( scope.fileObj, onLoad, onProgress, onError );
|
|
|
+ }
|
|
|
+ console.timeEnd( 'Loading MTL textures' );
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ this.mtlLoader.setPath( this.pathTexture );
|
|
|
+ if ( this.dataAvailable ) {
|
|
|
+
|
|
|
+ processLoadedMaterials( ( this.mtlAsString != null ) ? this.mtlLoader.parse( this.mtlAsString ) : null );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if ( this.fileMtl == null ) {
|
|
|
+
|
|
|
+ processLoadedMaterials();
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ this.mtlLoader.load( this.fileMtl, processLoadedMaterials );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._receiveWorkerMessage = function ( event ) {
|
|
|
+ var payload = event.data;
|
|
|
+
|
|
|
+ switch ( payload.cmd ) {
|
|
|
+ case 'objData':
|
|
|
+
|
|
|
+ this.counter ++;
|
|
|
+ var bufferGeometry = new THREE.BufferGeometry();
|
|
|
+
|
|
|
+ bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( payload.vertices ), 3 ) );
|
|
|
+ if ( payload.normals !== null ) {
|
|
|
+
|
|
|
+ bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( payload.normals ), 3 ) );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ bufferGeometry.computeVertexNormals();
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( payload.uvs !== null ) {
|
|
|
+
|
|
|
+ bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( payload.uvs ), 2 ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var materialDescriptions = payload.materialDescriptions;
|
|
|
+ var materialDescription;
|
|
|
+ var material;
|
|
|
+ var materialName;
|
|
|
+ var createMultiMaterial = payload.multiMaterial;
|
|
|
+ var multiMaterials = [];
|
|
|
+
|
|
|
+ for ( var key in materialDescriptions ) {
|
|
|
+
|
|
|
+ materialDescription = materialDescriptions[ key ];
|
|
|
+ material = this.materials[ materialDescription.name ];
|
|
|
+
|
|
|
+ if ( materialDescription.default ) {
|
|
|
+
|
|
|
+ material = this.materials[ 'defaultMaterial' ];
|
|
|
+
|
|
|
+ } else if ( materialDescription.clone ) {
|
|
|
+
|
|
|
+ materialName = material.name + '_flat';
|
|
|
+ var materialClone = this.materials[ materialName ];
|
|
|
+ if ( ! materialClone ) {
|
|
|
+
|
|
|
+ materialClone = material.clone();
|
|
|
+ materialClone.name = materialName;
|
|
|
+ materialClone.shading = THREE.FlatShading;
|
|
|
+ this.materials[ materialName ] = name;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if ( ! material ) {
|
|
|
+
|
|
|
+ material = this.materials[ 'defaultMaterial' ];
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( createMultiMaterial ) multiMaterials.push( material );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( createMultiMaterial ) {
|
|
|
+
|
|
|
+ material = new THREE.MultiMaterial( multiMaterials );
|
|
|
+ var materialGroups = payload.materialGroups;
|
|
|
+ var materialGroup;
|
|
|
+ for ( var key in materialGroups ) {
|
|
|
+
|
|
|
+ materialGroup = materialGroups[ key ];
|
|
|
+ bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( this.callbacks.meshLoaded !== null ) {
|
|
|
+
|
|
|
+ var materialOverride = this.callbacks.meshLoaded( payload.meshName, material );
|
|
|
+ if ( materialOverride != null ) material = materialOverride;
|
|
|
+
|
|
|
+ }
|
|
|
+ var mesh = new THREE.Mesh( bufferGeometry, material );
|
|
|
+ mesh.name = payload.meshName;
|
|
|
+ if ( this.streamMeshes ) {
|
|
|
+
|
|
|
+ this.sceneGraphBaseNode.add( mesh );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ this.meshStore.push( mesh );
|
|
|
+
|
|
|
+ }
|
|
|
+ var output = '(' + this.counter + '): ' + payload.meshName;
|
|
|
+ this._announceProgress( 'Adding mesh', output );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'complete':
|
|
|
+
|
|
|
+ if ( ! this.streamMeshes ) {
|
|
|
+
|
|
|
+ for ( var key in this.meshStore ) {
|
|
|
+
|
|
|
+ this.sceneGraphBaseNode.add( this.meshStore[ key ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ console.timeEnd( 'WWOBJLoader2' );
|
|
|
+ if ( payload.msg != null ) {
|
|
|
+
|
|
|
+ this._announceProgress( payload.msg );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ this._announceProgress( '' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this._finalize( 'complete' );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'report_progress':
|
|
|
+ this._announceProgress( '', payload.output );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ console.error( 'Received unknown command: ' + payload.cmd );
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._terminate = function () {
|
|
|
+ if ( this.worker != null ) {
|
|
|
+
|
|
|
+ if ( this.running ) throw 'Unable to gracefully terminate worker as it is currently running!';
|
|
|
+
|
|
|
+ this.worker.terminate();
|
|
|
+ this.worker = null;
|
|
|
+ this.workerCode = null;
|
|
|
+ this._finalize( 'terminate' );
|
|
|
+
|
|
|
+ }
|
|
|
+ this.fileLoader = null;
|
|
|
+ this.mtlLoader = null;
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._finalize = function ( reason, requestTerminate ) {
|
|
|
+ this.running = false;
|
|
|
+ if ( reason === 'complete' ) {
|
|
|
+
|
|
|
+ if ( this.callbacks.completedLoading != null ) this.callbacks.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
|
|
|
+ if ( this.callbacks.director.completedLoading != null ) this.callbacks.director.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
|
|
|
+
|
|
|
+ } else if ( reason === 'error' ) {
|
|
|
+
|
|
|
+ if ( this.callbacks.errorWhileLoading != null ) this.callbacks.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
|
|
|
+ if ( this.callbacks.director.errorWhileLoading != null ) this.callbacks.director.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
|
|
|
+
|
|
|
+ }
|
|
|
+ this.validated = false;
|
|
|
+
|
|
|
+ this.setRequestTerminate( requestTerminate );
|
|
|
+
|
|
|
+ if ( this.requestTerminate ) {
|
|
|
+ this._terminate();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._announceProgress = function ( baseText, text ) {
|
|
|
+ var output = "";
|
|
|
+ if ( baseText !== null && baseText !== undefined ) {
|
|
|
+
|
|
|
+ output = baseText;
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( text !== null && text !== undefined ) {
|
|
|
+
|
|
|
+ output = output + " " + text;
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( this.callbacks.progress !== null ) {
|
|
|
+
|
|
|
+ this.callbacks.progress( output );
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( this.debug ) {
|
|
|
+
|
|
|
+ console.log( output );
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2.prototype._buildWebWorkerCode = function ( existingWorkerCode ) {
|
|
|
+ if ( existingWorkerCode != null ) this.workerCode = existingWorkerCode;
|
|
|
+ if ( this.workerCode == null ) {
|
|
|
+
|
|
|
+ console.time( 'buildWebWorkerCode' );
|
|
|
+ var wwDef = (function () {
|
|
|
+
|
|
|
+ function OBJLoader() {
|
|
|
+ this.meshCreator = new MeshCreator();
|
|
|
+ this.parser = new Parser( this.meshCreator );
|
|
|
+ this.validated = false;
|
|
|
+ this.cmdState = 'created';
|
|
|
+
|
|
|
+ this.debug = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Allows to set debug mode for the parser and the meshCreatorDebug
|
|
|
+ *
|
|
|
+ * @param parserDebug
|
|
|
+ * @param meshCreatorDebug
|
|
|
+ */
|
|
|
+ OBJLoader.prototype._setDebug = function ( parserDebug, meshCreatorDebug ) {
|
|
|
+ this.parser._setDebug( parserDebug );
|
|
|
+ this.meshCreator._setDebug( meshCreatorDebug );
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Validate status, then parse arrayBuffer, finalize and return objGroup
|
|
|
+ *
|
|
|
+ * @param arrayBuffer
|
|
|
+ */
|
|
|
+ OBJLoader.prototype.parse = function ( arrayBuffer ) {
|
|
|
+ console.log( 'Parsing arrayBuffer...' );
|
|
|
+ console.time( 'parseArrayBuffer' );
|
|
|
+
|
|
|
+ this._validate();
|
|
|
+ this.parser.parseArrayBuffer( arrayBuffer );
|
|
|
+ var objGroup = this._finalize();
|
|
|
+
|
|
|
+ console.timeEnd( 'parseArrayBuffer' );
|
|
|
+
|
|
|
+ return objGroup;
|
|
|
+ };
|
|
|
+
|
|
|
+ OBJLoader.prototype._validate = function () {
|
|
|
+ if ( this.validated ) return;
|
|
|
+
|
|
|
+ this.parser._validate();
|
|
|
+ this.meshCreator._validate();
|
|
|
+
|
|
|
+ this.validated = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ OBJLoader.prototype._finalize = function () {
|
|
|
+ console.log( 'Global output object count: ' + this.meshCreator.globalObjectCount );
|
|
|
+ this.parser._finalize();
|
|
|
+ this.meshCreator._finalize();
|
|
|
+ this.validated = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ OBJLoader.prototype.init = function ( payload ) {
|
|
|
+ this.cmdState = 'init';
|
|
|
+ this._setDebug( payload.debug, payload.debug );
|
|
|
+ };
|
|
|
+
|
|
|
+ OBJLoader.prototype.setMaterials = function ( payload ) {
|
|
|
+ this.cmdState = 'setMaterials';
|
|
|
+ this.meshCreator._setMaterials( payload.materialNames );
|
|
|
+ };
|
|
|
+
|
|
|
+ OBJLoader.prototype.run = function ( payload ) {
|
|
|
+ this.cmdState = 'run';
|
|
|
+
|
|
|
+ this.parse( payload.objAsArrayBuffer );
|
|
|
+ console.log( 'OBJ loading complete!' );
|
|
|
+
|
|
|
+ this.cmdState = 'complete';
|
|
|
+ self.postMessage( {
|
|
|
+ cmd: this.cmdState,
|
|
|
+ msg: null
|
|
|
+ } );
|
|
|
+ };
|
|
|
+
|
|
|
+ return OBJLoader;
|
|
|
+ })();
|
|
|
+
|
|
|
+ var wwMeshCreatorDef = (function () {
|
|
|
+
|
|
|
+ function MeshCreator() {
|
|
|
+ this.materials = null;
|
|
|
+ this.debug = false;
|
|
|
+ this.globalObjectCount = 1;
|
|
|
+ this.validated = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MeshCreator.prototype._setMaterials = function ( materials ) {
|
|
|
+ this.materials = ( materials == null ) ? ( this.materials == null ? { materials: [] } : this.materials ) : materials;
|
|
|
+ };
|
|
|
+
|
|
|
+ MeshCreator.prototype._setDebug = function ( debug ) {
|
|
|
+ this.debug = ( debug == null ) ? this.debug : debug;
|
|
|
+ };
|
|
|
+
|
|
|
+ MeshCreator.prototype._validate = function () {
|
|
|
+ if ( this.validated ) return;
|
|
|
+
|
|
|
+ this._setMaterials( null );
|
|
|
+ this._setDebug( null );
|
|
|
+ this.globalObjectCount = 1;
|
|
|
+ };
|
|
|
+
|
|
|
+ MeshCreator.prototype._finalize = function () {
|
|
|
+ this.materials = null;
|
|
|
+ this.validated = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * RawObjectDescriptions are transformed to THREE.Mesh.
|
|
|
+ * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check).
|
|
|
+ *
|
|
|
+ * @param rawObjectDescriptions
|
|
|
+ * @param inputObjectCount
|
|
|
+ * @param absoluteVertexCount
|
|
|
+ * @param absoluteNormalCount
|
|
|
+ * @param absoluteUvCount
|
|
|
+ */
|
|
|
+ MeshCreator.prototype._buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) {
|
|
|
+ if ( this.debug ) console.log( 'OBJLoader.buildMesh:\nInput object no.: ' + inputObjectCount );
|
|
|
+
|
|
|
+ var vertexFa = new Float32Array( absoluteVertexCount );
|
|
|
+ var normalFA = ( absoluteNormalCount > 0 ) ? new Float32Array( absoluteNormalCount ) : null;
|
|
|
+ var uvFA = ( absoluteUvCount > 0 ) ? new Float32Array( absoluteUvCount ) : null;
|
|
|
+
|
|
|
+ var rawObjectDescription;
|
|
|
+ var materialDescription;
|
|
|
+ var materialDescriptions = [];
|
|
|
+
|
|
|
+ var createMultiMaterial = ( rawObjectDescriptions.length > 1 );
|
|
|
+ var materialIndex = 0;
|
|
|
+ var materialIndexMapping = [];
|
|
|
+ var selectedMaterialIndex;
|
|
|
+ var materialGroup;
|
|
|
+ var materialGroups = [];
|
|
|
+
|
|
|
+ var vertexBAOffset = 0;
|
|
|
+ var vertexGroupOffset = 0;
|
|
|
+ var vertexLength;
|
|
|
+ var normalOffset = 0;
|
|
|
+ var uvOffset = 0;
|
|
|
+
|
|
|
+ for ( var oodIndex in rawObjectDescriptions ) {
|
|
|
+ rawObjectDescription = rawObjectDescriptions[ oodIndex ];
|
|
|
+
|
|
|
+ materialDescription = { name: rawObjectDescription.materialName, flat: false, default: false };
|
|
|
+ if ( this.materials[ materialDescription.name ] === null ) {
|
|
|
+
|
|
|
+ materialDescription.default = true;
|
|
|
+ console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' );
|
|
|
+
|
|
|
+ }
|
|
|
+ // Attach '_flat' to materialName in case flat shading is needed due to smoothingGroup 0
|
|
|
+ if ( rawObjectDescription.smoothingGroup === 0 ) materialDescription.flat = true;
|
|
|
+
|
|
|
+ vertexLength = rawObjectDescription.vertices.length;
|
|
|
+ if ( createMultiMaterial ) {
|
|
|
+
|
|
|
+ // re-use material if already used before. Reduces materials array size and eliminates duplicates
|
|
|
+
|
|
|
+ selectedMaterialIndex = materialIndexMapping[ materialDescription.name ];
|
|
|
+ if ( ! selectedMaterialIndex ) {
|
|
|
+
|
|
|
+ selectedMaterialIndex = materialIndex;
|
|
|
+ materialIndexMapping[ materialDescription.name ] = materialIndex;
|
|
|
+ materialDescriptions.push( materialDescription );
|
|
|
+ materialIndex++;
|
|
|
+
|
|
|
+ }
|
|
|
+ materialGroup = {
|
|
|
+ start: vertexGroupOffset,
|
|
|
+ count: vertexLength / 3,
|
|
|
+ index: selectedMaterialIndex
|
|
|
+ };
|
|
|
+ materialGroups.push( materialGroup );
|
|
|
+ vertexGroupOffset += vertexLength / 3;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ materialDescriptions.push( materialDescription );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ vertexFa.set( rawObjectDescription.vertices, vertexBAOffset );
|
|
|
+ vertexBAOffset += vertexLength;
|
|
|
+
|
|
|
+ if ( normalFA ) {
|
|
|
+
|
|
|
+ normalFA.set( rawObjectDescription.normals, normalOffset );
|
|
|
+ normalOffset += rawObjectDescription.normals.length;
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( uvFA ) {
|
|
|
+
|
|
|
+ uvFA.set( rawObjectDescription.uvs, uvOffset );
|
|
|
+ uvOffset += rawObjectDescription.uvs.length;
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ self.postMessage( {
|
|
|
+ cmd: 'objData',
|
|
|
+ meshName: rawObjectDescription.objectName,
|
|
|
+ multiMaterial: createMultiMaterial,
|
|
|
+ materialDescriptions: materialDescriptions,
|
|
|
+ materialGroups: materialGroups,
|
|
|
+ vertices: vertexFa,
|
|
|
+ normals: normalFA,
|
|
|
+ uvs: uvFA
|
|
|
+ }, [ vertexFa.buffer ], normalFA !== null ? [ normalFA.buffer ] : null, uvFA !== null ? [ uvFA.buffer ] : null );
|
|
|
+
|
|
|
+ this.globalObjectCount++;
|
|
|
+ };
|
|
|
+
|
|
|
+ return MeshCreator;
|
|
|
+ })();
|
|
|
+
|
|
|
+ var wwLoaderRunnerDef = (function () {
|
|
|
+
|
|
|
+ function OBJLoaderRunner() {
|
|
|
+ self.addEventListener( 'message', this.runner, false );
|
|
|
+ }
|
|
|
+
|
|
|
+ OBJLoaderRunner.prototype.runner = function ( event ) {
|
|
|
+ var payload = event.data;
|
|
|
+
|
|
|
+ console.log( 'Command state before: ' + OBJLoaderRef.cmdState );
|
|
|
+
|
|
|
+ switch ( payload.cmd ) {
|
|
|
+ case 'init':
|
|
|
+
|
|
|
+ OBJLoaderRef.init( payload );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'setMaterials':
|
|
|
+
|
|
|
+ OBJLoaderRef.setMaterials( payload );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'run':
|
|
|
+
|
|
|
+ OBJLoaderRef.run( payload );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ console.error( 'OBJLoader: Received unknown command: ' + payload.cmd );
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log( 'Command state after: ' + OBJLoaderRef.cmdState );
|
|
|
+ };
|
|
|
+
|
|
|
+ return OBJLoaderRunner;
|
|
|
+ })();
|
|
|
+
|
|
|
+ var buildObject = function ( fullName, object ) {
|
|
|
+ var objectString = fullName + ' = {\n';
|
|
|
+ var part;
|
|
|
+ for ( var name in object ) {
|
|
|
+
|
|
|
+ part = object[ name ];
|
|
|
+ if ( typeof( part ) === 'string' || part instanceof String ) {
|
|
|
+
|
|
|
+ part = part.replace( '\n', '\\n' );
|
|
|
+ part = part.replace( '\r', '\\r' );
|
|
|
+ objectString += '\t' + name + ': "' + part + '",\n';
|
|
|
+
|
|
|
+ } else if ( part instanceof Array ) {
|
|
|
+
|
|
|
+ objectString += '\t' + name + ': [' + part + '],\n';
|
|
|
+
|
|
|
+ } else if ( Number.isInteger( part ) ) {
|
|
|
+
|
|
|
+ objectString += '\t' + name + ': ' + part + ',\n';
|
|
|
+
|
|
|
+ } else if ( typeof part === 'function' ) {
|
|
|
+
|
|
|
+ objectString += '\t' + name + ': ' + part + ',\n';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ objectString += '}\n\n';
|
|
|
+
|
|
|
+ return objectString;
|
|
|
+ };
|
|
|
+
|
|
|
+ var buildSingelton = function ( fullName, internalName, object ) {
|
|
|
+ var objectString = fullName + ' = (function () {\n\n';
|
|
|
+ objectString += '\t' + object.prototype.constructor.toString() + '\n\n';
|
|
|
+
|
|
|
+ var funcString;
|
|
|
+ var objectPart;
|
|
|
+ for ( var name in object.prototype ) {
|
|
|
+
|
|
|
+ objectPart = object.prototype[ name ];
|
|
|
+ if ( typeof objectPart === 'function' ) {
|
|
|
+
|
|
|
+ funcString = objectPart.toString();
|
|
|
+ objectString += '\t' + internalName + '.prototype.' + name + ' = ' + funcString + ';\n\n';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ objectString += '\treturn ' + internalName + ';\n';
|
|
|
+ objectString += '})();\n\n';
|
|
|
+
|
|
|
+ return objectString;
|
|
|
+ };
|
|
|
+
|
|
|
+ this.workerCode = '';
|
|
|
+ this.workerCode += '/**\n';
|
|
|
+ this.workerCode += ' * This code was constructed by WWOBJLoader2._buildWebWorkerCode\n';
|
|
|
+ this.workerCode += ' */\n\n';
|
|
|
+
|
|
|
+ // parser re-construction
|
|
|
+ var objLoaderHelper = new THREE.OBJLoader2();
|
|
|
+ this.workerCode += objLoaderHelper._buildWebWorkerCode( buildObject, buildSingelton );
|
|
|
+
|
|
|
+ // web worker construction
|
|
|
+ this.workerCode += buildSingelton( 'OBJLoader', 'OBJLoader', wwDef );
|
|
|
+ this.workerCode += buildSingelton( 'MeshCreator', 'MeshCreator', wwMeshCreatorDef );
|
|
|
+ this.workerCode += 'OBJLoaderRef = new OBJLoader();\n\n';
|
|
|
+ this.workerCode += buildSingelton( 'OBJLoaderRunner', 'OBJLoaderRunner', wwLoaderRunnerDef );
|
|
|
+ this.workerCode += 'new OBJLoaderRunner();\n\n';
|
|
|
+
|
|
|
+ console.timeEnd( 'buildWebWorkerCode' );
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.workerCode;
|
|
|
+ };
|
|
|
+
|
|
|
+ return WWOBJLoader2;
|
|
|
+
|
|
|
+})();
|
|
|
+
|
|
|
+/**
|
|
|
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ from given ArrayBuffer and MTL from given String
|
|
|
+ *
|
|
|
+ * @param {string} modelName Overall name of the model
|
|
|
+ * @param {Uint8Array} objAsArrayBuffer OBJ file content as ArrayBuffer
|
|
|
+ * @param {string} pathTexture Path to texture files
|
|
|
+ * @param {string} mtlAsString MTL file content as string
|
|
|
+ * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
|
|
|
+ * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
|
|
|
+ * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
|
|
|
+ *
|
|
|
+ * @returns {{modelName: string, dataAvailable: boolean, objAsArrayBuffer: null, pathTexture: null, mtlAsString: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer = function ( modelName, objAsArrayBuffer, pathTexture, mtlAsString, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
|
|
|
+
|
|
|
+ var data = {
|
|
|
+ modelName: ( modelName == null ) ? 'none' : modelName,
|
|
|
+ dataAvailable: true,
|
|
|
+ objAsArrayBuffer: ( objAsArrayBuffer == null ) ? null : objAsArrayBuffer,
|
|
|
+ pathTexture: ( pathTexture == null ) ? null : pathTexture,
|
|
|
+ mtlAsString: ( mtlAsString == null ) ? null : mtlAsString,
|
|
|
+ sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
|
|
|
+ streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
|
|
|
+ requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
|
|
|
+ };
|
|
|
+
|
|
|
+ return data;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ and MTL from files
|
|
|
+ *
|
|
|
+ * @param {string} modelName Overall name of the model
|
|
|
+ * @param {string} pathObj Path to OBJ file
|
|
|
+ * @param {string} fileObj OBJ file name
|
|
|
+ * @param {string} pathTexture Path to texture files
|
|
|
+ * @param {string} fileMtl MTL file name
|
|
|
+ * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
|
|
|
+ * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
|
|
|
+ * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
|
|
|
+ *
|
|
|
+ * @returns {{modelName: string, dataAvailable: boolean, pathObj: null, fileObj: null, pathTexture: null, fileMtl: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+THREE.OBJLoader2.WWOBJLoader2.PrepDataFile = function ( modelName, pathObj, fileObj, pathTexture, fileMtl, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
|
|
|
+
|
|
|
+ var data = {
|
|
|
+ modelName: ( modelName == null ) ? 'none' : modelName,
|
|
|
+ dataAvailable: false,
|
|
|
+ pathObj: ( pathObj == null ) ? null : pathObj,
|
|
|
+ fileObj: ( fileObj == null ) ? null : fileObj,
|
|
|
+ pathTexture: ( pathTexture == null ) ? null : pathTexture,
|
|
|
+ fileMtl: ( fileMtl == null ) ? null : fileMtl,
|
|
|
+ sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
|
|
|
+ streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
|
|
|
+ requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
|
|
|
+ };
|
|
|
+
|
|
|
+ return data;
|
|
|
+};
|
|
|
+/**
|
|
|
+ * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
|
|
|
+ * Use:
|
|
|
+ * prepareWorkers
|
|
|
+ * enqueueForRun
|
|
|
+ * processQueue
|
|
|
+ * deregister
|
|
|
+ *
|
|
|
+ * @class
|
|
|
+ */
|
|
|
+THREE.OBJLoader2.WWOBJLoader2Director = (function () {
|
|
|
+
|
|
|
+ var MAX_WEB_WORKER = 16;
|
|
|
+ var MAX_QUEUE_SIZE = 1024;
|
|
|
+
|
|
|
+ function WWOBJLoader2Director() {
|
|
|
+ this.maxQueueSize = MAX_QUEUE_SIZE ;
|
|
|
+ this.maxWebWorkers = MAX_WEB_WORKER;
|
|
|
+ this.crossOrigin = null;
|
|
|
+
|
|
|
+ this.workerDescription = {
|
|
|
+ prototypeDef: THREE.OBJLoader2.WWOBJLoader2.prototype,
|
|
|
+ callbacks: {},
|
|
|
+ webWorkers: [],
|
|
|
+ codeBuffer: null
|
|
|
+ };
|
|
|
+ this.objectsCompleted = 0;
|
|
|
+ this.instructionQueue = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the maximum length of the instruction queue.
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ *
|
|
|
+ * @returns {number}
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.getMaxQueueSize = function () {
|
|
|
+ return this.maxQueueSize;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the maximum number of workers.
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ *
|
|
|
+ * @returns {number}
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.getMaxWebWorkers = function () {
|
|
|
+ return this.maxWebWorkers;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the CORS string to be used.
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ *
|
|
|
+ * @param {string} crossOrigin CORS value
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.setCrossOrigin = function ( crossOrigin ) {
|
|
|
+ this.crossOrigin = crossOrigin;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ *
|
|
|
+ * @param {callback[]} callbacks Register callbacks for all web workers:
|
|
|
+ * { progress: null, completedLoading: null, errorWhileLoading: null, materialsLoaded: null, meshLoaded: null }
|
|
|
+ * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
|
|
|
+ * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.prepareWorkers = function ( callbacks, maxQueueSize, maxWebWorkers ) {
|
|
|
+ if ( callbacks != null ) {
|
|
|
+
|
|
|
+ for ( var key in callbacks ) {
|
|
|
+
|
|
|
+ if ( callbacks.hasOwnProperty( key ) ) this.workerDescription.callbacks[ key ] = callbacks[ key ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
|
|
|
+ this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
|
|
|
+ this.objectsCompleted = 0;
|
|
|
+ this.instructionQueue = [];
|
|
|
+
|
|
|
+ var start = this.workerDescription.webWorkers.length;
|
|
|
+ if ( start < this.maxWebWorkers ) {
|
|
|
+
|
|
|
+ for ( i = start; i < this.maxWebWorkers; i ++ ) {
|
|
|
+
|
|
|
+ webWorker = this._buildWebWorker();
|
|
|
+ this.workerDescription.webWorkers[ i ] = webWorker;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ for ( var webWorker, i = start - 1; i >= this.maxWebWorkers; i-- ) {
|
|
|
+
|
|
|
+ webWorker = this.workerDescription.webWorkers[ i ];
|
|
|
+ webWorker.setRequestTerminate( true );
|
|
|
+
|
|
|
+ this.workerDescription.webWorkers.pop();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Store run instructions in internal instructionQueue
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ *
|
|
|
+ * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.enqueueForRun = function ( runParams ) {
|
|
|
+ if ( this.instructionQueue.length < this.maxQueueSize ) {
|
|
|
+ this.instructionQueue.push( runParams );
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Process the instructionQueue until it is depleted
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.processQueue = function () {
|
|
|
+ if ( this.instructionQueue.length === 0 ) return;
|
|
|
+
|
|
|
+ var webWorker;
|
|
|
+ var runParams;
|
|
|
+ var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
|
|
|
+ for ( var i = 0; i < length; i++ ) {
|
|
|
+
|
|
|
+ webWorker = this.workerDescription.webWorkers[ i ];
|
|
|
+ runParams = this.instructionQueue[ 0 ];
|
|
|
+ webWorker.prepareRun( runParams );
|
|
|
+ webWorker.run();
|
|
|
+ this.instructionQueue.shift();
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WWOBJLoader2Director.prototype._buildWebWorker = function () {
|
|
|
+ var webWorker = Object.create( this.workerDescription.prototypeDef );
|
|
|
+ webWorker._init();
|
|
|
+ if ( this.crossOrigin != null ) webWorker.setCrossOrigin( this.crossOrigin );
|
|
|
+
|
|
|
+ // Ensure code string is built once and then it is just passed on to every new instance
|
|
|
+ if ( this.workerDescription.codeBuffer == null ) {
|
|
|
+
|
|
|
+ this.workerDescription.codeBuffer = webWorker._buildWebWorkerCode();
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ webWorker._buildWebWorkerCode( this.workerDescription.codeBuffer );
|
|
|
+
|
|
|
+ }
|
|
|
+ for ( var key in this.workerDescription.callbacks ) {
|
|
|
+
|
|
|
+ if ( webWorker.callbacks.hasOwnProperty( key ) && this.workerDescription.callbacks.hasOwnProperty( key ) ) {
|
|
|
+
|
|
|
+ webWorker.callbacks[ key ] = this.workerDescription.callbacks[ key ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ var scope = this;
|
|
|
+ var managerCompletedLoading = function ( modelName, instanceNo, requestTerminate ) {
|
|
|
+ scope.objectsCompleted++;
|
|
|
+ if ( ! requestTerminate ) {
|
|
|
+
|
|
|
+ var rekick = scope.workerDescription.webWorkers[ instanceNo ];
|
|
|
+ var runParams = scope.instructionQueue[ 0 ];
|
|
|
+ if ( runParams != null ) {
|
|
|
+
|
|
|
+ rekick.prepareRun( runParams );
|
|
|
+ rekick.run();
|
|
|
+ scope.instructionQueue.shift();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ webWorker.callbacks.director[ 'completedLoading' ] = managerCompletedLoading;
|
|
|
+ webWorker.instanceNo = this.workerDescription.webWorkers.length;
|
|
|
+ this.workerDescription.webWorkers.push( webWorker );
|
|
|
+ return webWorker;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Terminate all workers
|
|
|
+ * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
|
|
|
+ */
|
|
|
+ WWOBJLoader2Director.prototype.deregister = function () {
|
|
|
+ console.log( 'WWOBJLoader2Director received the unregister call. Terminating all workers!' );
|
|
|
+ for ( var i = 0, webWorker, length = this.workerDescription.webWorkers.length; i < length; i++ ) {
|
|
|
+
|
|
|
+ webWorker = this.workerDescription.webWorkers[ i ];
|
|
|
+ webWorker.setRequestTerminate( true );
|
|
|
+
|
|
|
+ }
|
|
|
+ this.workerDescription.callbacks = {};
|
|
|
+ this.workerDescription.webWorkers = [];
|
|
|
+ this.workerDescription.codeBuffer = null;
|
|
|
+ };
|
|
|
+
|
|
|
+ return WWOBJLoader2Director;
|
|
|
+
|
|
|
+})();
|