Adding position, normals, and uvs to primitive level rather than mesh level

This commit is contained in:
Sean Lilley 2018-08-30 15:24:34 -04:00
parent 26db16d892
commit 574d06db2b
4 changed files with 90 additions and 89 deletions

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) {
var primitives = meshes[j].primitives;
var primitivesLength = primitives.length;
for (var k = 0; k < primitivesLength; ++k) {
// Reserve the 65535 index for primitive restart // Reserve the 65535 index for primitive restart
var vertexCount = meshes[j].positions.length / 3; var vertexCount = primitives[k].positions.length / 3;
if (vertexCount > 65534) { if (vertexCount > 65534) {
return true; 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;
mesh.normals = undefined;
mesh.uvs = undefined;
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(); var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer();
bufferState.indexBuffers.push(indexBuffer); bufferState.indexBuffers.push(indexBuffer);
bufferState.indexAccessors.push(indexAccessorIndex); bufferState.indexAccessors.push(indexAccessorIndex);
primitive.indices = undefined; // Unload resources // Unload resources
primitive.positions = undefined;
primitive.normals = undefined;
primitive.uvs = undefined;
primitive.indices = undefined;
var materialIndex = getMaterial(gltf, materials, primitive.material, options); var materialIndex = getMaterial(gltf, materials, primitive.material, options);
gltfPrimitives.push({ return {
attributes : attributes, attributes : attributes,
indices : indexAccessorIndex, indices : indexAccessorIndex,
material : materialIndex, material : materialIndex,
mode : WebGLConstants.TRIANGLES 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) {
gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options));
} }
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;
@ -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);
} }
} }
@ -614,10 +614,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

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

@ -94,9 +94,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 +105,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 +116,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 +132,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 +145,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 +154,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 +255,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();
}); });