gltf-pipeline
Version:
Content pipeline tools for optimizing glTF assets.
263 lines (224 loc) • 8.16 kB
JavaScript
const Cesium = require('cesium');
const ForEach = require('./ForEach');
const hasExtension = require('./hasExtension');
const defined = Cesium.defined;
module.exports = removeUnusedElements;
/**
* 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.
*
* @private
*/
function removeUnusedElements(gltf) {
removeUnusedElementsByType(gltf, 'accessor');
removeUnusedElementsByType(gltf, 'bufferView');
removeUnusedElementsByType(gltf, 'buffer');
return gltf;
}
const TypeToGltfElementName = {
accessor: 'accessors',
buffer: 'buffers',
bufferView: 'bufferViews'
};
function removeUnusedElementsByType(gltf, type) {
const name = TypeToGltfElementName[type];
const arrayOfObjects = gltf[name];
if (defined(arrayOfObjects)) {
let removed = 0;
const usedIds = getListOfElementsIdsInUse[type](gltf);
const length = arrayOfObjects.length;
for (let i = 0; i < length; ++i) {
if (!usedIds[i]) {
Remove[type](gltf, i - removed);
removed++;
}
}
}
}
/**
* Contains functions for removing elements from a glTF hierarchy.
* Since top-level glTF elements are arrays, when something is removed, referring
* indices need to be updated.
* @constructor
*
* @private
*/
function Remove() {}
Remove.accessor = function(gltf, accessorId) {
const accessors = gltf.accessors;
accessors.splice(accessorId, 1);
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
// Update accessor ids for the primitives.
ForEach.meshPrimitiveAttribute(primitive, function(attributeAccessorId, semantic) {
if (attributeAccessorId > accessorId) {
primitive.attributes[semantic]--;
}
});
// Update accessor ids for the targets.
ForEach.meshPrimitiveTarget(primitive, function(target) {
ForEach.meshPrimitiveTargetAttribute(target, function(attributeAccessorId, semantic) {
if (attributeAccessorId > accessorId) {
target[semantic]--;
}
});
});
const indices = primitive.indices;
if (defined(indices) && indices > accessorId) {
primitive.indices--;
}
});
});
ForEach.skin(gltf, function(skin) {
if (defined(skin.inverseBindMatrices) && skin.inverseBindMatrices > accessorId) {
skin.inverseBindMatrices--;
}
});
ForEach.animation(gltf, function(animation) {
ForEach.animationSampler(animation, function(sampler) {
if (defined(sampler.input) && sampler.input > accessorId) {
sampler.input--;
}
if (defined(sampler.output) && sampler.output > accessorId) {
sampler.output--;
}
});
});
};
Remove.buffer = function(gltf, bufferId) {
const buffers = gltf.buffers;
buffers.splice(bufferId, 1);
ForEach.bufferView(gltf, function(bufferView) {
if (defined(bufferView.buffer) && bufferView.buffer > bufferId) {
bufferView.buffer--;
}
});
};
Remove.bufferView = function(gltf, bufferViewId) {
const bufferViews = gltf.bufferViews;
bufferViews.splice(bufferViewId, 1);
ForEach.accessor(gltf, function(accessor) {
if (defined(accessor.bufferView) && accessor.bufferView > bufferViewId) {
accessor.bufferView--;
}
});
ForEach.shader(gltf, function(shader) {
if (defined(shader.bufferView) && shader.bufferView > bufferViewId) {
shader.bufferView--;
}
});
ForEach.image(gltf, function(image) {
if (defined(image.bufferView) && image.bufferView > bufferViewId) {
image.bufferView--;
}
ForEach.compressedImage(image, function(compressedImage) {
const compressedImageBufferView = compressedImage.bufferView;
if (defined(compressedImageBufferView) && compressedImageBufferView > bufferViewId) {
compressedImage.bufferView--;
}
});
});
if (hasExtension(gltf, 'KHR_draco_mesh_compression')) {
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) {
primitive.extensions.KHR_draco_mesh_compression.bufferView--;
}
}
});
});
}
};
/**
* Contains functions for getting a list of element ids in use by the glTF asset.
* @constructor
*
* @private
*/
function getListOfElementsIdsInUse() {}
getListOfElementsIdsInUse.accessor = function(gltf) {
// Calculate accessor's that are currently in use.
const usedAccessorIds = {};
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
ForEach.meshPrimitiveAttribute(primitive, function(accessorId) {
usedAccessorIds[accessorId] = true;
});
ForEach.meshPrimitiveTarget(primitive, function(target) {
ForEach.meshPrimitiveTargetAttribute(target, function(accessorId) {
usedAccessorIds[accessorId] = true;
});
});
const indices = primitive.indices;
if (defined(indices)) {
usedAccessorIds[indices] = true;
}
});
});
ForEach.skin(gltf, function(skin) {
if (defined(skin.inverseBindMatrices)) {
usedAccessorIds[skin.inverseBindMatrices] = true;
}
});
ForEach.animation(gltf, function(animation) {
ForEach.animationSampler(animation, function(sampler) {
if (defined(sampler.input)) {
usedAccessorIds[sampler.input] = true;
}
if (defined(sampler.output)) {
usedAccessorIds[sampler.output] = true;
}
});
});
return usedAccessorIds;
};
getListOfElementsIdsInUse.buffer = function(gltf) {
// Calculate buffer's that are currently in use.
const usedBufferIds = {};
ForEach.bufferView(gltf, function(bufferView) {
if (defined(bufferView.buffer)) {
usedBufferIds[bufferView.buffer] = true;
}
});
return usedBufferIds;
};
getListOfElementsIdsInUse.bufferView = function(gltf) {
// Calculate bufferView's that are currently in use.
const usedBufferViewIds = {};
ForEach.accessor(gltf, function(accessor) {
if (defined(accessor.bufferView)) {
usedBufferViewIds[accessor.bufferView] = true;
}
});
ForEach.shader(gltf, function(shader) {
if (defined(shader.bufferView)) {
usedBufferViewIds[shader.bufferView] = true;
}
});
ForEach.image(gltf, function(image) {
if (defined(image.bufferView)) {
usedBufferViewIds[image.bufferView] = true;
}
ForEach.compressedImage(image, function(compressedImage) {
if (defined(compressedImage.bufferView)) {
usedBufferViewIds[compressedImage.bufferView] = true;
}
});
});
if (hasExtension(gltf, 'KHR_draco_mesh_compression')) {
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
if (defined(primitive.extensions) &&
defined(primitive.extensions.KHR_draco_mesh_compression)) {
usedBufferViewIds[primitive.extensions.KHR_draco_mesh_compression.bufferView] = true;
}
});
});
}
return usedBufferViewIds;
};
;