Back port fixes from #153

This commit is contained in:
Sean Lilley 2018-09-18 20:34:56 -04:00
parent 332259e6ac
commit 485645e0b9
5 changed files with 99 additions and 82 deletions

View File

@ -1,6 +1,10 @@
Change Log
==========
### 1.3.3 2018-??-??
* Fixed handling of objs with mismatching attribute layouts. [#154](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/154)
### 1.3.2 2018-06-07
* Fixed greyscale images loading as alpha instead of luminance. [#144](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/144)

View File

@ -218,13 +218,17 @@ function createGltf(objData, options) {
var meshes = nodes[i].meshes;
var meshesLength = meshes.length;
for (var j = 0; j < meshesLength; ++j) {
var primitives = meshes[j].primitives;
var primitivesLength = primitives.length;
for (var k = 0; k < primitivesLength; ++k) {
// Reserve the 65535 index for primitive restart
var vertexCount = meshes[j].positions.length / 3;
var vertexCount = primitives[k].positions.length / 3;
if (vertexCount > 65534) {
return true;
}
}
}
}
return false;
}
@ -250,26 +254,6 @@ function createGltf(objData, options) {
var meshId = mesh.name;
gltfNodeMeshes.push(meshId);
var hasPositions = mesh.positions.length > 0;
var hasNormals = mesh.normals.length > 0;
var hasUVs = mesh.uvs.length > 0;
var attributes = {};
if (hasPositions) {
attributes.POSITION = addVertexAttribute(mesh.positions, 3);
}
if (hasNormals) {
attributes.NORMAL = addVertexAttribute(mesh.normals, 3);
}
if (hasUVs) {
attributes.TEXCOORD_0 = addVertexAttribute(mesh.uvs, 2);
}
// Unload resources
mesh.positions = undefined;
mesh.normals = undefined;
mesh.uvs = undefined;
var gltfMeshPrimitives = [];
gltf.meshes[meshId] = {
name : meshId,
@ -281,10 +265,31 @@ function createGltf(objData, options) {
var primitivesLength = primitives.length;
for (var k = 0; k < primitivesLength; ++k) {
var primitive = primitives[k];
var hasPositions = primitive.positions.length > 0;
var hasNormals = primitive.normals.length > 0;
var hasUVs = primitive.uvs.length > 0;
var attributes = {};
if (hasPositions) {
attributes.POSITION = addVertexAttribute(primitive.positions, 3);
}
if (hasNormals) {
attributes.NORMAL = addVertexAttribute(primitive.normals, 3);
}
if (hasUVs) {
attributes.TEXCOORD_0 = addVertexAttribute(primitive.uvs, 2);
}
var indexAccessorId = addIndexArray(primitive.indices, uint32Indices);
primitive.indices = undefined; // Unload resources
var materialId = primitive.material;
// Unload resources
primitive.positions = undefined;
primitive.normals = undefined;
primitive.uvs = undefined;
primitive.indices = undefined;
if (!defined(materialId)) {
// Create a default material if the primitive does not specify one
materialId = 'default';

View File

@ -38,14 +38,14 @@ function Node() {
function Mesh() {
this.name = undefined;
this.primitives = [];
this.positions = new ArrayStorage(ComponentDatatype.FLOAT);
this.normals = new ArrayStorage(ComponentDatatype.FLOAT);
this.uvs = new ArrayStorage(ComponentDatatype.FLOAT);
}
function Primitive() {
this.material = undefined;
this.indices = new ArrayStorage(ComponentDatatype.UNSIGNED_INT);
this.positions = new ArrayStorage(ComponentDatatype.FLOAT);
this.normals = new ArrayStorage(ComponentDatatype.FLOAT);
this.uvs = new ArrayStorage(ComponentDatatype.FLOAT);
}
// OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
@ -87,7 +87,7 @@ function loadObj(objPath, options) {
// All nodes seen in the obj
var nodes = [];
// Used to build the indices. The vertex cache is unique to each mesh.
// Used to build the indices. The vertex cache is unique to each primitive.
var vertexCache = {};
var vertexCacheLimit = 1000000;
var vertexCacheCount = 0;
@ -123,16 +123,16 @@ function loadObj(objPath, options) {
mesh.name = getName(name);
node.meshes.push(mesh);
addPrimitive();
// Clear the vertex cache for each new mesh
vertexCache = {};
vertexCacheCount = 0;
vertexCount = 0;
}
function addPrimitive() {
primitive = new Primitive();
mesh.primitives.push(primitive);
// Clear the vertex cache for each new primitive
vertexCache = {};
vertexCacheCount = 0;
vertexCount = 0;
}
function useMaterial(name) {
@ -167,9 +167,9 @@ function loadObj(objPath, options) {
var px = positions.get(pi + 0);
var py = positions.get(pi + 1);
var pz = positions.get(pi + 2);
mesh.positions.push(px);
mesh.positions.push(py);
mesh.positions.push(pz);
primitive.positions.push(px);
primitive.positions.push(py);
primitive.positions.push(pz);
}
// Normals
@ -178,9 +178,9 @@ function loadObj(objPath, options) {
var nx = normals.get(ni + 0);
var ny = normals.get(ni + 1);
var nz = normals.get(ni + 2);
mesh.normals.push(nx);
mesh.normals.push(ny);
mesh.normals.push(nz);
primitive.normals.push(nx);
primitive.normals.push(ny);
primitive.normals.push(nz);
}
// UVs
@ -188,8 +188,8 @@ function loadObj(objPath, options) {
var ui = getOffset(u, uvs, 2);
var ux = uvs.get(ui + 0);
var uy = uvs.get(ui + 1);
mesh.uvs.push(ux);
mesh.uvs.push(uy);
primitive.uvs.push(ux);
primitive.uvs.push(uy);
}
}
@ -382,6 +382,16 @@ function loadObj(objPath, options) {
var isWindingCorrect = true;
var faceNormal;
var firstFace = primitive.indices.length === 0;
var faceHasUvs = uvs[0].length > 0;
var faceHasNormals = normals[0].length > 0;
var primitiveHasUvs = primitive.uvs.length > 0;
var primitiveHasNormals = primitive.normals.length > 0;
if (!firstFace && (faceHasUvs !== primitiveHasUvs || faceHasNormals !== primitiveHasNormals)) {
// Discard faces that don't use the same attributes
return;
}
// 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[0].length > 0) {
@ -614,10 +624,10 @@ function removeEmptyMeshes(meshes) {
return meshes.filter(function(mesh) {
// Remove empty primitives
mesh.primitives = mesh.primitives.filter(function(primitive) {
return primitive.indices.length > 0;
return primitive.indices.length > 0 && primitive.positions.length > 0;
});
// Valid meshes must have at least one primitive and contain positions
return (mesh.primitives.length > 0) && (mesh.positions.length > 0);
// Valid meshes must have at least one primitive
return (mesh.primitives.length > 0);
});
}

View File

@ -206,7 +206,7 @@ describe('createGltf', function() {
});
it('sets constant material when there are no normals', function() {
boxObjData.nodes[0].meshes[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0;
var material = new Material();
material.diffuseTexture = diffuseTextureUrl;
@ -255,7 +255,7 @@ describe('createGltf', function() {
it('handles material used with and without normals (1)', function() {
// Two meshes - one with normals, and one without
boxObjData.nodes.push(duplicateBoxObjData.nodes[0]);
boxObjData.nodes[1].meshes[0].normals.length = 0;
boxObjData.nodes[1].meshes[0].primitives[0].normals.length = 0;
var gltf = createGltf(boxObjData, defaultOptions);
var kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
@ -268,7 +268,7 @@ describe('createGltf', function() {
it('handles material used with and without normals (2)', function() {
// Now test in a different order
boxObjData.nodes.push(duplicateBoxObjData.nodes[0]);
boxObjData.nodes[0].meshes[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0;
var gltf = createGltf(boxObjData, defaultOptions);
var kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
@ -279,7 +279,7 @@ describe('createGltf', function() {
});
it('runs without normals', function() {
boxObjData.nodes[0].meshes[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0;
var gltf = createGltf(boxObjData, defaultOptions);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
@ -289,7 +289,7 @@ describe('createGltf', function() {
});
it('runs without uvs', function() {
boxObjData.nodes[0].meshes[0].uvs.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].uvs.length = 0;
var gltf = createGltf(boxObjData, defaultOptions);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
@ -299,8 +299,8 @@ describe('createGltf', function() {
});
it('runs without uvs and normals', function() {
boxObjData.nodes[0].meshes[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].uvs.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].primitives[0].uvs.length = 0;
var gltf = createGltf(boxObjData, defaultOptions);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
@ -310,11 +310,11 @@ describe('createGltf', function() {
});
function expandObjData(objData, duplicatesLength) {
var mesh = objData.nodes[0].meshes[0];
var indices = mesh.primitives[0].indices;
var positions = mesh.positions;
var normals = mesh.normals;
var uvs = mesh.uvs;
var primitive = objData.nodes[0].meshes[0].primitives[0];
var indices = primitive.indices;
var positions = primitive.positions;
var normals = primitive.normals;
var uvs = primitive.uvs;
var indicesLength = indices.length;
var vertexCount = positions.length / 3;
@ -338,18 +338,18 @@ describe('createGltf', function() {
it('detects need to use uint32 indices', function() {
expandObjData(boxObjData, 2731); // Right above 65536 limit
var mesh = boxObjData.nodes[0].meshes[0];
var indicesLength = mesh.primitives[0].indices.length;
var vertexCount = mesh.positions.length / 3;
var primitive = boxObjData.nodes[0].meshes[0].primitives[0];
var indicesLength = primitive.indices.length;
var vertexCount = primitive.positions.length / 3;
var gltf = createGltf(boxObjData, defaultOptions);
var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0];
var indicesAccessor = gltf.accessors[primitive.indices];
var gltfPrimitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0];
var indicesAccessor = gltf.accessors[gltfPrimitive.indices];
expect(indicesAccessor.count).toBe(indicesLength);
expect(indicesAccessor.max[0]).toBe(vertexCount - 1);
expect(indicesAccessor.componentType).toBe(WebGLConstants.UNSIGNED_INT);
var positionAccessor = gltf.accessors[primitive.attributes.POSITION];
var positionAccessor = gltf.accessors[gltfPrimitive.attributes.POSITION];
expect(positionAccessor.count).toBe(vertexCount);
});

View File

@ -91,9 +91,9 @@ describe('loadObj', function() {
expect(node.name).toBe('Cube');
expect(mesh.name).toBe('Cube-Mesh');
expect(mesh.positions.length / 3).toBe(24);
expect(mesh.normals.length / 3).toBe(24);
expect(mesh.uvs.length / 2).toBe(24);
expect(primitive.positions.length / 3).toBe(24);
expect(primitive.normals.length / 3).toBe(24);
expect(primitive.uvs.length / 2).toBe(24);
expect(primitive.indices.length).toBe(36);
expect(primitive.material).toBe('Material');
}), done).toResolve();
@ -102,20 +102,20 @@ describe('loadObj', function() {
it('loads obj with normals', function(done) {
expect(loadObj(objNormalsUrl, defaultOptions)
.then(function(data) {
var mesh = getMeshes(data)[0];
expect(mesh.positions.length / 3).toBe(24);
expect(mesh.normals.length / 3).toBe(24);
expect(mesh.uvs.length / 2).toBe(0);
var primitive = getPrimitives(data)[0];
expect(primitive.positions.length / 3).toBe(24);
expect(primitive.normals.length / 3).toBe(24);
expect(primitive.uvs.length / 2).toBe(0);
}), done).toResolve();
});
it('loads obj with uvs', function(done) {
expect(loadObj(objUvsUrl, defaultOptions)
.then(function(data) {
var mesh = getMeshes(data)[0];
expect(mesh.positions.length / 3).toBe(20);
expect(mesh.normals.length / 3).toBe(0);
expect(mesh.uvs.length / 2).toBe(20);
var primitive = getPrimitives(data)[0];
expect(primitive.positions.length / 3).toBe(20);
expect(primitive.normals.length / 3).toBe(0);
expect(primitive.uvs.length / 2).toBe(20);
}), done).toResolve();
});
@ -125,8 +125,8 @@ describe('loadObj', function() {
loadObj(objNegativeIndicesUrl, defaultOptions)
])
.then(function(results) {
var positionsReference = getMeshes(results[0])[0].positions.toFloatBuffer();
var positions = getMeshes(results[1])[0].positions.toFloatBuffer();
var positionsReference = getPrimitives(results[0])[0].positions.toFloatBuffer();
var positions = getPrimitives(results[1])[0].positions.toFloatBuffer();
expect(positions).toEqual(positionsReference);
}), done).toResolve();
});
@ -134,9 +134,8 @@ describe('loadObj', function() {
it('loads obj with triangle faces', function(done) {
expect(loadObj(objTrianglesUrl, defaultOptions)
.then(function(data) {
var mesh = getMeshes(data)[0];
var primitive = getPrimitives(data)[0];
expect(mesh.positions.length / 3).toBe(24);
expect(primitive.positions.length / 3).toBe(24);
expect(primitive.indices.length).toBe(36);
}), done).toResolve();
});
@ -201,9 +200,8 @@ describe('loadObj', function() {
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.positions.length / 3).toBe(30);
expect(primitive.indices.length).toBe(48);
}), done).toResolve();
});
@ -365,12 +363,12 @@ describe('loadObj', function() {
});
function getFirstPosition(data) {
var positions = data.nodes[0].meshes[0].positions;
var positions = data.nodes[0].meshes[0].primitives[0].positions;
return new Cartesian3(positions.get(0), positions.get(1), positions.get(2));
}
function getFirstNormal(data) {
var normals = data.nodes[0].meshes[0].normals;
var normals = data.nodes[0].meshes[0].primitives[0].normals;
return new Cartesian3(normals.get(0), normals.get(1), normals.get(2));
}