loads concave faces with more than 3 vertices

This commit is contained in:
Rachel Hwang 2017-06-14 16:55:03 -04:00
parent 133e428cb4
commit 50d26f4fd7
6 changed files with 230 additions and 103 deletions

View File

@ -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 u1, u2, u3, n1, n2, n3;
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 (vertices.length === 3) {
if (uvs) {
u1 = uvs[0];
u2 = uvs[1];
u3 = uvs[2];
u4 = uvs[3];
}
if (uvs) {
u1 = uvs[0];
u2 = uvs[1];
u3 = uvs[2];
}
if (normals) {
n1 = normals[0];
n2 = normals[1];
n3 = normals[2];
n4 = normals[3];
}
if (normals) {
n1 = normals[0];
n2 = normals[1];
n3 = normals[2];
}
var index1 = addVertex(v1, p1, u1, n1);
var index2 = addVertex(v2, p2, u2, n2);
var index3 = addVertex(v3, p3, u3, n3);
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);
primitive.indices.push(index1);
primitive.indices.push(index2);
primitive.indices.push(index3);
// 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 ..."
} 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 = [];
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]);
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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {