diff --git a/lib/loadObj.js b/lib/loadObj.js index 04f3928d..bddcc332 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -57,8 +57,15 @@ var facePattern2 = /f(\s+-?\d+\/-?\d+){3,}/; var facePattern3 = /f(\s+-?\d+\/-?\d+\/-?\d+){3,}/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... var facePattern4 = /f(\s+-?\d+\/\/-?\d+){3,}/; // f vertex//normal vertex//normal vertex//normal ... -var faceSpacePattern = /f?\s+/; -var faceSpaceOrSlashPattern = /(f?\s+)|(\/+\s*)/g; +// Just for line continuations +var facePattern5 = /((\s|^)+-?\d+\/?(\s|$)){1,}/; // f vertex vertex vertex ... +var facePattern6 = /((\s|^)+-?\d+\/-?\d+(\s|$)){1,}/; // f vertex/uv vertex/uv vertex/uv ... +var facePattern7 = /((\s|^)+-?\d+\/-?\d+\/-?\d+(\s|$)+){1,}/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... +var facePattern8 = /((\s|^)+-?\d+\/\/-?\d+(\s|$)){1,}/; // f vertex//normal vertex//normal vertex//normal ... + + +var faceSpacePattern = /(f?\s+)|(\s+\/)|(\s*\\)/g; +var faceSpaceOrSlashPattern = /(f?\s+)|(\/+\s*)|(\s+\/)|(\s*\\)/g; var scratchCartesian = new Cartesian3(); /** @@ -101,6 +108,12 @@ function loadObj(objPath, options) { // All mtl paths seen in the obj var mtlPaths = []; + // Buffers for face data that spans multiple lines + var faceVertices = []; + var facePositions = []; + var faceUvs = []; + var faceNormals = []; + function getName(name) { return (name === '' ? undefined : name); } @@ -166,6 +179,20 @@ function loadObj(objPath, options) { var zMag = Cartesian3.magnitude(zAxis); var min = Math.min(xMag, yMag, zMag); + // If all the points are on a line, just remove one of the zero dimensions + if (xMag === 0 && (yMag === 0 || zMag === 0)) { + var i; + for (i = 0; i < positions.length; i++) { + positions2D[i] = new Cartesian2(positions[i].y, positions[i].z); + } + return positions2D; + } else if (yMag === 0 && zMag === 0) { + for (i = 0; i < positions.length; i++) { + positions2D[i] = new Cartesian2(positions[i].x, positions[i].y); + } + return positions2D; + } + var center = obb.center; var planeXAxis; var planeYAxis; @@ -200,7 +227,7 @@ function loadObj(objPath, options) { Plane.fromPointNormal(origin, normal, plane); ray.direction = normal; - for (var i = 0; i < positions.length; i++) { + for (i = 0; i < positions.length; i++) { ray.origin = positions[i]; var intersectionPoint = IntersectionTests.rayPlane(ray, plane, intPoint); @@ -279,6 +306,14 @@ function loadObj(objPath, options) { return index; } + function get3DPoint(index, result) { + var pi = getOffset(index, positions, 3); + var px = positions.get(pi + 0); + var py = positions.get(pi + 1); + var pz = positions.get(pi + 2); + return Cartesian3.fromElements(px, py, pz, result); + } + // Given a sequence of three points A B C, determine whether vector BC // "turns" clockwise (positive) or counter-clockwise (negative) from vector AB var scratch1 = new Cartesian3(); @@ -333,6 +368,7 @@ function loadObj(objPath, options) { var i; for (i = 0; i < vertices.length; ++i) { + var u = (defined(uvs)) ? uvs[i] : undefined; var n = (defined(normals)) ? normals[i] : undefined; @@ -340,24 +376,18 @@ function loadObj(objPath, options) { vertexIndices.push(index); // Collect the vertex positions as 3D points - var pi = getOffset(index + 1, positions, 3); - var px = mesh.positions.get(pi + 0); - var py = mesh.positions.get(pi + 1); - var pz = mesh.positions.get(pi + 2); - positions3D.push(new Cartesian3(px, py, pz)); + positions3D.push(get3DPoint(positions[i], new Cartesian3())); } var positions2D = projectTo2D(positions3D); if (isConvex(positions2D)) { - // Use the fan method to triangulate for (i=1; i < vertices.length-1; ++i) { primitive.indices.push(vertexIndices[0]); primitive.indices.push(vertexIndices[i]); primitive.indices.push(vertexIndices[i+1]); } } else { - // Since the projection doesn't respect winding order, reverse the order of // the vertices before triangulating to enforce counter clockwise. var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); @@ -372,7 +402,66 @@ function loadObj(objPath, options) { } } } + } + + function trimEmpty(entry) { return entry.trim() !== ''; } + + // Parse a line of face data. + // Because face data can span multiple lines, attribute data is buffered in parallel lists + // faceVertices : names of a vertices for caching + // facePosition : indices into position array + // faceUvs : indices into uv array + // faceNormals : indices into normal array + function parseFaceLine(line, hasUvs, hasNormals) { + + // get vertex data (attributes '/' separated) + faceVertices = faceVertices.concat(line.replace(faceSpacePattern, ' ').split(' ').filter(trimEmpty)); + // get all vertex attributes for this line + var faceAttributes = line.replace(faceSpaceOrSlashPattern, ' ').split(' ').filter(trimEmpty); + + var i; + if (hasUvs && hasNormals) { + for (i=0; i <= faceAttributes.length - 3; i += 3) + { + facePositions.push(faceAttributes[i]); + faceUvs.push(faceAttributes[i+1]); + faceNormals.push(faceAttributes[i+2]); + } + } else if (hasUvs) { + for (i=0; i <= faceAttributes.length - 2; i += 2) + { + facePositions.push(faceAttributes[i]); + faceUvs.push(faceAttributes[i+1]); + } + faceNormals = undefined; + } else if (hasNormals) { + for (i=0; i <= faceAttributes.length - 2; i += 2) + { + facePositions.push(faceAttributes[i]); + faceNormals.push(faceAttributes[i+1]); + } + faceUvs = undefined; + } else { + for (i=0; i < faceAttributes.length; ++i) + { + facePositions.push(faceAttributes[i]); + } + faceUvs = undefined; + faceNormals = undefined; + } + + // If there's a line continuation, buffer the results and don't create face yet + if (line.slice(-1) === '\\') { + return; + } + addFace(faceVertices, facePositions, faceUvs, faceNormals); + + // Clear buffered data once face has been added. + faceVertices = []; + facePositions = []; + faceUvs = []; + faceNormals = []; } function parseLine(line) { @@ -418,38 +507,18 @@ function loadObj(objPath, options) { } else if ((result = uvPattern.exec(line)) !== null) { uvs.push(parseFloat(result[1])); uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image - } else { - var faceVertices = line.replace(faceSpacePattern, ' ').substring(1).split(' '); // get vertex data (attributes '/' separated) - var faceAttributes = line.replace(faceSpaceOrSlashPattern, ' ').substring(1).split(' '); // get vertex attributes - var facePositions = []; - var faceUvs = []; - var faceNormals = []; - - if (facePattern1.test(line)) { // format "f v v v ..." - addFace(faceVertices, faceAttributes, undefined, undefined); - } else if (facePattern2.test(line)) { // format "f v/uv v/uv v/uv ..." - var i; - for (i=0; i <= faceAttributes.length - 2; i += 2) - { - facePositions.push(faceAttributes[i]); - faceUvs.push(faceAttributes[i+1]); - } - addFace(faceVertices, facePositions, faceUvs, undefined); - } else if (facePattern3.test(line)) { // format "v/uv/n v/uv/n v/uv/n ..." - for (i=0; i <= faceAttributes.length - 3; i += 3) - { - facePositions.push(faceAttributes[i]); - faceUvs.push(faceAttributes[i+1]); - faceNormals.push(faceAttributes[i+2]); - } - addFace(faceVertices, facePositions, faceUvs, faceNormals); - } else if (facePattern4.test(line)) { // format "v//n v//n v//n ..." - for (i=0; i <= faceAttributes.length - 2; i += 2) - { - facePositions.push(faceAttributes[i]); - faceNormals.push(faceAttributes[i+1]); - } - addFace(faceVertices, facePositions, undefined, faceNormals); + } else { // face line or invalid line + // If we already have buffered data, this is a continued line + var continuedLine = faceVertices.length > 0; + + if (facePattern1.test(line) || (continuedLine && facePattern5.test(line))) { // format "f v v v ..." + parseFaceLine(line, false, false); + } else if (facePattern2.test(line) || (continuedLine && facePattern6.test(line))) { // format "f v/uv v/uv v/uv ..." + parseFaceLine(line, true, false); + } else if (facePattern3.test(line) || (continuedLine && facePattern7.test(line))) { // format "v/uv/n v/uv/n v/uv/n ..." + parseFaceLine(line, true, true); + } else if (facePattern4.test(line) || (continuedLine && facePattern8.test(line))) { // format "v//n v//n v//n ..." + parseFaceLine(line, false, true); } } }