Merge pull request #153 from AnalyticalGraphicsInc/fix-uv-error

Remove faces that don't match the same attribute layout
This commit is contained in:
Matthew Amato 2018-09-19 15:34:13 -04:00 committed by GitHub
commit 6554214010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 182 additions and 103 deletions

View File

@ -1,13 +1,14 @@
Change Log Change Log
========== ==========
### 2.3.0 ??? ### 2.3.0 2018-??-??
* Fixed normalization on Windows paths running the converter on Linux. [#150](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/150) * Fixed handling of objs with mismatching attribute layouts. [#153](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/153)
* Fixed normalization oo Windows paths when running the converter on Linux. [#150](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/150)
* Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#133](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/133) * Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#133](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/133)
* Fixed handling of unnormalized input normals. [#136](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/136) * Fixed handling of unnormalized input normals. [#136](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/136)
### 2.2.0 2017-01-29 ### 2.2.0 2018-01-29
* Fixed handling of materials where the diffuse and ambient texture are the same. [#127](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/127) * Fixed handling of materials where the diffuse and ambient texture are the same. [#127](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/127)
* Added ability to load alpha textures. [#124](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/124) * Added ability to load alpha textures. [#124](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/124)

View File

@ -131,10 +131,8 @@ function addBufferView(gltf, buffers, accessors, byteStride, target) {
} }
function addBuffers(gltf, bufferState, name) { function addBuffers(gltf, bufferState, name) {
// Positions and normals share the same byte stride so they can share the same bufferView addBufferView(gltf, bufferState.positionBuffers, bufferState.positionAccessors, 12, WebGLConstants.ARRAY_BUFFER);
var positionsAndNormalsAccessors = bufferState.positionAccessors.concat(bufferState.normalAccessors); addBufferView(gltf, bufferState.normalBuffers, bufferState.normalAccessors, 12, WebGLConstants.ARRAY_BUFFER);
var positionsAndNormalsBuffers = bufferState.positionBuffers.concat(bufferState.normalBuffers);
addBufferView(gltf, positionsAndNormalsBuffers, positionsAndNormalsAccessors, 12, WebGLConstants.ARRAY_BUFFER);
addBufferView(gltf, bufferState.uvBuffers, bufferState.uvAccessors, 8, WebGLConstants.ARRAY_BUFFER); addBufferView(gltf, bufferState.uvBuffers, bufferState.uvAccessors, 8, WebGLConstants.ARRAY_BUFFER);
addBufferView(gltf, bufferState.indexBuffers, bufferState.indexAccessors, undefined, WebGLConstants.ELEMENT_ARRAY_BUFFER); addBufferView(gltf, bufferState.indexBuffers, bufferState.indexAccessors, undefined, WebGLConstants.ELEMENT_ARRAY_BUFFER);
@ -297,68 +295,73 @@ function requiresUint32Indices(nodes) {
var meshes = nodes[i].meshes; var meshes = nodes[i].meshes;
var meshesLength = meshes.length; var meshesLength = meshes.length;
for (var j = 0; j < meshesLength; ++j) { for (var j = 0; j < meshesLength; ++j) {
// Reserve the 65535 index for primitive restart var primitives = meshes[j].primitives;
var vertexCount = meshes[j].positions.length / 3; var primitivesLength = primitives.length;
if (vertexCount > 65534) { for (var k = 0; k < primitivesLength; ++k) {
return true; // Reserve the 65535 index for primitive restart
var vertexCount = primitives[k].positions.length / 3;
if (vertexCount > 65534) {
return true;
}
} }
} }
} }
return false; return false;
} }
function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index, options) {
var hasPositions = mesh.positions.length > 0; var hasPositions = primitive.positions.length > 0;
var hasNormals = mesh.normals.length > 0; var hasNormals = primitive.normals.length > 0;
var hasUVs = mesh.uvs.length > 0; var hasUVs = primitive.uvs.length > 0;
// Vertex attributes are shared by all primitives in the mesh
var accessorIndex; var accessorIndex;
var attributes = {}; var attributes = {};
if (hasPositions) { if (hasPositions) {
accessorIndex = addVertexAttribute(gltf, mesh.positions, 3, mesh.name + '_positions'); accessorIndex = addVertexAttribute(gltf, primitive.positions, 3, mesh.name + '_' + index + '_positions');
attributes.POSITION = accessorIndex; attributes.POSITION = accessorIndex;
bufferState.positionBuffers.push(mesh.positions.toFloatBuffer()); bufferState.positionBuffers.push(primitive.positions.toFloatBuffer());
bufferState.positionAccessors.push(accessorIndex); bufferState.positionAccessors.push(accessorIndex);
} }
if (hasNormals) { if (hasNormals) {
accessorIndex = addVertexAttribute(gltf, mesh.normals, 3, mesh.name + '_normals'); accessorIndex = addVertexAttribute(gltf, primitive.normals, 3, mesh.name + '_' + index + '_normals');
attributes.NORMAL = accessorIndex; attributes.NORMAL = accessorIndex;
bufferState.normalBuffers.push(mesh.normals.toFloatBuffer()); bufferState.normalBuffers.push(primitive.normals.toFloatBuffer());
bufferState.normalAccessors.push(accessorIndex); bufferState.normalAccessors.push(accessorIndex);
} }
if (hasUVs) { if (hasUVs) {
accessorIndex = addVertexAttribute(gltf, mesh.uvs, 2, mesh.name + '_texcoords'); accessorIndex = addVertexAttribute(gltf, primitive.uvs, 2, mesh.name + '_' + index + '_texcoords');
attributes.TEXCOORD_0 = accessorIndex; attributes.TEXCOORD_0 = accessorIndex;
bufferState.uvBuffers.push(mesh.uvs.toFloatBuffer()); bufferState.uvBuffers.push(primitive.uvs.toFloatBuffer());
bufferState.uvAccessors.push(accessorIndex); bufferState.uvAccessors.push(accessorIndex);
} }
// Unload resources var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + index + '_indices');
mesh.positions = undefined; var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer();
mesh.normals = undefined; bufferState.indexBuffers.push(indexBuffer);
mesh.uvs = undefined; bufferState.indexAccessors.push(indexAccessorIndex);
// Unload resources
primitive.positions = undefined;
primitive.normals = undefined;
primitive.uvs = undefined;
primitive.indices = undefined;
var materialIndex = getMaterial(gltf, materials, primitive.material, options);
return {
attributes : attributes,
indices : indexAccessorIndex,
material : materialIndex,
mode : WebGLConstants.TRIANGLES
};
}
function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) {
var gltfPrimitives = []; var gltfPrimitives = [];
var primitives = mesh.primitives; var primitives = mesh.primitives;
var primitivesLength = primitives.length; var primitivesLength = primitives.length;
for (var i = 0; i < primitivesLength; ++i) { for (var i = 0; i < primitivesLength; ++i) {
var primitive = primitives[i]; gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options));
var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices');
var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer();
bufferState.indexBuffers.push(indexBuffer);
bufferState.indexAccessors.push(indexAccessorIndex);
primitive.indices = undefined; // Unload resources
var materialIndex = getMaterial(gltf, materials, primitive.material, options);
gltfPrimitives.push({
attributes : attributes,
indices : indexAccessorIndex,
material : materialIndex,
mode : WebGLConstants.TRIANGLES
});
} }
var gltfMesh = { var gltfMesh = {

View File

@ -36,14 +36,14 @@ function Node() {
function Mesh() { function Mesh() {
this.name = undefined; this.name = undefined;
this.primitives = []; this.primitives = [];
this.positions = new ArrayStorage(ComponentDatatype.FLOAT);
this.normals = new ArrayStorage(ComponentDatatype.FLOAT);
this.uvs = new ArrayStorage(ComponentDatatype.FLOAT);
} }
function Primitive() { function Primitive() {
this.material = undefined; this.material = undefined;
this.indices = new ArrayStorage(ComponentDatatype.UNSIGNED_INT); 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) // OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
@ -76,7 +76,7 @@ function loadObj(objPath, options) {
// All nodes seen in the obj // All nodes seen in the obj
var nodes = []; 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 vertexCache = {};
var vertexCacheLimit = 1000000; var vertexCacheLimit = 1000000;
var vertexCacheCount = 0; var vertexCacheCount = 0;
@ -112,17 +112,17 @@ function loadObj(objPath, options) {
mesh.name = getName(name); mesh.name = getName(name);
node.meshes.push(mesh); node.meshes.push(mesh);
addPrimitive(); addPrimitive();
// Clear the vertex cache for each new mesh
vertexCache = {};
vertexCacheCount = 0;
vertexCount = 0;
} }
function addPrimitive() { function addPrimitive() {
primitive = new Primitive(); primitive = new Primitive();
primitive.material = activeMaterial; primitive.material = activeMaterial;
mesh.primitives.push(primitive); mesh.primitives.push(primitive);
// Clear the vertex cache for each new primitive
vertexCache = {};
vertexCacheCount = 0;
vertexCount = 0;
} }
function useMaterial(name) { function useMaterial(name) {
@ -158,9 +158,9 @@ function loadObj(objPath, options) {
var px = positions.get(pi + 0); var px = positions.get(pi + 0);
var py = positions.get(pi + 1); var py = positions.get(pi + 1);
var pz = positions.get(pi + 2); var pz = positions.get(pi + 2);
mesh.positions.push(px); primitive.positions.push(px);
mesh.positions.push(py); primitive.positions.push(py);
mesh.positions.push(pz); primitive.positions.push(pz);
} }
// Normals // Normals
@ -169,9 +169,9 @@ function loadObj(objPath, options) {
var nx = normals.get(ni + 0); var nx = normals.get(ni + 0);
var ny = normals.get(ni + 1); var ny = normals.get(ni + 1);
var nz = normals.get(ni + 2); var nz = normals.get(ni + 2);
mesh.normals.push(nx); primitive.normals.push(nx);
mesh.normals.push(ny); primitive.normals.push(ny);
mesh.normals.push(nz); primitive.normals.push(nz);
} }
// UVs // UVs
@ -179,8 +179,8 @@ function loadObj(objPath, options) {
var ui = getOffset(u, uvs, 2); var ui = getOffset(u, uvs, 2);
var ux = uvs.get(ui + 0); var ux = uvs.get(ui + 0);
var uy = uvs.get(ui + 1); var uy = uvs.get(ui + 1);
mesh.uvs.push(ux); primitive.uvs.push(ux);
mesh.uvs.push(uy); primitive.uvs.push(uy);
} }
} }
@ -373,6 +373,17 @@ function loadObj(objPath, options) {
var isWindingCorrect = true; var isWindingCorrect = true;
var faceNormal; 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 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 no face normal, we have to assume the winding is correct.
if (normals[0].length > 0) { if (normals[0].length > 0) {
@ -502,7 +513,7 @@ function loadObj(objPath, options) {
uvs = undefined; uvs = undefined;
// Load materials and textures // Load materials and textures
return finishLoading(nodes, mtlPaths, objPath, options); return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options);
}); });
} }
@ -523,7 +534,7 @@ function getMtlPaths(mtllibLine) {
return mtlPaths; return mtlPaths;
} }
function finishLoading(nodes, mtlPaths, objPath, options) { function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) {
nodes = cleanNodes(nodes); nodes = cleanNodes(nodes);
if (nodes.length === 0) { if (nodes.length === 0) {
throw new RuntimeError(objPath + ' does not have any geometry data'); throw new RuntimeError(objPath + ' does not have any geometry data');
@ -531,7 +542,9 @@ function finishLoading(nodes, mtlPaths, objPath, options) {
var name = path.basename(objPath, path.extname(objPath)); var name = path.basename(objPath, path.extname(objPath));
return loadMtls(mtlPaths, objPath, options) return loadMtls(mtlPaths, objPath, options)
.then(function(materials) { .then(function(materials) {
assignDefaultMaterial(nodes, materials); if (materials.length > 0 && !usesMaterials) {
assignDefaultMaterial(nodes, materials, usesMaterials);
}
return { return {
nodes : nodes, nodes : nodes,
materials : materials, materials : materials,
@ -591,9 +604,6 @@ function loadMtls(mtlPaths, objPath, options) {
} }
function assignDefaultMaterial(nodes, materials) { function assignDefaultMaterial(nodes, materials) {
if (materials.length === 0) {
return;
}
var defaultMaterial = materials[0].name; var defaultMaterial = materials[0].name;
var nodesLength = nodes.length; var nodesLength = nodes.length;
for (var i = 0; i < nodesLength; ++i) { for (var i = 0; i < nodesLength; ++i) {
@ -614,10 +624,10 @@ function removeEmptyMeshes(meshes) {
return meshes.filter(function(mesh) { return meshes.filter(function(mesh) {
// Remove empty primitives // Remove empty primitives
mesh.primitives = mesh.primitives.filter(function(primitive) { 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 // Valid meshes must have at least one primitive
return (mesh.primitives.length > 0) && (mesh.positions.length > 0); return (mesh.primitives.length > 0);
}); });
} }

View File

@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Material
Ns 96.078431
Ka 0.100000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.100000
Ni 1.000000
d 1.000000
illum 2

View File

@ -0,0 +1,46 @@
# Blender v2.78 (sub 0) OBJ File: ''
# www.blender.org
mtllib box-mixed-attributes.mtl
o Cube
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
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
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3 4 8 7
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 7//5 5//5 1//5
f 8/19 4/6 2/15 6/20

View File

@ -133,42 +133,42 @@ describe('createGltf', function() {
}); });
it('runs without normals', 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, options); var gltf = createGltf(boxObjData, options);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; var attributes = gltf.meshes[0].primitives[0].attributes;
expect(attributes.POSITION).toBeDefined(); expect(attributes.POSITION).toBeDefined();
expect(attributes.NORMAL).toBeUndefined(); expect(attributes.NORMAL).toBeUndefined();
expect(attributes.TEXCOORD_0).toBeDefined(); expect(attributes.TEXCOORD_0).toBeDefined();
}); });
it('runs without uvs', 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, options); var gltf = createGltf(boxObjData, options);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; var attributes = gltf.meshes[0].primitives[0].attributes;
expect(attributes.POSITION).toBeDefined(); expect(attributes.POSITION).toBeDefined();
expect(attributes.NORMAL).toBeDefined(); expect(attributes.NORMAL).toBeDefined();
expect(attributes.TEXCOORD_0).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeUndefined();
}); });
it('runs without uvs and normals', function() { it('runs without uvs and normals', function() {
boxObjData.nodes[0].meshes[0].normals.length = 0; boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0;
boxObjData.nodes[0].meshes[0].uvs.length = 0; boxObjData.nodes[0].meshes[0].primitives[0].uvs.length = 0;
var gltf = createGltf(boxObjData, options); var gltf = createGltf(boxObjData, options);
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes; var attributes = gltf.meshes[0].primitives[0].attributes;
expect(attributes.POSITION).toBeDefined(); expect(attributes.POSITION).toBeDefined();
expect(attributes.NORMAL).toBeUndefined(); expect(attributes.NORMAL).toBeUndefined();
expect(attributes.TEXCOORD_0).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeUndefined();
}); });
function expandObjData(objData, duplicatesLength) { function expandObjData(objData, duplicatesLength) {
var mesh = objData.nodes[0].meshes[0]; var primitive = objData.nodes[0].meshes[0].primitives[0];
var indices = mesh.primitives[0].indices; var indices = primitive.indices;
var positions = mesh.positions; var positions = primitive.positions;
var normals = mesh.normals; var normals = primitive.normals;
var uvs = mesh.uvs; var uvs = primitive.uvs;
var indicesLength = indices.length; var indicesLength = indices.length;
var vertexCount = positions.length / 3; var vertexCount = positions.length / 3;
@ -192,12 +192,12 @@ describe('createGltf', function() {
it('detects need to use uint32 indices', function() { it('detects need to use uint32 indices', function() {
expandObjData(boxObjData, 2731); // Right above 65536 limit expandObjData(boxObjData, 2731); // Right above 65536 limit
var mesh = boxObjData.nodes[0].meshes[0]; var primitive = boxObjData.nodes[0].meshes[0].primitives[0];
var indicesLength = mesh.primitives[0].indices.length; var indicesLength = primitive.indices.length;
var vertexCount = mesh.positions.length / 3; var vertexCount = primitive.positions.length / 3;
var gltf = createGltf(boxObjData, options); var gltf = createGltf(boxObjData, options);
var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0]; primitive = gltf.meshes[0].primitives[0];
var indicesAccessor = gltf.accessors[primitive.indices]; var indicesAccessor = gltf.accessors[primitive.indices];
expect(indicesAccessor.count).toBe(indicesLength); expect(indicesAccessor.count).toBe(indicesLength);
expect(indicesAccessor.max[0]).toBe(vertexCount - 1); expect(indicesAccessor.max[0]).toBe(vertexCount - 1);

View File

@ -38,6 +38,7 @@ var objWindowsPaths = 'specs/data/box-windows-paths/box-windows-paths.obj';
var objInvalidContentsPath = 'specs/data/box/box.mtl'; var objInvalidContentsPath = 'specs/data/box/box.mtl';
var objConcavePath = 'specs/data/concave/concave.obj'; var objConcavePath = 'specs/data/concave/concave.obj';
var objUnnormalizedPath = 'specs/data/box-unnormalized/box-unnormalized.obj'; var objUnnormalizedPath = 'specs/data/box-unnormalized/box-unnormalized.obj';
var objMixedAttributesPath = 'specs/data/box-mixed-attributes/box-mixed-attributes.obj';
var objInvalidPath = 'invalid.obj'; var objInvalidPath = 'invalid.obj';
function getMeshes(data) { function getMeshes(data) {
@ -94,9 +95,9 @@ describe('loadObj', function() {
expect(node.name).toBe('Cube'); expect(node.name).toBe('Cube');
expect(mesh.name).toBe('Cube-Mesh'); expect(mesh.name).toBe('Cube-Mesh');
expect(mesh.positions.length / 3).toBe(24); expect(primitive.positions.length / 3).toBe(24);
expect(mesh.normals.length / 3).toBe(24); expect(primitive.normals.length / 3).toBe(24);
expect(mesh.uvs.length / 2).toBe(24); expect(primitive.uvs.length / 2).toBe(24);
expect(primitive.indices.length).toBe(36); expect(primitive.indices.length).toBe(36);
expect(primitive.material).toBe('Material'); expect(primitive.material).toBe('Material');
}), done).toResolve(); }), done).toResolve();
@ -105,10 +106,10 @@ describe('loadObj', function() {
it('loads obj with normals', function(done) { it('loads obj with normals', function(done) {
expect(loadObj(objNormalsPath, options) expect(loadObj(objNormalsPath, options)
.then(function(data) { .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(mesh.normals.length / 3).toBe(24); expect(primitive.normals.length / 3).toBe(24);
expect(mesh.uvs.length / 2).toBe(0); expect(primitive.uvs.length / 2).toBe(0);
}), done).toResolve(); }), done).toResolve();
}); });
@ -116,8 +117,8 @@ describe('loadObj', function() {
expect(loadObj(objUnnormalizedPath, options) expect(loadObj(objUnnormalizedPath, options)
.then(function(data) { .then(function(data) {
var scratchNormal = new Cesium.Cartesian3(); var scratchNormal = new Cesium.Cartesian3();
var mesh = getMeshes(data)[0]; var primitive = getPrimitives(data)[0];
var normals = mesh.normals; var normals = primitive.normals;
var normalsLength = normals.length / 3; var normalsLength = normals.length / 3;
for (var i = 0; i < normalsLength; ++i) { for (var i = 0; i < normalsLength; ++i) {
var normalX = normals.get(i * 3); var normalX = normals.get(i * 3);
@ -132,10 +133,10 @@ describe('loadObj', function() {
it('loads obj with uvs', function(done) { it('loads obj with uvs', function(done) {
expect(loadObj(objUvsPath, options) expect(loadObj(objUvsPath, options)
.then(function(data) { .then(function(data) {
var mesh = getMeshes(data)[0]; var primitive = getPrimitives(data)[0];
expect(mesh.positions.length / 3).toBe(20); expect(primitive.positions.length / 3).toBe(20);
expect(mesh.normals.length / 3).toBe(0); expect(primitive.normals.length / 3).toBe(0);
expect(mesh.uvs.length / 2).toBe(20); expect(primitive.uvs.length / 2).toBe(20);
}), done).toResolve(); }), done).toResolve();
}); });
@ -145,8 +146,8 @@ describe('loadObj', function() {
loadObj(objNegativeIndicesPath, options) loadObj(objNegativeIndicesPath, options)
]) ])
.then(function(results) { .then(function(results) {
var positionsReference = getMeshes(results[0])[0].positions.toFloatBuffer(); var positionsReference = getPrimitives(results[0])[0].positions.toFloatBuffer();
var positions = getMeshes(results[1])[0].positions.toFloatBuffer(); var positions = getPrimitives(results[1])[0].positions.toFloatBuffer();
expect(positions).toEqual(positionsReference); expect(positions).toEqual(positionsReference);
}), done).toResolve(); }), done).toResolve();
}); });
@ -154,9 +155,8 @@ describe('loadObj', function() {
it('loads obj with triangle faces', function(done) { it('loads obj with triangle faces', function(done) {
expect(loadObj(objTrianglesPath, options) expect(loadObj(objTrianglesPath, options)
.then(function(data) { .then(function(data) {
var mesh = getMeshes(data)[0];
var primitive = getPrimitives(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); expect(primitive.indices.length).toBe(36);
}), done).toResolve(); }), done).toResolve();
}); });
@ -256,9 +256,8 @@ describe('loadObj', function() {
it('loads obj with concave face containing 5 vertices', function(done) { it('loads obj with concave face containing 5 vertices', function(done) {
expect(loadObj(objConcavePath, options) expect(loadObj(objConcavePath, options)
.then(function(data) { .then(function(data) {
var mesh = getMeshes(data)[0];
var primitive = getPrimitives(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); expect(primitive.indices.length).toBe(48);
}), done).toResolve(); }), done).toResolve();
}); });
@ -488,6 +487,14 @@ describe('loadObj', function() {
}), done).toResolve(); }), done).toResolve();
}); });
it('discards faces that don\'t use the same attributes as other faces in the primitive', function(done) {
expect(loadObj(objMixedAttributesPath, options)
.then(function(data) {
var primitive = getPrimitives(data)[0];
expect(primitive.indices.length).toBe(18); // 3 faces removed
}), done).toResolve();
});
it('throws when file has invalid contents', function(done) { it('throws when file has invalid contents', function(done) {
expect(loadObj(objInvalidContentsPath, options), done).toRejectWith(RuntimeError); expect(loadObj(objInvalidContentsPath, options), done).toRejectWith(RuntimeError);
}); });