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(); });