From bab00f25581086a1b33dcf245aca190fa07dfe93 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 21:10:54 -0400 Subject: [PATCH 01/16] Same as #161 --- lib/loadObj.js | 91 ++++++++++++++++++++++++++++------------ specs/lib/loadObjSpec.js | 10 +++-- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 8659507..28a5b3a 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -78,6 +78,7 @@ function loadObj(objPath, options) { var node; var mesh; var primitive; + var activeMaterial; // All nodes seen in the obj var nodes = []; @@ -125,6 +126,7 @@ function loadObj(objPath, options) { function addPrimitive() { primitive = new Primitive(); + primitive.material = activeMaterial; mesh.primitives.push(primitive); // Clear the vertex cache for each new primitive @@ -132,22 +134,42 @@ function loadObj(objPath, options) { vertexCount = 0; } - function useMaterial(name) { - // Look to see if this material has already been used by a primitive in the mesh - var material = getName(name); + 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(); - primitive.material = getName(name); + } + + 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) { @@ -269,16 +291,7 @@ 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]); @@ -347,10 +360,12 @@ function loadObj(objPath, options) { positions.push(position.y); positions.push(position.z); } else if ((result = normalPattern.exec(line) ) !== null) { - var normal = scratchCartesian; - normal.x = parseFloat(result[1]); - normal.y = parseFloat(result[2]); - normal.z = parseFloat(result[3]); + var normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal); + if (Cartesian3.equals(normal, Cartesian3.ZERO)) { + Cartesian3.clone(Cartesian3.UNIT_Z, normal); + } else { + Cartesian3.normalize(normal, normal); + } if (defined(axisTransform)) { Matrix4.multiplyByPointAsVector(axisTransform, normal, normal); } @@ -375,7 +390,9 @@ function loadObj(objPath, options) { faceUvs.push(result[2]); faceNormals.push(result[3]); } - addFace(faceVertices, facePositions, faceUvs, faceNormals); + if (faceVertices.length > 2) { + addFace(faceVertices, facePositions, faceUvs, faceNormals); + } faceVertices.length = 0; facePositions.length = 0; @@ -398,17 +415,20 @@ function loadObj(objPath, options) { uvs = undefined; // Load materials and images - return finishLoading(nodes, mtlPaths, objPath, options); + return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options); }); } -function finishLoading(nodes, mtlPaths, objPath, options) { +function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { nodes = cleanNodes(nodes); if (nodes.length === 0) { return Promise.reject(new RuntimeError(objPath + ' does not have any geometry data')); } return loadMaterials(mtlPaths, objPath, options) .then(function(materials) { + if (materials.length > 0 && !usesMaterials) { + assignDefaultMaterial(nodes, materials, usesMaterials); + } var imagePaths = getImagePaths(materials); return loadImages(imagePaths, objPath, options) .then(function(images) { @@ -494,6 +514,23 @@ function getImagePaths(materials) { return Object.keys(imagePaths); } +function assignDefaultMaterial(nodes, materials) { + var defaultMaterial = materials[0].name; + 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]; + primitive.material = defaultValue(primitive.material, defaultMaterial); + } + } + } +} + function removeEmptyMeshes(meshes) { return meshes.filter(function(mesh) { // Remove empty primitives diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index f635878..d4b9424 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -417,11 +417,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(objMixedAttributesUrl, defaultOptions) .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 c60f4ebde31b25bd4bea36df60f7bd5c4874c39f Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 23:15:47 -0400 Subject: [PATCH 02/16] Same as #162 --- lib/createGltf.js | 89 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/lib/createGltf.js b/lib/createGltf.js index f183757..4e62cac 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -3,8 +3,9 @@ var Cesium = require('cesium'); var path = require('path'); var Material = require('./Material'); -var defined = Cesium.defined; +var clone = Cesium.clone; var defaultValue = Cesium.defaultValue; +var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; module.exports = createGltf; @@ -27,6 +28,9 @@ function createGltf(objData, options) { var vertexBufferViewId = 'bufferView_vertex'; var indexBufferViewId = 'bufferView_index'; + // Split materials used by primitives with different types of attributes + materials = splitIncompatibleMaterials(nodes, materials); + var gltf = { accessors : {}, asset : {}, @@ -290,23 +294,8 @@ function createGltf(objData, options) { primitive.uvs = undefined; primitive.indices = undefined; - if (!defined(materialId)) { - // Create a default material if the primitive does not specify one - materialId = 'default'; - } - var material = materials[materialId]; - material = defined(material) ? material : new Material(); var gltfMaterial = gltf.materials[materialId]; - if (defined(gltfMaterial)) { - // Check if this material has already been added but with incompatible shading - var normalShading = (gltfMaterial.extensions.KHR_materials_common.technique !== 'CONSTANT'); - if (hasNormals !== normalShading) { - materialId += (hasNormals ? '_shaded' : '_constant'); - gltfMaterial = gltf.materials[materialId]; - } - } - if (!defined(gltfMaterial)) { gltf.materials[materialId] = createMaterial(material, hasNormals, options); } @@ -350,3 +339,71 @@ function createGltf(objData, options) { return gltf; } + +function primitiveInfoMatch(a, b) { + return a.hasUvs === b.hasUvs && + a.hasNormals === b.hasNormals; +} + +function cloneMaterial(material, removeTextures) { + material = clone(material, true); + if (removeTextures) { + material.ambientTexture = undefined; + material.emissionTexture = undefined; + material.diffuseTexture = undefined; + material.specularTexture = undefined; + material.specularShininessMap = undefined; + material.normalMap = undefined; + material.alphaMap = undefined; + } + return material; +} + +function splitIncompatibleMaterials(nodes, materials) { + 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 = splitMaterials[materialName]; + if (defined(material)) { + continue; + } + + material = materials[originalMaterialName]; + if (defined(material)) { + material = cloneMaterial(material, !hasUvs); + } else { + material = new Material(); + } + splitMaterials[materialName] = material; + } + } + } + return splitMaterials; +} From 721198557a789667c41ee6c63543bd8b42f407b4 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 23:16:41 -0400 Subject: [PATCH 03/16] Updated CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 85f0199..d28fb68 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ Change Log ========== +### 1.3.5 ????-??-?? +* Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Improved parsing of faces with mismatching attributes. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) + ### 1.3.4 2018-10-16 * Improved parsing models with concave or n-sided faces. [#158](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/158) From 50f1366689459131c4472b01be32e74ca783f700 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 29 Oct 2018 23:57:33 -0400 Subject: [PATCH 04/16] Tests --- .../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 | 64 +++++++++++++++++ 4 files changed, 144 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 zcmV(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRaEcS%G+RCwC#oo9Gd*O|x9t<#&N8I39uLIp`kAk^p~*aj!Y!HaR+aBNJRcoUmA zaeDD)ySD-2&6m&QXT?LCbVEC_}I#9)BzcMA?5_ld~Z7=5Fk5qcS}*ZqH5G^vSfJ(jK& z>}tlU5iq2@B*x2t9xvJD#cw+KPx?w$uF5zAoHGEf0000^llXKKk)z|Xw2WCbRhk1! z`gL1J0PFCOolY?5;guh4rD};H3zny2Y-%3Ekn$3c(E!%&rfxXFh?h&m`WtDyBo!+( zvQ{;Zl$RL8((6v*$~e*Q=91%$Y&xPe1r!-WS`sNwkqrB>)(QIKF+Rv9p_3qRY-TD^ zW??fl98#V#2C4vbxryd6;^qVgrT`gwqRPq_7+4%b%99CR09Tzvvx6A+P6^HZf6OYP zDjm!;g(w^;FTQ^6+5~ZC7$5WVU<#wB@wyCbrZGg~NO`f>T)T^C8YUc5&(GZng9=}m zi4_?_i8Tw8NXi@ZVaJE4UKi5bXOL+i7iV&IHH(xdC2j^Z4O6WS9xc32)|LR2r&IN5 zAsxvh>kF*sDB!SJ(Br427g$I9%R-Pk(9qFg~+^BnnCPho!lf&87 z98w;%t2^T$&kO<{b+DqF76bFMsH&6zp~#Vk5?5r#f>>i8+3!Z(YjpDf0yxpjbUM{{ zWiu(X;+&tde4YTnm2vXJUJydAPC5u2Tbx5wn1YI$c2SV>f*fe0m}({+S#U2wJ?>H$MWF)KI+UM8pFn$re0=Gk1D{)IpUD5*Y~kt zZx7YyLb8*G_A%BOpjYKHdYP8mWKbt&^c-W&u8l@|*-GyHoL9$CWT84rxE{Ov^DVIt-4A$6-1AzM3Xt5iqT(iog zCNK;*16yo*;n6e#+0F*}Et@APifOuU|UDXw`o@s2}sO zAGEuUb-3j^u)^`ep+0(bUI3HGvq*N{wq=}b>_*;BJT^V`%sgAVVX}ebsF(d+)3A*G z@2-5h+~`+Ooabi1v3?AxM>KgUs_!h#m|S)MfK(m%!jg<4t6Hvle$dT$I6^_n`*4sB zqAX)#F>A=L*4wm^r97rdY<<1$ue;;;z;MFhK1xuC@@_Z@lp7!#9;{3;sv#VShQc4M zN|md&9plV(k4ApwF+4anfG)|6s-LaQ)WVjUk)|$9*T_}pLtU&l>OMDG(OeiKT|v~( zil;h9C$3;^u3oM>hgklsgO*cyt`K&10Es6m%I#Wl1xxK38LNG9Fcjr+wWySLYM5eB zqPu9Uj{%F=>&!G1Zcq_&)m#AJ!-1%7Di^Q3AwPC?6b&q*LPrzPTq31c$r)jHPq4jy zl?=)|J3=9M7bSj%kFL1PEpTE06NLCGZ@`DQjU$H_Ei5m1f+Eex@q+Bmvbi1=}0}UXZ`nIoS`y)k2K&27LHUbgoAtF7)}t6>Jt;P+A;5wkM#9 zlk%EJk;9X2dZORk2fqVg%*Qr$c@**rrw8NW^Cp7eI*RZloSqQC_KRa2%;b~+z_#X5 zRx$HgeJ-{?NX1-vmnLWdjcFyN-4WP%X)N;3?`<8wGU!*-D_nx_2H}(!U;*0f9t)0j zxc}?yNZ4JUydZ3A9)16+Q)%_T<_LK>IPt-K`nsEBQ4;sqaIVMO>kQpjYPr(}f{%y% zyRVG*PJ|R#c|icJPP#UQjg|6RMsWa8kQXZkUCi&AhSPN9tPE|gMP=3y7yuJ~uFn~| zG~gR@GfJ*}^B^Rz z#3ZziL&z!8kFq?tHjW}Z#H7Oj2jk~(iIvyo!2=8$)grfh4EmeEU}dg;(e3s&jPXe)h->*U6h6bW(kCvQ6}|B2!hi7RSO&7Hc#b48w$Q ztNY>esyG5*%QZr=7RfTv56w=Uo*Gq-EmweRXc<4(?Lpq}uhFZBRppl2Y5K@djP&*O z1p@UCnwJ(D!~P1xVpJ1PRHx0GrvJlv$GDG0YP(HsuKvNw6b%Kp zvqY!Ug;ieoC-!=k=}G@ZHhK+O0>!w@GB_DGpdPa=GinhqKS= z9fk6FlX*24BY!9JU}b8Ss1g3iMDM}#TRysRXn3OM)?c)0V|m`Z&s9D&D}MoW)PiGA z)mmRZ-0uw~+s1;rY5KZpViMo_g6<<1wl-be*FAKN7dQX_MXJj4=FKm=zpi8jhT$YR znYt$1F|fUp?vr%^qc&H+wmJ>A6fbbQPQ143{}WH(v(KDQOD}zL`Ad1WvaYVKJN}rTfByM9{qN); z&d7JJ+is+unU@ZmQ`7Am`^EOJA8Tn4ggZK?;fbE(ExYpVWma=G)WdChis6Nh(O`1O zPDSG1T$pZxTn*v{?!ONH!#hV_39)yygVQ^5qGh+$oN1)(&~YBiVKVkCG>JW3XM_DM z`hxC%dFRQiJ!ivy(&Zcd#g4DH^)w)QS>wu`MA3zDMSy+8{6N3$cMsyIz3p8~^~G0DPPmWU+)W3d06BHhjw$bVu{O ze?0WBor70k1HLD!t+CrC7$X)>H&kUqhj&{V|G8`EV-a5`N_hg7I7VrW@~c^n+1Rkb z7l6y)WKsoL;L9=7e*+)how&05Nb@$ah7Cb0kK+@-C?{L@9B$q!Nd-nGdf(Xh zBiI$TLaX`5C*{GbJJ3FB&2QvzkY8+kP)AgImE;Jytr+T!T@+oluo2A-%+Giwwb@GUJgR>1GCe(U|e>vOb4^SvC)Kr1h9>t;)47)5M4 z-+b`ry(2dyuMEpEn|8h6@lQaN(yNFkYpoKVzm%5z6xID@7T8iBT-*UQX?%xqst;Ou z@#$>yQdFzTp^b9v@_WZFzZat_jk*Tj*!u(6fUh7`y|PS+0iTY>pQ^ROMl!u4H{Ndi z$LPPE=Xq%5k$B8E=V`?nW%I$G$FyP>+K%pPdK0$PvLch{Ym#FZ$*irm!d{^p2>CWP zYzQ%d=qrHVSw(mJ2Z0H(H%eLxq)~#5e`CY9Br(e5Z2$0A*L%;$;f-R-UB#v{_^ZF) zI`Fgp(GE!}pdz)<%A;c(F^lsMZyk84f3!UgZN~B3rd{85`^KP3iSyzyq99FuUnz7X zbGUiy$<{q_s2(m$m?thOI?}xD#FgD~Yq@dv&})DG-X#0{B#c}o$Dksfs)e5A?iy;{ z()iC3RzppT@Od!{^Tdq=yM{iN)+mv1x%2q$Q@@SRXkAKof`KP$(qOlI`hxC_4c}r| zDfhh+I4Q#C#U#!XH~ZxedN%EPA;bpa--_=Z+tk{18aCj2Y;IbbPPTw=d5Kv>MyxmY z|72*qTjD{a$^@-EEg6&LGjTKiZ|?u;;Mh%BT9FsH-|YU5)9ZjLrJ?Y3bv9CtfUnr5 zUID*@>)`nb^K=ccWxc8iX3q((NSVl z6JLSf#LzKt`JKZr$D%%3+91F3#O>_p9K7bI9WkHeOFq$<^B zh|_&>Uo5rEfIOWf0O5_jKX5qvVpJuq3SN064kS!dx1z*cWQ8}%z8{TD^a5od2>fe* z{@%EI2&$Bd#MjkYMI2~_r8Hf0cQJIrzx`)6e{|z;3@3NgY0%0802pX#>?Q8h!htiJ z&tGo@O2X|M+qCQ391j=pWtr&Jm9Y)@EE;lc4RqR}w!Y@=AN(?QwIT^xOPj?gFGVYX z&l7i2tF5njTP%$tn(MvicYgRPY$qLy9RE$+ER$<13p4K##zrbQn~KY%E^bv+A&6U9uFBSlyJJX0;a{zXz9?bB zGxFM>UyM(9cZ)n*X;jKHlbj~venFg3#O(x86ykQ%tSqz4%7EKIO5oV~dRqqM9X+3H z^csz)O)||zQ7I3@K(2|1+$gZIv$)-F3Pi*XLL39@>a3A3XJZ(6d~O=-@;3nB-D8_R z?miPwYp7_QMVl3s@&Eu?hVY%ImcqX_&nC_&useR0iMU` z`vQ>XUWpl*wUQ}M5QN)qFQJVRH)5LYYI6QJ5nI41%eQOP#vF5*WXb~ocs!oyt92{g zqFhFaGn)_oJo*IABKSk&KefZ(Z6p=5^OxWlA*J$sK3|j_t^kDJ?f-Gdz-2Hs2r+?Q z?)cj{?}0dU;hn=5{v_jDm**^w@RN}$PY?vJ*Becpwjce~sn)$J-U%keXX)2Lec>n;nTl}39JCFSu_Tr-CpuPX%D_ftA^16-)Ww)_mgS^Y% zwM8qih!q8)Jip)1Fwpzs1t7e0%AYn{HJxWxtF0hbM^gSzxSN9X`n<< zk+UrF4@2&ycER0M;tRNbwd2`+r+*KoHXQ?(f4X^f=U|KEPv!+~$A_-Aw>-PWVOIC61&k}bVe5`{&+% z@AyW^Zv+vCPGf%I3vZD)9Yf`*RI2RkJD#I-`^W!sZp&ZJZ_3>gs2Hac{&AOY zv}gF*&7s!z{!4+77b!^`I7UoMFUhl)O-rAdX3W&7O?iHWTu_m0u$X8nwh!V@1{a$x}teTzMu<`m)MLQAJVrBL7(O*L=f$|89T3P)K)Fe}cr4v#)d1rjCW!9out5FLjC0W)Pi;EtUa-IE<4m}kXM*)gqssxIf@1wV{1l^;SmET8k0 zvW(a+hvKn*hXQN$nguVSwT%R#uJ{Y{3LlDjZytv?nVGTpOY@#XEt(LN=Pz77>+5mI z5QN9)eP(AbUs1IVwX4D@$*Fyy>c#j(M|j-cs=i?L=VpBwwXR~BZm)Q-_J=W=Ly=Q? z0DvVWYZsI~fZAAr6l7LDvfw4CG-QdSJPcrWm#)8O4$6$7EN14`Jv#sA6du?4FFgM5 zIe*4OtLmRaqg{ninZIOB?T@6|?-@CyEN?qIXN8*5Z#(`Z^DE!mPI;9mKn*BdA9f|EMnS z0U0xUi^&-(Ek4Hi?&(+B2AYu|;)7nB^7!JP*$sse1m)f0Qg7p~(;GRSMf!*l3(Fr^HuEVAC}IPLf|LgU7<9GmZ2I>R zXAe?|v}jcN2kJKz+2$)~h@&Xw0RR{-aP;FpocOo_$wev(>=h5xy{M;Cl{L6ln(}UO zbF6visegC-#*uEKfxwA-<~&(D{Q(8e+bdLg002MZ-h1IcTDp!Q0f{5mR`R8q4XN5( zWmZqQ%Dct&p{Bhbz3%jkASFc(O{!PST{kE93rb$1RJ!s201OvwYTtSE@}D>!EwzQG zJa57Bif0U}G=0t9_ zH0|;!G-~5Lv!ATYzAJ$aTP2wC006weUmZGe_~P3RS3lC6n$|6y^>Ed+dnrPbST#>P z<$a<*?&`p?#>-pATmw^6b1F)+xO`1b{z{tACSv^)Re87d*V1?7c~DjJLtI3 z_5P`Ad;9@cG6{|*)w2thR^=^EH_k|!Qv^w+yjw6_uw&%x<(^|7cQ+;a&X+t}S#{y^ z!t{Bn#0=)9mhx^B}z1Dj!u?~FX+Da<(mKJB!>*=&)ZJlIR-faSm`^Lz* z_JQ-)`_KE7+7VNvsxWg-ab``vZEoyxhau(3G!_B;sJExvaix9mqw50~7$zWZeF&VK zW-Fa$E1Pbs%ruq6?R-B{o}6$zJM8XoczTAMoxP6heWPs*8;rTRG2Cj(%1$lJNGZ%t zDX|&y2KP2 literal 0 HcmV?d00001 diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 87ea212..fd51b20 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -12,10 +12,12 @@ var WebGLConstants = Cesium.WebGLConstants; var boxObjUrl = 'specs/data/box/box.obj'; var groupObjUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; +var mixedAttributesObjUrl = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj'; var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; var defaultOptions = obj2gltf.defaults; +var defined = Cesium.defined; var checkTransparencyOptions = clone(defaultOptions); checkTransparencyOptions.checkTransparency = true; @@ -23,6 +25,7 @@ describe('createGltf', function() { var boxObjData; var duplicateBoxObjData; var groupObjData; + var mixedAttributesObjData; var diffuseTexture; var transparentDiffuseTexture; @@ -47,7 +50,12 @@ describe('createGltf', function() { loadImage(transparentDiffuseTextureUrl, checkTransparencyOptions) .then(function(image) { transparentDiffuseTexture = image; + }), + loadObj(mixedAttributesObjUrl, defaultOptions) + .then(function(data) { + mixedAttributesObjData = data; }) + ]).then(done); }); @@ -309,6 +317,62 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); + function getDiffuse(material) { + return material.extensions.KHR_materials_common.values.diffuse; + } + + fit('splits incompatible materials', function() { + var gltf = createGltf(mixedAttributesObjData, defaultOptions); + var materials = gltf.materials; + var materialNames = Object.keys(materials).sort(); + + // Expect three copies of each material for + // * positions/normals/uvs + // * positions/normals + // * positions/uvs + expect(materialNames).toEqual([ + 'Material', + 'Material-2', + 'Material-3', + 'Missing', + 'Missing-2', + 'Missing-3', + 'default', + 'default-2', + 'default-3' + ]); + console.log(materials['Material']); + console.log(materials['Material-2']); + console.log(materials['Material-3']); + + + expect(getDiffuse(materials['Material'])).toBe('texture_cesium'); + expect(getDiffuse(materials['Material-2'])).toEqual('texture_cesium'); + //expect(getDiffuse(materials['Material-3'])).toBe('texture_cesium'); + // expect(getDiffuse(materials['Missing'])).toEqual([0.0, 0.0, 0.0, 1.0]); + // expect(getDiffuse(materials['Missing-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); + // expect(getDiffuse(materials['Missing-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); + // expect(getDiffuse(materials['default'])).toEqual([0.0, 0.0, 0.0, 1.0]); + // expect(getDiffuse(materials['default-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); + // expect(getDiffuse(materials['default-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); + + // // Test that primitives without uvs reference materials without textures + // for (var meshName in meshes) { + // if (meshes.hasOwnProperty(meshName)) { + // var mesh = meshes[meshName]; + // var primitives = mesh.primitives; + // var primitivesLength = primitives.length; + // for (var i = 0; i < primitivesLength; ++i) { + // var primitive = primitives[i]; + // var material = materials[primitive.material]; + // if (!defined(primitive.attributes.TEXCOORD_0)) { + // expect(material.extensions.KHR_materials_common.diffuse).toEqual([ 0.5, 0.5, 0.5, 1 ]); + // } + // } + // } + // } + }); + function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices; From d42e2548c8b3025c77fab88567abdaf251f52d50 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 21:39:53 -0400 Subject: [PATCH 05/16] PR updates --- lib/createGltf.js | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/lib/createGltf.js b/lib/createGltf.js index 4e62cac..c1ef37d 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -359,6 +359,18 @@ function cloneMaterial(material, removeTextures) { return material; } +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) { var splitMaterials = {}; var primitiveInfoByMaterial = {}; @@ -378,30 +390,22 @@ function splitIncompatibleMaterials(nodes, materials) { 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 = splitMaterials[materialName]; - if (defined(material)) { + var splitMaterial = splitMaterials[splitMaterialName]; + if (defined(splitMaterial)) { continue; } - material = materials[originalMaterialName]; - if (defined(material)) { - material = cloneMaterial(material, !hasUvs); + var originalMaterial = materials[originalMaterialName]; + if (defined(originalMaterial)) { + splitMaterial = cloneMaterial(originalMaterial, !hasUvs); } else { - material = new Material(); + splitMaterial = new Material(); } - splitMaterials[materialName] = material; + splitMaterials[splitMaterialName] = splitMaterial; } } } From 99806cc4a78d475fcc6152fe5069ba1583d71824 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 21:41:09 -0400 Subject: [PATCH 06/16] Add specs and CHANGES updates for porting over #120 #133 #136 --- CHANGES.md | 3 ++ lib/loadObj.js | 10 +++- .../box-missing-usemtl/box-missing-usemtl.mtl | 12 +++++ .../box-missing-usemtl/box-missing-usemtl.obj | 45 ++++++++++++++++++ .../box-unnormalized/box-unnormalized.mtl | 12 +++++ .../box-unnormalized/box-unnormalized.obj | 46 +++++++++++++++++++ specs/lib/loadObjSpec.js | 28 +++++++++++ 7 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 specs/data/box-missing-usemtl/box-missing-usemtl.mtl create mode 100644 specs/data/box-missing-usemtl/box-missing-usemtl.obj create mode 100644 specs/data/box-unnormalized/box-unnormalized.mtl create mode 100644 specs/data/box-unnormalized/box-unnormalized.obj diff --git a/CHANGES.md b/CHANGES.md index d28fb68..bfd31d0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,9 @@ Change Log * Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Improved parsing of faces with mismatching attributes. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of unnormalized input normals. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) ### 1.3.4 2018-10-16 diff --git a/lib/loadObj.js b/lib/loadObj.js index 28a5b3a..ff1333a 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -426,7 +426,7 @@ function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { } return loadMaterials(mtlPaths, objPath, options) .then(function(materials) { - if (materials.length > 0 && !usesMaterials) { + if (Object.keys(materials).length > 0 && !usesMaterials) { assignDefaultMaterial(nodes, materials, usesMaterials); } var imagePaths = getImagePaths(materials); @@ -455,6 +455,12 @@ function loadMaterials(mtlPaths, objPath, options) { var logger = options.logger; var objDirectory = path.dirname(objPath); var materials = {}; + + // Remove duplicates + mtlPaths = mtlPaths.filter(function(value, index, self) { + return self.indexOf(value) === index; + }); + return Promise.map(mtlPaths, function(mtlPath) { mtlPath = normalizeMtlPath(mtlPath, objDirectory); if (secure && outsideDirectory(mtlPath, objPath)) { @@ -515,7 +521,7 @@ function getImagePaths(materials) { } function assignDefaultMaterial(nodes, materials) { - var defaultMaterial = materials[0].name; + var defaultMaterial = Object.keys(materials)[0]; var nodesLength = nodes.length; for (var i = 0; i < nodesLength; ++i) { var meshes = nodes[i].meshes; diff --git a/specs/data/box-missing-usemtl/box-missing-usemtl.mtl b/specs/data/box-missing-usemtl/box-missing-usemtl.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-missing-usemtl/box-missing-usemtl.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-missing-usemtl/box-missing-usemtl.obj b/specs/data/box-missing-usemtl/box-missing-usemtl.obj new file mode 100644 index 0000000..4fc5eee --- /dev/null +++ b/specs/data/box-missing-usemtl/box-missing-usemtl.obj @@ -0,0 +1,45 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-missing-usemtl.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 +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-unnormalized/box-unnormalized.mtl b/specs/data/box-unnormalized/box-unnormalized.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-unnormalized/box-unnormalized.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-unnormalized/box-unnormalized.obj b/specs/data/box-unnormalized/box-unnormalized.obj new file mode 100644 index 0000000..f52251e --- /dev/null +++ b/specs/data/box-unnormalized/box-unnormalized.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-unnormalized.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 0.0000 0.0000 0.0000 +vn 0.0000 0.0000 0.5 +vn 1.0000 0.1000 0.0000 +vn 0.0000 0.0000 -0.9 +vn 0.0000 1.0000 0.0000 +vn 10.0000 -10.0000 5.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/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index d4b9424..b0b5a4b 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -7,6 +7,7 @@ var obj2gltf = require('../../lib/obj2gltf'); var Cartesian3 = Cesium.Cartesian3; var clone = Cesium.clone; +var CesiumMath = Cesium.Math; var RuntimeError = Cesium.RuntimeError; var objUrl = 'specs/data/box/box.obj'; @@ -20,12 +21,14 @@ var objObjectsUrl = 'specs/data/box-objects/box-objects.obj'; var objGroupsUrl = 'specs/data/box-groups/box-groups.obj'; var objObjectsGroupsUrl = 'specs/data/box-objects-groups/box-objects-groups.obj'; var objConcaveUrl = 'specs/data/concave/concave.obj'; +var objUnnormalizedUrl = 'specs/data/box-unnormalized/box-unnormalized.obj'; var objUsemtlUrl = 'specs/data/box-usemtl/box-usemtl.obj'; var objNoMaterialsUrl = 'specs/data/box-no-materials/box-no-materials.obj'; var objMultipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.obj'; var objUncleanedUrl = 'specs/data/box-uncleaned/box-uncleaned.obj'; var objMtllibUrl = 'specs/data/box-mtllib/box-mtllib.obj'; var objMissingMtllibUrl = 'specs/data/box-missing-mtllib/box-missing-mtllib.obj'; +var objMissingUsemtlUrl = 'specs/data/box-missing-usemtl/box-missing-usemtl.obj'; var objExternalResourcesUrl = 'specs/data/box-external-resources/box-external-resources.obj'; var objTexturedUrl = 'specs/data/box-textured/box-textured.obj'; var objMissingTextureUrl = 'specs/data/box-missing-texture/box-missing-texture.obj'; @@ -110,6 +113,23 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('normalizes normals', function(done) { + expect(loadObj(objUnnormalizedUrl, defaultOptions) + .then(function(data) { + var scratchNormal = new Cesium.Cartesian3(); + var mesh = getMeshes(data)[0]; + var normals = mesh.normals; + var normalsLength = normals.length / 3; + for (var i = 0; i < normalsLength; ++i) { + var normalX = normals.get(i * 3); + var normalY = normals.get(i * 3 + 1); + var normalZ = normals.get(i * 3 + 2); + var normal = Cartesian3.fromElements(normalX, normalY, normalZ, scratchNormal); + expect(Cartesian3.magnitude(normal)).toEqualEpsilon(1.0, CesiumMath.EPSILON5); + } + }), done).toResolve(); + }); + it('loads obj with uvs', function(done) { expect(loadObj(objUvsUrl, defaultOptions) .then(function(data) { @@ -301,6 +321,14 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with missing usemtl', function(done) { + expect(loadObj(objMissingUsemtlUrl, defaultOptions) + .then(function(data) { + expect(data.materials.length).toBe(1); + expect(data.nodes[0].meshes[0].primitives[0].material).toBe('Material'); + }), done).toResolve(); + }); + it('loads resources outside of the obj directory', function(done) { expect(loadObj(objExternalResourcesUrl, defaultOptions) .then(function(data) { From e538ded7716c22ef66b296f9de501f4a261f4a6a Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 21:47:48 -0400 Subject: [PATCH 07/16] Tests from #109 --- CHANGES.md | 1 + specs/data/box-texture-options/ambient.gif | Bin 0 -> 4040 bytes .../box-texture-options.mtl | 19 ++++++++ .../box-texture-options.obj | 46 ++++++++++++++++++ specs/data/box-texture-options/bump.png | Bin 0 -> 4998 bytes specs/data/box-texture-options/diffuse.png | Bin 0 -> 8889 bytes specs/data/box-texture-options/emission.jpg | Bin 0 -> 7092 bytes specs/data/box-texture-options/shininess.png | Bin 0 -> 4094 bytes specs/data/box-texture-options/specular.jpeg | Bin 0 -> 5618 bytes specs/lib/loadMtlSpec.js | 23 +++++++++ 10 files changed, 89 insertions(+) create mode 100644 specs/data/box-texture-options/ambient.gif create mode 100644 specs/data/box-texture-options/box-texture-options.mtl create mode 100644 specs/data/box-texture-options/box-texture-options.obj create mode 100644 specs/data/box-texture-options/bump.png create mode 100644 specs/data/box-texture-options/diffuse.png create mode 100644 specs/data/box-texture-options/emission.jpg create mode 100644 specs/data/box-texture-options/shininess.png create mode 100644 specs/data/box-texture-options/specular.jpeg diff --git a/CHANGES.md b/CHANGES.md index bfd31d0..77c3ce7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ Change Log ### 1.3.3 2018-09-19 * Fixed handling of objs with mismatching attribute layouts. [#154](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/154) +* Fixed parsing mtl textures that contain texture map options. [#151](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/151) ### 1.3.2 2018-06-07 * Fixed greyscale images loading as alpha instead of luminance. [#144](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/144) diff --git a/specs/data/box-texture-options/ambient.gif b/specs/data/box-texture-options/ambient.gif new file mode 100644 index 0000000000000000000000000000000000000000..1276823d350c3a7939ce8021a937381ee08564ae GIT binary patch literal 4040 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-texture-options/box-texture-options.mtl b/specs/data/box-texture-options/box-texture-options.mtl new file mode 100644 index 0000000..3434533 --- /dev/null +++ b/specs/data/box-texture-options/box-texture-options.mtl @@ -0,0 +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 diff --git a/specs/data/box-texture-options/box-texture-options.obj b/specs/data/box-texture-options/box-texture-options.obj new file mode 100644 index 0000000..3207f00 --- /dev/null +++ b/specs/data/box-texture-options/box-texture-options.obj @@ -0,0 +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 diff --git a/specs/data/box-texture-options/bump.png b/specs/data/box-texture-options/bump.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec3a936601922682f2efadd6f19a39d80c7150 GIT binary patch literal 4998 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-texture-options/diffuse.png b/specs/data/box-texture-options/diffuse.png new file mode 100644 index 0000000000000000000000000000000000000000..8704966c032736dd3b504891393c5a69e90f0a7b GIT binary patch literal 8889 zcmV;qB1YYbP)VGd000McNliru;0zWI8XH>%AZh>rAOJ~3 zK~#9!?VWjiQ&sx^KX=R8q+8N-r7d)&g+ie%6cDP&qHKcJQ3n**~(r@p)1|DBu#R2e}5qSToBvdo8%@v@4xzj`s-t>Y!vPo1cQIS8?=Nio*UTt7A)Rn{ChH-1r*OLqrTN zAums$>-!e>HfWXO7=|9(X65@fxAOsVt*=(WnEh4ENrs^c>)QCwHGXFfY{;jL}q7@9X~*^?ZNb(Kxl_g|ab>lEo>B8ER3`t&pE zxH%S^b8M4^Hwix4PQ8k04%V=Rt?kY?gxwL_^YSPs>3t#w%vx2N5~gS8neF^7%~n3B zyR~1#(H4W2{iekxEd1x#$RCIpy4?_{$S*{%pXqR7cB91+L7s`12v9SPdKLTW*+z5T zyOZNC5;6ENe_MM#BT&V>SZ8)zZ|9vXasR8s(3B9S;|_wtb5Gn5v5ttrR}cj1>5b)6 zXj)iU-{y!XuHHF<)NHL*LH(<^xN6bT2M5}T7`%`Nmh9ugV?yTG99Yn7{gbW2<4ebfn~4~_fV{#&7S_0#R^ItSYrE5*xUu8`x6PziEGfI_Snzsol!b^v zro*AU&1Ltt^TNw5-EdaO?V?c7?csXmD~*Mw=X_;q)+fa9zNTKREpt7Diz{L-UN6dj~2Qq#C?*6{G-L=0}^wtRs} z^)LTtqlJIm&N~_6j`DzIC?O_LnO|D%nC^}7gx85-{+e_BH5{|H(c*|C?k7+5D#mQo zv3Je8F1&zft(^VUguIk8qE28o!rwlqb{ zQd=4=d^mA4@4~3%T1A^?i6U^THXmmQ$o*RLJ}y3{Mu6_U07S;WnF-1j+<7TY2tyZTq+H@2MW$t;8^QbxA)5js0dD zuO)6kxJ;XkXOCA~zgoVxdUCfA!#}<%8^qAm9(P{DNbnvkb|-zL(z@!sA1>bQ?%#In zY|{L7XVYkb*<-akm89DV*MegxAud>b&#Yml4}4AxvsV?zD;4Y^i`}UvT}|kyAmR^Ms(00IxNbH{5Gbck{}mAe zK?-Wkb{)r1hx77_6kaEW=eLxUYXwBu}( zl{b+tBe;?vEo)5;VSD7Jhe?*qQPN>jILCiUgg|fy^=%GSSu=LaGG@rzc>bQ+Hpghv zO$2vww$9q~t({fNWHn5BDE~r)R>`0B=8Tx#kx)H{9sx@Ds~Aue&}8LN-e^Nvlg(?| zz|a&@!&NujpB1{z<6_9m6KFoGtf+ykCwK)kMPW#+A9CXKF#276j-%e}!1m&1>^R#3 zJ1_q$olQDLGtY$w=jBkM&QgmWCeee-pCUr=4nq7mJT)>Lx&4CwPzV4Jq+xMOpCJ5W zWCSAp<^Qs;Zn5bj)fZQJ^vy7{;7pVf+*y(=Q2xXODe>?S6Es}so_{unfF(av<5Z2j zuS?|^3WIuRhTonZ@|{QC3`Ui5EfIqJ2~e}Re^5xLg#Z8wmco65LazBHkpp&KKuNuQ zHH5BTO5J5KZG3^VhK z{LFU#b>e2;F+55O*N84B&Y(hXll;$R6;)gAS+c7($!%f?57fV5wmUV%)#T5>DCy2q zW%lrseS2*VCuO$TSGq+E^S&tUS=-9rL)^?eNDYs+f#ju%f%3& zWj=C{TnSzQO#y)#>E<`o91WdP{weeX0F}-5_c|L)cgm?3tUs4s+h!k3+{`=BDj3ib z?@RVmddG9Hs@Wd&{*N_JxJ(QhmRU+t%z6(NyVC__dXER3uebdpFR$aHm=1sNiFFr7 z)U-KLh>Lj-4yS+HbM17khp179x@l&~3va$|JSeCnJusGB`Z>o7-GBKz*>Q&-a z-pQegHk3=SCA;42!0yskue8CrhW6Q#h++1s;`my#J)O9fcOnSD%0ugYpwX=aM$nN+HkA^MO7BBwn1GR|J0v- z=9;t6^YSkQIXV6!?-Ur~Qr^$NC@tE^xT)fTKRYCu_|%_rI}2tc#N@d|)p zU3xP-wc*0WW~;+U+{pJZXj$|L)1XJ75&>!k6o5txkMbrP4p*8{-D3B-Hmf<>8D^mU z=4VG)I`wAw+q%-BL=TXsQM_H2%$^B3xl@fnz!1WI9g@F!SXg7 zt+Ehd$c#V@i-D0^q(xj>JL$k0d*9#w!|%TH_`370zLuhv!llFPHzxR_mq8`EyatN{ zg{Pab>s%|^9Zuqj9wgFVfg2M1kshf5O?B#wYZCw?MN#&vis6xh;y&Q%!@gv!2!91~ z`vs$Cuu}4}o2@(wjy7Q5`BsqkgnRK*GMJPah|Fj|F?Xa6qbTb8s}7UGw94s~&Azb= z&(IXc_YOc#yuPzfj1Hq$F_<>cfWgsz_;7zM8q5yjrEVazhaYZD3xb*xeL2C+0In_u z-ia~3V5|T&gXx(eE_oYVjd-I9k6#~#|Lv{CiHjEEtuBJ4DcsgC2-z_@sfx_GYRr(A zC(zCPO0C|Wm@v{`frp2fMBOdgRh$Cw<>7j4FK#BDD=``cjfb;MhzXMP0p|eVPf?T^ z_{BOyZNIZA-Y$gbKqVf%F4QAJ04`nHb!%D>ZcOke-Yao@@aV8msfBQPWR!KK|1SR# zs!@*jW`jiqC@^cNsY}oiW=*!2Ggq;JTN#! zihxQ|K}d1?b$5oQfP1ry2-UlEz%t}_VzAhpUYUxDV<_A|COMkaMoBLc5Lx@^6j2O&y z^bOY_Ctgpyyu)u(Ba|%d88IjsT<2{Do1Q7X!7S~qxY&3Bjjc5hB;H^#Atey8G9Avz ziQIm{2-8a&XW1R?sBJ1oQ(N61eIyn5c?FsW;=IXVx26RnLZYd*n#MA0+5Zs^oc;zE z8!ue>ca2Jq)R@5-k(P@-u|q`v4ntoO@XOmO&}Ji>`>sY-v>&pfrAd`#Zf`;1PoHD= ziLY?J`XrpfrQ5?fR*BS@!N^XTfUKl3pePyudR?CMz_S}KRMeXtVcuY{%pQK2I?y1x z9H+oz&CYkQX4kvmo!8z|)F*Zb9vt^F{I%kLx2Vd3CA+H0#=hSWrdQzaIbl#R;-jm5 zC%?wBjZdT1+H}p&MVor#p3%=CEIwARL|kDoN&PJzbxz)nl-hzdnOvBmABc1wQ zyWN7lCpRG_W-x+up`z*#Z&c%St(EL@{h7ZGFd)Gw?wpAL0sr0b6t*8;)oB6kKm84* z;1pYEtHu)L4F>y5zhH!ld~!H=JKp&6QCz4w)ur#->{h(;`F&mf{$*$in4W3$ie!PV zW=O0~lJvYQzkMCwA1&ze_j%sYmlh}?MD7O5j?p1Qf*i!3ZhINWN_UHXvn*OY8E z_J1TvovP~3VcEuKM3<8kqQ>Z?fNm)S6-Q%wma)S(g1fpeYz|8#t>&m6zHYGUhVxjq z@o7oxw&%n~Y(4O?=yJv+1t2j*)$PP^NB>}i_;Df~*orsT%oF{V1^|n#)kHI(B7E6k zc1JrF7tDpZUHTjYAAj>NlvEXojvuBCGIn+)NV5y zXwWeDzQK;Uq>c{Wj>QFYV77`o*``-9xF^HdE#il$00kyX5GU*`T8qMi`I6O35CknP zIHB}agY7!D9@`Fn?$Wx}G?roMS5JxFz7cO!;ihEYjvv$>c^*?x4rHVJ$jw7qF zqdp)KKrfP=Eq3b)XS}8gav3tIdqX1#@7tyY5D8 zilPOYq6C}g4Q4=)NQ1T8Em&MI*QEyQC@a5v9jD6n%fc?6WMra0QbNT~2rS?9EGp{G zy0oqwqim;HhHCSq!K8WuF5mPVD(Z@5;RL*s$D3>Bqs3e=i#Mhx=w(o0TyOEi-fagz z$F5`R-BM?ocTyf3CP_6mE_(jCh z>SOq5>q58GS-~l+G)uwk34=*BYf@T$+-(NyEJsSd$J!s3h%QH}SvI#kT`6E%hCz~Z zCyNW_!tQ8yQ@v?BZF35Z?lzcI^XZoMR=l}p9&8TzrW*Qk$D24-x<_>UaMu7sr!ozB ziqS~{NRr^q^x7Az0X_{yNrS3MER4c8^Ha>%@`g5{&4yVB5?X`bHQ(LX5 za?~7+8CfA})XUi4nRSfQyV6)-b$|Lx6+skgRKgTGl>Dm@su{#YW#qP|K zsv>;+&A+@Vs61A>2Vd@ZQ*=4Qdg?GBQeG+dq&mjhW^Kmeg1K<;?#~{jU{zJLxs54v zm%;AN5WjEVY;DHkf;k>Bm{`{Sumnd+z87829qGXc(a9&3#?8G05oZv8pnvIxCs5l| z?vXYqIAtllVtTAq#nLXnreRD{fFw5{EdA;!)HIgC8xaI2-dXoJ8d|DFm7`>7%*ZrK za>tNZdYja^F;RRjij8|$;K1o^9&3kEsriw<6r545uKXdX&iZcJy8`=9ZG|_}Vy?%V zYvzM@ii;m2{1upz=GpimP|ad$hWJaB&y*j;%I&XttQ{<)K+upr`)B~rDw%3m{g70* zK%6N*h`iv`nM5@3_4hEB&^}GLzL&e)gmm|e}Qe9zuN`UBWssaSOyM6)cnkzlp7kW)_ z3vl@sZkpcjiXR!*I}p7j2(08_Fv;TwiQ1)LFC|2AXZ(;Fs=@W~{<3)>h()IjthgeE zCYybe)IX3Cs>aAf-!Yggkm`*Ws^@UKn{rhIX;@4hC_YX&d0`(`ZGS@+{ot=L9K9lj z^JdEdDNj}fYFJDiXcTQQsm9iFMygmSvxgrB$GA3rxO}a?Mj`GzShKkfZ>^axdJ$48 z{FM6du83jrP4VsiYNl06gK0$?OsZKIZ$PS9GAZ*`T#ZSofui&0IRzf?tbGg(truls zdkPe^YTeo^VgP_ZH7m&;B`Gr0NHrhtP4R;i>n93^!i-GUq<~5b*I;;@_*7hLcD;+E z=XS})_QCpa$N21r%YP*Xor>NmwqU7pmr1o!>rF_tvL2%2aA&%-@q^Ki!(9?2X)HST z0}8%>TUK^A1x8n0{`-m;oN#_7N?{UZwUlZPk2fOKo~!goEk-8zOHwB_N8|oM;?wsw zw$|XCb$^#loNyU~f9T%diJ@{+=ynxLJ3VPIsdjgIGg9rw9iI}2K4Ibx1Ti#)`v!#| zOfNp8&ZTYab+WU!zc#qwcVfuPqnsgrY-LvsCN^-BYTv&vAjLTingV8I8N0k6jH0B; zr~_O&;B@@_UfJ1Msn7_Ky?U(rofrVXU(Np5nZh(2jRyyZNM8C zdyq=OQ26TrgCrSsq&oWQ3Ze`N4cSq-maB^4`q+S{hxgK>BTEjwf>n6phHyzvtjXD6 zorwAxf{%r6due+Qjbhz_Z}yz916e<@&3q& z^25%O7W{Co6%7{0FNRbU5{xPg>#0L(m`0MT@uj*h$CZTmM=t-ZoqmNN2(JT}d+BeW z(c(a>%?XaCVDw`py~Brh@>uxEG|?As`7$bn2G8HU3gN*$B`vqv%A?83gQY1Lw5;gt zNWbN?Z~uc0yWf+oT?2I{e$lM$97R#WRfkDGv++NKIDUB5u| z<;$+d7#RKKZ~yIgVxTCh9l%;*FnQt0+r>M|m23H}O|q`1TBQ@B49PG5sTh97Wx1a& zClZmn!Q{w_Ezi3wKb;hOxb-CzR~(YP9TOt@pUdfgOJhgHumynR-YP)AdmI0Ovz14P zXLs>I^4hAIvfRM<%}<|V+fSc)g&iXeaaT1QQ=9ibXW|R-^x-};)@-Nux|HK;>{9JTyY5h{`_8- z8u#G12`qc(CE{`f1_aM+r*0{Y zg@;y>{YGx4S9ltxkA4o(rrwg5%{zH)-uD4kf4|tR`L(4&tM(VhWzLEkpM7_^7%^l3 z_|whkEtUp+z3(G@z5gS!hTuUMnn6b57>pZqAEHgkqRVseHtaZ3fUowxFZ)~=*J5z* zaX-$Q_-dx8H^a|@CvLDHqIP{@?qQ%f+Zk$rvRKBXk(tu`j9WGoviF4J* zP+V~sR$CkKI+;UL45Cd*hzU(Wv?&<|eK@o#e`r)X*c~>sS)0*nX+U{hG0szk`M;#%H^Q{YijRfLkNqHfoAilPw{ zn)+CW9vU6_P~P+v*5n?8-Xm^B@HSFov&$wAo%N+;VgP_@i+XNIKvX+1FTtB86B2u5 z%I7LbFmcqXkr{WLb&D9LjecfZdVJ0Sq^?iwkmpGk5M(nvN*H-~ z%CJYee4J9$9rE|zwD6_)$o{3I8wj4p;2*&!#S9)R>dhjy5|M_K(RxiFIk-UZ7!)XE zC5>G;D*cWMkBZ^mvCo{$NFJLkgA{ zA-J2c;8Za1Yrj6V^{2KKoXEnR4n+*vdG_FD=&*T!(|?w z^nZ8v?K$)q=`MmR}LNivcU zBw<9qTi4uq{SymZCN@`Ae%h#~S7s#Km`76#=}Cep1}ESA(~L1MOmgX)$JLFPIpNh8 zhV_~B0a>9Ybd|n6hn4r9I6l+m!!UO==Kh-(-kqH~el6)%LT5>c8c?5+aC2%7mE-UU zF#y2B<6oJWnK*ho>0Uxd=@p*VJS<^S%GL6(d9@e-VD{uiBl`Cmae#C);m_z1l4QQF z&+UC(F@-7%F#tfzZDARyu|p1#?k4;Z(Wd0K5$U=8Zb+Zv*0VAmi)#Pxi&;DNo!UB- zbUoph#6|RL9-fw)l9M*2%-z3Dd#cfMr@S?6NXmruWR{6=nWU(Uy6aP?#Cbvp9(^-h z=Dn|;Uh&=GFYor2Y>@=lkk)hfg@FlU`?@PwmZcZ~;J@pidSU0Wb8}W*61_cU( zQ^)T~9(U7FcTAtkUJL;6-lk`#{&eb_Ye;++oSJw8PL_?+UQDWkHok|)>6FMg3R?{9i` z`k~?-OX`{{$iZqaN3Hb3(A0?^-975rsqzixWzHvCUow@|mVA4-WJeNlDeojYB*~na zJa)>&?1whVxt?CL;YS0$4hr^F0MQjPFzjiL>S`QGm}Qmo-%CCQg5lVSMG&)d+lSd96h&lUUOR=S(A_% z3RZ=z9(Aclnh^Z5@ zA6V~=_4oQ+a-V$jukpu9_pCTqeJqH$qh|;Sh;n2ljeRwD#QZ0vzRQS+p$ojb?(hFT zet!R4G7alK)GD1YApXWJy+Zrne&fL0CU0+#Pwk&xUbtLkGoO09sASjF`j#q|xT|YW zDYQtByM9lcDP`)YLDNtBO1pgWFhPFdTYimgW$)VXMp^&>0y;@VK~x?&xAQLzE!8B* zmIx)MM!#Mo4#Y?Fn>Hr%o+4jspHH8x$uE4%?_$%Lw@;THyuGfag5>J$B05cwkQ$r4 zGde8!;c*%F9re|Ac8gQ`-yIuiNU+33qqeptoBrd z*@uQw9IHfhNK$=hQ1nh?Q0$T^LuPIuJx;{XF<}(tDV#OhyBeEo?x<=wmtIZsBoZ09fdCKS?zQL%8S~Pmdw2P!?i5NulTK+t>-cNl8@8rj{SQ`7)HkLMa|^`Um7e`IZdU&x6-UC74Z;IRLVP9x4w#NZ|hOO~r@&exA>YHJwT zW@${b+S($_?JdErmPT!3YYk^}SS9^Ey(Y*R;1^=nX#(msN_~Y!sXL?71Z@w{nKq0W zIJJ`W2oZxU5d?wTaNxtZruwpEo5K?06ohb=Rrx#kc0DifD!bjn2?F#E-p&93%P86? zO0ZF2oD9P@a4grTV3g$?rz&MxMTtVCJTrdagSEu6uHpXy+Z=n9JiAZ^00000NkvXX Hu0mjfJUwN5 literal 0 HcmV?d00001 diff --git a/specs/data/box-texture-options/emission.jpg b/specs/data/box-texture-options/emission.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5c85a943f6c5c95ca46b1bfd5fc9e8d7f1c4b77 GIT binary patch literal 7092 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-texture-options/shininess.png b/specs/data/box-texture-options/shininess.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcf1a2c625c38a8da19b7c7390102dde2b3f90a GIT binary patch literal 4094 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-texture-options/specular.jpeg b/specs/data/box-texture-options/specular.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..661bf98060289f196f149b5c0b6b05ff737f9ff8 GIT binary patch literal 5618 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 IJS9*353{Fhng9R* literal 0 HcmV?d00001 diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index 9d13473..dd3d417 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -4,6 +4,7 @@ var loadMtl = require('../../lib/loadMtl'); var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; +var texturedWithOptionsMaterialUrl = 'specs/data/box-texture-options/box-texture-options.mtl'; function getImagePath(objPath, relativePath) { return path.normalize(path.join(path.dirname(objPath), relativePath)); @@ -40,4 +41,26 @@ describe('loadMtl', function() { expect(materials.Blue.diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); }), done).toResolve(); }); + + it('loads mtl with textures having options', function(done) { + options.metallicRoughness = true; + expect(loadMtl(texturedWithOptionsMaterialUrl) + .then(function(materials) { + expect(materials.length).toBe(1); + var material = materials[0]; + var pbr = material.pbrMetallicRoughness; + expect(pbr.baseColorTexture).toBeDefined(); + expect(pbr.metallicRoughnessTexture).toBeDefined(); + expect(pbr.baseColorFactor).toEqual([1.0, 1.0, 1.0, 0.9]); + expect(pbr.metallicFactor).toBe(1.0); + expect(pbr.roughnessFactor).toBe(1.0); + expect(material.name).toBe('Material'); + expect(material.emissiveTexture).toBeDefined(); + expect(material.normalTexture).toBeDefined(); + expect(material.occlusionTexture).toBeDefined(); + expect(material.emissiveFactor).toEqual([1.0, 1.0, 1.0]); + expect(material.alphaMode).toBe('BLEND'); + expect(material.doubleSided).toBe(true); + }), done).toResolve(); + }); }); From 05e9788feab072c423874c11f993a3173a37b6a4 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 22:02:13 -0400 Subject: [PATCH 08/16] Port over #116 --- CHANGES.md | 1 + lib/loadObj.js | 21 +++++++- .../box-mtllib-spaces/box mtllib blue.mtl | 12 +++++ .../box-mtllib-spaces/box mtllib green.mtl | 12 +++++ .../data/box-mtllib-spaces/box mtllib red.mtl | 12 +++++ specs/data/box-mtllib-spaces/box mtllib.obj | 50 +++++++++++++++++++ specs/lib/loadObjSpec.js | 12 +++++ 7 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 specs/data/box-mtllib-spaces/box mtllib blue.mtl create mode 100644 specs/data/box-mtllib-spaces/box mtllib green.mtl create mode 100644 specs/data/box-mtllib-spaces/box mtllib red.mtl create mode 100644 specs/data/box-mtllib-spaces/box mtllib.obj diff --git a/CHANGES.md b/CHANGES.md index 77c3ce7..74a16fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Change Log * Fixed handling of unnormalized input normals. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) ### 1.3.4 2018-10-16 diff --git a/lib/loadObj.js b/lib/loadObj.js index ff1333a..441504c 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -346,8 +346,8 @@ function loadObj(objPath, options) { var materialName = line.substring(7).trim(); useMaterial(materialName); } else if (/^mtllib/i.test(line)) { - var paths = line.substring(7).trim().split(' '); - mtlPaths = mtlPaths.concat(paths); + var mtllibLine = line.substring(7).trim(); + mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine)); } else if ((result = vertexPattern.exec(line)) !== null) { var position = scratchCartesian; position.x = parseFloat(result[1]); @@ -419,6 +419,23 @@ function loadObj(objPath, options) { }); } +function getMtlPaths(mtllibLine) { + // Handle paths with spaces. E.g. mtllib my material file.mtl + var mtlPaths = []; + var splits = mtllibLine.split(' '); + var length = splits.length; + var startIndex = 0; + for (var i = 0; i < length; ++i) { + if (path.extname(splits[i]) !== '.mtl') { + continue; + } + var mtlPath = splits.slice(startIndex, i + 1).join(' '); + mtlPaths.push(mtlPath); + startIndex = i + 1; + } + return mtlPaths; +} + function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { nodes = cleanNodes(nodes); if (nodes.length === 0) { diff --git a/specs/data/box-mtllib-spaces/box mtllib blue.mtl b/specs/data/box-mtllib-spaces/box mtllib blue.mtl new file mode 100644 index 0000000..d3fe863 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib blue.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Blue +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib green.mtl b/specs/data/box-mtllib-spaces/box mtllib green.mtl new file mode 100644 index 0000000..89abba9 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib green.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Green +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.640000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib red.mtl b/specs/data/box-mtllib-spaces/box mtllib red.mtl new file mode 100644 index 0000000..3721d86 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib red.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Red +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib.obj b/specs/data/box-mtllib-spaces/box mtllib.obj new file mode 100644 index 0000000..fe03d89 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib.obj @@ -0,0 +1,50 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend' +# www.blender.org +mtllib box mtllib red.mtl +mtllib box mtllib green.mtl box mtllib blue.mtl +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 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +usemtl Red +f 3/1/1 7/2/1 5/3/1 1/4/1 +usemtl Green +f 1/9/3 2/10/3 4/11/3 3/12/3 +usemtl Blue +f 3/1/5 4/6/5 8/17/5 7/18/5 +usemtl Red +f 8/5/2 4/6/2 2/7/2 6/8/2 +usemtl Green +f 7/13/4 8/14/4 6/15/4 5/16/4 +usemtl Blue +f 5/19/6 6/20/6 2/7/6 1/4/6 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index b0b5a4b..2e26545 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -27,6 +27,7 @@ var objNoMaterialsUrl = 'specs/data/box-no-materials/box-no-materials.obj'; var objMultipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.obj'; var objUncleanedUrl = 'specs/data/box-uncleaned/box-uncleaned.obj'; var objMtllibUrl = 'specs/data/box-mtllib/box-mtllib.obj'; +var objMtllibSpacesUrl = 'specs/data/box-mtllib-spaces/box mtllib.obj'; var objMissingMtllibUrl = 'specs/data/box-missing-mtllib/box-missing-mtllib.obj'; var objMissingUsemtlUrl = 'specs/data/box-missing-usemtl/box-missing-usemtl.obj'; var objExternalResourcesUrl = 'specs/data/box-external-resources/box-external-resources.obj'; @@ -313,6 +314,17 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with mtllib paths with spaces', function(done) { + expect(loadObj(objMtllibSpacesUrl, defaultOptions) + .then(function(data) { + var materials = data.materials; + expect(Object.keys(materials).length).toBe(3); + expect(materials['Blue'].diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials['Green'].diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); + expect(materials['Red'].diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); + }), done).toResolve(); + }); + it('loads obj with missing mtllib', function(done) { expect(loadObj(objMissingMtllibUrl, defaultOptions) .then(function(data) { From 96154b559cc2b9069a8a7c2fbf0073598c4cd5a6 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 22:46:27 -0400 Subject: [PATCH 09/16] Port over #117 --- CHANGES.md | 1 + lib/loadObj.js | 78 ++++++++++++------ .../box-external-resources-in-root.mtl | 13 +++ .../box-external-resources-in-root.obj | 46 +++++++++++ .../box-external-resources-in-root/box.mtl | 12 +++ .../box-external-resources-in-root/cesium.png | Bin 0 -> 7665 bytes .../box-resources-in-root.mtl | 13 +++ .../box-resources-in-root.obj | 46 +++++++++++ specs/data/box-resources-in-root/cesium.png | Bin 0 -> 7665 bytes specs/lib/loadObjSpec.js | 35 +++++++- 10 files changed, 215 insertions(+), 29 deletions(-) create mode 100644 specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl create mode 100644 specs/data/box-external-resources-in-root/box-external-resources-in-root.obj create mode 100644 specs/data/box-external-resources-in-root/box.mtl create mode 100644 specs/data/box-external-resources-in-root/cesium.png create mode 100644 specs/data/box-resources-in-root/box-resources-in-root.mtl create mode 100644 specs/data/box-resources-in-root/box-resources-in-root.obj create mode 100644 specs/data/box-resources-in-root/cesium.png diff --git a/CHANGES.md b/CHANGES.md index 74a16fa..5b81896 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Change Log * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) ### 1.3.4 2018-10-16 diff --git a/lib/loadObj.js b/lib/loadObj.js index 441504c..0003687 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -463,13 +463,11 @@ function normalizeMtlPath(mtlPath, objDirectory) { return path.normalize(path.join(objDirectory, mtlPath)); } -function outsideDirectory(filePath, objPath) { - return (path.relative(path.dirname(objPath), filePath).indexOf('..') === 0); +function outsideDirectory(file, directory) { + return (path.relative(directory, file).indexOf('..') === 0); } function loadMaterials(mtlPaths, objPath, options) { - var secure = options.secure; - var logger = options.logger; var objDirectory = path.dirname(objPath); var materials = {}; @@ -480,39 +478,69 @@ function loadMaterials(mtlPaths, objPath, options) { return Promise.map(mtlPaths, function(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; + var shallowPath = path.join(objDirectory, path.basename(mtlPath)); + if (options.secure && outsideDirectory(mtlPath, objPath)) { + // Try looking for the .mtl in the same directory as the obj + optionslogger('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.'); + return loadMtl(shallowPath) + .then(function(materialsInMtl) { + Object.assign(materials, materialsInMtl); + }) + .catch(function(error) { + console.logger(error.message); + console.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); + }); } return loadMtl(mtlPath) - .then(function(materialsInMtl) { - materials = Object.assign(materials, materialsInMtl); + .catch(function(error) { + // Try looking for the .mtl in the same directory as the obj + options.logger(error.message); + options.logger('Could not read material file at ' + mtlPath + '. Attempting to read the material file from within the obj directory instead.'); + return loadMtl(shallowPath); }) - .catch(function() { - logger('Could not read mtl file at ' + mtlPath + '. Using default material instead.'); + .then(function(materialsInMtl) { + Object.assign(materials, materialsInMtl); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); }); }, {concurrency : 10}) - .thenReturn(materials); + .then(function() { + return materials; + }); } function loadImages(imagePaths, objPath, options) { - var secure = options.secure; - var logger = options.logger; var images = {}; + var objDirectory = path.dirname(objPath); return Promise.map(imagePaths, function(imagePath) { - if (secure && outsideDirectory(imagePath, objPath)) { - logger('Could not read image file at ' + imagePath + ' because it is outside of the obj directory and the secure flag is true. Material will ignore this image.'); - return; + var shallowPath = path.join(objDirectory, path.basename(imagePath)); + if (options.secure && outsideDirectory(imagePath, objDirectory)) { + // Try looking for the image in the same directory as the obj + options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); + }); + } else { + return loadImage(imagePath, options) + .catch(function(error) { + // Try looking for the image in the same directory as the obj + options.logger(error.message); + options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); + }); } - return loadImage(imagePath, options) - .then(function(image) { - images[imagePath] = image; - }) - .catch(function() { - logger('Could not read image file at ' + imagePath + '. Material will ignore this image.'); - }); }, {concurrency : 10}) - .thenReturn(images); + .then(function(images) { + return images; + }); } function getImagePaths(materials) { diff --git a/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl b/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl new file mode 100644 index 0000000..2e2d3a5 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl MaterialTextured +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 ../box-textured/cesium.png diff --git a/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj b/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj new file mode 100644 index 0000000..6b17fc5 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend' +# www.blender.org +mtllib box-external-resources-in-root.mtl +mtllib ../box/box.mtl +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 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +usemtl MaterialTextured +f 3/1/1 7/2/1 5/3/1 1/4/1 +f 1/9/3 2/10/3 4/11/3 3/12/3 +f 3/1/5 4/6/5 8/17/5 7/18/5 +usemtl Material +f 8/5/2 4/6/2 2/7/2 6/8/2 +f 7/13/4 8/14/4 6/15/4 5/16/4 +f 5/19/6 6/20/6 2/7/6 1/4/6 diff --git a/specs/data/box-external-resources-in-root/box.mtl b/specs/data/box-external-resources-in-root/box.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box.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-external-resources-in-root/cesium.png b/specs/data/box-external-resources-in-root/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/data/box-resources-in-root/box-resources-in-root.mtl b/specs/data/box-resources-in-root/box-resources-in-root.mtl new file mode 100644 index 0000000..7bc1846 --- /dev/null +++ b/specs/data/box-resources-in-root/box-resources-in-root.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 resources/textures/cesium.png diff --git a/specs/data/box-resources-in-root/box-resources-in-root.obj b/specs/data/box-resources-in-root/box-resources-in-root.obj new file mode 100644 index 0000000..1e9d945 --- /dev/null +++ b/specs/data/box-resources-in-root/box-resources-in-root.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib resources/box-resources-in-root.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-resources-in-root/cesium.png b/specs/data/box-resources-in-root/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 2e26545..b117f1f 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -31,6 +31,8 @@ var objMtllibSpacesUrl = 'specs/data/box-mtllib-spaces/box mtllib.obj'; var objMissingMtllibUrl = 'specs/data/box-missing-mtllib/box-missing-mtllib.obj'; var objMissingUsemtlUrl = 'specs/data/box-missing-usemtl/box-missing-usemtl.obj'; var objExternalResourcesUrl = 'specs/data/box-external-resources/box-external-resources.obj'; +var objResourcesInRootUrl = 'specs/data/box-resources-in-root/box-resources-in-root.obj'; +var objExternalResourcesInRootUrl = 'specs/data/box-external-resources-in-root/box-external-resources-in-root.obj'; 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'; @@ -329,7 +331,10 @@ describe('loadObj', function() { expect(loadObj(objMissingMtllibUrl, defaultOptions) .then(function(data) { expect(data.materials).toEqual({}); - expect(console.log.calls.argsFor(0)[0].indexOf('Could not read mtl file') >= 0).toBe(true); + expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('Attempting to read the material file from within the obj directory instead.') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(3)[0].indexOf('Could not read material file') >= 0).toBe(true); }), done).toResolve(); }); @@ -360,8 +365,27 @@ describe('loadObj', function() { expect(data.images[imagePath]).toBeUndefined(); expect(data.materials.MaterialTextured).toBeDefined(); expect(data.materials.Material).toBeUndefined(); // Not in directory, so not included - expect(console.log.calls.argsFor(0)[0].indexOf('Could not read mtl file') >= 0).toBe(true); - expect(console.log.calls.argsFor(1)[0].indexOf('Could not read image file') >= 0).toBe(true); + expect(spy.calls.argsFor(0)[0].indexOf('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.') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('Could not read material file') >= 0).toBe(true); + }), done).toResolve(); + }); + + it('loads resources from root directory when the .mtl path does not exist', function(done) { + expect(loadObj(objResourcesInRootUrl, options) + .then(function(data) { + expect(data.materials['Material'].diffuseTexture.source).toBeDefined(); + expect(diffuseTexture.source).toBeDefined(); + }), done).toResolve(); + }); + + it('loads resources from root directory when the .mtl path is outside of the obj directory and secure is true', function(done) { + options.secure = true; + expect(loadObj(objExternalResourcesInRootUrl, options) + .then(function(data) { + var materials = data.materials; + expect(Object.keys(materials).length).toBe(2); + expect(materials['MaterialTextured'].diffuseTexture.source).toBeDefined(); }), done).toResolve(); }); @@ -380,7 +404,10 @@ describe('loadObj', function() { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); expect(data.images[imagePath]).toBeUndefined(); expect(data.materials.Material.diffuseTexture).toEqual(imagePath); - expect(console.log.calls.argsFor(0)[0].indexOf('Could not read image file') >= 0).toBe(true); + expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('Attempting to read the image file from within the obj directory instead.') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(3)[0].indexOf('Could not read image file') >= 0).toBe(true); }), done).toResolve(); }); From 099d6ee65e5ac93d168fad6f7f89445f341d9143 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 22:52:12 -0400 Subject: [PATCH 10/16] Tests from #123 --- CHANGES.md | 1 + .../box-objects-groups-materials-2.mtl | 32 +++++ .../box-objects-groups-materials-2.obj | 133 ++++++++++++++++++ specs/lib/loadObjSpec.js | 37 +++++ 4 files changed, 203 insertions(+) create mode 100644 specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl create mode 100644 specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj diff --git a/CHANGES.md b/CHANGES.md index 5b81896..1706e17 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ Change Log * Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) ### 1.3.4 2018-10-16 diff --git a/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl new file mode 100644 index 0000000..2f9b11a --- /dev/null +++ b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl @@ -0,0 +1,32 @@ +# Blender MTL File: 'box-objects.blend' +# Material Count: 3 + +newmtl Blue +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Green +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.640000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Red +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj new file mode 100644 index 0000000..aed6a84 --- /dev/null +++ b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj @@ -0,0 +1,133 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend' +# www.blender.org +mtllib box-objects-groups-materials-2.mtl +usemtl Blue +o Cube +v -1.000000 -1.000000 -4.000000 +v -1.000000 1.000000 -4.000000 +v -1.000000 -1.000000 -6.000000 +v -1.000000 1.000000 -6.000000 +v 1.000000 -1.000000 -4.000000 +v 1.000000 1.000000 -4.000000 +v 1.000000 -1.000000 -6.000000 +v 1.000000 1.000000 -6.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 +g Blue +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 +usemtl Green +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 +v 4.000000 -1.000000 1.000000 +v 4.000000 1.000000 1.000000 +v 4.000000 -1.000000 -1.000000 +v 4.000000 1.000000 -1.000000 +v 6.000000 -1.000000 1.000000 +v 6.000000 1.000000 1.000000 +v 6.000000 -1.000000 -1.000000 +v 6.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 Green +g Green +f 9/21/7 10/22/7 12/23/7 11/24/7 +f 11/25/8 12/26/8 16/27/8 15/28/8 +f 15/29/9 16/30/9 14/31/9 13/32/9 +usemtl Red +f 13/33/10 14/34/10 10/35/10 9/36/10 +f 11/25/11 15/37/11 13/38/11 9/36/11 +f 16/39/12 12/26/12 10/35/12 14/40/12 +usemtl Red +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 +g Red +f 21/53/16 22/54/16 18/55/16 17/56/16 +f 19/45/17 23/57/17 21/58/17 17/56/17 +f 24/59/18 20/46/18 18/55/18 22/60/18 +usemtl Blue +f 17/41/13 18/42/13 20/43/13 19/44/13 +f 19/45/14 20/46/14 24/47/14 23/48/14 +f 23/49/15 24/50/15 22/51/15 21/52/15 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index b117f1f..ba38277 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -20,6 +20,8 @@ var objTrianglesUrl = 'specs/data/box-triangles/box-triangles.obj'; var objObjectsUrl = 'specs/data/box-objects/box-objects.obj'; var objGroupsUrl = 'specs/data/box-groups/box-groups.obj'; var objObjectsGroupsUrl = 'specs/data/box-objects-groups/box-objects-groups.obj'; +var objObjectsGroupsMaterialsUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; +var objObjectsGroupsMaterials2Url = 'specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj'; var objConcaveUrl = 'specs/data/concave/concave.obj'; var objUnnormalizedUrl = 'specs/data/box-unnormalized/box-unnormalized.obj'; var objUsemtlUrl = 'specs/data/box-usemtl/box-usemtl.obj'; @@ -221,6 +223,41 @@ describe('loadObj', function() { }), done).toResolve(); }); + function loadsObjWithObjectsGroupsAndMaterials(data) { + var nodes = data.nodes; + expect(nodes.length).toBe(1); + expect(nodes[0].name).toBe('Cube'); + var meshes = getMeshes(data); + expect(meshes.length).toBe(3); + expect(meshes[0].name).toBe('Blue'); + expect(meshes[1].name).toBe('Green'); + expect(meshes[2].name).toBe('Red'); + var primitives = getPrimitives(data); + expect(primitives.length).toBe(6); + expect(primitives[0].material).toBe('Blue'); + expect(primitives[1].material).toBe('Green'); + expect(primitives[2].material).toBe('Green'); + expect(primitives[3].material).toBe('Red'); + expect(primitives[4].material).toBe('Red'); + expect(primitives[5].material).toBe('Blue'); + } + + it('loads obj with objects, groups, and materials', function(done) { + expect(loadObj(objObjectsGroupsMaterialsUrl, defaultOptions) + .then(function(data) { + loadsObjWithObjectsGroupsAndMaterials(data); + }), done).toResolve(); + }); + + it('loads obj with objects, groups, and materials (2)', function(done) { + // The usemtl lines are placed in an unordered fashion but + // should produce the same result as the previous test + expect(loadObj(objObjectsGroupsMaterials2Url, defaultOptions) + .then(function(data) { + loadsObjWithObjectsGroupsAndMaterials(data); + }), done).toResolve(); + }); + it('loads obj with concave face containing 5 vertices', function(done) { expect(loadObj(objConcaveUrl, defaultOptions) .then(function(data) { From 9fc526b692618350613a663d370af9d1cfd8aff6 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 23:00:22 -0400 Subject: [PATCH 11/16] Sceure/logging/outside fixes --- lib/loadObj.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 0003687..937d83f 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -481,14 +481,14 @@ function loadMaterials(mtlPaths, objPath, options) { var shallowPath = path.join(objDirectory, path.basename(mtlPath)); if (options.secure && outsideDirectory(mtlPath, objPath)) { // Try looking for the .mtl in the same directory as the obj - optionslogger('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.'); + 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.'); return loadMtl(shallowPath) .then(function(materialsInMtl) { Object.assign(materials, materialsInMtl); }) .catch(function(error) { - console.logger(error.message); - console.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); + options.logger(error.message); + options.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); }); } return loadMtl(mtlPath) From ade20d8c1d2b2e87fb242bd26ed9681c9fca9e15 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 31 Oct 2018 23:06:13 -0400 Subject: [PATCH 12/16] Port over #127 --- CHANGES.md | 1 + lib/loadMtl.js | 13 +++++ .../box-diffuse-ambient-same.mtl | 14 ++++++ .../box-diffuse-ambient-same.obj | 46 ++++++++++++++++++ .../data/box-diffuse-ambient-same/cesium.png | Bin 0 -> 7665 bytes specs/lib/loadMtlSpec.js | 11 +++++ 6 files changed, 85 insertions(+) create mode 100644 specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl create mode 100644 specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj create mode 100644 specs/data/box-diffuse-ambient-same/cesium.png diff --git a/CHANGES.md b/CHANGES.md index 1706e17..6220fb0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Change Log * Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of materials where the diffuse and ambient texture are the same. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) ### 1.3.4 2018-10-16 diff --git a/lib/loadMtl.js b/lib/loadMtl.js index 8a7aa96..454ae70 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -86,10 +86,23 @@ function loadMtl(mtlPath) { return readLines(mtlPath, parseLine) .then(function() { + cleanupMaterials(materials); return materials; }); } +function cleanupMaterials(materials) { + for (var name in materials) { + if (materials.hasOwnProperty(name)) { + var material = materials[name]; + if (material.diffuseTexture === material.ambientTexture) { + // OBJ models are often exported with the same texture in the diffuse and ambient slots but this is usually not desirable + material.ambientTexture = undefined; + } + } + } +} + function normalizeTexturePath(texturePath, mtlDirectory) { // Removes texture options from texture name // Assumes no spaces in texture name diff --git a/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl new file mode 100644 index 0000000..cb21081 --- /dev/null +++ b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl @@ -0,0 +1,14 @@ +# 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 cesium.png +map_Ka cesium.png diff --git a/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj new file mode 100644 index 0000000..5d52f28 --- /dev/null +++ b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib box-diffuse-ambient-same.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-diffuse-ambient-same/cesium.png b/specs/data/box-diffuse-ambient-same/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 dd3d417..21c9b0c 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -3,6 +3,7 @@ var path = require('path'); var loadMtl = require('../../lib/loadMtl'); var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; +var diffuseAmbientSameMaterialUrl = 'specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; var texturedWithOptionsMaterialUrl = 'specs/data/box-texture-options/box-texture-options.mtl'; @@ -63,4 +64,14 @@ describe('loadMtl', function() { expect(material.doubleSided).toBe(true); }), done).toResolve(); }); + + it('ambient texture is ignored if it is the same as the diffuse texture', function(done) { + expect(loadMtl(diffuseAmbientSameMaterialUrl, options) + .then(function(materials) { + expect(Object.keys(materials).length).toBe(1); + var material = materials['Material']; + expect(material.diffuseTexture).toBeDefined(); + expect(material.ambientTexture).toBeUndefined(); + }), done).toResolve(); + }); }); From ede9aaf0c6a9e4e91f086859349b08d497bba42b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 1 Nov 2018 00:04:28 -0400 Subject: [PATCH 13/16] Code and test fixes --- lib/loadObj.js | 56 ++++++++------ .../data/box-complex-material-alpha/alpha.png | Bin 0 -> 2255 bytes .../box-complex-material-alpha/ambient.gif | Bin 0 -> 4040 bytes .../box-complex-material-alpha.mtl | 20 +++++ .../box-complex-material-alpha.obj | 46 ++++++++++++ .../data/box-complex-material-alpha/bump.png | Bin 0 -> 4998 bytes .../box-complex-material-alpha/diffuse.png | Bin 0 -> 8889 bytes .../box-complex-material-alpha/emission.jpg | Bin 0 -> 7092 bytes .../box-complex-material-alpha/shininess.png | Bin 0 -> 4094 bytes .../box-complex-material-alpha/specular.jpeg | Bin 0 -> 5618 bytes .../box-complex-material.mtl | 1 - .../box-objects-groups-materials.obj | 6 +- specs/data/box/box.mtl | 4 +- specs/lib/createGltfSpec.js | 71 +----------------- specs/lib/loadMtlSpec.js | 50 ++++++------ specs/lib/loadObjSpec.js | 35 ++++++--- 16 files changed, 154 insertions(+), 135 deletions(-) create mode 100644 specs/data/box-complex-material-alpha/alpha.png create mode 100644 specs/data/box-complex-material-alpha/ambient.gif create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.obj create mode 100644 specs/data/box-complex-material-alpha/bump.png create mode 100644 specs/data/box-complex-material-alpha/diffuse.png create mode 100644 specs/data/box-complex-material-alpha/emission.jpg create mode 100644 specs/data/box-complex-material-alpha/shininess.png create mode 100644 specs/data/box-complex-material-alpha/specular.jpeg diff --git a/lib/loadObj.js b/lib/loadObj.js index 937d83f..c84b61b 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -479,7 +479,7 @@ function loadMaterials(mtlPaths, objPath, options) { return Promise.map(mtlPaths, function(mtlPath) { mtlPath = normalizeMtlPath(mtlPath, objDirectory); var shallowPath = path.join(objDirectory, path.basename(mtlPath)); - if (options.secure && outsideDirectory(mtlPath, objPath)) { + 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.'); return loadMtl(shallowPath) @@ -511,34 +511,40 @@ function loadMaterials(mtlPaths, objPath, options) { }); } +function loadImagePath(imagePath, objPath, options) { + var objDirectory = path.dirname(objPath); + var shallowPath = path.join(objDirectory, path.basename(imagePath)); + if (options.secure && outsideDirectory(imagePath, objDirectory)) { + // Try looking for the image in the same directory as the obj + options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); + }); + } + return loadImage(imagePath, options) + .catch(function(error) { + // Try looking for the image in the same directory as the obj + options.logger(error.message); + options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); + }); +} + function loadImages(imagePaths, objPath, options) { var images = {}; - var objDirectory = path.dirname(objPath); return Promise.map(imagePaths, function(imagePath) { - var shallowPath = path.join(objDirectory, path.basename(imagePath)); - if (options.secure && outsideDirectory(imagePath, objDirectory)) { - // Try looking for the image in the same directory as the obj - options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); - return loadImage(shallowPath, options) - .catch(function(error) { - options.logger(error.message); - options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); - }); - } else { - return loadImage(imagePath, options) - .catch(function(error) { - // Try looking for the image in the same directory as the obj - options.logger(error.message); - options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); - return loadImage(shallowPath, options); - }) - .catch(function(error) { - options.logger(error.message); - options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); - }); - } + return loadImagePath(imagePath, objPath, options) + .then(function(image) { + images[imagePath] = image; + }); }, {concurrency : 10}) - .then(function(images) { + .then(function() { return images; }); } diff --git a/specs/data/box-complex-material-alpha/alpha.png b/specs/data/box-complex-material-alpha/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..d04eb82f52c40bc1af9a3a8f516c0de93da592d7 GIT binary patch literal 2255 zcmV;=2r&1FP)FfK>5mnqYvqeW=j14KyJn zTA*voO{Lw0+M=4J2`y=E&Bb<#ofyaW*sbk%ACRVTF7~l~F7Jc<|JKM({mXMM?|XdS z=KvH^NFjw3qCyz?esyuF*=#l$OY}O1B;v7XI1~y+;;0~1jV;ZFk}^wWWo3Dp>HD9B zgMOdS7YN3dXQo{~`XR>EeXXWyTkAE#FY#P*dZQC(Ct~zMx&}6s+B!{QUK$#8hV3UW zQV7v+y0gaGX3pjR$iIho_-xn*-D1Ly* zk-Li&W^4-+d9t)wui!4;{wa|!M^-6S73&{}ym`Dvk$rr?L*&lfrjl$wR@Mja)la-> zlKqcmxl1#*B$5GFGJa;aBPv+7WAaBqo>T`sLRjAqpmWs9Pq-t)oQQ zoZBV`L;Hw>q!e|e$fZ5fJ4#+zQrJOAig=&bN$e>9eQR{IM(~5g4Ca{P;(mUS+^(+q+!$ys>Zu^K?;a<(gZ5U+u_Y0QhljR{Yy2T zc7MJQ^Gp>D7o8j((ey9SWSR^PQ{e#HX3nkyP!v+#@$aQvAZ0K7j3x&#&}cY$H3!I^y+uG- z*36FNu!GDK`ZCv-bwzA1brXiRijZ>a@kn+jME$P^fI0^-u`H7r$xgw%rW0X4im5{HE191r(>wBeo)jhiG=dpiX2(m~|smL!fO+ zTt$liOt%AG){3jhmVZ$Wfxfe1Ao`JZ+94y?irKVoq22*K+eB1EGuc8t1TJ=FuE?zj z9MG{nb49*hhX6wCeVHn<8G{3u^&2w{8vowJppewRlYY}`9@hhk1iD;lD{`Pqg9D?hm47uHe z2x9rUAf(9xm{L_Q1le7O3evPo2yz>;itG@8Sd4%xW-~wJMk_K%Q!hVcrwtjTZGazw z%>mHJ3#qlBgH%@YLDtu!gRET32kBpl4$}S;K1hQG9YnK&2ck0qzLaXY5Nj1eNNo)l z(y{^}#Af3{I+_te+E#HP4Jh}KIxfTj7-QfDi&ehLl-QLdr|o5DQL7 zB^OeT5>jFQkyL~WF`IGjBPKSav=rx2%gBb5AU%&5*pOnt89f_PgcPDauq2g!G^?P}AtpA6cnMDT~`wt&v*o6)<`wbst5#MOb=i`IG z96{zF4&a3x!*)uX_?#bd+KugWXy=E7BG^V+5q=1Kh-X6NbR6crKiJ5B%VttTk2|H=Z?CKU~4esz~Y> zmPyH#k0D(}pkW%xx1xbHo^%xf*9ja$;N%HU`ij8DGbkWe`_4hyiolmcC}tvdmV@W( z^=B|Zm}faZ;l5^UHT{tDoG#PcijYAB72$qHp2h3BA?nxWe|0@%t_UO^puQmYgBS>$ zJ4(6Z5Kl`W-o6MOqjW=>c`o+*g2Cq z^rFCZUBMQhA;;DV>dL`PJU$IsLckHdiMpGb{rf(HtRXPg+Cv-UFR$fvZKU-qRV{K; zcRqon1!y*E!t@eCDBMy5J49C$M>8;Q4jDR4M?GMzv&uYU=4s4sDKM>NS_P_ey%#zWHEW zmGj{8J0d%IihV-mmBYGDqta7G!gyYOLh>&)HTO*^{yfCd-^ua^8W>V|yE=WNOr`F6 zH>F@X5^u@(jZ@_#i;78H_sjWcHeLTW3OVe(GOr%4e@=-RO6+O*$K1ue@;|(1YJ(yZ zQ_EhN9SGEB0x(}X}s3D7_lh-x< z-Imt%qQ)2g;pxEeX$ddRNIeTTv|QU%TG!H^rr-JHv1w0uW@JX%PAsxqPAIoF)*FoF z71h<%mF3?Th=lw;uh+ZeUtV@O-7>Ugkny^RzQklQ84blnT81RA#$wTEB(i)33Mr(J dLJ9$Z{{r)zV|143F1i2!002ovPDHLkV1iPOEQ|mE literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/ambient.gif b/specs/data/box-complex-material-alpha/ambient.gif new file mode 100644 index 0000000000000000000000000000000000000000..1276823d350c3a7939ce8021a937381ee08564ae GIT binary patch literal 4040 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl new file mode 100644 index 0000000..5c598ff --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'None' +# 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 ambient.gif +map_Ke emission.jpg +map_Kd diffuse.png +map_Ks specular.jpeg +map_Ns shininess.png +map_Bump bump.png +map_d alpha.png +illum 2 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj new file mode 100644 index 0000000..15d7f4a --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-complex-material-alpha.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-complex-material-alpha/bump.png b/specs/data/box-complex-material-alpha/bump.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec3a936601922682f2efadd6f19a39d80c7150 GIT binary patch literal 4998 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/diffuse.png b/specs/data/box-complex-material-alpha/diffuse.png new file mode 100644 index 0000000000000000000000000000000000000000..8704966c032736dd3b504891393c5a69e90f0a7b GIT binary patch literal 8889 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 Hu0mjfJUwN5 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/emission.jpg b/specs/data/box-complex-material-alpha/emission.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5c85a943f6c5c95ca46b1bfd5fc9e8d7f1c4b77 GIT binary patch literal 7092 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/shininess.png b/specs/data/box-complex-material-alpha/shininess.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcf1a2c625c38a8da19b7c7390102dde2b3f90a GIT binary patch literal 4094 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/specular.jpeg b/specs/data/box-complex-material-alpha/specular.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..661bf98060289f196f149b5c0b6b05ff737f9ff8 GIT binary patch literal 5618 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 IJS9*353{Fhng9R* literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material/box-complex-material.mtl b/specs/data/box-complex-material/box-complex-material.mtl index 5c598ff..1ab3601 100644 --- a/specs/data/box-complex-material/box-complex-material.mtl +++ b/specs/data/box-complex-material/box-complex-material.mtl @@ -16,5 +16,4 @@ map_Kd diffuse.png map_Ks specular.jpeg map_Ns shininess.png map_Bump bump.png -map_d alpha.png illum 2 diff --git a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj index 1bb7698..b2fb821 100644 --- a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj +++ b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj @@ -36,7 +36,7 @@ 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 -g CubeBlue_CubeBlue_Blue +g Blue usemtl Blue 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 @@ -79,7 +79,7 @@ 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 -g CubeGreen_CubeGreen_Green +g Green usemtl Green f 9/21/7 10/22/7 12/23/7 11/24/7 f 11/25/8 12/26/8 16/27/8 15/28/8 @@ -122,7 +122,7 @@ 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 -g CubeRed_CubeRed_Red +g Red usemtl Red f 17/41/13 18/42/13 20/43/13 19/44/13 f 19/45/14 20/46/14 24/47/14 23/48/14 diff --git a/specs/data/box/box.mtl b/specs/data/box/box.mtl index a304b43..4f8d129 100644 --- a/specs/data/box/box.mtl +++ b/specs/data/box/box.mtl @@ -3,10 +3,10 @@ newmtl Material Ns 96.078431 -Ka 0.000000 0.000000 0.000000 +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.000000 +Ke 0.000000 0.000000 0.100000 Ni 1.000000 d 1.000000 illum 2 diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index fd51b20..9097848 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -17,7 +17,6 @@ var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; var defaultOptions = obj2gltf.defaults; -var defined = Cesium.defined; var checkTransparencyOptions = clone(defaultOptions); checkTransparencyOptions.checkTransparency = true; @@ -25,7 +24,6 @@ describe('createGltf', function() { var boxObjData; var duplicateBoxObjData; var groupObjData; - var mixedAttributesObjData; var diffuseTexture; var transparentDiffuseTexture; @@ -50,12 +48,7 @@ describe('createGltf', function() { loadImage(transparentDiffuseTextureUrl, checkTransparencyOptions) .then(function(image) { transparentDiffuseTexture = image; - }), - loadObj(mixedAttributesObjUrl, defaultOptions) - .then(function(data) { - mixedAttributesObjData = data; }) - ]).then(done); }); @@ -266,8 +259,8 @@ describe('createGltf', function() { boxObjData.nodes[1].meshes[0].primitives[0].normals.length = 0; var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_constant.extensions.KHR_materials_common; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('PHONG'); expect(kmc2.technique).toBe('CONSTANT'); @@ -279,8 +272,8 @@ describe('createGltf', function() { boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0; var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_shaded.extensions.KHR_materials_common; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('CONSTANT'); expect(kmc2.technique).toBe('PHONG'); @@ -317,62 +310,6 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); - function getDiffuse(material) { - return material.extensions.KHR_materials_common.values.diffuse; - } - - fit('splits incompatible materials', function() { - var gltf = createGltf(mixedAttributesObjData, defaultOptions); - var materials = gltf.materials; - var materialNames = Object.keys(materials).sort(); - - // Expect three copies of each material for - // * positions/normals/uvs - // * positions/normals - // * positions/uvs - expect(materialNames).toEqual([ - 'Material', - 'Material-2', - 'Material-3', - 'Missing', - 'Missing-2', - 'Missing-3', - 'default', - 'default-2', - 'default-3' - ]); - console.log(materials['Material']); - console.log(materials['Material-2']); - console.log(materials['Material-3']); - - - expect(getDiffuse(materials['Material'])).toBe('texture_cesium'); - expect(getDiffuse(materials['Material-2'])).toEqual('texture_cesium'); - //expect(getDiffuse(materials['Material-3'])).toBe('texture_cesium'); - // expect(getDiffuse(materials['Missing'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['Missing-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['Missing-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); - - // // Test that primitives without uvs reference materials without textures - // for (var meshName in meshes) { - // if (meshes.hasOwnProperty(meshName)) { - // var mesh = meshes[meshName]; - // var primitives = mesh.primitives; - // var primitivesLength = primitives.length; - // for (var i = 0; i < primitivesLength; ++i) { - // var primitive = primitives[i]; - // var material = materials[primitive.material]; - // if (!defined(primitive.attributes.TEXCOORD_0)) { - // expect(material.extensions.KHR_materials_common.diffuse).toEqual([ 0.5, 0.5, 0.5, 1 ]); - // } - // } - // } - // } - }); - function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices; diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index 208fe4d..ae579d1 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -2,7 +2,7 @@ var path = require('path'); var loadMtl = require('../../lib/loadMtl'); -var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; +var complexMaterialAlphaUrl = 'specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl'; var diffuseAmbientSameMaterialUrl = 'specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; var texturedWithOptionsMaterialUrl = 'specs/data/box-texture-options/box-texture-options.mtl'; @@ -14,7 +14,7 @@ function getImagePath(objPath, relativePath) { describe('loadMtl', function() { it('loads complex material', function(done) { - expect(loadMtl(complexMaterialUrl) + expect(loadMtl(complexMaterialAlphaUrl) .then(function(materials) { var material = materials.Material; expect(material).toBeDefined(); @@ -24,13 +24,13 @@ describe('loadMtl', function() { expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); expect(material.specularShininess).toEqual(96.078431); expect(material.alpha).toEqual(0.9); - expect(material.ambientTexture).toEqual(getImagePath(complexMaterialUrl, 'ambient.gif')); - expect(material.emissionTexture).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg')); - expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialUrl, 'diffuse.png')); - expect(material.specularTexture).toEqual(getImagePath(complexMaterialUrl, 'specular.jpeg')); - expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialUrl, 'shininess.png')); - expect(material.normalMap).toEqual(getImagePath(complexMaterialUrl, 'bump.png')); - expect(material.alphaMap).toEqual(getImagePath(complexMaterialUrl, 'alpha.png')); + expect(material.ambientTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'bump.png')); + expect(material.alphaMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'alpha.png')); }), done).toResolve(); }); @@ -45,29 +45,27 @@ describe('loadMtl', function() { }); it('loads mtl with textures having options', function(done) { - options.metallicRoughness = true; expect(loadMtl(texturedWithOptionsMaterialUrl) .then(function(materials) { - expect(materials.length).toBe(1); - var material = materials[0]; - var pbr = material.pbrMetallicRoughness; - expect(pbr.baseColorTexture).toBeDefined(); - expect(pbr.metallicRoughnessTexture).toBeDefined(); - expect(pbr.baseColorFactor).toEqual([1.0, 1.0, 1.0, 0.9]); - expect(pbr.metallicFactor).toBe(1.0); - expect(pbr.roughnessFactor).toBe(1.0); - expect(material.name).toBe('Material'); - expect(material.emissiveTexture).toBeDefined(); - expect(material.normalTexture).toBeDefined(); - expect(material.occlusionTexture).toBeDefined(); - expect(material.emissiveFactor).toEqual([1.0, 1.0, 1.0]); - expect(material.alphaMode).toBe('BLEND'); - expect(material.doubleSided).toBe(true); + var material = materials.Material; + expect(material).toBeDefined(); + expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]); + expect(material.emissionColor).toEqual([0.1, 0.1, 0.1, 1.0]); + expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]); + expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); + expect(material.specularShininess).toEqual(96.078431); + expect(material.alpha).toEqual(0.9); + expect(material.ambientTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'bump.png')); }), done).toResolve(); }); it('ambient texture is ignored if it is the same as the diffuse texture', function(done) { - expect(loadMtl(diffuseAmbientSameMaterialUrl, options) + expect(loadMtl(diffuseAmbientSameMaterialUrl) .then(function(materials) { expect(Object.keys(materials).length).toBe(1); var material = materials['Material']; diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index ba38277..7d3bc0d 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -122,8 +122,8 @@ describe('loadObj', function() { expect(loadObj(objUnnormalizedUrl, defaultOptions) .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); @@ -365,7 +365,11 @@ describe('loadObj', function() { }); it('loads obj with missing mtllib', function(done) { - expect(loadObj(objMissingMtllibUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingMtllibUrl, options) .then(function(data) { expect(data.materials).toEqual({}); expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); @@ -378,7 +382,7 @@ describe('loadObj', function() { it('loads obj with missing usemtl', function(done) { expect(loadObj(objMissingUsemtlUrl, defaultOptions) .then(function(data) { - expect(data.materials.length).toBe(1); + expect(Object.keys(data.materials).length).toBe(1); expect(data.nodes[0].meshes[0].primitives[0].material).toBe('Material'); }), done).toResolve(); }); @@ -394,6 +398,8 @@ describe('loadObj', function() { it('does not load resources outside of the obj directory when secure is true', function(done) { var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; options.secure = true; expect(loadObj(objExternalResourcesUrl, options) @@ -409,20 +415,23 @@ describe('loadObj', function() { }); it('loads resources from root directory when the .mtl path does not exist', function(done) { - expect(loadObj(objResourcesInRootUrl, options) + expect(loadObj(objResourcesInRootUrl, defaultOptions) .then(function(data) { - expect(data.materials['Material'].diffuseTexture.source).toBeDefined(); - expect(diffuseTexture.source).toBeDefined(); + var material = data.materials['Material']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); }), done).toResolve(); }); it('loads resources from root directory when the .mtl path is outside of the obj directory and secure is true', function(done) { + var options = clone(defaultOptions); options.secure = true; expect(loadObj(objExternalResourcesInRootUrl, options) .then(function(data) { - var materials = data.materials; - expect(Object.keys(materials).length).toBe(2); - expect(materials['MaterialTextured'].diffuseTexture.source).toBeDefined(); + expect(Object.keys(data.materials).length).toBe(2); + var material = data.materials['MaterialTextured']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); }), done).toResolve(); }); @@ -436,7 +445,11 @@ describe('loadObj', function() { }); it('loads obj with missing texture', function(done) { - expect(loadObj(objMissingTextureUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingTextureUrl, options) .then(function(data) { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); expect(data.images[imagePath]).toBeUndefined(); From 376d228ae06ee0965e1e62be044a44762a69a831 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 1 Nov 2018 00:09:48 -0400 Subject: [PATCH 14/16] Reorder CHANGES.md --- CHANGES.md | 12 +++++++----- lib/loadObj.js | 2 +- specs/lib/createGltfSpec.js | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4fda981..191806c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,16 +2,17 @@ Change Log ========== ### 1.3.5 ????-??-?? + * Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Improved parsing of faces with mismatching attributes. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) -* Fixed handling of unnormalized input normals. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) -* Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) -* Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) -* Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) -* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of unnormalized input normals. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Fixed handling of materials where the diffuse and ambient texture are the same. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Improved handling of materials with alpha. If the alpha value is 0.0 it is now treated as 1.0. [#164](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/164) ### 1.3.4 2018-10-16 @@ -23,6 +24,7 @@ Change Log * Fixed handling of objs with mismatching attribute layouts. [#154](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/154) * Fixed parsing mtl textures that contain texture map options. [#151](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/151) +* Fixed normalization of Windows paths when running the converter on Linux. [#151](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/151) ### 1.3.2 2018-06-07 * Fixed greyscale images loading as alpha instead of luminance. [#144](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/144) diff --git a/lib/loadObj.js b/lib/loadObj.js index c84b61b..863066e 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -444,7 +444,7 @@ function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { return loadMaterials(mtlPaths, objPath, options) .then(function(materials) { if (Object.keys(materials).length > 0 && !usesMaterials) { - assignDefaultMaterial(nodes, materials, usesMaterials); + assignDefaultMaterial(nodes, materials); } var imagePaths = getImagePaths(materials); return loadImages(imagePaths, objPath, options) diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 9097848..4aa141f 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -12,7 +12,6 @@ var WebGLConstants = Cesium.WebGLConstants; var boxObjUrl = 'specs/data/box/box.obj'; var groupObjUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; -var mixedAttributesObjUrl = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj'; var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; From 3ca2822690d0059138093eca365176953969f6c8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 1 Nov 2018 00:33:13 -0400 Subject: [PATCH 15/16] Naming changes to be more like master --- lib/loadObj.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 863066e..b02bea2 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -439,9 +439,9 @@ function getMtlPaths(mtllibLine) { function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { nodes = cleanNodes(nodes); if (nodes.length === 0) { - return Promise.reject(new RuntimeError(objPath + ' does not have any geometry data')); + throw new RuntimeError(objPath + ' does not have any geometry data'); } - return loadMaterials(mtlPaths, objPath, options) + return loadMtls(mtlPaths, objPath, options) .then(function(materials) { if (Object.keys(materials).length > 0 && !usesMaterials) { assignDefaultMaterial(nodes, materials); @@ -467,7 +467,7 @@ function outsideDirectory(file, directory) { return (path.relative(directory, file).indexOf('..') === 0); } -function loadMaterials(mtlPaths, objPath, options) { +function loadMtls(mtlPaths, objPath, options) { var objDirectory = path.dirname(objPath); var materials = {}; From 5ce5f6be2aaaf59892c485d0732177083a9bbaf8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 1 Nov 2018 08:59:06 -0400 Subject: [PATCH 16/16] Re-add mixed attributes spec --- specs/lib/createGltfSpec.js | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 4aa141f..329d12f 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -8,10 +8,12 @@ var loadObj = require('../../lib/loadObj'); var Material = require('../../lib/Material'); var clone = Cesium.clone; +var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; var boxObjUrl = 'specs/data/box/box.obj'; var groupObjUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; +var mixedAttributesObjUrl = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj'; var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; @@ -23,6 +25,7 @@ describe('createGltf', function() { var boxObjData; var duplicateBoxObjData; var groupObjData; + var mixedAttributesObjData; var diffuseTexture; var transparentDiffuseTexture; @@ -40,6 +43,10 @@ describe('createGltf', function() { .then(function(data) { groupObjData = data; }), + loadObj(mixedAttributesObjUrl, defaultOptions) + .then(function(data) { + mixedAttributesObjData = data; + }), loadImage(diffuseTextureUrl, defaultOptions) .then(function(image) { diffuseTexture = image; @@ -309,6 +316,57 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); + function getMaterialValue(material, property) { + return material.extensions.KHR_materials_common.values[property]; + } + + it('splits incompatible materials', function() { + var gltf = createGltf(mixedAttributesObjData, defaultOptions); + var meshes = gltf.meshes; + var materials = gltf.materials; + var materialNames = Object.keys(materials).sort(); + + // Expect three copies of each material for + // * positions/normals/uvs + // * positions/normals + // * positions/uvs + expect(materialNames).toEqual([ + 'Material', + 'Material-2', + 'Material-3', + 'Missing', + 'Missing-2', + 'Missing-3', + 'default', + 'default-2', + 'default-3' + ]); + + expect(getMaterialValue(materials['Material'], 'diffuse')).toBe('texture_cesium'); + expect(getMaterialValue(materials['Material'], 'emission')).toEqual([0.0, 0.0, 0.1, 1.0]); + expect(getMaterialValue(materials['Material-2'], 'diffuse')).toEqual([0.0, 0.0, 0.0, 1.0]); + expect(getMaterialValue(materials['Material-2'], 'emission')).toBe('texture_cesium'); + expect(getMaterialValue(materials['Material-3'], 'diffuse')).toEqual([0.64, 0.64, 0.64, 1.0]); + expect(getMaterialValue(materials['Material-3'], 'emission')).toEqual([0.0, 0.0, 0.1, 1.0]); + + // Test that primitives without uvs reference materials without textures + for (var meshName in meshes) { + if (meshes.hasOwnProperty(meshName)) { + var mesh = meshes[meshName]; + var primitives = mesh.primitives; + var primitivesLength = primitives.length; + for (var i = 0; i < primitivesLength; ++i) { + var primitive = primitives[i]; + var material = materials[primitive.material]; + if (!defined(primitive.attributes.TEXCOORD_0)) { + expect(typeof getMaterialValue(material, 'diffuse') === 'string').toBe(false); + expect(typeof getMaterialValue(material, 'emission') === 'string').toBe(false); + } + } + } + } + }); + function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices;