You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
429 lines
14 KiB
JavaScript
429 lines
14 KiB
JavaScript
import ForEach from './ForEach.js'
|
|
import hasExtension from './hasExtension.js'
|
|
import defaultValue from '../../Core/defaultValue.js'
|
|
import defined from '../../Core/defined.js'
|
|
|
|
var allElementTypes = ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'];
|
|
|
|
/**
|
|
* Removes unused elements from gltf.
|
|
*
|
|
* @param {Object} gltf A javascript object containing a glTF asset.
|
|
* @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) {
|
|
elementTypes = defaultValue(elementTypes, allElementTypes);
|
|
allElementTypes.forEach(function(type) {
|
|
if (elementTypes.indexOf(type) > -1) {
|
|
removeUnusedElementsByType(gltf, type);
|
|
}
|
|
});
|
|
return gltf;
|
|
}
|
|
|
|
var TypeToGltfElementName = {
|
|
accessor: 'accessors',
|
|
buffer: 'buffers',
|
|
bufferView: 'bufferViews',
|
|
node: 'nodes',
|
|
material: 'materials',
|
|
mesh: 'meshes'
|
|
};
|
|
|
|
function removeUnusedElementsByType(gltf, type) {
|
|
var name = TypeToGltfElementName[type];
|
|
var arrayOfObjects = gltf[name];
|
|
|
|
if (defined(arrayOfObjects)) {
|
|
var removed = 0;
|
|
var usedIds = getListOfElementsIdsInUse[type](gltf);
|
|
var length = arrayOfObjects.length;
|
|
|
|
for (var 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) {
|
|
var 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]--;
|
|
}
|
|
});
|
|
});
|
|
var 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) {
|
|
var 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) {
|
|
var 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) {
|
|
var 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--;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
Remove.mesh = function(gltf, meshId) {
|
|
var meshes = gltf.meshes;
|
|
meshes.splice(meshId, 1);
|
|
|
|
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 node.mesh;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
Remove.node = function(gltf, nodeId) {
|
|
var nodes = gltf.nodes;
|
|
nodes.splice(nodeId, 1);
|
|
|
|
// Shift all node references
|
|
ForEach.skin(gltf, function(skin) {
|
|
if (defined(skin.skeleton) && skin.skeleton > nodeId) {
|
|
skin.skeleton--;
|
|
}
|
|
|
|
skin.joints = skin.joints.map(function(x) {
|
|
return x > nodeId ? x - 1 : x;
|
|
});
|
|
});
|
|
ForEach.animation(gltf, function(animation) {
|
|
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 > nodeId) {
|
|
uniform.node--;
|
|
}
|
|
});
|
|
});
|
|
ForEach.node(gltf, function(node) {
|
|
if (!defined(node.children)) {
|
|
return;
|
|
}
|
|
|
|
node.children = node.children
|
|
.filter(function(x) {
|
|
return x !== nodeId; // Remove
|
|
})
|
|
.map(function(x) {
|
|
return x > nodeId ? x - 1 : x; // Shift indices
|
|
});
|
|
});
|
|
ForEach.scene(gltf, function(scene) {
|
|
scene.nodes = scene.nodes
|
|
.filter(function(x) {
|
|
return x !== nodeId; // Remove
|
|
})
|
|
.map(function(x) {
|
|
return x > nodeId ? x - 1 : x; // Shift indices
|
|
});
|
|
});
|
|
};
|
|
|
|
Remove.material = function(gltf, materialId) {
|
|
var materials = gltf.materials;
|
|
materials.splice(materialId, 1);
|
|
|
|
// Shift other material ids
|
|
ForEach.mesh(gltf, function(mesh) {
|
|
ForEach.meshPrimitive(mesh, function(primitive) {
|
|
if (defined(primitive.material) && primitive.material > materialId) {
|
|
primitive.material--;
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 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.
|
|
var 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;
|
|
});
|
|
});
|
|
var 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.
|
|
var 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.
|
|
var 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;
|
|
};
|
|
|
|
getListOfElementsIdsInUse.mesh = function(gltf) {
|
|
var usedMeshIds = {};
|
|
ForEach.node(gltf, function(node) {
|
|
if (defined(node.mesh && defined(gltf.meshes))) {
|
|
var 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
|
|
function nodeIsEmpty(gltf, node) {
|
|
if (defined(node.mesh) || defined(node.camera) || defined(node.skin)
|
|
|| defined(node.weights) || defined(node.extras)
|
|
|| (defined(node.extensions) && node.extensions.length !== 0)) {
|
|
return false;
|
|
}
|
|
|
|
// Empty if no children or children are all empty nodes
|
|
return !defined(node.children)
|
|
|| node.children.filter(function(n) {
|
|
return !nodeIsEmpty(gltf, gltf.nodes[n]);
|
|
}).length === 0;
|
|
}
|
|
|
|
getListOfElementsIdsInUse.node = function(gltf) {
|
|
var usedNodeIds = {};
|
|
ForEach.node(gltf, function(node, nodeId) {
|
|
if (!nodeIsEmpty(gltf, node)) {
|
|
usedNodeIds[nodeId] = true;
|
|
}
|
|
});
|
|
ForEach.skin(gltf, function(skin) {
|
|
if (defined(skin.skeleton)) {
|
|
usedNodeIds[skin.skeleton] = true;
|
|
}
|
|
|
|
ForEach.skinJoint(skin, function(joint) {
|
|
usedNodeIds[joint] = true;
|
|
});
|
|
});
|
|
ForEach.animation(gltf, function(animation) {
|
|
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) {
|
|
if (defined(uniform.node)) {
|
|
usedNodeIds[uniform.node] = true;
|
|
}
|
|
});
|
|
});
|
|
|
|
return usedNodeIds;
|
|
};
|
|
|
|
getListOfElementsIdsInUse.material = function(gltf) {
|
|
var usedMaterialIds = {};
|
|
|
|
ForEach.mesh(gltf, function(mesh) {
|
|
ForEach.meshPrimitive(mesh, function(primitive) {
|
|
if (defined(primitive.material)) {
|
|
usedMaterialIds[primitive.material] = true;
|
|
}
|
|
});
|
|
});
|
|
|
|
return usedMaterialIds;
|
|
};
|
|
|
|
export default removeUnusedElements;
|