From fbefce69af75e43cc24fcbe23c38ac0b4d9a2e58 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 30 Aug 2018 12:18:32 -0400 Subject: [PATCH] Fixed normalized Windows paths --- bin/obj2gltf.js | 15 ++++-- lib/loadMtl.js | 25 +++++++--- lib/loadObj.js | 7 ++- .../box-windows-paths/box-windows-paths.obj | 46 ++++++++++++++++++ .../materials/box-windows-paths.mtl | 13 +++++ .../materials/images/cesium.png | Bin 0 -> 7665 bytes specs/lib/loadMtlSpec.js | 2 +- specs/lib/loadObjSpec.js | 12 ++++- 8 files changed, 107 insertions(+), 13 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/bin/obj2gltf.js b/bin/obj2gltf.js index 478664c..2c76c20 100644 --- a/bin/obj2gltf.js +++ b/bin/obj2gltf.js @@ -21,14 +21,23 @@ var argv = yargs alias: 'i', describe: 'Path to the obj file.', type: 'string', - normalize: true, - demandOption: true + coerce : function (p) { + if (p.length === 0) { + throw new Error('Input path must be a file name'); + } + return path.resolve(p); + } }, output : { alias: 'o', describe: 'Path of the converted glTF file.', type: 'string', - normalize: true + coerce : function (p) { + if (p.length === 0) { + throw new Error('Output path must be a file name'); + } + return path.resolve(p); + } }, binary : { alias: 'b', diff --git a/lib/loadMtl.js b/lib/loadMtl.js index 5b5c72b..8a7aa96 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -68,19 +68,19 @@ function loadMtl(mtlPath) { value = line.substring(3).trim(); material.alpha = 1.0 - parseFloat(value); } else if (/^map_Ka /i.test(line)) { - material.ambientTexture = path.resolve(mtlDirectory, line.substring(7).trim()); + material.ambientTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } else if (/^map_Ke /i.test(line)) { - material.emissionTexture = path.resolve(mtlDirectory, line.substring(7).trim()); + material.emissionTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } else if (/^map_Kd /i.test(line)) { - material.diffuseTexture = path.resolve(mtlDirectory, line.substring(7).trim()); + material.diffuseTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } else if (/^map_Ks /i.test(line)) { - material.specularTexture = path.resolve(mtlDirectory, line.substring(7).trim()); + material.specularTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } else if (/^map_Ns /i.test(line)) { - material.specularShininessMap = path.resolve(mtlDirectory, line.substring(7).trim()); + material.specularShininessMap = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); } else if (/^map_Bump /i.test(line)) { - material.normalMap = path.resolve(mtlDirectory, line.substring(9).trim()); + material.normalMap = normalizeTexturePath(line.substring(9).trim(), mtlDirectory); } else if (/^map_d /i.test(line)) { - material.alphaMap = path.resolve(mtlDirectory, line.substring(6).trim()); + material.alphaMap = normalizeTexturePath(line.substring(6).trim(), mtlDirectory); } } @@ -89,3 +89,14 @@ function loadMtl(mtlPath) { return materials; }); } + +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(texturePath)) { + texturePath = texturePath.split(/\s+/).pop(); + } + texturePath = texturePath.replace(/\\/g, '/'); + return path.normalize(path.join(mtlDirectory, texturePath)); +} diff --git a/lib/loadObj.js b/lib/loadObj.js index eb5131b..efe97a9 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -537,6 +537,11 @@ function finishLoading(nodes, mtlPaths, objPath, options) { }); } +function normalizeMtlPath(mtlPath, objDirectory) { + mtlPath = mtlPath.replace(/\\/g, '/'); + return path.normalize(path.join(objDirectory, mtlPath)); +} + function outsideDirectory(filePath, objPath) { return (path.relative(path.dirname(objPath), filePath).indexOf('..') === 0); } @@ -547,7 +552,7 @@ function loadMaterials(mtlPaths, objPath, options) { var objDirectory = path.dirname(objPath); var materials = {}; return Promise.map(mtlPaths, function(mtlPath) { - mtlPath = path.resolve(objDirectory, mtlPath); + mtlPath = normalizeMtlPath(mtlPath, objDirectory); if (secure && outsideDirectory(mtlPath, objPath)) { logger('Could not read mtl file at ' + mtlPath + ' because it is outside of the obj directory and the secure flag is true. Using default material instead.'); return; 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/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index 976b322..9d13473 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -6,7 +6,7 @@ var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.m var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; function getImagePath(objPath, relativePath) { - return path.resolve(path.dirname(objPath), relativePath); + return path.normalize(path.join(path.dirname(objPath), relativePath)); } describe('loadMtl', function() { diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index a43930d..bfe4c44 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -30,6 +30,7 @@ var objExternalResourcesUrl = 'specs/data/box-external-resources/box-external-re var objTexturedUrl = 'specs/data/box-textured/box-textured.obj'; var objMissingTextureUrl = 'specs/data/box-missing-texture/box-missing-texture.obj'; var objSubdirectoriesUrl = 'specs/data/box-subdirectories/box-textured.obj'; +var objWindowsPathsUrl = 'specs/data/box-windows-paths/box-windows-paths.obj'; var objComplexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.obj'; var objInvalidContentsUrl = 'specs/data/box/box.mtl'; var objInvalidUrl = 'invalid.obj'; @@ -59,7 +60,7 @@ function getPrimitives(data) { } function getImagePath(objPath, relativePath) { - return path.resolve(path.dirname(objPath), relativePath); + return path.normalize(path.join(path.dirname(objPath), relativePath)); } var defaultOptions = obj2gltf.defaults; @@ -346,6 +347,15 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with windows paths', function(done) { + expect(loadObj(objWindowsPathsUrl, defaultOptions) + .then(function(data) { + var imagePath = getImagePath(objWindowsPathsUrl, path.join('materials', 'images', 'cesium.png')); + expect(data.images[imagePath]).toBeDefined(); + expect(data.materials.Material.diffuseTexture).toEqual(imagePath); + }), done).toResolve(); + }); + it('loads obj with complex material', function(done) { expect(loadObj(objComplexMaterialUrl, defaultOptions) .then(function(data) {