Newer
Older
casic-smartcity-well-front / static / Cesium / ThirdParty / GltfPipeline / removeUnusedElements.js
[wangxitong] on 8 Jul 2021 14 KB mars3d总览
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;