From 133e428cb479d2a10c85085d9c9b43a1aef5511b Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Tue, 13 Jun 2017 20:38:38 -0400 Subject: [PATCH 01/11] get n-many vertices for a face --- lib/loadObj.js | 108 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 33 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 148ff319..eb8b4642 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -44,11 +44,13 @@ function Primitive() { var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float -var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/; // f vertex vertex vertex ... -var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)?/; // f vertex/uv vertex/uv vertex/uv ... -var facePattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... -var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/; // f vertex//normal vertex//normal vertex//normal ... +var facePattern1 = /f(\s+-?\d+\/?\/?){3,}/; // f vertex vertex vertex ... +var facePattern2 = /f(\s+-?\d+\/-?\d+){3,}/; // f vertex/uv vertex/uv vertex/uv ... +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; var scratchCartesian = new Cartesian3(); /** @@ -195,7 +197,33 @@ function loadObj(objPath, options) { return index; } - function addFace(v1, p1, u1, n1, v2, p2, u2, n2, v3, p3, u3, n3, v4, p4, u4, n4) { + function addFace(vertices, positions, uvs, normals) { + + var u1, u2, u3, u4, n1, n2, n3, n4; + + var v1 = vertices[0]; + var v2 = vertices[1]; + var v3 = vertices[2]; + var v4 = vertices[3]; + var p1 = positions[0]; + var p2 = positions[1]; + var p3 = positions[2]; + var p4 = positions[3]; + + if (uvs) { + u1 = uvs[0]; + u2 = uvs[1]; + u3 = uvs[2]; + u4 = uvs[3]; + } + + if (normals) { + n1 = normals[0]; + n2 = normals[1]; + n3 = normals[2]; + n4 = normals[3]; + } + var index1 = addVertex(v1, p1, u1, n1); var index2 = addVertex(v2, p2, u2, n2); var index3 = addVertex(v3, p3, u3, n3); @@ -211,6 +239,7 @@ function loadObj(objPath, options) { primitive.indices.push(index3); primitive.indices.push(index4); } + } function parseLine(line) { @@ -256,34 +285,47 @@ 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 if ((result = facePattern1.exec(line)) !== null) { - addFace( - result[1], result[1], undefined, undefined, - result[2], result[2], undefined, undefined, - result[3], result[3], undefined, undefined, - result[4], result[4], undefined, undefined - ); - } else if ((result = facePattern2.exec(line)) !== null) { - addFace( - result[1], result[2], result[3], undefined, - result[4], result[5], result[6], undefined, - result[7], result[8], result[9], undefined, - result[10], result[11], result[12], undefined - ); - } else if ((result = facePattern3.exec(line)) !== null) { - addFace( - result[1], result[2], result[3], result[4], - result[5], result[6], result[7], result[8], - result[9], result[10], result[11], result[12], - result[13], result[14], result[15], result[16] - ); - } else if ((result = facePattern4.exec(line)) !== null) { - addFace( - result[1], result[2], undefined, result[3], - result[4], result[5], undefined, result[6], - result[7], result[8], undefined, result[9], - result[10], result[11], undefined, result[12] - ); + } else if (facePattern1.test(line)) { // format "f v v v ..." + var faceVertices = line.replace(faceSpaceOrSlashPattern, ' ').substring(1).split(' '); // get vertices + addFace(faceVertices, faceVertices, undefined, undefined); + } else if (facePattern2.test(line)) { // format "f v/uv v/uv v/uv ..." + 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 = []; + for (var 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 ..." + 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 = []; + for (var 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 ..." + 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 faceNormals = []; + for (var i=0; i <= faceAttributes.length - 2; i += 2) + { + facePositions.push(faceAttributes[i]); + faceNormals.push(faceAttributes[i+1]); + } + addFace(faceVertices, facePositions, undefined, faceNormals); } } From 50d26f4fd7c0b799ae966efefabf0a3e607c22f0 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Wed, 14 Jun 2017 16:55:03 -0400 Subject: [PATCH 02/11] loads concave faces with more than 3 vertices --- lib/loadObj.js | 226 +++++++++++++++------ specs/data/box-triangles/box-triangles.mtl | 14 +- specs/data/box-triangles/box-triangles.obj | 44 ++-- specs/data/concave/concave.mtl | 10 + specs/data/concave/concave.obj | 30 +++ specs/lib/loadObjSpec.js | 11 + 6 files changed, 231 insertions(+), 104 deletions(-) create mode 100644 specs/data/concave/concave.mtl create mode 100644 specs/data/concave/concave.obj diff --git a/lib/loadObj.js b/lib/loadObj.js index eb8b4642..b547be50 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -9,12 +9,20 @@ var loadMtl = require('./loadMtl'); var readLines = require('./readLines'); var Axis = Cesium.Axis; +var Cartesian2 = Cesium.Cartesian2; var Cartesian3 = Cesium.Cartesian3; var ComponentDatatype = Cesium.ComponentDatatype; var defaultValue = Cesium.defaultValue; var defined = Cesium.defined; +var IntersectionTests = Cesium.IntersectionTests; +var Matrix3 = Cesium.Matrix3; var Matrix4 = Cesium.Matrix4; +var OrientedBoundingBox = Cesium.OrientedBoundingBox; +var Plane = Cesium.Plane; +var PolygonPipeline = Cesium.PolygonPipeline; +var Ray = Cesium.Ray; var RuntimeError = Cesium.RuntimeError; +var WindingOrder = Cesium.WindingOrder; module.exports = loadObj; @@ -137,6 +145,80 @@ function loadObj(objPath, options) { primitive.material = getName(name); } + var intPoint = new Cartesian3(); + var xAxis = Cesium.Cartesian3.UNIT_X.clone(); + var yAxis = Cesium.Cartesian3.UNIT_Y.clone(); + var zAxis = Cesium.Cartesian3.UNIT_Z.clone(); + var origin = new Cartesian3(); + var normal = new Cartesian3(); + var ray = new Ray(); + var plane = new Plane(Cesium.Cartesian3.UNIT_X, 0); + function projectTo2D(positions) { + var positions2D = new Array(positions.length); + var obb = OrientedBoundingBox.fromPoints(positions); + var halfAxes = obb.halfAxes; + Matrix3.getColumn(halfAxes, 0, xAxis); + Matrix3.getColumn(halfAxes, 1, yAxis); + Matrix3.getColumn(halfAxes, 2, zAxis); + + var xMag = Cartesian3.magnitude(xAxis); + var yMag = Cartesian3.magnitude(yAxis); + var zMag = Cartesian3.magnitude(zAxis); + var min = Math.min(xMag, yMag, zMag); + + var center = obb.center; + var planeXAxis; + var planeYAxis; + if (min === xMag) { + if (!xAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, xAxis, origin); + Cartesian3.normalize(xAxis, normal); + } + planeXAxis = Cartesian3.normalize(yAxis, yAxis); + planeYAxis = Cartesian3.normalize(zAxis, zAxis); + } else if (min === yMag) { + if (!yAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, yAxis, origin); + Cartesian3.normalize(yAxis, normal); + } + planeXAxis = Cartesian3.normalize(xAxis, xAxis); + planeYAxis = Cartesian3.normalize(zAxis, zAxis); + } else { + if (!zAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, zAxis, origin); + Cartesian3.normalize(zAxis, normal); + } + planeXAxis = Cartesian3.normalize(xAxis, xAxis); + planeYAxis = Cartesian3.normalize(yAxis, yAxis); + } + + if (min === 0) { + normal = Cartesian3.cross(planeXAxis, planeYAxis, normal); + normal = Cartesian3.normalize(normal, normal); + } + + Plane.fromPointNormal(origin, normal, plane); + ray.direction = normal; + + for (var i = 0; i < positions.length; i++) { + ray.origin = positions[i]; + + var intersectionPoint = IntersectionTests.rayPlane(ray, plane, intPoint); + + if (!defined(intersectionPoint)) { + Cartesian3.negate(ray.direction, ray.direction); + intersectionPoint = IntersectionTests.rayPlane(ray, plane, intPoint); + } + var v = Cartesian3.subtract(intersectionPoint, origin, intersectionPoint); + var x = Cartesian3.dot(planeXAxis, v); + var y = Cartesian3.dot(planeYAxis, v); + + positions2D[i] = new Cartesian2(x, y); + } + + return positions2D; + } + function getOffset(a, attributeData, components) { var i = parseInt(a); if (i < 0) { @@ -199,45 +281,63 @@ function loadObj(objPath, options) { function addFace(vertices, positions, uvs, normals) { - var u1, u2, u3, u4, n1, n2, n3, n4; - - var v1 = vertices[0]; - var v2 = vertices[1]; - var v3 = vertices[2]; - var v4 = vertices[3]; - var p1 = positions[0]; - var p2 = positions[1]; - var p3 = positions[2]; - var p4 = positions[3]; - - if (uvs) { - u1 = uvs[0]; - u2 = uvs[1]; - u3 = uvs[2]; - u4 = uvs[3]; - } + var u1, u2, u3, n1, n2, n3; - if (normals) { - n1 = normals[0]; - n2 = normals[1]; - n3 = normals[2]; - n4 = normals[3]; - } + if (vertices.length === 3) { - var index1 = addVertex(v1, p1, u1, n1); - var index2 = addVertex(v2, p2, u2, n2); - var index3 = addVertex(v3, p3, u3, n3); + if (uvs) { + u1 = uvs[0]; + u2 = uvs[1]; + u3 = uvs[2]; + } - primitive.indices.push(index1); - primitive.indices.push(index2); - primitive.indices.push(index3); + if (normals) { + n1 = normals[0]; + n2 = normals[1]; + n3 = normals[2]; + } + + var index1 = addVertex(vertices[0], positions[0], u1, n1); + var index2 = addVertex(vertices[1], positions[1], u2, n2); + var index3 = addVertex(vertices[2], positions[2], u3, n3); - // Triangulate if the face is a quad - if (defined(v4)) { - var index4 = addVertex(v4, p4, u4, n4); primitive.indices.push(index1); + primitive.indices.push(index2); primitive.indices.push(index3); - primitive.indices.push(index4); + } else { // Triangulate if the face is not a triangle + var positions3D = []; + var vertexIndices = []; + + var i; + for (i=0; i < vertices.length; ++i) { + var u = (defined(uvs)) ? uvs[i] : undefined; + var n = (defined(normals)) ? normals[i] : undefined; + + var index = addVertex(vertices[i], positions[i], u, n); + vertexIndices.push(index); + + 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)); + } + var positions2D = projectTo2D(positions3D); + + var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + + // Since the projection doesn't respect winding order, reverse the order of + // the vertices before triangulating to enforce counter clockwise. + if (windingOrder === WindingOrder.CLOCKWISE) { + positions2D.reverse(); + } + + var positionIndices = PolygonPipeline.triangulate(positions2D); + + for (i=0; i < positionIndices.length; ++i) { + primitive.indices.push(vertexIndices[positionIndices[i]]); + } } } @@ -285,47 +385,39 @@ 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 if (facePattern1.test(line)) { // format "f v v v ..." - var faceVertices = line.replace(faceSpaceOrSlashPattern, ' ').substring(1).split(' '); // get vertices - addFace(faceVertices, faceVertices, undefined, undefined); - } else if (facePattern2.test(line)) { // format "f v/uv v/uv v/uv ..." - 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 = []; - for (var 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 ..." + } 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 = []; - for (var 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 ..." - 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 faceNormals = []; - for (var i=0; i <= faceAttributes.length - 2; i += 2) - { - facePositions.push(faceAttributes[i]); - faceNormals.push(faceAttributes[i+1]); + 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); } - addFace(faceVertices, facePositions, undefined, faceNormals); } } diff --git a/specs/data/box-triangles/box-triangles.mtl b/specs/data/box-triangles/box-triangles.mtl index abbc294b..70d3ba1d 100644 --- a/specs/data/box-triangles/box-triangles.mtl +++ b/specs/data/box-triangles/box-triangles.mtl @@ -1,12 +1,10 @@ -# Blender MTL File: 'box.blend' +# Blender MTL File: 'None' # Material Count: 1 -newmtl Material -Ns 96.078431 +newmtl None +Ns 0 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 +Kd 0.8 0.8 0.8 +Ks 0.8 0.8 0.8 +d 1 illum 2 diff --git a/specs/data/box-triangles/box-triangles.obj b/specs/data/box-triangles/box-triangles.obj index 124ab2de..b97f0d4b 100644 --- a/specs/data/box-triangles/box-triangles.obj +++ b/specs/data/box-triangles/box-triangles.obj @@ -1,7 +1,7 @@ -# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# Blender v2.78 (sub 0) OBJ File: '' # www.blender.org mtllib box-triangles.mtl -o Cube +o Cube_Cube.001 v -1.000000 -1.000000 1.000000 v -1.000000 1.000000 1.000000 v -1.000000 -1.000000 -1.000000 @@ -10,37 +10,23 @@ 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 +usemtl None 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 +f 2//1 3//1 1//1 +f 4//2 7//2 3//2 +f 8//3 5//3 7//3 +f 6//4 1//4 5//4 +f 7//5 1//5 3//5 +f 4//6 6//6 8//6 +f 2//1 4//1 3//1 +f 4//2 8//2 7//2 +f 8//3 6//3 5//3 +f 6//4 2//4 1//4 +f 7//5 5//5 1//5 +f 4//6 2//6 6//6 diff --git a/specs/data/concave/concave.mtl b/specs/data/concave/concave.mtl new file mode 100644 index 00000000..70d3ba1d --- /dev/null +++ b/specs/data/concave/concave.mtl @@ -0,0 +1,10 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl None +Ns 0 +Ka 0.000000 0.000000 0.000000 +Kd 0.8 0.8 0.8 +Ks 0.8 0.8 0.8 +d 1 +illum 2 diff --git a/specs/data/concave/concave.obj b/specs/data/concave/concave.obj new file mode 100644 index 00000000..aeb002d0 --- /dev/null +++ b/specs/data/concave/concave.obj @@ -0,0 +1,30 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib concave.mtl +o Plane +v -1.458150 0.363522 1.000000 +v 0.541850 0.363522 1.000000 +v -1.458150 0.363522 -1.000000 +v 0.541850 0.363522 -1.000000 +v -0.336510 0.363522 0.000000 +v -1.458150 -0.363522 1.000000 +v 0.541850 -0.363522 1.000000 +v -1.458150 -0.363522 -1.000000 +v 0.541850 -0.363522 -1.000000 +v -0.336510 -0.363522 0.000000 +vn 0.0000 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.7513 0.0000 -0.6599 +vn 0.7513 0.0000 0.6599 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +usemtl None +s off +f 1//1 2//1 5//1 4//1 3//1 +f 6//2 8//2 9//2 10//2 7//2 +f 2//3 7//3 10//3 5//3 +f 5//4 10//4 9//4 4//4 +f 3//5 8//5 6//5 1//5 +f 4//6 9//6 8//6 3//6 +f 1//7 6//7 7//7 2//7 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index ca7d2a73..a43930da 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -19,6 +19,7 @@ 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 objConcaveUrl = 'specs/data/concave/concave.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'; @@ -196,6 +197,16 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with concave face containing 5 vertices', function(done) { + expect(loadObj(objConcaveUrl, defaultOptions) + .then(function(data) { + var mesh = getMeshes(data)[0]; + var primitive = getPrimitives(data)[0]; + expect(mesh.positions.length / 3).toBe(30); + expect(primitive.indices.length).toBe(48); + }), done).toResolve(); + }); + it('loads obj with usemtl only', function(done) { expect(loadObj(objUsemtlUrl, defaultOptions) .then(function(data) { From 7e82e90b3588ceff05e78359b709c10cbfc30c78 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Thu, 15 Jun 2017 11:11:27 -0400 Subject: [PATCH 03/11] adding convexivity test and fan method for convex polygon triangulation --- lib/loadObj.js | 73 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index b547be50..04f3928d 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -49,13 +49,13 @@ function Primitive() { } // OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js) -var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float -var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float -var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float -var facePattern1 = /f(\s+-?\d+\/?\/?){3,}/; // f vertex vertex vertex ... -var facePattern2 = /f(\s+-?\d+\/-?\d+){3,}/; // f vertex/uv vertex/uv vertex/uv ... -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 vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float +var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float +var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float +var facePattern1 = /f(\s+-?\d+\/?\/?){3,}/; // f vertex vertex vertex ... +var facePattern2 = /f(\s+-?\d+\/-?\d+){3,}/; // f vertex/uv vertex/uv vertex/uv ... +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; @@ -279,6 +279,29 @@ function loadObj(objPath, options) { return index; } + // 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(); + var scratch2 = new Cartesian3(); + function getTurnDirection(pointA, pointB, pointC) { + var vector1 = Cartesian2.subtract(pointA, pointB, scratch1); + var vector2 = Cartesian2.subtract(pointC, pointB, scratch2); + return vector1.x * vector2.y - vector1.y * vector2.x; + } + + // Given the cartesian 2 vertices of a polygon, determine if convex + function isConvex(positions2D) { + var i; + var turnDirection = getTurnDirection(positions2D[0], positions2D[1], positions2D[2]); + for (i=1; i < positions2D.length-2; ++i) { + var currentTurnDirection = getTurnDirection(positions2D[i], positions2D[i+1], positions2D[i+2]); + if (turnDirection * currentTurnDirection < 0) { + return false; + } + } + return true; + } + function addFace(vertices, positions, uvs, normals) { var u1, u2, u3, n1, n2, n3; @@ -309,34 +332,44 @@ function loadObj(objPath, options) { var vertexIndices = []; var i; - for (i=0; i < vertices.length; ++i) { + for (i = 0; i < vertices.length; ++i) { var u = (defined(uvs)) ? uvs[i] : undefined; var n = (defined(normals)) ? normals[i] : undefined; var index = addVertex(vertices[i], positions[i], u, n); vertexIndices.push(index); - var pi = getOffset(index+1, positions, 3); + // 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)); } - var positions2D = projectTo2D(positions3D); - var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + var positions2D = projectTo2D(positions3D); - // Since the projection doesn't respect winding order, reverse the order of - // the vertices before triangulating to enforce counter clockwise. - if (windingOrder === WindingOrder.CLOCKWISE) { - positions2D.reverse(); - } + 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 { - var positionIndices = PolygonPipeline.triangulate(positions2D); + // 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); + if (windingOrder === WindingOrder.CLOCKWISE) { + positions2D.reverse(); + } - for (i=0; i < positionIndices.length; ++i) { - primitive.indices.push(vertexIndices[positionIndices[i]]); + // Use an ear-clipping algorithm to triangulate + var positionIndices = PolygonPipeline.triangulate(positions2D); + for (i = 0; i < positionIndices.length; ++i) { + primitive.indices.push(vertexIndices[positionIndices[i]]); + } } } From 2396899f5dd33bdf2ff2b395dcf471bc1978d5ae Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Thu, 15 Jun 2017 17:57:05 -0400 Subject: [PATCH 04/11] refactored face line parsing to support multiline faces --- lib/loadObj.js | 153 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 42 deletions(-) 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); } } } From 2105e7bf82bca7ee9e5426637d97be8eff5b89d8 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Fri, 16 Jun 2017 13:51:29 -0400 Subject: [PATCH 05/11] adding winding order sanitization when normals are provided --- lib/loadObj.js | 70 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index bddcc332..bdca9b48 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -58,10 +58,10 @@ var facePattern3 = /f(\s+-?\d+\/-?\d+\/-?\d+){3,}/; var facePattern4 = /f(\s+-?\d+\/\/-?\d+){3,}/; // f vertex//normal vertex//normal vertex//normal ... // 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 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; @@ -314,6 +314,14 @@ function loadObj(objPath, options) { return Cartesian3.fromElements(px, py, pz, result); } + function get3DNormal(index, result) { + var ni = getOffset(index, normals, 3); + var nx = normals.get(ni + 0); + var ny = normals.get(ni + 1); + var nz = normals.get(ni + 2); + return Cartesian3.fromElements(nx, ny, nz, 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(); @@ -337,9 +345,45 @@ function loadObj(objPath, options) { return true; } + var scratch3 = new Cartesian3(); + // Checks if winding order matches the given normal. + function isWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normal) { + var A = get3DPoint(positionIndex1, scratch1); + var B = get3DPoint(positionIndex2, scratch2); + var C = get3DPoint(positionIndex3, scratch3); + + Cartesian3.subtract(A, B, scratch1); + Cartesian3.subtract(C, B, scratch2); + var cross = Cartesian3.cross(A, C, scratch1); + + return (Cartesian3.dot(normal, cross) >= 0); + } + + function addTriangle(index1, index2, index3, correctWinding) { + if (correctWinding) { + primitive.indices.push(index1); + primitive.indices.push(index2); + primitive.indices.push(index3); + } else { + primitive.indices.push(index1); + primitive.indices.push(index3); + primitive.indices.push(index2); + } + } + + var scratchNormal = new Cartesian3(); function addFace(vertices, positions, uvs, normals) { var u1, u2, u3, n1, n2, n3; + var windingCorrect = true; + var faceNormal; + + // If normals are defined, find a face normal to use in winding order sanitization. + // If no face normal, we have to assume the winding is correct. + if (normals) { + faceNormal = get3DNormal(normals[0], scratchNormal); + windingCorrect = isWindingCorrect(positions[0], positions[1], positions[2], faceNormal); + } if (vertices.length === 3) { @@ -359,9 +403,7 @@ function loadObj(objPath, options) { var index2 = addVertex(vertices[1], positions[1], u2, n2); var index3 = addVertex(vertices[2], positions[2], u3, n3); - primitive.indices.push(index1); - primitive.indices.push(index2); - primitive.indices.push(index3); + addTriangle(index1, index2, index3, windingCorrect); } else { // Triangulate if the face is not a triangle var positions3D = []; var vertexIndices = []; @@ -383,22 +425,20 @@ function loadObj(objPath, options) { if (isConvex(positions2D)) { for (i=1; i < vertices.length-1; ++i) { - primitive.indices.push(vertexIndices[0]); - primitive.indices.push(vertexIndices[i]); - primitive.indices.push(vertexIndices[i+1]); + addTriangle(vertexIndices[0], vertexIndices[i], vertexIndices[i+1], windingCorrect); } } else { - // Since the projection doesn't respect winding order, reverse the order of + // Since the projection doesn't preserve winding order, reverse the order of // the vertices before triangulating to enforce counter clockwise. - var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); - if (windingOrder === WindingOrder.CLOCKWISE) { + var projectedWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (projectedWindingOrder === WindingOrder.CLOCKWISE) { positions2D.reverse(); } // Use an ear-clipping algorithm to triangulate var positionIndices = PolygonPipeline.triangulate(positions2D); - for (i = 0; i < positionIndices.length; ++i) { - primitive.indices.push(vertexIndices[positionIndices[i]]); + for (i = 0; i < positionIndices.length-2; i += 3) { + addTriangle(vertexIndices[positionIndices[i]], vertexIndices[positionIndices[i+1]], vertexIndices[positionIndices[i+2]], windingCorrect); } } } From a07ef6a3abea1b68f5d3453d867103fbd96cab56 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Mon, 26 Jun 2017 12:23:34 -0400 Subject: [PATCH 06/11] refactor line buffering --- lib/loadObj.js | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index bdca9b48..7fdf8975 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -109,10 +109,7 @@ function loadObj(objPath, options) { var mtlPaths = []; // Buffers for face data that spans multiple lines - var faceVertices = []; - var facePositions = []; - var faceUvs = []; - var faceNormals = []; + var lineBuffer = ""; function getName(name) { return (name === '' ? undefined : name); @@ -455,10 +452,14 @@ function loadObj(objPath, options) { function parseFaceLine(line, hasUvs, hasNormals) { // get vertex data (attributes '/' separated) - faceVertices = faceVertices.concat(line.replace(faceSpacePattern, ' ').split(' ').filter(trimEmpty)); + var faceVertices = line.replace(faceSpacePattern, ' ').split(' ').filter(trimEmpty); // get all vertex attributes for this line var faceAttributes = line.replace(faceSpaceOrSlashPattern, ' ').split(' ').filter(trimEmpty); + var facePositions = []; + var faceUvs = []; + var faceNormals = []; + var i; if (hasUvs && hasNormals) { for (i=0; i <= faceAttributes.length - 3; i += 3) @@ -489,19 +490,7 @@ function loadObj(objPath, options) { 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) { @@ -548,18 +537,25 @@ function loadObj(objPath, options) { uvs.push(parseFloat(result[1])); uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image } 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 ..." + // Because face lines can contain n vertices, we use a line buffer in case the face data spans multiple lines. + // If there's a line continuation don't create face yet + if (line.slice(-1) === '\\') { + lineBuffer += line.substring(0, -1); + return; + } + + lineBuffer += line; + if (facePattern1.test(lineBuffer)) { // 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 ..." + } else if (facePattern2.test(lineBuffer)) { // 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 ..." + } else if (facePattern3.test(lineBuffer)) { // 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 ..." + } else if (facePattern4.test(lineBuffer)) { // format "v//n v//n v//n ..." parseFaceLine(line, false, true); } + lineBuffer = ""; } } From 2706b95b5b654911beafb4f3e8d017b041164b10 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Mon, 26 Jun 2017 12:47:08 -0400 Subject: [PATCH 07/11] correct cross product calculation --- lib/loadObj.js | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 7fdf8975..b73c3887 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -57,13 +57,6 @@ 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 ... -// 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(); @@ -344,13 +337,13 @@ function loadObj(objPath, options) { var scratch3 = new Cartesian3(); // Checks if winding order matches the given normal. - function isWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normal) { + function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normal) { var A = get3DPoint(positionIndex1, scratch1); var B = get3DPoint(positionIndex2, scratch2); var C = get3DPoint(positionIndex3, scratch3); - Cartesian3.subtract(A, B, scratch1); - Cartesian3.subtract(C, B, scratch2); + Cartesian3.subtract(B, A, B); + Cartesian3.subtract(C, A, C); var cross = Cartesian3.cross(A, C, scratch1); return (Cartesian3.dot(normal, cross) >= 0); @@ -372,14 +365,14 @@ function loadObj(objPath, options) { function addFace(vertices, positions, uvs, normals) { var u1, u2, u3, n1, n2, n3; - var windingCorrect = true; + var isWindingCorrect = true; var faceNormal; // If normals are defined, find a face normal to use in winding order sanitization. // If no face normal, we have to assume the winding is correct. if (normals) { faceNormal = get3DNormal(normals[0], scratchNormal); - windingCorrect = isWindingCorrect(positions[0], positions[1], positions[2], faceNormal); + isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], faceNormal); } if (vertices.length === 3) { @@ -400,7 +393,7 @@ function loadObj(objPath, options) { var index2 = addVertex(vertices[1], positions[1], u2, n2); var index3 = addVertex(vertices[2], positions[2], u3, n3); - addTriangle(index1, index2, index3, windingCorrect); + addTriangle(index1, index2, index3, isWindingCorrect); } else { // Triangulate if the face is not a triangle var positions3D = []; var vertexIndices = []; @@ -422,7 +415,7 @@ function loadObj(objPath, options) { if (isConvex(positions2D)) { for (i=1; i < vertices.length-1; ++i) { - addTriangle(vertexIndices[0], vertexIndices[i], vertexIndices[i+1], windingCorrect); + addTriangle(vertexIndices[0], vertexIndices[i], vertexIndices[i+1], isWindingCorrect); } } else { // Since the projection doesn't preserve winding order, reverse the order of @@ -435,7 +428,7 @@ function loadObj(objPath, options) { // Use an ear-clipping algorithm to triangulate var positionIndices = PolygonPipeline.triangulate(positions2D); for (i = 0; i < positionIndices.length-2; i += 3) { - addTriangle(vertexIndices[positionIndices[i]], vertexIndices[positionIndices[i+1]], vertexIndices[positionIndices[i+2]], windingCorrect); + addTriangle(vertexIndices[positionIndices[i]], vertexIndices[positionIndices[i+1]], vertexIndices[positionIndices[i+2]], isWindingCorrect); } } } From f526e9b0d893f6a12a44a74fe7464141df7abe91 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Tue, 27 Jun 2017 16:31:14 -0400 Subject: [PATCH 08/11] refactor regexes and correct line buffering bug --- lib/loadObj.js | 137 +++++++++++++------------------------------------ 1 file changed, 36 insertions(+), 101 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index b73c3887..e26fb349 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -52,13 +52,8 @@ function Primitive() { var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float -var facePattern1 = /f(\s+-?\d+\/?\/?){3,}/; // f vertex vertex vertex ... -var facePattern2 = /f(\s+-?\d+\/-?\d+){3,}/; // f vertex/uv vertex/uv vertex/uv ... -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 facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g; // for any face format "f v", "f v/v", "f v//v", "f v/v/v" -var faceSpacePattern = /(f?\s+)|(\s+\/)|(\s*\\)/g; -var faceSpaceOrSlashPattern = /(f?\s+)|(\/+\s*)|(\s+\/)|(\s*\\)/g; var scratchCartesian = new Cartesian3(); /** @@ -102,7 +97,16 @@ function loadObj(objPath, options) { var mtlPaths = []; // Buffers for face data that spans multiple lines - var lineBuffer = ""; + var lineBuffer = ''; + + // Used for parsing face data + var faceVertices = []; // names of vertices for caching + var facePositions = []; // indices into position array + var faceUvs = []; // indices into uv array + var faceNormals = []; // indices into normal array + + var positions3D = []; + var vertexIndices = []; function getName(name) { return (name === '' ? undefined : name); @@ -247,7 +251,7 @@ function loadObj(objPath, options) { function createVertex(p, u, n) { // Positions - if (defined(p)) { + if (p.length > 0) { var pi = getOffset(p, positions, 3); var px = positions.get(pi + 0); var py = positions.get(pi + 1); @@ -258,7 +262,7 @@ function loadObj(objPath, options) { } // Normals - if (defined(n)) { + if (n.length > 0) { var ni = getOffset(n, normals, 3); var nx = normals.get(ni + 0); var ny = normals.get(ni + 1); @@ -269,7 +273,7 @@ function loadObj(objPath, options) { } // UVs - if (defined(u)) { + if (u.length > 0) { var ui = getOffset(u, uvs, 2); var ux = uvs.get(ui + 0); var uy = uvs.get(ui + 1); @@ -363,48 +367,28 @@ function loadObj(objPath, options) { var scratchNormal = new Cartesian3(); function addFace(vertices, positions, uvs, normals) { - - var u1, u2, u3, n1, n2, n3; var isWindingCorrect = true; var faceNormal; // If normals are defined, find a face normal to use in winding order sanitization. // If no face normal, we have to assume the winding is correct. - if (normals) { + if (normals[0].length > 0) { faceNormal = get3DNormal(normals[0], scratchNormal); isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], faceNormal); } if (vertices.length === 3) { - - if (uvs) { - u1 = uvs[0]; - u2 = uvs[1]; - u3 = uvs[2]; - } - - if (normals) { - n1 = normals[0]; - n2 = normals[1]; - n3 = normals[2]; - } - - var index1 = addVertex(vertices[0], positions[0], u1, n1); - var index2 = addVertex(vertices[1], positions[1], u2, n2); - var index3 = addVertex(vertices[2], positions[2], u3, n3); - + var index1 = addVertex(vertices[0], positions[0], uvs[0], normals[0]); + var index2 = addVertex(vertices[1], positions[1], uvs[1], normals[1]); + var index3 = addVertex(vertices[2], positions[2], uvs[2], normals[2]); addTriangle(index1, index2, index3, isWindingCorrect); } else { // Triangulate if the face is not a triangle - var positions3D = []; - var vertexIndices = []; + positions3D.length = 0; + vertexIndices.length = 0; var i; for (i = 0; i < vertices.length; ++i) { - - var u = (defined(uvs)) ? uvs[i] : undefined; - var n = (defined(normals)) ? normals[i] : undefined; - - var index = addVertex(vertices[i], positions[i], u, n); + var index = addVertex(vertices[i], positions[i], uvs[i], normals[i]); vertexIndices.push(index); // Collect the vertex positions as 3D points @@ -434,58 +418,6 @@ 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) - var faceVertices = line.replace(faceSpacePattern, ' ').split(' ').filter(trimEmpty); - // get all vertex attributes for this line - var faceAttributes = line.replace(faceSpaceOrSlashPattern, ' ').split(' ').filter(trimEmpty); - - var facePositions = []; - var faceUvs = []; - var faceNormals = []; - - 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; - } - addFace(faceVertices, facePositions, faceUvs, faceNormals); - } - function parseLine(line) { line = line.trim(); var result; @@ -530,25 +462,28 @@ function loadObj(objPath, options) { uvs.push(parseFloat(result[1])); uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image } else { // face line or invalid line - // Because face lines can contain n vertices, we use a line buffer in case the face data spans multiple lines. // If there's a line continuation don't create face yet if (line.slice(-1) === '\\') { - lineBuffer += line.substring(0, -1); + lineBuffer += line.substring(0, line.length-1); return; } - lineBuffer += line; - if (facePattern1.test(lineBuffer)) { // format "f v v v ..." - parseFaceLine(line, false, false); - } else if (facePattern2.test(lineBuffer)) { // format "f v/uv v/uv v/uv ..." - parseFaceLine(line, true, false); - } else if (facePattern3.test(lineBuffer)) { // format "v/uv/n v/uv/n v/uv/n ..." - parseFaceLine(line, true, true); - } else if (facePattern4.test(lineBuffer)) { // format "v//n v//n v//n ..." - parseFaceLine(line, false, true); + if (lineBuffer.substring(0, 2) === 'f ') { + while ( (result = facePattern.exec(lineBuffer)) !== null ) { + faceVertices.push(result[0]); + facePositions.push(result[1]); + faceUvs.push(result[2]); + faceNormals.push(result[3]); + } + addFace(faceVertices, facePositions, faceUvs, faceNormals); + + faceVertices.length = 0; + facePositions.length = 0; + faceNormals.length = 0; + faceUvs.length = 0; } - lineBuffer = ""; + lineBuffer = ''; } } From 9da383e434070dc23bbfa1e0fa7103f8814d1ce0 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 24 Jul 2017 16:42:03 -0400 Subject: [PATCH 09/11] Style edits --- lib/loadObj.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index e26fb349..4bf63886 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -160,7 +160,9 @@ function loadObj(objPath, options) { var normal = new Cartesian3(); var ray = new Ray(); var plane = new Plane(Cesium.Cartesian3.UNIT_X, 0); + function projectTo2D(positions) { + var i; var positions2D = new Array(positions.length); var obb = OrientedBoundingBox.fromPoints(positions); var halfAxes = obb.halfAxes; @@ -175,7 +177,6 @@ function loadObj(objPath, options) { // 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); } @@ -328,15 +329,14 @@ function loadObj(objPath, options) { // Given the cartesian 2 vertices of a polygon, determine if convex function isConvex(positions2D) { - var i; - var turnDirection = getTurnDirection(positions2D[0], positions2D[1], positions2D[2]); - for (i=1; i < positions2D.length-2; ++i) { - var currentTurnDirection = getTurnDirection(positions2D[i], positions2D[i+1], positions2D[i+2]); + var turnDirection = getTurnDirection(positions2D[0], positions2D[1], positions2D[2]); + for (var i=1; i < positions2D.length-2; ++i) { + var currentTurnDirection = getTurnDirection(positions2D[i], positions2D[i+1], positions2D[i+2]); if (turnDirection * currentTurnDirection < 0) { return false; } - } - return true; + } + return true; } var scratch3 = new Cartesian3(); @@ -470,7 +470,7 @@ function loadObj(objPath, options) { } lineBuffer += line; if (lineBuffer.substring(0, 2) === 'f ') { - while ( (result = facePattern.exec(lineBuffer)) !== null ) { + while ((result = facePattern.exec(lineBuffer)) !== null) { faceVertices.push(result[0]); facePositions.push(result[1]); faceUvs.push(result[2]); From 563d5243a2390a57997cc5a4795e48253da89af0 Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Thu, 27 Jul 2017 13:45:37 -0400 Subject: [PATCH 10/11] cleanup edits --- lib/loadObj.js | 207 ++++++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 97 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 4bf63886..7359ff92 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -100,12 +100,11 @@ function loadObj(objPath, options) { var lineBuffer = ''; // Used for parsing face data - var faceVertices = []; // names of vertices for caching - var facePositions = []; // indices into position array - var faceUvs = []; // indices into uv array - var faceNormals = []; // indices into normal array + var faceVertices = []; + var facePositions = []; + var faceUvs = []; + var faceNormals = []; - var positions3D = []; var vertexIndices = []; function getName(name) { @@ -152,95 +151,6 @@ function loadObj(objPath, options) { primitive.material = getName(name); } - var intPoint = new Cartesian3(); - var xAxis = Cesium.Cartesian3.UNIT_X.clone(); - var yAxis = Cesium.Cartesian3.UNIT_Y.clone(); - var zAxis = Cesium.Cartesian3.UNIT_Z.clone(); - var origin = new Cartesian3(); - var normal = new Cartesian3(); - var ray = new Ray(); - var plane = new Plane(Cesium.Cartesian3.UNIT_X, 0); - - function projectTo2D(positions) { - var i; - var positions2D = new Array(positions.length); - var obb = OrientedBoundingBox.fromPoints(positions); - var halfAxes = obb.halfAxes; - Matrix3.getColumn(halfAxes, 0, xAxis); - Matrix3.getColumn(halfAxes, 1, yAxis); - Matrix3.getColumn(halfAxes, 2, zAxis); - - var xMag = Cartesian3.magnitude(xAxis); - var yMag = Cartesian3.magnitude(yAxis); - 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)) { - 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; - if (min === xMag) { - if (!xAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, xAxis, origin); - Cartesian3.normalize(xAxis, normal); - } - planeXAxis = Cartesian3.normalize(yAxis, yAxis); - planeYAxis = Cartesian3.normalize(zAxis, zAxis); - } else if (min === yMag) { - if (!yAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, yAxis, origin); - Cartesian3.normalize(yAxis, normal); - } - planeXAxis = Cartesian3.normalize(xAxis, xAxis); - planeYAxis = Cartesian3.normalize(zAxis, zAxis); - } else { - if (!zAxis.equals(Cartesian3.ZERO)) { - Cartesian3.add(center, zAxis, origin); - Cartesian3.normalize(zAxis, normal); - } - planeXAxis = Cartesian3.normalize(xAxis, xAxis); - planeYAxis = Cartesian3.normalize(yAxis, yAxis); - } - - if (min === 0) { - normal = Cartesian3.cross(planeXAxis, planeYAxis, normal); - normal = Cartesian3.normalize(normal, normal); - } - - Plane.fromPointNormal(origin, normal, plane); - ray.direction = normal; - - for (i = 0; i < positions.length; i++) { - ray.origin = positions[i]; - - var intersectionPoint = IntersectionTests.rayPlane(ray, plane, intPoint); - - if (!defined(intersectionPoint)) { - Cartesian3.negate(ray.direction, ray.direction); - intersectionPoint = IntersectionTests.rayPlane(ray, plane, intPoint); - } - var v = Cartesian3.subtract(intersectionPoint, origin, intersectionPoint); - var x = Cartesian3.dot(planeXAxis, v); - var y = Cartesian3.dot(planeYAxis, v); - - positions2D[i] = new Cartesian2(x, y); - } - - return positions2D; - } - function getOffset(a, attributeData, components) { var i = parseInt(a); if (i < 0) { @@ -301,6 +211,106 @@ function loadObj(objPath, options) { return index; } + // Given a set of 3D points, project them onto whichever axis will produce the least distortion. + var scratchIntersectionPoint = new Cartesian3(); + var scratchXAxis = new Cartesian3(); + var scratchYAxis = new Cartesian3(); + var scratchZAxis = new Cartesian3(); + var scratchOrigin = new Cartesian3(); + var scratchNormal = new Cartesian3(); + var scratchRay = new Ray(); + var scratchPlane = new Plane(Cesium.Cartesian3.UNIT_X, 0); + var scratchPositions2D = [new Cartesian2(), new Cartesian2(), new Cartesian2()]; + function projectTo2D(positions) { + var positions2D = []; + var obb = OrientedBoundingBox.fromPoints(positions); + var halfAxes = obb.halfAxes; + Matrix3.getColumn(halfAxes, 0, scratchXAxis); + Matrix3.getColumn(halfAxes, 1, scratchYAxis); + Matrix3.getColumn(halfAxes, 2, scratchZAxis); + + var xMag = Cartesian3.magnitude(scratchXAxis); + var yMag = Cartesian3.magnitude(scratchYAxis); + var zMag = Cartesian3.magnitude(scratchZAxis); + var min = Math.min(xMag, yMag, zMag); + + var i; + // If all the points are on a line, just remove one of the zero dimensions + if (xMag === 0 && (yMag === 0 || zMag === 0)) { + for (i = 0; i < positions.length; i++) { + if (i === scratchPositions2D.length) { + scratchPositions2D.push(new Cartesian2()); + } + positions2D[i] = new Cartesian2.fromElements(positions[i].y, positions[i].z, scratchPositions2D[i]); + } + return positions2D; + } else if (yMag === 0 && zMag === 0) { + for (i = 0; i < positions.length; i++) { + if (i === scratchPositions2D.length) { + scratchPositions2D.push(new Cartesian2()); + } + positions2D[i] = new Cartesian2.fromElements(positions[i].x, positions[i].y, scratchPositions2D[i]); + } + return positions2D; + } + + var center = obb.center; + var planeXAxis; + var planeYAxis; + if (min === xMag) { + if (!scratchXAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, scratchXAxis, scratchOrigin); + Cartesian3.normalize(scratchXAxis, scratchNormal); + } + planeXAxis = Cartesian3.normalize(scratchYAxis, scratchYAxis); + planeYAxis = Cartesian3.normalize(scratchZAxis, scratchZAxis); + } else if (min === yMag) { + if (!scratchYAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, scratchYAxis, scratchOrigin); + Cartesian3.normalize(scratchYAxis, scratchNormal); + } + planeXAxis = Cartesian3.normalize(scratchXAxis, scratchXAxis); + planeYAxis = Cartesian3.normalize(scratchZAxis, scratchZAxis); + } else { + if (!scratchZAxis.equals(Cartesian3.ZERO)) { + Cartesian3.add(center, scratchZAxis, scratchOrigin); + Cartesian3.normalize(scratchZAxis, scratchNormal); + } + planeXAxis = Cartesian3.normalize(scratchXAxis, scratchXAxis); + planeYAxis = Cartesian3.normalize(scratchYAxis, scratchYAxis); + } + + if (min === 0) { + scratchNormal = Cartesian3.cross(planeXAxis, planeYAxis, scratchNormal); + scratchNormal = Cartesian3.normalize(scratchNormal, scratchNormal); + } + + Plane.fromPointNormal(scratchOrigin, scratchNormal, scratchPlane); + scratchRay.direction = scratchNormal; + + for (i = 0; i < positions.length; i++) { + scratchRay.origin = positions[i]; + + var intersectionPoint = IntersectionTests.rayPlane(scratchRay, scratchPlane, scratchIntersectionPoint); + + if (!defined(intersectionPoint)) { + Cartesian3.negate(scratchRay.direction, scratchRay.direction); + intersectionPoint = IntersectionTests.rayPlane(scratchRay, scratchPlane, scratchIntersectionPoint); + } + var v = Cartesian3.subtract(intersectionPoint, scratchOrigin, intersectionPoint); + var x = Cartesian3.dot(planeXAxis, v); + var y = Cartesian3.dot(planeYAxis, v); + + if (i === scratchPositions2D.length) { + scratchPositions2D.push(new Cartesian2()); + } + + positions2D[i] = new Cartesian2.fromElements(x, y, scratchPositions2D[i]); + } + + return positions2D; + } + function get3DPoint(index, result) { var pi = getOffset(index, positions, 3); var px = positions.get(pi + 0); @@ -365,7 +375,7 @@ function loadObj(objPath, options) { } } - var scratchNormal = new Cartesian3(); + var scratchPositions3D = [new Cartesian3(), new Cartesian3(), new Cartesian3()]; function addFace(vertices, positions, uvs, normals) { var isWindingCorrect = true; var faceNormal; @@ -383,7 +393,7 @@ function loadObj(objPath, options) { var index3 = addVertex(vertices[2], positions[2], uvs[2], normals[2]); addTriangle(index1, index2, index3, isWindingCorrect); } else { // Triangulate if the face is not a triangle - positions3D.length = 0; + var positions3D = []; vertexIndices.length = 0; var i; @@ -392,7 +402,10 @@ function loadObj(objPath, options) { vertexIndices.push(index); // Collect the vertex positions as 3D points - positions3D.push(get3DPoint(positions[i], new Cartesian3())); + if (i === scratchPositions3D.length) { + scratchPositions3D.push(new Cartesian3()); + } + positions3D.push(get3DPoint(positions[i], scratchPositions3D[i])); } var positions2D = projectTo2D(positions3D); From 32936211551bc39d2b10295235c42a14ec95a36f Mon Sep 17 00:00:00 2001 From: Rachel Hwang Date: Tue, 8 Aug 2017 11:59:02 -0400 Subject: [PATCH 11/11] correct winding order check bug --- lib/loadObj.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/loadObj.js b/lib/loadObj.js index 7359ff92..eb5131b1 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -350,15 +350,17 @@ function loadObj(objPath, options) { } var scratch3 = new Cartesian3(); + var scratch4 = new Cartesian3(); + var scratch5 = new Cartesian3(); // Checks if winding order matches the given normal. function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normal) { var A = get3DPoint(positionIndex1, scratch1); var B = get3DPoint(positionIndex2, scratch2); var C = get3DPoint(positionIndex3, scratch3); - Cartesian3.subtract(B, A, B); - Cartesian3.subtract(C, A, C); - var cross = Cartesian3.cross(A, C, scratch1); + var BA = Cartesian3.subtract(B, A, scratch4); + var CA = Cartesian3.subtract(C, A, scratch5); + var cross = Cartesian3.cross(BA, CA, scratch3); return (Cartesian3.dot(normal, cross) >= 0); }