diff --git a/lib/ForEach.js b/lib/ForEach.js index 93a64482..c2953b3a 100644 --- a/lib/ForEach.js +++ b/lib/ForEach.js @@ -348,6 +348,21 @@ ForEach.skin = function(gltf, handler) { return ForEach.topLevel(gltf, 'skins', handler); }; +ForEach.skinJoint = function(skin, handler) { + const joints = skin.joints; + if (defined(joints)) { + const jointsLength = joints.length; + for (let i = 0; i < jointsLength; i++) { + const joint = joints[i]; + const value = handler(joint); + + if (defined(value)) { + return value; + } + } + } +}; + ForEach.techniqueAttribute = function(technique, handler) { const attributes = technique.attributes; for (const attributeName in attributes) { diff --git a/lib/processGltf.js b/lib/processGltf.js index 10d316fa..bed45c07 100644 --- a/lib/processGltf.js +++ b/lib/processGltf.js @@ -84,7 +84,9 @@ function getStages(options) { stages.push(compressDracoMeshes); } if (!options.keepUnusedElements) { - stages.push((gltf, options) => removeUnusedElements(gltf)); + stages.push(function(gltf, options) { + removeUnusedElements(gltf); + }); } return stages; } diff --git a/lib/removeUnusedElements.js b/lib/removeUnusedElements.js index e764b3f0..b3c991ce 100644 --- a/lib/removeUnusedElements.js +++ b/lib/removeUnusedElements.js @@ -3,27 +3,25 @@ const Cesium = require('cesium'); const ForEach = require('./ForEach'); const hasExtension = require('./hasExtension'); +const defaultValue = Cesium.defaultValue; const defined = Cesium.defined; module.exports = removeUnusedElements; +const allElementTypes = ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer']; + /** * Removes unused elements from gltf. - * This function currently only works for accessors, buffers, and bufferViews. * * @param {Object} gltf A javascript object containing a glTF asset. - * @param {Array} elementTypes Element types to be removed. Needs to be a subset of - * ['node', 'mesh', 'material', 'accessor', 'bufferView', 'buffer'], other items - * will be ignored. + * @param {String[]} [elementTypes=['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer']] Element types to be removed. Needs to be a subset of ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'], other items will be ignored. * * @private */ function removeUnusedElements(gltf, elementTypes) { - if(elementTypes === undefined) { - elementTypes = ['node', 'mesh', 'material', 'accessor', 'bufferView', 'buffer']; - } - ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'].forEach(function(type) { - if (elementTypes.includes(type)) { + elementTypes = defaultValue(elementTypes, allElementTypes); + allElementTypes.forEach(function(type) { + if (elementTypes.indexOf(type) > -1) { removeUnusedElementsByType(gltf, type); } }); @@ -156,8 +154,8 @@ Remove.bufferView = function(gltf, bufferViewId) { }); if (hasExtension(gltf, 'KHR_draco_mesh_compression')) { - ForEach.mesh(gltf, function (mesh) { - ForEach.meshPrimitive(mesh, function (primitive) { + ForEach.mesh(gltf, function(mesh) { + ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.extensions) && defined(primitive.extensions.KHR_draco_mesh_compression)) { if (primitive.extensions.KHR_draco_mesh_compression.bufferView > bufferViewId) { @@ -173,13 +171,13 @@ Remove.mesh = function(gltf, meshId) { const meshes = gltf.meshes; meshes.splice(meshId, 1); - ForEach.node(gltf, function(n) { - if (defined(n.mesh)) { - if (n.mesh > meshId) { - --n.mesh; - } else if (n.mesh === meshId) { + ForEach.node(gltf, function(node) { + if (defined(node.mesh)) { + if (node.mesh > meshId) { + node.mesh--; + } else if (node.mesh === meshId) { // Remove reference to deleted mesh - delete n.mesh; + delete node.mesh; } } }); @@ -190,33 +188,35 @@ Remove.node = function(gltf, nodeId) { nodes.splice(nodeId, 1); // Shift all node references - ForEach.skin(gltf, function(s) { - if (s.skeleton) { - s.skeleton -= s.skeleton > nodeId ? 1 : 0; + ForEach.skin(gltf, function(skin) { + if (defined(skin.skeleton) && skin.skeleton > nodeId) { + skin.skeleton--; } - s.joints -= s.joints.map(function(x) { + skin.joints = skin.joints.map(function(x) { return x > nodeId ? x - 1 : x; }); }); ForEach.animation(gltf, function(animation) { - if(animation.target && animation.target.node) { - animation.target.node -= animation.target.node > nodeId ? 1 : 0; - } + ForEach.animationChannel(animation, function(channel) { + if (defined(channel.target) && defined(channel.target.node) && (channel.target.node > nodeId)) { + channel.target.node--; + } + }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueUniform(technique, function(uniform) { - if (defined(uniform.node)) { - uniform.node -= uniform.node > nodeId ? 1 : 0; + if (defined(uniform.node) && uniform.node > nodeId) { + uniform.node--; } }); }); - ForEach.node(gltf, function(n) { - if (!n.children) { + ForEach.node(gltf, function(node) { + if (!defined(node.children)) { return; } - n.children = n.children + node.children = node.children .filter(function(x) { return x !== nodeId; // Remove }) @@ -224,8 +224,8 @@ Remove.node = function(gltf, nodeId) { return x > nodeId ? x - 1 : x; // Shift indices }); }); - ForEach.scene(gltf, function(s, i) { - s.nodes = s.nodes + ForEach.scene(gltf, function(scene) { + scene.nodes = scene.nodes .filter(function(x) { return x !== nodeId; // Remove }) @@ -240,13 +240,13 @@ Remove.material = function(gltf, materialId) { materials.splice(materialId, 1); // Shift other material ids - ForEach.meshPrimitive(gltf, function(p) { - if (p.material > materialId) { - --p.material; - } + ForEach.mesh(gltf, function(mesh) { + ForEach.meshPrimitive(mesh, function(primitive) { + if (defined(primitive.material) && primitive.material > materialId) { + primitive.material--; + } + }); }); - - return gltf; }; /** @@ -354,29 +354,20 @@ getListOfElementsIdsInUse.bufferView = function(gltf) { getListOfElementsIdsInUse.mesh = function(gltf) { const usedMeshIds = {}; - ForEach.mesh(gltf, function(mesh, i) { - if (!defined(mesh.primitives) || mesh.primitives.length === 0) { - usedMeshIds[i] = false; - } - }); - - ForEach.node(gltf, function(node, i) { - if (!defined(node.mesh)) { - return; - } - // Mesh marked as empty in previous step? - const meshIsEmpty = defined(usedMeshIds[node.mesh]); - - if (!meshIsEmpty) { - usedMeshIds[node.mesh] = true; + ForEach.node(gltf, function(node) { + if (defined(node.mesh && defined(gltf.meshes))) { + const mesh = gltf.meshes[node.mesh]; + if (defined(mesh) && defined(mesh.primitives) && (mesh.primitives.length > 0)) { + usedMeshIds[node.mesh] = true; + } } }); return usedMeshIds; }; -/* Check if node is empty. It is considered empty if neither referencing - * mesh, camera, extensions and has no children */ +// Check if node is empty. It is considered empty if neither referencing +// mesh, camera, extensions and has no children function nodeIsEmpty(gltf, node) { if (defined(node.mesh) || defined(node.camera) || defined(node.skin) || defined(node.weights) || defined(node.extras) @@ -398,19 +389,21 @@ getListOfElementsIdsInUse.node = function(gltf) { usedNodeIds[nodeId] = true; } }); - ForEach.skin(gltf, function(s) { - if (s.skeleton) { - usedNodeIds[s.skeleton] = true; + ForEach.skin(gltf, function(skin) { + if (defined(skin.skeleton)) { + usedNodeIds[skin.skeleton] = true; } - ForEach.joint(s, function(j) { - usedNodeIds[j] = true; + ForEach.skinJoint(skin, function(joint) { + usedNodeIds[joint] = true; }); }); ForEach.animation(gltf, function(animation) { - if(animation.target && animation.target.node) { - usedNodeIds[animation.target.node] = true; - } + ForEach.animationChannel(animation, function(channel) { + if (defined(channel.target) && defined(channel.target.node)) { + usedNodeIds[channel.target.node] = true; + } + }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueUniform(technique, function(uniform) { @@ -420,20 +413,6 @@ getListOfElementsIdsInUse.node = function(gltf) { }); }); - ForEach.animation(gltf, function(anim, animId) { - if (!defined(anim.channels)) { - return; - } - - anim.channels.forEach(function(c) { - if (defined(c.target) && defined(c.target.node)) { - /* Keep all nodes that are being targeted - * by an animation */ - usedNodeIds[c.target.node] = true; - } - }); - }); - return usedNodeIds; }; @@ -441,8 +420,10 @@ getListOfElementsIdsInUse.material = function(gltf) { const usedMaterialIds = {}; ForEach.mesh(gltf, function(mesh) { - ForEach.meshPrimitive(mesh, function(p) { - usedMaterialIds[p.material] = true; + ForEach.meshPrimitive(mesh, function(primitive) { + if (defined(primitive.material)) { + usedMaterialIds[primitive.material] = true; + } }); }); diff --git a/specs/lib/removeUnusedElementsSpec.js b/specs/lib/removeUnusedElementsSpec.js index a84a0c26..4111e4eb 100644 --- a/specs/lib/removeUnusedElementsSpec.js +++ b/specs/lib/removeUnusedElementsSpec.js @@ -379,13 +379,13 @@ describe('removeUnusedElements', () => { expect(Object.keys(gltf)).toContain(k); expect(gltf[k].length).toBe(remaining[k].length); - /* Check that at least the remaining elements are present */ - ForEach.topLevel(gltf, k, (element, index) => { + // Check that at least the remaining elements are present + ForEach.topLevel(gltf, k, (element) => { expect(remaining[k]).toContain(element.name); }); - /* Check that all the elements should actually remain */ - remaining[k].forEach((name, index) => { + // Check that all the elements should actually remain + remaining[k].forEach((name) => { expect(gltf[k].map(x => x.name)).toContain(name); }); });