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) 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 0000000..3b8baee Binary files /dev/null and b/specs/data/box-windows-paths/materials/images/cesium.png differ 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); });