mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2025-02-06 23:13:02 -05:00
loads concave faces with more than 3 vertices
This commit is contained in:
parent
133e428cb4
commit
50d26f4fd7
224
lib/loadObj.js
224
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
10
specs/data/concave/concave.mtl
Normal file
10
specs/data/concave/concave.mtl
Normal 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
|
30
specs/data/concave/concave.obj
Normal file
30
specs/data/concave/concave.obj
Normal 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
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user