From cb2280ffc2dacdace87ee513b5c6fe3ec7c961c7 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Sun, 17 Dec 2017 17:47:34 +0100 Subject: [PATCH] OBJLoader2 V2.2.1 Bugfixes: - Original Repo Issue 27: Multiple mesh definitions (vertices, normals, uvs and faces) within one group are now supported. Needed to remove early release of vertex data from memory. - Original Repo Issue 28: Negative face indices are now supported. - Original Repo Issue 29: Cleaned loadMtl API and clarified `WorkerSupport.run` contract. Transferable is automatically attached if data is an ArrayBuffer. --- examples/js/loaders/LoaderSupport.js | 14 +- examples/js/loaders/OBJLoader2.js | 182 +++++++++--------------- examples/webgl_loader_obj2.html | 6 +- examples/webgl_loader_obj2_options.html | 7 +- 4 files changed, 82 insertions(+), 127 deletions(-) diff --git a/examples/js/loaders/LoaderSupport.js b/examples/js/loaders/LoaderSupport.js index d3bd705160..b844948ec7 100644 --- a/examples/js/loaders/LoaderSupport.js +++ b/examples/js/loaders/LoaderSupport.js @@ -277,7 +277,7 @@ THREE.LoaderSupport.ResourceDescriptor = (function () { if ( urlParts.length < 2 ) { this.path = null; - this.name = this.name = url; + this.name = url; this.url = url; } else { @@ -989,7 +989,7 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { */ THREE.LoaderSupport.WorkerSupport = (function () { - var WORKER_SUPPORT_VERSION = '2.0.0'; + var WORKER_SUPPORT_VERSION = '2.0.1'; var Validator = THREE.LoaderSupport.Validator; @@ -1110,7 +1110,15 @@ THREE.LoaderSupport.WorkerSupport = (function () { LoaderWorker.prototype._postMessage = function () { if ( Validator.isValid( this.queuedMessage ) && Validator.isValid( this.worker ) ) { - this.worker.postMessage( this.queuedMessage ); + if ( this.queuedMessage.data.input instanceof ArrayBuffer ) { + + this.worker.postMessage( this.queuedMessage, [ this.queuedMessage.data.input ] ); + + } else { + + this.worker.postMessage( this.queuedMessage ); + + } } }; diff --git a/examples/js/loaders/OBJLoader2.js b/examples/js/loaders/OBJLoader2.js index f3b71ef569..4e497ffb23 100644 --- a/examples/js/loaders/OBJLoader2.js +++ b/examples/js/loaders/OBJLoader2.js @@ -18,7 +18,7 @@ if ( THREE.LoaderSupport === undefined ) console.error( '"THREE.LoaderSupport" i */ THREE.OBJLoader2 = (function () { - var OBJLOADER2_VERSION = '2.2.0'; + var OBJLOADER2_VERSION = '2.2.1'; var LoaderBase = THREE.LoaderSupport.LoaderBase; var Validator = THREE.LoaderSupport.Validator; var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; @@ -297,8 +297,7 @@ THREE.OBJLoader2 = (function () { input: content, options: null } - }, - [ content.buffer ] + } ); }; @@ -354,7 +353,6 @@ THREE.OBJLoader2 = (function () { }; this.logger = new ConsoleLogger(); this.totalBytes = 0; - this.reachedFaces = false; }; Parser.prototype.setUseAsync = function ( useAsync ) { @@ -560,28 +558,7 @@ THREE.OBJLoader2 = (function () { switch ( buffer[ 0 ] ) { case Consts.LINE_V: - // object complete instance required if reached faces already (= reached next block of v) - if ( this.reachedFaces ) { - - if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) { - - throw 'Vertex Colors were detected, but vertex count and color count do not match!'; - - } - // only when new vertices after faces are detected complete new mesh is prepared (reset offsets, etc) - this.processCompletedMesh( this.rawMesh.objectName, this.rawMesh.groupName, currentByte, true ); - this.reachedFaces = false; - - } - if ( bufferPointer === 4 ) { - - this.rawMesh.pushVertex( buffer ) - - } else { - - this.rawMesh.pushVertexAndVertextColors( buffer ); - - } + this.rawMesh.pushVertex( buffer, bufferPointer > 4 ); break; case Consts.LINE_VT: @@ -593,7 +570,6 @@ THREE.OBJLoader2 = (function () { break; case Consts.LINE_F: - this.reachedFaces = true; this.rawMesh.processFaces( buffer, bufferPointer, countSlashes( slashSpacePattern, slashSpacePatternPointer ) ); break; @@ -607,12 +583,15 @@ THREE.OBJLoader2 = (function () { break; case Consts.LINE_G: - this.processCompletedMesh( this.rawMesh.objectName, concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), currentByte, false ); + // 'g' leads to creation of mesh if valid data (faces declaration was done before), otherwise only groupName gets set + this.processCompletedMesh( currentByte ); + this.rawMesh.pushGroup( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) ); flushStringBuffer( buffer, bufferPointer ); break; case Consts.LINE_O: - this.processCompletedMesh( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), this.rawMesh.groupName, currentByte, false ); + // 'o' is pure meta-information and does not result in creation of new meshes + this.rawMesh.pushObject( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) ); flushStringBuffer( buffer, bufferPointer ); break; @@ -645,43 +624,40 @@ THREE.OBJLoader2 = (function () { '\n\tReal RawMeshSubGroup count: ' + report.subGroups; }; - Parser.prototype.processCompletedMesh = function ( objectName, groupName, currentByte, beginNewObject ) { + Parser.prototype.processCompletedMesh = function ( currentByte ) { var result = this.rawMesh.finalize(); if ( Validator.isValid( result ) ) { - this.inputObjectCount++; + if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) { + + throw 'Vertex Colors were detected, but vertex count and color count do not match!'; + + } if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) ); + this.inputObjectCount++; + this.buildMesh( result, currentByte ); var progressBytesPercent = currentByte / this.totalBytes; this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent ); - this.rawMesh = beginNewObject ? this.rawMesh.newInstanceResetOffsets() : this.rawMesh.newInstanceKeepOffsets(); + this.rawMesh.reset( this.rawMesh.smoothingGroup.splitMaterials ); + return true; + + } else { + + return false; } - // Always update group an object name in case they have changed and are valid - if ( this.rawMesh.objectName !== objectName && Validator.isValid( objectName ) ) this.rawMesh.pushObject( objectName ); - if ( this.rawMesh.groupName !== groupName && Validator.isValid( groupName ) ) this.rawMesh.pushGroup( groupName ); }; Parser.prototype.finalize = function ( currentByte ) { this.logger.logInfo( 'Global output object count: ' + this.outputObjectCount ); - var result = Validator.isValid( this.rawMesh ) ? this.rawMesh.finalize() : null; - if ( Validator.isValid( result ) ) { - - this.inputObjectCount++; - if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) ); - this.buildMesh( result, currentByte ); - - if ( this.logger.isEnabled() ) { - - var parserFinalReport = 'Overall counts: ' + - '\n\tVertices: ' + this.counts.vertices + - '\n\tFaces: ' + this.counts.faces + - '\n\tMultiple definitions: ' + this.counts.doubleIndicesCount; - this.logger.logInfo( parserFinalReport ); + if ( this.processCompletedMesh( currentByte ) && this.logger.isEnabled() ) { - } - var progressBytesPercent = currentByte / this.totalBytes; - this.callbackProgress( 'Completed Parsing: 100.00%', progressBytesPercent ); + var parserFinalReport = 'Overall counts: ' + + '\n\tVertices: ' + this.counts.vertices + + '\n\tFaces: ' + this.counts.faces + + '\n\tMultiple definitions: ' + this.counts.doubleIndicesCount; + this.logger.logInfo( parserFinalReport ); } }; @@ -836,11 +812,11 @@ THREE.OBJLoader2 = (function () { if ( this.logger.isDebug() ) { var materialIndexLine = Validator.isValid( selectedMaterialIndex ) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : ''; var createdReport = 'Output Object no.: ' + this.outputObjectCount + - '\n\t\tobjectName: ' + rawObjectDescription.objectName + '\n\t\tgroupName: ' + rawObjectDescription.groupName + - '\n\t\tmaterialName: ' + rawObjectDescription.materialName + materialIndexLine + + '\n\t\tmaterialName: ' + rawObjectDescription.materialName + '\n\t\tsmoothingGroup: ' + rawObjectDescription.smoothingGroup + + '\n\t\tobjectName: ' + rawObjectDescription.objectName + '\n\t\t#vertices: ' + rawObjectDescription.vertices.length / 3 + '\n\t\t#indices: ' + rawObjectDescription.indices.length + '\n\t\t#colors: ' + rawObjectDescription.colors.length / 3 + @@ -892,82 +868,51 @@ THREE.OBJLoader2 = (function () { */ var RawMesh = (function () { - function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals, activeMtlName ) { - this.globalVertexOffset = 1; - this.globalUvOffset = 1; - this.globalNormalOffset = 1; - + function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals ) { this.vertices = []; this.colors = []; this.normals = []; this.uvs = []; - // faces are stored according combined index of group, material and smoothingGroup (0 or not) - this.activeMtlName = Validator.verifyInput( activeMtlName, '' ); + this.useIndices = useIndices === true; + this.disregardNormals = disregardNormals === true; + this.objectName = ''; this.groupName = ''; + this.activeMtlName = ''; this.mtllibName = ''; + this.reset( materialPerSmoothingGroup ); + } + + RawMesh.prototype.reset = function ( materialPerSmoothingGroup ) { + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.subGroups = []; + this.subGroupInUse = null; this.smoothingGroup = { splitMaterials: materialPerSmoothingGroup === true, normalized: -1, real: -1 }; - this.useIndices = useIndices === true; - this.disregardNormals = disregardNormals === true; - - this.mtlCount = 0; - this.smoothingGroupCount = 0; - - this.subGroups = []; - this.subGroupInUse = null; // this default index is required as it is possible to define faces without 'g' or 'usemtl' this.pushSmoothingGroup( 1 ); this.doubleIndicesCount = 0; this.faceCount = 0; - } - - RawMesh.prototype.newInstanceResetOffsets = function () { - var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName ); - - // move indices forward - newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; - newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; - newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; - - return newRawObject; - }; - - RawMesh.prototype.newInstanceKeepOffsets = function () { - var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName ); - // keep objectName - newRawObject.pushObject( this.objectName ); - - // keep current buffers and indices forward - newRawObject.vertices = this.vertices; - newRawObject.colors = this.colors; - newRawObject.uvs = this.uvs; - newRawObject.normals = this.normals; - newRawObject.globalVertexOffset = this.globalVertexOffset; - newRawObject.globalUvOffset = this.globalUvOffset; - newRawObject.globalNormalOffset = this.globalNormalOffset; - - return newRawObject; + this.mtlCount = 0; + this.smoothingGroupCount = 0; }; - RawMesh.prototype.pushVertex = function ( buffer ) { + RawMesh.prototype.pushVertex = function ( buffer, haveVertexColors ) { this.vertices.push( parseFloat( buffer[ 1 ] ) ); this.vertices.push( parseFloat( buffer[ 2 ] ) ); this.vertices.push( parseFloat( buffer[ 3 ] ) ); - }; + if ( haveVertexColors ) { - RawMesh.prototype.pushVertexAndVertextColors = function ( buffer ) { - this.vertices.push( parseFloat( buffer[ 1 ] ) ); - this.vertices.push( parseFloat( buffer[ 2 ] ) ); - this.vertices.push( parseFloat( buffer[ 3 ] ) ); - this.colors.push( parseFloat( buffer[ 4 ] ) ); - this.colors.push( parseFloat( buffer[ 5 ] ) ); - this.colors.push( parseFloat( buffer[ 6 ] ) ); + this.colors.push( parseFloat( buffer[ 4 ] ) ); + this.colors.push( parseFloat( buffer[ 5 ] ) ); + this.colors.push( parseFloat( buffer[ 6 ] ) ); + + } }; RawMesh.prototype.pushUv = function ( buffer ) { @@ -981,6 +926,10 @@ THREE.OBJLoader2 = (function () { this.normals.push( parseFloat( buffer[ 3 ] ) ); }; + RawMesh.prototype.pushGroup = function ( groupName ) { + this.groupName = Validator.verifyInput( groupName, '' ); + }; + RawMesh.prototype.pushObject = function ( objectName ) { this.objectName = Validator.verifyInput( objectName, '' ); }; @@ -989,10 +938,6 @@ THREE.OBJLoader2 = (function () { this.mtllibName = Validator.verifyInput( mtllibName, '' ); }; - RawMesh.prototype.pushGroup = function ( groupName ) { - this.groupName = Validator.verifyInput( groupName, '' ); - }; - RawMesh.prototype.pushUsemtl = function ( mtlName ) { if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; this.activeMtlName = mtlName; @@ -1037,7 +982,7 @@ THREE.OBJLoader2 = (function () { // "f vertex ..." if ( slashesCount === 0 ) { - for ( i = 2, length = bufferLength - 1; i < length; i ++ ) { + for ( i = 2, length = bufferLength; i < length; i ++ ) { this.buildFace( buffer[ 1 ] ); this.buildFace( buffer[ i ] ); @@ -1081,20 +1026,22 @@ THREE.OBJLoader2 = (function () { } }; + RawMesh.prototype.buildFace = function ( faceIndexV, faceIndexU, faceIndexN ) { var sgiu = this.subGroupInUse; if ( this.disregardNormals ) faceIndexN = undefined; var scope = this; var updateRawObjectDescriptionInUse = function () { - var indexPointerV = ( parseInt( faceIndexV ) - scope.globalVertexOffset ) * 3; - var indexPointerC = scope.colors.length > 0 ? indexPointerV : null; + var faceIndexVi = parseInt( faceIndexV ); + var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 ); var vertices = sgiu.vertices; vertices.push( scope.vertices[ indexPointerV++ ] ); vertices.push( scope.vertices[ indexPointerV++ ] ); vertices.push( scope.vertices[ indexPointerV ] ); + var indexPointerC = scope.colors.length > 0 ? indexPointerV : null; if ( indexPointerC !== null ) { var colors = sgiu.colors; @@ -1106,7 +1053,8 @@ THREE.OBJLoader2 = (function () { if ( faceIndexU ) { - var indexPointerU = ( parseInt( faceIndexU ) - scope.globalUvOffset ) * 2; + var faceIndexUi = parseInt( faceIndexU ); + var indexPointerU = 2 * ( faceIndexUi > 0 ? faceIndexUi - 1 : faceIndexUi + scope.uvs.length / 2 ); var uvs = sgiu.uvs; uvs.push( scope.uvs[ indexPointerU++ ] ); uvs.push( scope.uvs[ indexPointerU ] ); @@ -1114,7 +1062,8 @@ THREE.OBJLoader2 = (function () { } if ( faceIndexN ) { - var indexPointerN = ( parseInt( faceIndexN ) - scope.globalNormalOffset ) * 3; + var faceIndexNi = parseInt( faceIndexN ); + var indexPointerN = 3 * ( faceIndexNi > 0 ? faceIndexNi - 1 : faceIndexNi + scope.normals.length / 3 ); var normals = sgiu.normals; normals.push( scope.normals[ indexPointerN++ ] ); normals.push( scope.normals[ indexPointerN++ ] ); @@ -1348,12 +1297,11 @@ THREE.OBJLoader2 = (function () { * @memberOf THREE.OBJLoader2 * * @param {string} url URL to the file - * @param {string} name The name of the object * @param {Object} content The file content as arraybuffer or text * @param {function} callbackOnLoad * @param {string} [crossOrigin] CORS value */ - OBJLoader2.prototype.loadMtl = function ( url, name, content, callbackOnLoad, crossOrigin ) { + OBJLoader2.prototype.loadMtl = function ( url, content, callbackOnLoad, crossOrigin ) { var resource = new THREE.LoaderSupport.ResourceDescriptor( url, 'MTL' ); resource.setContent( content ); this._loadMtl( resource, callbackOnLoad, crossOrigin ); diff --git a/examples/webgl_loader_obj2.html b/examples/webgl_loader_obj2.html index 179f9f4c2e..860ae300d4 100644 --- a/examples/webgl_loader_obj2.html +++ b/examples/webgl_loader_obj2.html @@ -1,7 +1,7 @@ - three.js webgl - OBJLoader2 + three.js webgl - OBJLoader2 basic usage @@ -152,12 +152,10 @@ var onLoadMtl = function ( materials ) { objLoader.setModelName( modelName ); objLoader.setMaterials( materials ); - objLoader.setUseIndices( true ); - objLoader.setDisregardNormals( false ); objLoader.getLogger().setDebug( true ); objLoader.load( 'obj/female02/female02.obj', callbackOnLoad, null, null, null, false ); }; - objLoader.loadMtl( 'obj/female02/female02.mtl', 'female02.mtl', null, onLoadMtl ); + objLoader.loadMtl( 'obj/female02/female02.mtl', null, onLoadMtl ); }; OBJLoader2Example.prototype._reportProgress = function( event ) { diff --git a/examples/webgl_loader_obj2_options.html b/examples/webgl_loader_obj2_options.html index c57df98142..4838976141 100644 --- a/examples/webgl_loader_obj2_options.html +++ b/examples/webgl_loader_obj2_options.html @@ -178,7 +178,7 @@ } ); }; - objLoader.loadMtl( 'obj/female02/female02.mtl', 'female02.mtl', null, onLoadMtl ); + objLoader.loadMtl( 'obj/female02/female02.mtl', null, onLoadMtl ); }; @@ -231,9 +231,10 @@ var onLoadMtl = function ( materials ) { objLoader.setModelName( modelName ); objLoader.setMaterials( materials ); + objLoader.setUseIndices( true ); objLoader.load( 'obj/male02/male02.obj', callbackOnLoad, null, null, null, false ); }; - objLoader.loadMtl( 'obj/male02/male02.mtl', 'female02.mtl', null, onLoadMtl ); + objLoader.loadMtl( 'obj/male02/male02.mtl', null, onLoadMtl ); }; WWOBJLoader2Example.prototype.useLoadAsync = function () { @@ -262,7 +263,7 @@ objLoader.terminateWorkerOnLoad = false; objLoader.load( 'obj/walt/WaltHead.obj', callbackOnLoad, null, null, null, true ); }; - objLoader.loadMtl( 'obj/walt//WaltHead.mtl', 'WaltHead.mtl', null, onLoadMtl ); + objLoader.loadMtl( 'obj/walt/WaltHead.mtl', null, onLoadMtl ); }; WWOBJLoader2Example.prototype.useRunSync = function () { -- GitLab