From 26db16d8920de3d8e9073e6b2ca2671bf5635c51 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 10:42:10 -0400 Subject: [PATCH 01/23] Fixed normalizing Windows paths --- lib/loadMtl.js | 34 +++---- lib/loadObj.js | 9 +- .../box-texture-options.mtl | 38 ++++---- .../box-texture-options.obj | 92 +++++++++--------- .../box-windows-paths/box-windows-paths.obj | 46 +++++++++ .../materials/box-windows-paths.mtl | 13 +++ .../materials/images/cesium.png | Bin 0 -> 7665 bytes specs/lib/loadObjSpec.js | 10 ++ 8 files changed, 156 insertions(+), 86 deletions(-) create mode 100644 specs/data/box-windows-paths/box-windows-paths.obj create mode 100644 specs/data/box-windows-paths/materials/box-windows-paths.mtl create mode 100644 specs/data/box-windows-paths/materials/images/cesium.png diff --git a/lib/loadMtl.js b/lib/loadMtl.js index 4299041..25fa3b9 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -81,19 +81,15 @@ function loadMtl(mtlPath, options) { materials.push(material); } - /** - * Removes texture options from texture name - * NOTE: assumes no spaces in texture name - * - * @param {String} name - * @returns {String} The clean texture name - */ - function cleanTextureName (name) { + function normalizeTexturePath(texturePath, mtlDirectory) { + // Removes texture options from texture name + // Assumes no spaces in texture name var re = /-(bm|t|s|o|blendu|blendv|boost|mm|texres|clamp|imfchan|type)/; - if (re.test(name)) { - return name.split(/\s+/).pop(); + if (re.test(texturePath)) { + texturePath = texturePath.split(/\s+/).pop(); } - return name; + texturePath = texturePath.replace(/\\/g, '/'); + return path.normalize(path.join(mtlDirectory, texturePath)); } function parseLine(line) { @@ -144,31 +140,31 @@ function loadMtl(mtlPath, options) { material.alpha = correctAlpha(1.0 - parseFloat(value)); } else if (/^map_Ka /i.test(line)) { if (!defined(overridingAmbientTexture)) { - material.ambientTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(7).trim())); + material.ambientTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } } else if (/^map_Ke /i.test(line)) { if (!defined(overridingEmissiveTexture)) { - material.emissiveTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(7).trim())); + material.emissiveTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } } else if (/^map_Kd /i.test(line)) { if (!defined(overridingDiffuseTexture)) { - material.diffuseTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(7).trim())); + material.diffuseTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } } else if (/^map_Ks /i.test(line)) { if (!defined(overridingSpecularTexture)) { - material.specularTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(7).trim())); + material.specularTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } } else if (/^map_Ns /i.test(line)) { if (!defined(overridingSpecularShininessTexture)) { - material.specularShininessTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(7).trim())); + material.specularShininessTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } } else if (/^map_Bump /i.test(line)) { if (!defined(overridingNormalTexture)) { - material.normalTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(9).trim())); + material.normalTexture = normalizeTexturePath(line.substring(9).trim(), mtlDirectory); } } else if (/^map_d /i.test(line)) { if (!defined(overridingAlphaTexture)) { - material.alphaTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(6).trim())); + material.alphaTexture = normalizeTexturePath(line.substring(6).trim(), mtlDirectory); } } } @@ -244,7 +240,7 @@ function loadMaterialTexture(material, name, textureOptions, mtlDirectory, textu var texturePromise = texturePromiseMap[texturePath]; if (!defined(texturePromise)) { - var shallowPath = path.resolve(path.join(mtlDirectory, path.basename(texturePath))); + var shallowPath = path.join(mtlDirectory, path.basename(texturePath)); if (options.secure && outsideDirectory(texturePath, mtlDirectory)) { // Try looking for the texture in the same directory as the obj options.logger('Texture file is outside of the mtl directory and the secure flag is true. Attempting to read the texture file from within the obj directory instead.'); diff --git a/lib/loadObj.js b/lib/loadObj.js index f99b4c6..bce53ea 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -540,6 +540,11 @@ function finishLoading(nodes, mtlPaths, objPath, options) { }); } +function normalizeMtlPath(mtlPath, objDirectory) { + mtlPath = mtlPath.replace(/\\/g, '/'); + return path.normalize(path.join(objDirectory, mtlPath)); +} + function loadMtls(mtlPaths, objPath, options) { var objDirectory = path.dirname(objPath); var materials = []; @@ -550,8 +555,8 @@ function loadMtls(mtlPaths, objPath, options) { }); return Promise.map(mtlPaths, function(mtlPath) { - mtlPath = path.resolve(objDirectory, mtlPath); - var shallowPath = path.resolve(path.join(objDirectory, path.basename(mtlPath))); + mtlPath = normalizeMtlPath(mtlPath, objDirectory); + var shallowPath = path.join(objDirectory, path.basename(mtlPath)); if (options.secure && outsideDirectory(mtlPath, objDirectory)) { // Try looking for the .mtl in the same directory as the obj options.logger('The material file is outside of the obj directory and the secure flag is true. Attempting to read the material file from within the obj directory instead.'); diff --git a/specs/data/box-texture-options/box-texture-options.mtl b/specs/data/box-texture-options/box-texture-options.mtl index 0d45c3b..3434533 100644 --- a/specs/data/box-texture-options/box-texture-options.mtl +++ b/specs/data/box-texture-options/box-texture-options.mtl @@ -1,19 +1,19 @@ -# Blender MTL File: 'box.blend' -# Material Count: 1 - -newmtl Material -Ns 96.078431 -Ka 0.200000 0.200000 0.200000 -Kd 0.640000 0.640000 0.640000 -Ks 0.500000 0.500000 0.500000 -Ke 0.100000 0.100000 0.100000 -Ni 1.000000 -d 0.900000 -Tr 0.100000 -map_Ka -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 ambient.gif -map_Ke -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 emission.jpg -map_Kd -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 diffuse.png -map_Ks -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 specular.jpeg -map_Ns -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 shininess.png -map_Bump -bm 0.2 bump.png -illum 2 +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.200000 0.200000 0.200000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.100000 0.100000 0.100000 +Ni 1.000000 +d 0.900000 +Tr 0.100000 +map_Ka -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 ambient.gif +map_Ke -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 emission.jpg +map_Kd -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 diffuse.png +map_Ks -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 specular.jpeg +map_Ns -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 shininess.png +map_Bump -bm 0.2 bump.png +illum 2 diff --git a/specs/data/box-texture-options/box-texture-options.obj b/specs/data/box-texture-options/box-texture-options.obj index b8d1dde..3207f00 100644 --- a/specs/data/box-texture-options/box-texture-options.obj +++ b/specs/data/box-texture-options/box-texture-options.obj @@ -1,46 +1,46 @@ -# Blender v2.78 (sub 0) OBJ File: 'box.blend' -# www.blender.org -mtllib box-texture-options.mtl -o Cube -v -1.000000 -1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v 1.000000 1.000000 1.000000 -v 1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -vt 0.0000 0.0000 -vt 1.0000 0.0000 -vt 1.0000 1.0000 -vt 0.0000 1.0000 -vt 0.0000 0.0000 -vt 1.0000 0.0000 -vt 1.0000 1.0000 -vt 0.0000 1.0000 -vt 0.0000 0.0000 -vt 1.0000 0.0000 -vt 1.0000 1.0000 -vt 0.0000 1.0000 -vt 0.0000 0.0000 -vt 1.0000 0.0000 -vt 1.0000 1.0000 -vt 0.0000 1.0000 -vt 1.0000 0.0000 -vt 1.0000 1.0000 -vt 0.0000 0.0000 -vt 0.0000 1.0000 -vn -1.0000 0.0000 0.0000 -vn 0.0000 0.0000 -1.0000 -vn 1.0000 0.0000 0.0000 -vn 0.0000 0.0000 1.0000 -vn 0.0000 -1.0000 0.0000 -vn 0.0000 1.0000 0.0000 -usemtl Material -s off -f 1/1/1 2/2/1 4/3/1 3/4/1 -f 3/5/2 4/6/2 8/7/2 7/8/2 -f 7/9/3 8/10/3 6/11/3 5/12/3 -f 5/13/4 6/14/4 2/15/4 1/16/4 -f 3/5/5 7/17/5 5/18/5 1/16/5 -f 8/19/6 4/6/6 2/15/6 6/20/6 +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib box-texture-options.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl Material +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-windows-paths/box-windows-paths.obj b/specs/data/box-windows-paths/box-windows-paths.obj new file mode 100644 index 0000000..1c37d5b --- /dev/null +++ b/specs/data/box-windows-paths/box-windows-paths.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib materials\\box-windows-paths.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl Material +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-windows-paths/materials/box-windows-paths.mtl b/specs/data/box-windows-paths/materials/box-windows-paths.mtl new file mode 100644 index 0000000..22bca2d --- /dev/null +++ b/specs/data/box-windows-paths/materials/box-windows-paths.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd .\images\cesium.png diff --git a/specs/data/box-windows-paths/materials/images/cesium.png b/specs/data/box-windows-paths/materials/images/cesium.png new file mode 100644 index 0000000000000000000000000000000000000000..3b8baee1bce0079bd7afeb38d5e0b1bde9732ba5 GIT binary patch literal 7665 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 literal 0 HcmV?d00001 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index f1a04fb..bd266ac 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -34,6 +34,7 @@ var objExternalResourcesInRootPath = 'specs/data/box-external-resources-in-root/ var objTexturedPath = 'specs/data/box-textured/box-textured.obj'; var objMissingTexturePath = 'specs/data/box-missing-texture/box-missing-texture.obj'; var objSubdirectoriesPath = 'specs/data/box-subdirectories/box-textured.obj'; +var objWindowsPaths = 'specs/data/box-windows-paths/box-windows-paths.obj'; var objInvalidContentsPath = 'specs/data/box/box.mtl'; var objConcavePath = 'specs/data/concave/concave.obj'; var objUnnormalizedPath = 'specs/data/box-unnormalized/box-unnormalized.obj'; @@ -478,6 +479,15 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with windows paths', function(done) { + expect(loadObj(objWindowsPaths, options) + .then(function(data) { + var baseColorTexture = data.materials[0].pbrMetallicRoughness.baseColorTexture; + expect(baseColorTexture.name).toBe('cesium'); + expect(baseColorTexture.source).toBeDefined(); + }), done).toResolve(); + }); + it('throws when file has invalid contents', function(done) { expect(loadObj(objInvalidContentsPath, options), done).toRejectWith(RuntimeError); }); From 574d06db2b85734242a80721ac16f03891786bab Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 15:24:34 -0400 Subject: [PATCH 02/23] Adding position, normals, and uvs to primitive level rather than mesh level --- lib/createGltf.js | 81 +++++++++++++++++++------------------ lib/loadObj.js | 30 +++++++------- specs/lib/createGltfSpec.js | 32 +++++++-------- specs/lib/loadObjSpec.js | 36 ++++++++--------- 4 files changed, 90 insertions(+), 89 deletions(-) diff --git a/lib/createGltf.js b/lib/createGltf.js index 5b6c044..b5f3f89 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -131,10 +131,8 @@ function addBufferView(gltf, buffers, accessors, byteStride, target) { } function addBuffers(gltf, bufferState, name) { - // Positions and normals share the same byte stride so they can share the same bufferView - var positionsAndNormalsAccessors = bufferState.positionAccessors.concat(bufferState.normalAccessors); - var positionsAndNormalsBuffers = bufferState.positionBuffers.concat(bufferState.normalBuffers); - addBufferView(gltf, positionsAndNormalsBuffers, positionsAndNormalsAccessors, 12, WebGLConstants.ARRAY_BUFFER); + addBufferView(gltf, bufferState.positionBuffers, bufferState.positionAccessors, 12, WebGLConstants.ARRAY_BUFFER); + addBufferView(gltf, bufferState.normalBuffers, bufferState.normalAccessors, 12, WebGLConstants.ARRAY_BUFFER); addBufferView(gltf, bufferState.uvBuffers, bufferState.uvAccessors, 8, WebGLConstants.ARRAY_BUFFER); addBufferView(gltf, bufferState.indexBuffers, bufferState.indexAccessors, undefined, WebGLConstants.ELEMENT_ARRAY_BUFFER); @@ -297,68 +295,73 @@ function requiresUint32Indices(nodes) { var meshes = nodes[i].meshes; var meshesLength = meshes.length; for (var j = 0; j < meshesLength; ++j) { - // Reserve the 65535 index for primitive restart - var vertexCount = meshes[j].positions.length / 3; - if (vertexCount > 65534) { - return true; + var primitives = meshes[j].primitives; + var primitivesLength = primitives.length; + for (var k = 0; k < primitivesLength; ++k) { + // Reserve the 65535 index for primitive restart + var vertexCount = primitives[k].positions.length / 3; + if (vertexCount > 65534) { + return true; + } } } } return false; } -function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { - var hasPositions = mesh.positions.length > 0; - var hasNormals = mesh.normals.length > 0; - var hasUVs = mesh.uvs.length > 0; +function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index, options) { + var hasPositions = primitive.positions.length > 0; + var hasNormals = primitive.normals.length > 0; + var hasUVs = primitive.uvs.length > 0; - // Vertex attributes are shared by all primitives in the mesh var accessorIndex; var attributes = {}; if (hasPositions) { - accessorIndex = addVertexAttribute(gltf, mesh.positions, 3, mesh.name + '_positions'); + accessorIndex = addVertexAttribute(gltf, primitive.positions, 3, mesh.name + '_' + index + '_positions'); attributes.POSITION = accessorIndex; - bufferState.positionBuffers.push(mesh.positions.toFloatBuffer()); + bufferState.positionBuffers.push(primitive.positions.toFloatBuffer()); bufferState.positionAccessors.push(accessorIndex); } if (hasNormals) { - accessorIndex = addVertexAttribute(gltf, mesh.normals, 3, mesh.name + '_normals'); + accessorIndex = addVertexAttribute(gltf, primitive.normals, 3, mesh.name + '_' + index + '_normals'); attributes.NORMAL = accessorIndex; - bufferState.normalBuffers.push(mesh.normals.toFloatBuffer()); + bufferState.normalBuffers.push(primitive.normals.toFloatBuffer()); bufferState.normalAccessors.push(accessorIndex); } if (hasUVs) { - accessorIndex = addVertexAttribute(gltf, mesh.uvs, 2, mesh.name + '_texcoords'); + accessorIndex = addVertexAttribute(gltf, primitive.uvs, 2, mesh.name + '_' + index + '_texcoords'); attributes.TEXCOORD_0 = accessorIndex; - bufferState.uvBuffers.push(mesh.uvs.toFloatBuffer()); + bufferState.uvBuffers.push(primitive.uvs.toFloatBuffer()); bufferState.uvAccessors.push(accessorIndex); } - // Unload resources - mesh.positions = undefined; - mesh.normals = undefined; - mesh.uvs = undefined; + var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + index + '_indices'); + var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer(); + bufferState.indexBuffers.push(indexBuffer); + bufferState.indexAccessors.push(indexAccessorIndex); + // Unload resources + primitive.positions = undefined; + primitive.normals = undefined; + primitive.uvs = undefined; + primitive.indices = undefined; + + var materialIndex = getMaterial(gltf, materials, primitive.material, options); + + return { + attributes : attributes, + indices : indexAccessorIndex, + material : materialIndex, + mode : WebGLConstants.TRIANGLES + }; +} + +function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { var gltfPrimitives = []; var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - var primitive = primitives[i]; - var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices'); - var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer(); - bufferState.indexBuffers.push(indexBuffer); - bufferState.indexAccessors.push(indexAccessorIndex); - - primitive.indices = undefined; // Unload resources - - var materialIndex = getMaterial(gltf, materials, primitive.material, options); - - gltfPrimitives.push({ - attributes : attributes, - indices : indexAccessorIndex, - material : materialIndex, - mode : WebGLConstants.TRIANGLES - }); + gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options)); } var gltfMesh = { diff --git a/lib/loadObj.js b/lib/loadObj.js index bce53ea..290a087 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -36,14 +36,14 @@ function Node() { function Mesh() { this.name = undefined; this.primitives = []; - this.positions = new ArrayStorage(ComponentDatatype.FLOAT); - this.normals = new ArrayStorage(ComponentDatatype.FLOAT); - this.uvs = new ArrayStorage(ComponentDatatype.FLOAT); } function Primitive() { this.material = undefined; this.indices = new ArrayStorage(ComponentDatatype.UNSIGNED_INT); + this.positions = new ArrayStorage(ComponentDatatype.FLOAT); + this.normals = new ArrayStorage(ComponentDatatype.FLOAT); + this.uvs = new ArrayStorage(ComponentDatatype.FLOAT); } // OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js) @@ -76,7 +76,7 @@ function loadObj(objPath, options) { // All nodes seen in the obj var nodes = []; - // Used to build the indices. The vertex cache is unique to each mesh. + // Used to build the indices. The vertex cache is unique to each primitive. var vertexCache = {}; var vertexCacheLimit = 1000000; var vertexCacheCount = 0; @@ -158,9 +158,9 @@ function loadObj(objPath, options) { var px = positions.get(pi + 0); var py = positions.get(pi + 1); var pz = positions.get(pi + 2); - mesh.positions.push(px); - mesh.positions.push(py); - mesh.positions.push(pz); + primitive.positions.push(px); + primitive.positions.push(py); + primitive.positions.push(pz); } // Normals @@ -169,9 +169,9 @@ function loadObj(objPath, options) { var nx = normals.get(ni + 0); var ny = normals.get(ni + 1); var nz = normals.get(ni + 2); - mesh.normals.push(nx); - mesh.normals.push(ny); - mesh.normals.push(nz); + primitive.normals.push(nx); + primitive.normals.push(ny); + primitive.normals.push(nz); } // UVs @@ -179,8 +179,8 @@ function loadObj(objPath, options) { var ui = getOffset(u, uvs, 2); var ux = uvs.get(ui + 0); var uy = uvs.get(ui + 1); - mesh.uvs.push(ux); - mesh.uvs.push(uy); + primitive.uvs.push(ux); + primitive.uvs.push(uy); } } @@ -614,10 +614,10 @@ function removeEmptyMeshes(meshes) { return meshes.filter(function(mesh) { // Remove empty primitives mesh.primitives = mesh.primitives.filter(function(primitive) { - return primitive.indices.length > 0; + return primitive.indices.length > 0 && primitive.positions.length > 0; }); - // Valid meshes must have at least one primitive and contain positions - return (mesh.primitives.length > 0) && (mesh.positions.length > 0); + // Valid meshes must have at least one primitive + return (mesh.primitives.length > 0); }); } diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 4114c0d..1c4818a 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -133,42 +133,42 @@ describe('createGltf', function() { }); it('runs without normals', function() { - boxObjData.nodes[0].meshes[0].normals.length = 0; + boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0; var gltf = createGltf(boxObjData, options); - var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; + var attributes = gltf.meshes[0].primitives[0].attributes; expect(attributes.POSITION).toBeDefined(); expect(attributes.NORMAL).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeDefined(); }); it('runs without uvs', function() { - boxObjData.nodes[0].meshes[0].uvs.length = 0; + boxObjData.nodes[0].meshes[0].primitives[0].uvs.length = 0; var gltf = createGltf(boxObjData, options); - var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; + var attributes = gltf.meshes[0].primitives[0].attributes; expect(attributes.POSITION).toBeDefined(); expect(attributes.NORMAL).toBeDefined(); expect(attributes.TEXCOORD_0).toBeUndefined(); }); it('runs without uvs and normals', function() { - boxObjData.nodes[0].meshes[0].normals.length = 0; - boxObjData.nodes[0].meshes[0].uvs.length = 0; + boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0; + boxObjData.nodes[0].meshes[0].primitives[0].uvs.length = 0; var gltf = createGltf(boxObjData, options); - var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; + var attributes = gltf.meshes[0].primitives[0].attributes; expect(attributes.POSITION).toBeDefined(); expect(attributes.NORMAL).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeUndefined(); }); function expandObjData(objData, duplicatesLength) { - var mesh = objData.nodes[0].meshes[0]; - var indices = mesh.primitives[0].indices; - var positions = mesh.positions; - var normals = mesh.normals; - var uvs = mesh.uvs; + var primitive = objData.nodes[0].meshes[0].primitives[0]; + var indices = primitive.indices; + var positions = primitive.positions; + var normals = primitive.normals; + var uvs = primitive.uvs; var indicesLength = indices.length; var vertexCount = positions.length / 3; @@ -192,12 +192,12 @@ describe('createGltf', function() { it('detects need to use uint32 indices', function() { expandObjData(boxObjData, 2731); // Right above 65536 limit - var mesh = boxObjData.nodes[0].meshes[0]; - var indicesLength = mesh.primitives[0].indices.length; - var vertexCount = mesh.positions.length / 3; + var primitive = boxObjData.nodes[0].meshes[0].primitives[0]; + var indicesLength = primitive.indices.length; + var vertexCount = primitive.positions.length / 3; var gltf = createGltf(boxObjData, options); - var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0]; + primitive = gltf.meshes[0].primitives[0]; var indicesAccessor = gltf.accessors[primitive.indices]; expect(indicesAccessor.count).toBe(indicesLength); expect(indicesAccessor.max[0]).toBe(vertexCount - 1); diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index bd266ac..dbc6fee 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -94,9 +94,9 @@ describe('loadObj', function() { expect(node.name).toBe('Cube'); expect(mesh.name).toBe('Cube-Mesh'); - expect(mesh.positions.length / 3).toBe(24); - expect(mesh.normals.length / 3).toBe(24); - expect(mesh.uvs.length / 2).toBe(24); + expect(primitive.positions.length / 3).toBe(24); + expect(primitive.normals.length / 3).toBe(24); + expect(primitive.uvs.length / 2).toBe(24); expect(primitive.indices.length).toBe(36); expect(primitive.material).toBe('Material'); }), done).toResolve(); @@ -105,10 +105,10 @@ describe('loadObj', function() { it('loads obj with normals', function(done) { expect(loadObj(objNormalsPath, options) .then(function(data) { - var mesh = getMeshes(data)[0]; - expect(mesh.positions.length / 3).toBe(24); - expect(mesh.normals.length / 3).toBe(24); - expect(mesh.uvs.length / 2).toBe(0); + var primitive = getPrimitives(data)[0]; + expect(primitive.positions.length / 3).toBe(24); + expect(primitive.normals.length / 3).toBe(24); + expect(primitive.uvs.length / 2).toBe(0); }), done).toResolve(); }); @@ -116,8 +116,8 @@ describe('loadObj', function() { expect(loadObj(objUnnormalizedPath, options) .then(function(data) { var scratchNormal = new Cesium.Cartesian3(); - var mesh = getMeshes(data)[0]; - var normals = mesh.normals; + var primitive = getPrimitives(data)[0]; + var normals = primitive.normals; var normalsLength = normals.length / 3; for (var i = 0; i < normalsLength; ++i) { var normalX = normals.get(i * 3); @@ -132,10 +132,10 @@ describe('loadObj', function() { it('loads obj with uvs', function(done) { expect(loadObj(objUvsPath, options) .then(function(data) { - var mesh = getMeshes(data)[0]; - expect(mesh.positions.length / 3).toBe(20); - expect(mesh.normals.length / 3).toBe(0); - expect(mesh.uvs.length / 2).toBe(20); + var primitive = getPrimitives(data)[0]; + expect(primitive.positions.length / 3).toBe(20); + expect(primitive.normals.length / 3).toBe(0); + expect(primitive.uvs.length / 2).toBe(20); }), done).toResolve(); }); @@ -145,8 +145,8 @@ describe('loadObj', function() { loadObj(objNegativeIndicesPath, options) ]) .then(function(results) { - var positionsReference = getMeshes(results[0])[0].positions.toFloatBuffer(); - var positions = getMeshes(results[1])[0].positions.toFloatBuffer(); + var positionsReference = getPrimitives(results[0])[0].positions.toFloatBuffer(); + var positions = getPrimitives(results[1])[0].positions.toFloatBuffer(); expect(positions).toEqual(positionsReference); }), done).toResolve(); }); @@ -154,9 +154,8 @@ describe('loadObj', function() { it('loads obj with triangle faces', function(done) { expect(loadObj(objTrianglesPath, options) .then(function(data) { - var mesh = getMeshes(data)[0]; var primitive = getPrimitives(data)[0]; - expect(mesh.positions.length / 3).toBe(24); + expect(primitive.positions.length / 3).toBe(24); expect(primitive.indices.length).toBe(36); }), done).toResolve(); }); @@ -256,9 +255,8 @@ describe('loadObj', function() { it('loads obj with concave face containing 5 vertices', function(done) { expect(loadObj(objConcavePath, options) .then(function(data) { - var mesh = getMeshes(data)[0]; var primitive = getPrimitives(data)[0]; - expect(mesh.positions.length / 3).toBe(30); + expect(primitive.positions.length / 3).toBe(30); expect(primitive.indices.length).toBe(48); }), done).toResolve(); }); From a7d47947ac036f24cb2391e56591597de2a466a9 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 22:49:46 -0400 Subject: [PATCH 03/23] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 320fed1..191aa7a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ Change Log ### 2.3.0 ??? +* Fixed normalization on Windows paths running the converter on Linux. [#150](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/150) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#133](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/133) * Fixed handling of unnormalized input normals. [#136](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/136) From 0c897a6847a9c794b10a0e24069bae9acb317e0d Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 23:04:45 -0400 Subject: [PATCH 04/23] Only assign default material if obj file doesn't call usemtl --- lib/loadObj.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 290a087..a714457 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -502,7 +502,7 @@ function loadObj(objPath, options) { uvs = undefined; // Load materials and textures - return finishLoading(nodes, mtlPaths, objPath, options); + return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options); }); } @@ -523,7 +523,7 @@ function getMtlPaths(mtllibLine) { return mtlPaths; } -function finishLoading(nodes, mtlPaths, objPath, options) { +function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { nodes = cleanNodes(nodes); if (nodes.length === 0) { throw new RuntimeError(objPath + ' does not have any geometry data'); @@ -531,7 +531,9 @@ function finishLoading(nodes, mtlPaths, objPath, options) { var name = path.basename(objPath, path.extname(objPath)); return loadMtls(mtlPaths, objPath, options) .then(function(materials) { - assignDefaultMaterial(nodes, materials); + if (materials.length > 0 && !usesMaterials) { + assignDefaultMaterial(nodes, materials, usesMaterials); + } return { nodes : nodes, materials : materials, @@ -591,9 +593,6 @@ function loadMtls(mtlPaths, objPath, options) { } function assignDefaultMaterial(nodes, materials) { - if (materials.length === 0) { - return; - } var defaultMaterial = materials[0].name; var nodesLength = nodes.length; for (var i = 0; i < nodesLength; ++i) { From 30bf9095fa5dc26b495e0b0d9740367c766f6946 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 23:36:28 -0400 Subject: [PATCH 05/23] Discard faces that don't have the same attributes --- lib/loadObj.js | 21 +++++++-- .../box-mixed-attributes.mtl | 12 +++++ .../box-mixed-attributes.obj | 46 +++++++++++++++++++ specs/lib/loadObjSpec.js | 9 ++++ 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 specs/data/box-mixed-attributes/box-mixed-attributes.mtl create mode 100644 specs/data/box-mixed-attributes/box-mixed-attributes.obj diff --git a/lib/loadObj.js b/lib/loadObj.js index a714457..0fd3f21 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -112,17 +112,17 @@ function loadObj(objPath, options) { mesh.name = getName(name); node.meshes.push(mesh); addPrimitive(); - - // Clear the vertex cache for each new mesh - vertexCache = {}; - vertexCacheCount = 0; - vertexCount = 0; } function addPrimitive() { primitive = new Primitive(); primitive.material = activeMaterial; mesh.primitives.push(primitive); + + // Clear the vertex cache for each new primitive + vertexCache = {}; + vertexCacheCount = 0; + vertexCount = 0; } function useMaterial(name) { @@ -373,6 +373,17 @@ function loadObj(objPath, options) { var isWindingCorrect = true; var faceNormal; + var firstFace = primitive.indices.length === 0; + var faceHasUvs = uvs[0].length > 0; + var faceHasNormals = normals[0].length > 0; + var primitiveHasUvs = primitive.uvs.length > 0; + var primitiveHasNormals = primitive.normals.length > 0; + + if (!firstFace && (faceHasUvs !== primitiveHasUvs || faceHasNormals !== primitiveHasNormals)) { + // Discard faces that don't use the same attributes + return; + } + // If normals are defined, find a face normal to use in winding order sanitization. // If no face normal, we have to assume the winding is correct. if (normals[0].length > 0) { diff --git a/specs/data/box-mixed-attributes/box-mixed-attributes.mtl b/specs/data/box-mixed-attributes/box-mixed-attributes.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-mixed-attributes/box-mixed-attributes.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mixed-attributes/box-mixed-attributes.obj b/specs/data/box-mixed-attributes/box-mixed-attributes.obj new file mode 100644 index 0000000..cb9055e --- /dev/null +++ b/specs/data/box-mixed-attributes/box-mixed-attributes.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-mixed-attributes.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl Material +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3 4 8 7 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3//5 7//5 5//5 1//5 +f 8/19 4/6 2/15 6/20 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index dbc6fee..2426169 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -38,6 +38,7 @@ var objWindowsPaths = 'specs/data/box-windows-paths/box-windows-paths.obj'; var objInvalidContentsPath = 'specs/data/box/box.mtl'; var objConcavePath = 'specs/data/concave/concave.obj'; var objUnnormalizedPath = 'specs/data/box-unnormalized/box-unnormalized.obj'; +var objMixedAttributesPath = 'specs/data/box-mixed-attributes/box-mixed-attributes.obj'; var objInvalidPath = 'invalid.obj'; function getMeshes(data) { @@ -486,6 +487,14 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('discards faces that don\'t use the same attributes as other faces in the primitive', function(done) { + expect(loadObj(objMixedAttributesPath, options) + .then(function(data) { + var primitive = getPrimitives(data)[0]; + expect(primitive.indices.length).toBe(18); // 3 faces removed + }), done).toResolve(); + }); + it('throws when file has invalid contents', function(done) { expect(loadObj(objInvalidContentsPath, options), done).toRejectWith(RuntimeError); }); From bcf01b111cac2f6219bfdfb703846612d91b1def Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 18 Sep 2018 20:11:04 -0400 Subject: [PATCH 06/23] Update CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 320fed1..8c474bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,10 +3,11 @@ Change Log ### 2.3.0 ??? +* Fixed handling of objs with mismatching attribute layouts. [#153](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/153) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#133](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/133) * Fixed handling of unnormalized input normals. [#136](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/136) -### 2.2.0 2017-01-29 +### 2.2.0 2018-01-29 * Fixed handling of materials where the diffuse and ambient texture are the same. [#127](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/127) * Added ability to load alpha textures. [#124](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/124) From 0197ffa15fb7861514d0cb0d50482484526e8e8a Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 19 Sep 2018 20:02:25 -0400 Subject: [PATCH 07/23] Update to 2.3.0 --- CHANGES.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 804fd88..9357ae6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,10 @@ Change Log ========== -### 2.3.0 2018-??-?? +### 2.3.0 2018-09-19 * Fixed handling of objs with mismatching attribute layouts. [#153](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/153) -* Fixed normalization oo Windows paths when running the converter on Linux. [#150](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/150) +* Fixed normalization of Windows paths when running the converter on Linux. [#150](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/150) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#133](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/133) * Fixed handling of unnormalized input normals. [#136](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/136) diff --git a/package.json b/package.json index 610b3b1..f7b402a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obj2gltf", - "version": "2.2.0", + "version": "2.3.0", "description": "Convert OBJ model format to glTF", "license": "Apache-2.0", "contributors": [ From a87087b0ddc2e61ad3e16129c8506aed86df70ef Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 10 Oct 2018 18:27:56 -0400 Subject: [PATCH 08/23] Reset vertex count properly --- CHANGES.md | 4 ++++ lib/loadObj.js | 1 + package.json | 2 +- specs/lib/loadObjSpec.js | 8 ++++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9357ae6..da8e76e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ Change Log ========== +### 2.3.1 2018-10-11 + +* Fixed handling of objs with interleaved materials. [#155](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/155) + ### 2.3.0 2018-09-19 * Fixed handling of objs with mismatching attribute layouts. [#153](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/153) diff --git a/lib/loadObj.js b/lib/loadObj.js index 0fd3f21..e24e36d 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -135,6 +135,7 @@ function loadObj(objPath, options) { for (var i = 0; i < primitivesLength; ++i) { if (primitives[i].material === material) { primitive = primitives[i]; + vertexCount = primitive.positions.length / 3; return; } } diff --git a/package.json b/package.json index f7b402a..7dbe693 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obj2gltf", - "version": "2.3.0", + "version": "2.3.1", "description": "Convert OBJ model format to glTF", "license": "Apache-2.0", "contributors": [ diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index 2426169..5a624c6 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -309,6 +309,14 @@ describe('loadObj', function() { expect(primitives[0].material).toBe('Red'); expect(primitives[1].material).toBe('Green'); expect(primitives[2].material).toBe('Blue'); + + var expectedIndices = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]; + var length = expectedIndices.length; + for (var i = 0; i < 3; ++i) { + for (var j = 0; j < length; ++j) { + expect(primitives[i].indices.get(j)).toBe(expectedIndices[j]); + } + } }), done).toResolve(); }); From ce1591c8606d3c52e50da09fb3f9914449f8da97 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 11 Oct 2018 13:20:50 -0400 Subject: [PATCH 09/23] Clear vertex cache too --- lib/loadObj.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index e24e36d..da73003 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -96,6 +96,11 @@ function loadObj(objPath, options) { var vertexIndices = []; + function clearVertexCache() { + vertexCache = {}; + vertexCacheCount = 0; + } + function getName(name) { return (name === '' ? undefined : name); } @@ -120,8 +125,7 @@ function loadObj(objPath, options) { mesh.primitives.push(primitive); // Clear the vertex cache for each new primitive - vertexCache = {}; - vertexCacheCount = 0; + clearVertexCache(); vertexCount = 0; } @@ -135,6 +139,7 @@ function loadObj(objPath, options) { for (var i = 0; i < primitivesLength; ++i) { if (primitives[i].material === material) { primitive = primitives[i]; + clearVertexCache(); vertexCount = primitive.positions.length / 3; return; } @@ -196,8 +201,7 @@ function loadObj(objPath, options) { // may be some duplicate vertices. vertexCacheCount++; if (vertexCacheCount > vertexCacheLimit) { - vertexCacheCount = 0; - vertexCache = {}; + clearVertexCache(); } } return index; From 1426d0e43d86455b799f020a3b0e86431aeff381 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 11 Oct 2018 16:33:49 -0400 Subject: [PATCH 10/23] Update triangulation algorithm --- .eslintrc.json | 5 +- CHANGES.md | 1 + gulpfile.js | 1 - lib/loadObj.js | 211 ++++++++------------------------------- package.json | 32 +++--- specs/lib/loadObjSpec.js | 7 +- 6 files changed, 64 insertions(+), 193 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index fcf4081..d5e51ef 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,6 @@ { - "extends": "cesium/node" + "extends": "cesium/node", + "rules": { + "no-var": "off" + } } diff --git a/CHANGES.md b/CHANGES.md index da8e76e..4ef4149 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ Change Log ### 2.3.1 2018-10-11 +* Improved parsing models with concave or n-sided faces. [#157](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/157) * Fixed handling of objs with interleaved materials. [#155](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/155) ### 2.3.0 2018-09-19 diff --git a/gulpfile.js b/gulpfile.js index c512034..beac0e6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -62,7 +62,6 @@ gulp.task('coverage', function () { open('coverage/lcov-report/index.html'); }); - gulp.task('cloc', function() { var cmdLine; var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc'); diff --git a/lib/loadObj.js b/lib/loadObj.js index da73003..164f423 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -8,17 +8,12 @@ var loadMtl = require('./loadMtl'); var outsideDirectory = require('./outsideDirectory'); var readLines = require('./readLines'); -var Cartesian2 = Cesium.Cartesian2; var Cartesian3 = Cesium.Cartesian3; var ComponentDatatype = Cesium.ComponentDatatype; +var CoplanarPolygonGeometryLibrary = Cesium.CoplanarPolygonGeometryLibrary; var defaultValue = Cesium.defaultValue; var defined = Cesium.defined; -var IntersectionTests = Cesium.IntersectionTests; -var Matrix3 = Cesium.Matrix3; -var OrientedBoundingBox = Cesium.OrientedBoundingBox; -var Plane = Cesium.Plane; var PolygonPipeline = Cesium.PolygonPipeline; -var Ray = Cesium.Ray; var RuntimeError = Cesium.RuntimeError; var WindingOrder = Cesium.WindingOrder; @@ -94,8 +89,6 @@ function loadObj(objPath, options) { var faceUvs = []; var faceNormals = []; - var vertexIndices = []; - function clearVertexCache() { vertexCache = {}; vertexCacheCount = 0; @@ -207,107 +200,7 @@ function loadObj(objPath, options) { return index; } - // Given a set of 3D points, project them onto whichever axis will produce the least distortion. - var scratchIntersectionPoint = new Cartesian3(); - var scratchXAxis = new Cartesian3(); - var scratchYAxis = new Cartesian3(); - var scratchZAxis = new Cartesian3(); - var scratchOrigin = new Cartesian3(); - var scratchNormal = new Cartesian3(); - var scratchRay = new Ray(); - var scratchPlane = new Plane(Cesium.Cartesian3.UNIT_X, 0); - var scratchPositions2D = [new Cartesian2(), new Cartesian2(), new Cartesian2()]; - function projectTo2D(positions) { - var positions2D = []; - var obb = OrientedBoundingBox.fromPoints(positions); - var halfAxes = obb.halfAxes; - Matrix3.getColumn(halfAxes, 0, scratchXAxis); - Matrix3.getColumn(halfAxes, 1, scratchYAxis); - Matrix3.getColumn(halfAxes, 2, scratchZAxis); - - var xMag = Cartesian3.magnitude(scratchXAxis); - var yMag = Cartesian3.magnitude(scratchYAxis); - var zMag = Cartesian3.magnitude(scratchZAxis); - var min = Math.min(xMag, yMag, zMag); - - var i; - // If all the points are on a line, just remove one of the zero dimensions - if (xMag === 0 && (yMag === 0 || zMag === 0)) { - for (i = 0; i < positions.length; i++) { - if (i === scratchPositions2D.length) { - scratchPositions2D.push(new Cartesian2()); - } - positions2D[i] = new Cartesian2.fromElements(positions[i].y, positions[i].z, scratchPositions2D[i]); - } - return positions2D; - } else if (yMag === 0 && zMag === 0) { - for (i = 0; i < positions.length; i++) { - if (i === scratchPositions2D.length) { - scratchPositions2D.push(new Cartesian2()); - } - positions2D[i] = new Cartesian2.fromElements(positions[i].x, positions[i].y, scratchPositions2D[i]); - } - return positions2D; - } - - var center = obb.center; - var planeXAxis; - var planeYAxis; - if (min === xMag) { - if (!scratchXAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, scratchXAxis, scratchOrigin); - Cartesian3.normalize(scratchXAxis, scratchNormal); - } - planeXAxis = Cartesian3.normalize(scratchYAxis, scratchYAxis); - planeYAxis = Cartesian3.normalize(scratchZAxis, scratchZAxis); - } else if (min === yMag) { - if (!scratchYAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, scratchYAxis, scratchOrigin); - Cartesian3.normalize(scratchYAxis, scratchNormal); - } - planeXAxis = Cartesian3.normalize(scratchXAxis, scratchXAxis); - planeYAxis = Cartesian3.normalize(scratchZAxis, scratchZAxis); - } else { - if (!scratchZAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, scratchZAxis, scratchOrigin); - Cartesian3.normalize(scratchZAxis, scratchNormal); - } - planeXAxis = Cartesian3.normalize(scratchXAxis, scratchXAxis); - planeYAxis = Cartesian3.normalize(scratchYAxis, scratchYAxis); - } - - if (min === 0) { - scratchNormal = Cartesian3.cross(planeXAxis, planeYAxis, scratchNormal); - scratchNormal = Cartesian3.normalize(scratchNormal, scratchNormal); - } - - Plane.fromPointNormal(scratchOrigin, scratchNormal, scratchPlane); - scratchRay.direction = scratchNormal; - - for (i = 0; i < positions.length; i++) { - scratchRay.origin = positions[i]; - - var intersectionPoint = IntersectionTests.rayPlane(scratchRay, scratchPlane, scratchIntersectionPoint); - - if (!defined(intersectionPoint)) { - Cartesian3.negate(scratchRay.direction, scratchRay.direction); - intersectionPoint = IntersectionTests.rayPlane(scratchRay, scratchPlane, scratchIntersectionPoint); - } - var v = Cartesian3.subtract(intersectionPoint, scratchOrigin, intersectionPoint); - var x = Cartesian3.dot(planeXAxis, v); - var y = Cartesian3.dot(planeYAxis, v); - - if (i === scratchPositions2D.length) { - scratchPositions2D.push(new Cartesian2()); - } - - positions2D[i] = new Cartesian2.fromElements(x, y, scratchPositions2D[i]); - } - - return positions2D; - } - - function get3DPoint(index, result) { + function getPosition(index, result) { var pi = getOffset(index, positions, 3); var px = positions.get(pi + 0); var py = positions.get(pi + 1); @@ -315,7 +208,7 @@ function loadObj(objPath, options) { return Cartesian3.fromElements(px, py, pz, result); } - function get3DNormal(index, result) { + function getNormal(index, result) { var ni = getOffset(index, normals, 3); var nx = normals.get(ni + 0); var ny = normals.get(ni + 1); @@ -323,36 +216,28 @@ function loadObj(objPath, options) { return Cartesian3.fromElements(nx, ny, nz, result); } - // Given a sequence of three points A B C, determine whether vector BC - // "turns" clockwise (positive) or counter-clockwise (negative) from vector AB var scratch1 = new Cartesian3(); var scratch2 = new Cartesian3(); - function getTurnDirection(pointA, pointB, pointC) { - var vector1 = Cartesian2.subtract(pointA, pointB, scratch1); - var vector2 = Cartesian2.subtract(pointC, pointB, scratch2); - return vector1.x * vector2.y - vector1.y * vector2.x; - } - - // Given the cartesian 2 vertices of a polygon, determine if convex - function isConvex(positions2D) { - var turnDirection = getTurnDirection(positions2D[0], positions2D[1], positions2D[2]); - for (var i=1; i < positions2D.length-2; ++i) { - var currentTurnDirection = getTurnDirection(positions2D[i], positions2D[i+1], positions2D[i+2]); - if (turnDirection * currentTurnDirection < 0) { - return false; - } - } - return true; - } - var scratch3 = new Cartesian3(); var scratch4 = new Cartesian3(); var scratch5 = new Cartesian3(); - // Checks if winding order matches the given normal. - function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normal) { - var A = get3DPoint(positionIndex1, scratch1); - var B = get3DPoint(positionIndex2, scratch2); - var C = get3DPoint(positionIndex3, scratch3); + var scratchCenter = new Cartesian3(); + var scratchAxis1 = new Cartesian3(); + var scratchAxis2 = new Cartesian3(); + var scratchNormal = new Cartesian3(); + var scratchPositions = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()]; + var scratchVertexIndices = []; + var scratchPoints = []; + + function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normalIndex) { + if (normalIndex.length === 0) { + // If no face normal, we have to assume the winding is correct. + return true; + } + var normal = getNormal(normalIndex, scratchNormal); + var A = getPosition(positionIndex1, scratch1); + var B = getPosition(positionIndex2, scratch2); + var C = getPosition(positionIndex3, scratch3); var BA = Cartesian3.subtract(B, A, scratch4); var CA = Cartesian3.subtract(C, A, scratch5); @@ -373,11 +258,9 @@ function loadObj(objPath, options) { } } - var scratchPositions3D = [new Cartesian3(), new Cartesian3(), new Cartesian3()]; function addFace(vertices, positions, uvs, normals) { - var isWindingCorrect = true; - var faceNormal; - + var i; + var isWindingCorrect; var firstFace = primitive.indices.length === 0; var faceHasUvs = uvs[0].length > 0; var faceHasNormals = normals[0].length > 0; @@ -389,53 +272,39 @@ function loadObj(objPath, options) { return; } - // If normals are defined, find a face normal to use in winding order sanitization. - // If no face normal, we have to assume the winding is correct. - if (normals[0].length > 0) { - faceNormal = get3DNormal(normals[0], scratchNormal); - isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], faceNormal); - } - if (vertices.length === 3) { + isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], normals[0]); var index1 = addVertex(vertices[0], positions[0], uvs[0], normals[0]); var index2 = addVertex(vertices[1], positions[1], uvs[1], normals[1]); var index3 = addVertex(vertices[2], positions[2], uvs[2], normals[2]); addTriangle(index1, index2, index3, isWindingCorrect); } else { // Triangulate if the face is not a triangle - var positions3D = []; + var points = scratchPoints; + var vertexIndices = scratchVertexIndices; + + points.length = 0; vertexIndices.length = 0; - var i; for (i = 0; i < vertices.length; ++i) { var index = addVertex(vertices[i], positions[i], uvs[i], normals[i]); vertexIndices.push(index); - - // Collect the vertex positions as 3D points - if (i === scratchPositions3D.length) { - scratchPositions3D.push(new Cartesian3()); + if (i === scratchPositions.length) { + scratchPositions.push(new Cartesian3()); } - positions3D.push(get3DPoint(positions[i], scratchPositions3D[i])); + points.push(getPosition(positions[i], scratchPositions[i])); } - var positions2D = projectTo2D(positions3D); + var validGeometry = CoplanarPolygonGeometryLibrary.computeProjectTo2DArguments(points, scratchCenter, scratchAxis1, scratchAxis2); + if (!validGeometry) { + return; + } + var projectPoints = CoplanarPolygonGeometryLibrary.createProjectPointsTo2DFunction(scratchCenter, scratchAxis1, scratchAxis2); + var points2D = projectPoints(points); + var indices = PolygonPipeline.triangulate(points2D); + isWindingCorrect = PolygonPipeline.computeWindingOrder2D(points2D) !== WindingOrder.CLOCKWISE; - if (isConvex(positions2D)) { - for (i=1; i < vertices.length-1; ++i) { - addTriangle(vertexIndices[0], vertexIndices[i], vertexIndices[i+1], isWindingCorrect); - } - } else { - // Since the projection doesn't preserve winding order, reverse the order of - // the vertices before triangulating to enforce counter clockwise. - var projectedWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); - if (projectedWindingOrder === WindingOrder.CLOCKWISE) { - positions2D.reverse(); - } - - // Use an ear-clipping algorithm to triangulate - var positionIndices = PolygonPipeline.triangulate(positions2D); - for (i = 0; i < positionIndices.length-2; i += 3) { - addTriangle(vertexIndices[positionIndices[i]], vertexIndices[positionIndices[i+1]], vertexIndices[positionIndices[i+2]], isWindingCorrect); - } + for (i = 0; i < indices.length - 2; i += 3) { + addTriangle(vertexIndices[indices[i]], vertexIndices[indices[i+1]], vertexIndices[indices[i+2]], isWindingCorrect); } } } diff --git a/package.json b/package.json index 7dbe693..ff629e5 100644 --- a/package.json +++ b/package.json @@ -26,26 +26,26 @@ "node": ">=4.0.0" }, "dependencies": { - "bluebird": "^3.5.1", - "cesium": "^1.39.0", - "fs-extra": "^4.0.2", - "jpeg-js": "^0.3.3", - "mime": "^2.0.3", - "pngjs": "^3.3.0", - "yargs": "^10.0.3" + "bluebird": "^3.5.2", + "cesium": "^1.50.0", + "fs-extra": "^7.0.0", + "jpeg-js": "^0.3.4", + "mime": "^2.3.1", + "pngjs": "^3.3.3", + "yargs": "^12.0.2" }, "devDependencies": { - "cloc": "^2.3.3", - "coveralls": "^3.0.0", - "eslint": "^4.4.1", - "eslint-config-cesium": "^2.0.1", + "cloc": "^2.3.4", + "coveralls": "^3.0.2", + "eslint": "^5.6.1", + "eslint-config-cesium": "^6.0.0", "gulp": "^3.9.1", - "jasmine": "^2.7.0", - "jasmine-spec-reporter": "^4.2.0", - "jsdoc": "^3.5.4", - "nyc": "^11.1.0", + "jasmine": "^3.2.0", + "jasmine-spec-reporter": "^4.2.1", + "jsdoc": "^3.5.5", + "nyc": "^13.0.1", "open": "^0.0.5", - "requirejs": "^2.3.4" + "requirejs": "^2.3.6" }, "scripts": { "jsdoc": "jsdoc ./lib -R ./README.md -d doc", diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index 5a624c6..45a0799 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -310,11 +310,10 @@ describe('loadObj', function() { expect(primitives[1].material).toBe('Green'); expect(primitives[2].material).toBe('Blue'); - var expectedIndices = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]; - var length = expectedIndices.length; for (var i = 0; i < 3; ++i) { - for (var j = 0; j < length; ++j) { - expect(primitives[i].indices.get(j)).toBe(expectedIndices[j]); + var indices = primitives[i].indices; + for (var j = 0; j < indices.length; ++j) { + expect(indices.get(j)).toBeLessThan(8); } } }), done).toResolve(); From 41f53410dd6d11e6b2f406e698a2a41b44664712 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 16 Oct 2018 09:49:45 -0400 Subject: [PATCH 11/23] Update date --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4ef4149..2ea9df4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ Change Log ========== -### 2.3.1 2018-10-11 +### 2.3.1 2018-10-16 * Improved parsing models with concave or n-sided faces. [#157](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/157) * Fixed handling of objs with interleaved materials. [#155](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/155) From b3d5407a26083ea94435bdb05bd0a317ed2d9b90 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 17 Oct 2018 21:42:28 -0400 Subject: [PATCH 12/23] Don't discard primitives that don't match the attribute layout --- lib/loadObj.js | 51 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 164f423..1103e88 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -122,25 +122,44 @@ function loadObj(objPath, options) { vertexCount = 0; } - function useMaterial(name) { - var material = getName(name); - activeMaterial = material; - - // Look to see if this material has already been used by a primitive in the mesh + function reusePrimitive(callback) { var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - if (primitives[i].material === material) { - primitive = primitives[i]; - clearVertexCache(); - vertexCount = primitive.positions.length / 3; - return; + if (primitives[i].material === activeMaterial) { + if (!defined(callback) || callback(primitives[i])) { + primitive = primitives[i]; + clearVertexCache(); + vertexCount = primitive.positions.length / 3; + return; + } } } - // Add a new primitive with this material addPrimitive(); } + function useMaterial(name) { + activeMaterial = getName(name); + reusePrimitive(); + } + + function faceAndPrimitiveMatch(uvs, normals, primitive) { + var faceHasUvs = uvs[0].length > 0; + var faceHasNormals = normals[0].length > 0; + var primitiveHasUvs = primitive.uvs.length > 0; + var primitiveHasNormals = primitive.normals.length > 0; + return primitiveHasUvs === faceHasUvs && primitiveHasNormals === faceHasNormals; + } + + function checkPrimitive(uvs, normals) { + var firstFace = primitive.indices.length === 0; + if (!firstFace && !faceAndPrimitiveMatch(uvs, normals, primitive)) { + reusePrimitive(function(primitive) { + return faceAndPrimitiveMatch(uvs, normals, primitive); + }); + } + } + function getOffset(a, attributeData, components) { var i = parseInt(a); if (i < 0) { @@ -261,16 +280,8 @@ function loadObj(objPath, options) { function addFace(vertices, positions, uvs, normals) { var i; var isWindingCorrect; - var firstFace = primitive.indices.length === 0; - var faceHasUvs = uvs[0].length > 0; - var faceHasNormals = normals[0].length > 0; - var primitiveHasUvs = primitive.uvs.length > 0; - var primitiveHasNormals = primitive.normals.length > 0; - if (!firstFace && (faceHasUvs !== primitiveHasUvs || faceHasNormals !== primitiveHasNormals)) { - // Discard faces that don't use the same attributes - return; - } + checkPrimitive(uvs, normals); if (vertices.length === 3) { isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], normals[0]); From 82ad86370b5851848c5da19a85347f2fab2ca6c7 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 17 Oct 2018 21:52:14 -0400 Subject: [PATCH 13/23] Change test --- specs/lib/loadObjSpec.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index 45a0799..cd0b7cd 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -494,11 +494,15 @@ describe('loadObj', function() { }), done).toResolve(); }); - it('discards faces that don\'t use the same attributes as other faces in the primitive', function(done) { + it('separates faces that don\'t use the same attributes as other faces in the primitive', function(done) { expect(loadObj(objMixedAttributesPath, options) .then(function(data) { - var primitive = getPrimitives(data)[0]; - expect(primitive.indices.length).toBe(18); // 3 faces removed + var primitives = getPrimitives(data); + expect(primitives.length).toBe(4); + expect(primitives[0].indices.length).toBe(18); // 6 faces + expect(primitives[1].indices.length).toBe(6); // 2 faces + expect(primitives[2].indices.length).toBe(6); // 2 faces + expect(primitives[3].indices.length).toBe(6); // 2 faces }), done).toResolve(); }); From db3d5c4c11a2c2256dc494455a47d3e168f6d26b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 18:18:43 -0400 Subject: [PATCH 14/23] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2ea9df4..02cdb37 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ Change Log ========== +### 2.3.2 ????-??-?? + +* Improved parsing of faces with mismatching attributes. [#161](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/161) + ### 2.3.1 2018-10-16 * Improved parsing models with concave or n-sided faces. [#157](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/157) From 9cc1ba8ec52b514fce5f9043102e79e2097453ff Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 17 Oct 2018 22:59:54 -0400 Subject: [PATCH 15/23] Split incompatible materials --- lib/createGltf.js | 132 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 28 deletions(-) diff --git a/lib/createGltf.js b/lib/createGltf.js index b5f3f89..27e9b62 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -4,6 +4,7 @@ var getBufferPadded = require('./getBufferPadded'); var getDefaultMaterial = require('./loadMtl').getDefaultMaterial; var Texture = require('./Texture'); +var defaultValue = Cesium.defaultValue; var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; @@ -23,6 +24,9 @@ function createGltf(objData, options) { var materials = objData.materials; var name = objData.name; + // Split materials used by primitives with different types of attributes + materials = splitIncompatibleMaterials(nodes, materials, options); + var gltf = { accessors : [], asset : {}, @@ -70,14 +74,14 @@ function createGltf(objData, options) { var meshIndex; if (meshesLength === 1) { - meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, meshes[0], options); + meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, meshes[0]); addNode(gltf, node.name, meshIndex, undefined); } else { // Add meshes as child nodes var parentIndex = addNode(gltf, node.name); for (var j = 0; j < meshesLength; ++j) { var mesh = meshes[j]; - meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, mesh, options); + meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, mesh); addNode(gltf, mesh.name, meshIndex, parentIndex); } } @@ -194,6 +198,31 @@ function getTexture(gltf, texture) { }; } +function cloneMaterial(material, removeTextures) { + if (material === null || typeof material !== 'object') { + return material; + } else if (material instanceof Texture) { + if (removeTextures) { + return undefined; + } + return material; + } else if (Array.isArray(material)) { + var length = material.length; + var clonedArray = new Array(length); + for (var i = 0; i < length; ++i) { + clonedArray[i] = cloneMaterial(material[i], removeTextures); + } + return clonedArray; + } + var clonedObject = {}; + for (var name in material) { + if (material.hasOwnProperty(name)) { + clonedObject[name] = cloneMaterial(material[name], removeTextures); + } + } + return clonedObject; +} + function resolveTextures(gltf, material) { for (var name in material) { if (material.hasOwnProperty(name)) { @@ -214,35 +243,27 @@ function addMaterial(gltf, material) { return materialIndex; } -function getMaterial(gltf, materials, materialName, options) { - if (!defined(materialName)) { - // Create a default material if the primitive does not specify one - materialName = 'default'; - } - - var i; - var material; +function getMaterialByName(materials, materialName) { var materialsLength = materials.length; - for (i = 0; i < materialsLength; ++i) { + for (var i = 0; i < materialsLength; ++i) { if (materials[i].name === materialName) { - material = materials[i]; - break; + return materials[i]; } } +} - if (!defined(material)) { - material = getDefaultMaterial(options); - material.name = materialName; - } - - var materialIndex; - materialsLength = gltf.materials.length; - for (i = 0; i < materialsLength; ++i) { - if (gltf.materials[i].name === materialName) { - materialIndex = i; - break; +function getMaterialIndex(materials, materialName) { + var materialsLength = materials.length; + for (var i = 0; i < materialsLength; ++i) { + if (materials[i].name === materialName) { + return i; } } +} + +function getMaterial(gltf, materials, materialName) { + var material = getMaterialByName(materials, materialName); + var materialIndex = getMaterialIndex(gltf.materials, materialName); if (!defined(materialIndex)) { materialIndex = addMaterial(gltf, material); @@ -251,6 +272,61 @@ function getMaterial(gltf, materials, materialName, options) { return materialIndex; } +function primitiveInfoMatch(a, b) { + return a.hasUvs === b.hasUvs && + a.hasNormals === b.hasNormals; +} + +function splitIncompatibleMaterials(nodes, materials, options) { + var splitMaterials = []; + var primitiveInfoByMaterial = {}; + var nodesLength = nodes.length; + for (var i = 0; i < nodesLength; ++i) { + var meshes = nodes[i].meshes; + var meshesLength = meshes.length; + for (var j = 0; j < meshesLength; ++j) { + var primitives = meshes[j].primitives; + var primitivesLength = primitives.length; + for (var k = 0; k < primitivesLength; ++k) { + var primitive = primitives[k]; + var hasUvs = primitive.uvs.length > 0; + var hasNormals = primitive.normals.length > 0; + var primitiveInfo = { + hasUvs : hasUvs, + hasNormals : hasNormals + }; + var originalMaterialName = defaultValue(primitive.material, 'default'); + var materialName = originalMaterialName; + var suffix = 2; + while (defined(primitiveInfoByMaterial[materialName])) { + if (primitiveInfoMatch(primitiveInfo, primitiveInfoByMaterial[materialName])) { + break; + } + materialName = originalMaterialName + '-' + suffix++; + } + + primitive.material = materialName; + primitiveInfoByMaterial[materialName] = primitiveInfo; + + var material = getMaterialByName(splitMaterials, materialName); + if (defined(material)) { + continue; + } + + material = getMaterialByName(materials, originalMaterialName); + if (defined(material)) { + material = cloneMaterial(material, !hasUvs); + } else { + material = getDefaultMaterial(options); + } + material.name = materialName; + splitMaterials.push(material); + } + } + } + return splitMaterials; +} + function addVertexAttribute(gltf, array, components, name) { var count = array.length / components; var minMax = array.getMinMax(components); @@ -309,7 +385,7 @@ function requiresUint32Indices(nodes) { return false; } -function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index, options) { +function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index) { var hasPositions = primitive.positions.length > 0; var hasNormals = primitive.normals.length > 0; var hasUVs = primitive.uvs.length > 0; @@ -346,7 +422,7 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti primitive.uvs = undefined; primitive.indices = undefined; - var materialIndex = getMaterial(gltf, materials, primitive.material, options); + var materialIndex = getMaterial(gltf, materials, primitive.material); return { attributes : attributes, @@ -356,12 +432,12 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti }; } -function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { +function addMesh(gltf, materials, bufferState, uint32Indices, mesh) { var gltfPrimitives = []; var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options)); + gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i)); } var gltfMesh = { From 1bca54c376f2e16c51c1f4007192c91c73a0ae53 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 17:46:53 -0400 Subject: [PATCH 16/23] Added test --- .../box-mixed-attributes-2.mtl | 13 +++ .../box-mixed-attributes-2.obj | 67 +++++++++++++++ specs/data/box-mixed-attributes-2/cesium.png | Bin 0 -> 7665 bytes specs/lib/createGltfSpec.js | 76 ++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl create mode 100644 specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj create mode 100644 specs/data/box-mixed-attributes-2/cesium.png diff --git a/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl new file mode 100644 index 0000000..d81606d --- /dev/null +++ b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd cesium.png diff --git a/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj new file mode 100644 index 0000000..2762b61 --- /dev/null +++ b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj @@ -0,0 +1,67 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-mixed-attributes-2.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +# Using default material +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +usemtl Material +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +usemtl Missing +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +o CubeCopy +usemtl Material +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 diff --git a/specs/data/box-mixed-attributes-2/cesium.png b/specs/data/box-mixed-attributes-2/cesium.png new file mode 100644 index 0000000000000000000000000000000000000000..3b8baee1bce0079bd7afeb38d5e0b1bde9732ba5 GIT binary patch literal 7665 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 literal 0 HcmV?d00001 diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 1c4818a..26f6929 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -4,14 +4,17 @@ var Promise = require('bluebird'); var obj2gltf = require('../../lib/obj2gltf'); var createGltf = require('../../lib/createGltf'); var loadObj = require('../../lib/loadObj'); +var getDefaultMaterial = require('../../lib/loadMtl').getDefaultMaterial; var clone = Cesium.clone; +var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; var boxObjPath = 'specs/data/box/box.obj'; var groupObjPath = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; var complexObjPath = 'specs/data/box-complex-material/box-complex-material.obj'; var noMaterialsObjPath = 'specs/data/box-no-materials/box-no-materials.obj'; +var mixedAttributesObjPath = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj'; var options; @@ -20,6 +23,7 @@ describe('createGltf', function() { var groupObjData; var complexObjData; var noMaterialsObjData; + var mixedAttributesObjData; beforeEach(function(done) { options = clone(obj2gltf.defaults); @@ -42,6 +46,10 @@ describe('createGltf', function() { loadObj(noMaterialsObjPath, options) .then(function(data) { noMaterialsObjData = data; + }), + loadObj(mixedAttributesObjPath, options) + .then(function(data) { + mixedAttributesObjData = data; }) ]).then(done); }); @@ -163,6 +171,74 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); + it('splits incompatible materials', function() { + var gltf = createGltf(mixedAttributesObjData, options); + var materials = gltf.materials; + var meshes = gltf.meshes; + var i; + var material; + + var referenceMaterial = mixedAttributesObjData.materials[0]; + delete referenceMaterial.name; + referenceMaterial.pbrMetallicRoughness.baseColorTexture = { + index : 0 + }; + + var referenceMaterialNoTextures = clone(referenceMaterial, true); + referenceMaterialNoTextures.pbrMetallicRoughness.baseColorTexture = undefined; + + var defaultMaterial = getDefaultMaterial(options); + delete defaultMaterial.name; + + var materialNames = materials.map(function(material) { + var name = material.name; + delete material.name; + return name; + }); + + // Expect three copies of each material for + // * positions/normals/uvs + // * positions/normals + // * positions/uvs + expect(materialNames).toEqual([ + 'default', + 'default-2', + 'default-3', + 'Material', + 'Material-2', + 'Material-3', + 'Missing', + 'Missing-2', + 'Missing-3' + ]); + + expect(materials.length).toBe(9); + expect(materials[0]).toEqual(defaultMaterial); + expect(materials[1]).toEqual(defaultMaterial); + expect(materials[2]).toEqual(defaultMaterial); + expect(materials[3]).toEqual(referenceMaterial); + expect(materials[4]).toEqual(referenceMaterial); + expect(materials[5]).toEqual(referenceMaterialNoTextures); + expect(materials[6]).toEqual(defaultMaterial); + expect(materials[7]).toEqual(defaultMaterial); + expect(materials[8]).toEqual(defaultMaterial); + + // Test that primitives without uvs reference materials without textures + var meshesLength = meshes.length; + for (i = 0; i < meshesLength; ++i) { + var mesh = meshes[i]; + var primitives = mesh.primitives; + var primitivesLength = primitives.length; + for (var j = 0; j < primitivesLength; ++j) { + var primitive = primitives[j]; + material = materials[primitive.material]; + if (!defined(primitive.attributes.TEXCOORD_0)) { + expect(material.pbrMetallicRoughness.baseColorTexture).toBeUndefined(); + } + } + } + }); + function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices; From 1f0278ad0c230962baf00f0efbe8fb23bc00b26c Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 23:14:24 -0400 Subject: [PATCH 17/23] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2ea9df4..6c8718d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ Change Log ========== +### 2.3.2 ????-??-?? +* Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162) +* Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162) + ### 2.3.1 2018-10-16 * Improved parsing models with concave or n-sided faces. [#157](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/157) From a55ea696617cecf764f179d11657c378edf58ead Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 23:58:55 -0400 Subject: [PATCH 18/23] Cleanup test --- specs/lib/createGltfSpec.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 26f6929..06eeb58 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -175,8 +175,6 @@ describe('createGltf', function() { var gltf = createGltf(mixedAttributesObjData, options); var materials = gltf.materials; var meshes = gltf.meshes; - var i; - var material; var referenceMaterial = mixedAttributesObjData.materials[0]; delete referenceMaterial.name; @@ -225,13 +223,13 @@ describe('createGltf', function() { // Test that primitives without uvs reference materials without textures var meshesLength = meshes.length; - for (i = 0; i < meshesLength; ++i) { + for (var i = 0; i < meshesLength; ++i) { var mesh = meshes[i]; var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var j = 0; j < primitivesLength; ++j) { var primitive = primitives[j]; - material = materials[primitive.material]; + var material = materials[primitive.material]; if (!defined(primitive.attributes.TEXCOORD_0)) { expect(material.pbrMetallicRoughness.baseColorTexture).toBeUndefined(); } From eaca567a259a3d222fa75cfcab524dd027bdb15b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 20:47:52 -0400 Subject: [PATCH 19/23] Updates --- lib/createGltf.js | 52 +++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/createGltf.js b/lib/createGltf.js index 27e9b62..06a573d 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -199,7 +199,7 @@ function getTexture(gltf, texture) { } function cloneMaterial(material, removeTextures) { - if (material === null || typeof material !== 'object') { + if (typeof material !== 'object') { return material; } else if (material instanceof Texture) { if (removeTextures) { @@ -236,7 +236,7 @@ function resolveTextures(gltf, material) { } } -function addMaterial(gltf, material) { +function addGltfMaterial(gltf, material) { resolveTextures(gltf, material); var materialIndex = gltf.materials.length; gltf.materials.push(material); @@ -261,12 +261,12 @@ function getMaterialIndex(materials, materialName) { } } -function getMaterial(gltf, materials, materialName) { +function getOrCreateGltfMaterial(gltf, materials, materialName) { var material = getMaterialByName(materials, materialName); var materialIndex = getMaterialIndex(gltf.materials, materialName); if (!defined(materialIndex)) { - materialIndex = addMaterial(gltf, material); + materialIndex = addGltfMaterial(gltf, material); } return materialIndex; @@ -277,6 +277,18 @@ function primitiveInfoMatch(a, b) { a.hasNormals === b.hasNormals; } +function getSplitMaterialName(originalMaterialName, primitiveInfo, primitiveInfoByMaterial) { + var splitMaterialName = originalMaterialName; + var suffix = 2; + while (defined(primitiveInfoByMaterial[splitMaterialName])) { + if (primitiveInfoMatch(primitiveInfo, primitiveInfoByMaterial[splitMaterialName])) { + break; + } + splitMaterialName = originalMaterialName + '-' + suffix++; + } + return splitMaterialName; +} + function splitIncompatibleMaterials(nodes, materials, options) { var splitMaterials = []; var primitiveInfoByMaterial = {}; @@ -296,31 +308,23 @@ function splitIncompatibleMaterials(nodes, materials, options) { hasNormals : hasNormals }; var originalMaterialName = defaultValue(primitive.material, 'default'); - var materialName = originalMaterialName; - var suffix = 2; - while (defined(primitiveInfoByMaterial[materialName])) { - if (primitiveInfoMatch(primitiveInfo, primitiveInfoByMaterial[materialName])) { - break; - } - materialName = originalMaterialName + '-' + suffix++; - } + var splitMaterialName = getSplitMaterialName(originalMaterialName, primitiveInfo, primitiveInfoByMaterial); + primitive.material = splitMaterialName; + primitiveInfoByMaterial[splitMaterialName] = primitiveInfo; - primitive.material = materialName; - primitiveInfoByMaterial[materialName] = primitiveInfo; - - var material = getMaterialByName(splitMaterials, materialName); - if (defined(material)) { + var splitMaterial = getMaterialByName(splitMaterials, splitMaterialName); + if (defined(splitMaterial)) { continue; } - material = getMaterialByName(materials, originalMaterialName); - if (defined(material)) { - material = cloneMaterial(material, !hasUvs); + var originalMaterial = getMaterialByName(materials, originalMaterialName); + if (defined(originalMaterial)) { + splitMaterial = cloneMaterial(originalMaterial, !hasUvs); } else { - material = getDefaultMaterial(options); + splitMaterial = getDefaultMaterial(options); } - material.name = materialName; - splitMaterials.push(material); + splitMaterial.name = splitMaterialName; + splitMaterials.push(splitMaterial); } } } @@ -422,7 +426,7 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti primitive.uvs = undefined; primitive.indices = undefined; - var materialIndex = getMaterial(gltf, materials, primitive.material); + var materialIndex = getOrCreateGltfMaterial(gltf, materials, primitive.material); return { attributes : attributes, From 0b30a11e3a30915dc6791228f17fe49d8807356a Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 2 Nov 2018 22:25:58 -0400 Subject: [PATCH 20/23] Update to version 2.3.2 --- CHANGES.md | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8e7473d..1b91b64 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ Change Log ========== -### 2.3.2 ????-??-?? +### 2.3.2 2018-11-02 + * Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162) * Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162) * Improved parsing of faces with mismatching attributes. [#161](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/161) diff --git a/package.json b/package.json index ff629e5..65c4457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obj2gltf", - "version": "2.3.1", + "version": "2.3.2", "description": "Convert OBJ model format to glTF", "license": "Apache-2.0", "contributors": [ From fa1e5400ff91a9b7e49c677db325846674194a3a Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 6 Nov 2018 15:55:05 +0000 Subject: [PATCH 21/23] docs(readme): add Greenkeeper badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8107030..7c0347d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OBJ2GLTF +[![Greenkeeper badge](https://badges.greenkeeper.io/AnalyticalGraphicsInc/obj2gltf.svg)](https://greenkeeper.io/) + Convert OBJ assets to [glTF](https://www.khronos.org/gltf) 2.0. ## Getting Started From 94987d73431a87b6a642c25fd0c6275e64018080 Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Tue, 6 Nov 2018 12:15:18 -0500 Subject: [PATCH 22/23] Upgrade to gulp4 `gulp.task` was replaced with `module.exports` but no other changes were required. --- gulpfile.js | 29 ++++++++++++++++++----------- package.json | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index beac0e6..78a0389 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -22,7 +22,14 @@ process.env.PATH += environmentSeparator + nodeBinaries; var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**', '!doc/**', '!bin/**']; -gulp.task('test', function (done) { +module.exports = { + test: test, + 'test-watch': testWatch, + coverage: coverage, + cloc: cloc +}; + +function test(done) { var jasmine = new Jasmine(); jasmine.loadConfigFile('specs/jasmine.json'); jasmine.addReporter(new JasmineSpecReporter({ @@ -32,9 +39,9 @@ gulp.task('test', function (done) { jasmine.onComplete(function (passed) { done(argv.failTaskOnError && !passed ? 1 : 0); }); -}); +} -gulp.task('test-watch', function () { +function testWatch() { gulp.watch(specFiles).on('change', function () { // We can't simply depend on the test task because Jasmine // does not like being run multiple times in the same process. @@ -46,9 +53,9 @@ gulp.task('test-watch', function () { console.log('Tests failed to execute.'); } }); -}); +} -gulp.task('coverage', function () { +function coverage() { fsExtra.removeSync('coverage/server'); child_process.execSync('nyc' + ' --all' + @@ -57,12 +64,12 @@ gulp.task('coverage', function () { ' -x "specs/**" -x "coverage/**" -x "doc/**" -x "bin/**" -x "index.js" -x "gulpfile.js"' + ' node_modules/jasmine/bin/jasmine.js' + ' JASMINE_CONFIG_PATH=specs/jasmine.json', { - stdio: [process.stdin, process.stdout, process.stderr] - }); - open('coverage/lcov-report/index.html'); -}); + stdio: [process.stdin, process.stdout, process.stderr] + }); + return open('coverage/lcov-report/index.html'); +} -gulp.task('cloc', function() { +function cloc() { var cmdLine; var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc'); @@ -98,4 +105,4 @@ gulp.task('cloc', function() { }); }); }); -}); +} diff --git a/package.json b/package.json index 65c4457..c6ecc68 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "coveralls": "^3.0.2", "eslint": "^5.6.1", "eslint-config-cesium": "^6.0.0", - "gulp": "^3.9.1", + "gulp": "^4.0.0", "jasmine": "^3.2.0", "jasmine-spec-reporter": "^4.2.1", "jsdoc": "^3.5.5", From 62954bffb9e760629b02752b713511497663807e Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Tue, 6 Nov 2018 12:38:49 -0500 Subject: [PATCH 23/23] Proper gulp4 usage --- gulpfile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 78a0389..6d3a06a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -42,7 +42,7 @@ function test(done) { } function testWatch() { - gulp.watch(specFiles).on('change', function () { + return gulp.watch(specFiles).on('change', function () { // We can't simply depend on the test task because Jasmine // does not like being run multiple times in the same process. try { @@ -55,7 +55,7 @@ function testWatch() { }); } -function coverage() { +async function coverage() { fsExtra.removeSync('coverage/server'); child_process.execSync('nyc' + ' --all' + @@ -66,7 +66,7 @@ function coverage() { ' JASMINE_CONFIG_PATH=specs/jasmine.json', { stdio: [process.stdin, process.stdout, process.stderr] }); - return open('coverage/lcov-report/index.html'); + open('coverage/lcov-report/index.html'); } function cloc() {