diff --git a/lib/createGltf.js b/lib/createGltf.js index 5b6c044..b5f3f89 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -131,10 +131,8 @@ function addBufferView(gltf, buffers, accessors, byteStride, target) { } function addBuffers(gltf, bufferState, name) { - // Positions and normals share the same byte stride so they can share the same bufferView - var positionsAndNormalsAccessors = bufferState.positionAccessors.concat(bufferState.normalAccessors); - var positionsAndNormalsBuffers = bufferState.positionBuffers.concat(bufferState.normalBuffers); - addBufferView(gltf, positionsAndNormalsBuffers, positionsAndNormalsAccessors, 12, WebGLConstants.ARRAY_BUFFER); + addBufferView(gltf, bufferState.positionBuffers, bufferState.positionAccessors, 12, WebGLConstants.ARRAY_BUFFER); + addBufferView(gltf, bufferState.normalBuffers, bufferState.normalAccessors, 12, WebGLConstants.ARRAY_BUFFER); addBufferView(gltf, bufferState.uvBuffers, bufferState.uvAccessors, 8, WebGLConstants.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 meshesLength = meshes.length; for (var j = 0; j < meshesLength; ++j) { - // Reserve the 65535 index for primitive restart - var vertexCount = meshes[j].positions.length / 3; - if (vertexCount > 65534) { - return true; + 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 = primitives[k].positions.length / 3; + if (vertexCount > 65534) { + return true; + } } } } return false; } -function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { - var hasPositions = mesh.positions.length > 0; - var hasNormals = mesh.normals.length > 0; - var hasUVs = mesh.uvs.length > 0; +function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index, options) { + var hasPositions = primitive.positions.length > 0; + var hasNormals = primitive.normals.length > 0; + var hasUVs = primitive.uvs.length > 0; - // Vertex attributes are shared by all primitives in the mesh var accessorIndex; var attributes = {}; if (hasPositions) { - accessorIndex = addVertexAttribute(gltf, mesh.positions, 3, mesh.name + '_positions'); + accessorIndex = addVertexAttribute(gltf, primitive.positions, 3, mesh.name + '_' + index + '_positions'); attributes.POSITION = accessorIndex; - bufferState.positionBuffers.push(mesh.positions.toFloatBuffer()); + bufferState.positionBuffers.push(primitive.positions.toFloatBuffer()); bufferState.positionAccessors.push(accessorIndex); } if (hasNormals) { - accessorIndex = addVertexAttribute(gltf, mesh.normals, 3, mesh.name + '_normals'); + accessorIndex = addVertexAttribute(gltf, primitive.normals, 3, mesh.name + '_' + index + '_normals'); attributes.NORMAL = accessorIndex; - bufferState.normalBuffers.push(mesh.normals.toFloatBuffer()); + bufferState.normalBuffers.push(primitive.normals.toFloatBuffer()); bufferState.normalAccessors.push(accessorIndex); } 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; - bufferState.uvBuffers.push(mesh.uvs.toFloatBuffer()); + bufferState.uvBuffers.push(primitive.uvs.toFloatBuffer()); bufferState.uvAccessors.push(accessorIndex); } - // Unload resources - mesh.positions = undefined; - mesh.normals = undefined; - mesh.uvs = undefined; + var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + index + '_indices'); + var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer(); + bufferState.indexBuffers.push(indexBuffer); + 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 primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - var primitive = primitives[i]; - 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 - }); + gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options)); } var gltfMesh = { diff --git a/lib/loadObj.js b/lib/loadObj.js index bce53ea..290a087 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -36,14 +36,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) @@ -76,7 +76,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; @@ -158,9 +158,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 @@ -169,9 +169,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 @@ -179,8 +179,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); } } @@ -614,10 +614,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); }); } diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 4114c0d..1c4818a 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -133,42 +133,42 @@ 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, 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.NORMAL).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeDefined(); }); 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 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.NORMAL).toBeDefined(); expect(attributes.TEXCOORD_0).toBeUndefined(); }); 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, 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.NORMAL).toBeUndefined(); expect(attributes.TEXCOORD_0).toBeUndefined(); }); 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; @@ -192,12 +192,12 @@ 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, options); - var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0]; + primitive = gltf.meshes[0].primitives[0]; var indicesAccessor = gltf.accessors[primitive.indices]; expect(indicesAccessor.count).toBe(indicesLength); expect(indicesAccessor.max[0]).toBe(vertexCount - 1); diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index bd266ac..dbc6fee 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -94,9 +94,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(); @@ -105,10 +105,10 @@ describe('loadObj', function() { it('loads obj with normals', function(done) { expect(loadObj(objNormalsPath, options) .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(); }); @@ -116,8 +116,8 @@ describe('loadObj', function() { expect(loadObj(objUnnormalizedPath, options) .then(function(data) { var scratchNormal = new Cesium.Cartesian3(); - var mesh = getMeshes(data)[0]; - var normals = mesh.normals; + var primitive = getPrimitives(data)[0]; + var normals = primitive.normals; var normalsLength = normals.length / 3; for (var i = 0; i < normalsLength; ++i) { var normalX = normals.get(i * 3); @@ -132,10 +132,10 @@ describe('loadObj', function() { it('loads obj with uvs', function(done) { expect(loadObj(objUvsPath, options) .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(); }); @@ -145,8 +145,8 @@ describe('loadObj', function() { loadObj(objNegativeIndicesPath, options) ]) .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(); }); @@ -154,9 +154,8 @@ describe('loadObj', function() { it('loads obj with triangle faces', function(done) { expect(loadObj(objTrianglesPath, options) .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(); }); @@ -256,9 +255,8 @@ describe('loadObj', function() { it('loads obj with concave face containing 5 vertices', function(done) { expect(loadObj(objConcavePath, options) .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(); });