Newer
Older
casic-smartcity-well-front / static / Cesium / ThirdParty / GltfPipeline / updateVersion.js
[wangxitong] on 8 Jul 2021 38 KB mars3d总览
import addExtensionsUsed from './addExtensionsUsed.js'
import addToArray from './addToArray.js'
import findAccessorMinMax from './findAccessorMinMax.js'
import ForEach from './ForEach.js'
import getAccessorByteStride from './getAccessorByteStride.js'
import numberOfComponentsForType from './numberOfComponentsForType.js'
import moveTechniqueRenderStates from './moveTechniqueRenderStates.js'
import moveTechniquesToExtension from './moveTechniquesToExtension.js'
import removeUnusedElements from './removeUnusedElements.js'
import updateAccessorComponentTypes from './updateAccessorComponentTypes.js'
import Cartesian3 from '../../Core/Cartesian3.js'
import Cartesian4 from '../../Core/Cartesian4.js'
import clone from '../../Core/clone.js'
import ComponentDatatype from '../../Core/ComponentDatatype.js'
import defaultValue from '../../Core/defaultValue.js'
import defined from '../../Core/defined.js'
import Matrix4 from '../../Core/Matrix4.js'
import Quaternion from '../../Core/Quaternion.js'
import WebGLConstants from '../../Core/WebGLConstants.js'

    var updateFunctions = {
        '0.8': glTF08to10,
        '1.0': glTF10to20,
        '2.0': undefined
    };

    /**
     * Update the glTF version to the latest version (2.0), or targetVersion if specified.
     * Applies changes made to the glTF spec between revisions so that the core library
     * only has to handle the latest version.
     *
     * @param {Object} gltf A javascript object containing a glTF asset.
     * @param {Object} [options] Options for updating the glTF.
     * @param {String} [options.targetVersion] The glTF will be upgraded until it hits the specified version.
     * @returns {Object} The updated glTF asset.
     *
     * @private
     */
    function updateVersion(gltf, options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);
        var targetVersion = options.targetVersion;
        var version = gltf.version;

        gltf.asset = defaultValue(gltf.asset, {
            version: '1.0'
        });

        gltf.asset.version = defaultValue(gltf.asset.version, '1.0');
        version = defaultValue(version, gltf.asset.version).toString();

        // Invalid version
        if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) {
            // Try truncating trailing version numbers, could be a number as well if it is 0.8
            if (defined(version)) {
                version = version.substring(0, 3);
            }
            // Default to 1.0 if it cannot be determined
            if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) {
                version = '1.0';
            }
        }

        var updateFunction = updateFunctions[version];

        while (defined(updateFunction)) {
            if (version === targetVersion) {
                break;
            }
            updateFunction(gltf, options);
            version = gltf.asset.version;
            updateFunction = updateFunctions[version];
        }
        return gltf;
    }

    function updateInstanceTechniques(gltf) {
        var materials = gltf.materials;
        for (var materialId in materials) {
            if (Object.prototype.hasOwnProperty.call(materials, materialId)) {
                var material = materials[materialId];
                var instanceTechnique = material.instanceTechnique;
                if (defined(instanceTechnique)) {
                    material.technique = instanceTechnique.technique;
                    material.values = instanceTechnique.values;
                    delete material.instanceTechnique;
                }
            }
        }
    }

    function setPrimitiveModes(gltf) {
        var meshes = gltf.meshes;
        for (var meshId in meshes) {
            if (Object.prototype.hasOwnProperty.call(meshes, meshId)) {
                var mesh = meshes[meshId];
                var primitives = mesh.primitives;
                if (defined(primitives)) {
                    var primitivesLength = primitives.length;
                    for (var i = 0; i < primitivesLength; ++i) {
                        var primitive = primitives[i];
                        var defaultMode = defaultValue(primitive.primitive, WebGLConstants.TRIANGLES);
                        primitive.mode = defaultValue(primitive.mode, defaultMode);
                        delete primitive.primitive;
                    }
                }
            }
        }
    }

    function updateNodes(gltf) {
        var nodes = gltf.nodes;
        var axis = new Cartesian3();
        var quat = new Quaternion();
        for (var nodeId in nodes) {
            if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) {
                var node = nodes[nodeId];
                if (defined(node.rotation)) {
                    var rotation = node.rotation;
                    Cartesian3.fromArray(rotation, 0, axis);
                    Quaternion.fromAxisAngle(axis, rotation[3], quat);
                    node.rotation = [quat.x, quat.y, quat.z, quat.w];
                }
                var instanceSkin = node.instanceSkin;
                if (defined(instanceSkin)) {
                    node.skeletons = instanceSkin.skeletons;
                    node.skin = instanceSkin.skin;
                    node.meshes = instanceSkin.meshes;
                    delete node.instanceSkin;
                }
            }
        }
    }

    function updateAnimations(gltf) {
        var animations = gltf.animations;
        var accessors = gltf.accessors;
        var bufferViews = gltf.bufferViews;
        var buffers = gltf.buffers;
        var updatedAccessors = {};
        var axis = new Cartesian3();
        var quat = new Quaternion();
        for (var animationId in animations) {
            if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
                var animation = animations[animationId];
                var channels = animation.channels;
                var parameters = animation.parameters;
                var samplers = animation.samplers;
                if (defined(channels)) {
                    var channelsLength = channels.length;
                    for (var i = 0; i < channelsLength; ++i) {
                        var channel = channels[i];
                        if (channel.target.path === 'rotation') {
                            var accessorId = parameters[samplers[channel.sampler].output];
                            if (defined(updatedAccessors[accessorId])) {
                                continue;
                            }
                            updatedAccessors[accessorId] = true;
                            var accessor = accessors[accessorId];
                            var bufferView = bufferViews[accessor.bufferView];
                            var buffer = buffers[bufferView.buffer];
                            var source = buffer.extras._pipeline.source;
                            var byteOffset = source.byteOffset + bufferView.byteOffset + accessor.byteOffset;
                            var componentType = accessor.componentType;
                            var count = accessor.count;
                            var componentsLength = numberOfComponentsForType(accessor.type);
                            var length = accessor.count * componentsLength;
                            var typedArray = ComponentDatatype.createArrayBufferView(componentType, source.buffer, byteOffset, length);

                            for (var j = 0; j < count; j++) {
                                var offset = j * componentsLength;
                                Cartesian3.unpack(typedArray, offset, axis);
                                var angle = typedArray[offset + 3];
                                Quaternion.fromAxisAngle(axis, angle, quat);
                                Quaternion.pack(quat, typedArray, offset);
                            }
                        }
                    }
                }
            }
        }
    }

    function removeTechniquePasses(gltf) {
        var techniques = gltf.techniques;
        for (var techniqueId in techniques) {
            if (Object.prototype.hasOwnProperty.call(techniques, techniqueId)) {
                var technique = techniques[techniqueId];
                var passes = technique.passes;
                if (defined(passes)) {
                    var passName = defaultValue(technique.pass, 'defaultPass');
                    if (Object.prototype.hasOwnProperty.call(passes, passName)) {
                        var pass = passes[passName];
                        var instanceProgram = pass.instanceProgram;
                        technique.attributes = defaultValue(technique.attributes, instanceProgram.attributes);
                        technique.program = defaultValue(technique.program, instanceProgram.program);
                        technique.uniforms = defaultValue(technique.uniforms, instanceProgram.uniforms);
                        technique.states = defaultValue(technique.states, pass.states);
                    }
                    delete technique.passes;
                    delete technique.pass;
                }
            }
        }
    }

    function glTF08to10(gltf) {
        if (!defined(gltf.asset)) {
            gltf.asset = {};
        }
        var asset = gltf.asset;
        asset.version = '1.0';
        // Profile should be an object, not a string
        if (typeof asset.profile === 'string') {
            var split = asset.profile.split(' ');
            asset.profile = {
                api: split[0],
                version: split[1]
            };
        } else {
            asset.profile = {};
        }

        // Version property should be in asset, not on the root element
        if (defined(gltf.version)) {
            delete gltf.version;
        }
        // material.instanceTechnique properties should be directly on the material
        updateInstanceTechniques(gltf);
        // primitive.primitive should be primitive.mode
        setPrimitiveModes(gltf);
        // Node rotation should be quaternion, not axis-angle
        // node.instanceSkin is deprecated
        updateNodes(gltf);
        // Animations that target rotations should be quaternion, not axis-angle
        updateAnimations(gltf);
        // technique.pass and techniques.passes are deprecated
        removeTechniquePasses(gltf);
        // gltf.allExtensions -> extensionsUsed
        if (defined(gltf.allExtensions)) {
            gltf.extensionsUsed = gltf.allExtensions;
            delete gltf.allExtensions;
        }
        // gltf.lights -> khrMaterialsCommon.lights
        if (defined(gltf.lights)) {
            var extensions = defaultValue(gltf.extensions, {});
            gltf.extensions = extensions;
            var materialsCommon = defaultValue(extensions.KHR_materials_common, {});
            extensions.KHR_materials_common = materialsCommon;
            materialsCommon.lights = gltf.lights;
            delete gltf.lights;
            addExtensionsUsed(gltf, 'KHR_materials_common');
        }
    }

    function removeAnimationSamplersIndirection(gltf) {
        var animations = gltf.animations;
        for (var animationId in animations) {
            if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
                var animation = animations[animationId];
                var parameters = animation.parameters;
                if (defined(parameters)) {
                    var samplers = animation.samplers;
                    for (var samplerId in samplers) {
                        if (Object.prototype.hasOwnProperty.call(samplers, samplerId)) {
                            var sampler = samplers[samplerId];
                            sampler.input = parameters[sampler.input];
                            sampler.output = parameters[sampler.output];
                        }
                    }
                    delete animation.parameters;
                }
            }
        }
    }

    function objectToArray(object, mapping) {
        var array = [];
        for (var id in object) {
            if (Object.prototype.hasOwnProperty.call(object, id)) {
                var value = object[id];
                mapping[id] = array.length;
                array.push(value);
                if (!defined(value.name)) {
                    value.name = id;
                }
            }
        }
        return array;
    }

    function objectsToArrays(gltf) {
        var i;
        var globalMapping = {
            accessors: {},
            animations: {},
            buffers: {},
            bufferViews: {},
            cameras: {},
            images: {},
            materials: {},
            meshes: {},
            nodes: {},
            programs: {},
            samplers: {},
            scenes: {},
            shaders: {},
            skins: {},
            textures: {},
            techniques: {}
        };

        // Map joint names to id names
        var jointName;
        var jointNameToId = {};
        var nodes = gltf.nodes;
        for (var id in nodes) {
            if (Object.prototype.hasOwnProperty.call(nodes, id)) {
                jointName = nodes[id].jointName;
                if (defined(jointName)) {
                    jointNameToId[jointName] = id;
                }
            }
        }

        // Convert top level objects to arrays
        for (var topLevelId in gltf) {
            if (Object.prototype.hasOwnProperty.call(gltf, topLevelId) && defined(globalMapping[topLevelId])) {
                var objectMapping = {};
                var object = gltf[topLevelId];
                gltf[topLevelId] = objectToArray(object, objectMapping);
                globalMapping[topLevelId] = objectMapping;
            }
        }

        // Remap joint names to array indexes
        for (jointName in jointNameToId) {
            if (Object.prototype.hasOwnProperty.call(jointNameToId, jointName)) {
                jointNameToId[jointName] = globalMapping.nodes[jointNameToId[jointName]];
            }
        }

        // Fix references
        if (defined(gltf.scene)) {
            gltf.scene = globalMapping.scenes[gltf.scene];
        }
        ForEach.bufferView(gltf, function(bufferView) {
            if (defined(bufferView.buffer)) {
                bufferView.buffer = globalMapping.buffers[bufferView.buffer];
            }
        });
        ForEach.accessor(gltf, function(accessor) {
            if (defined(accessor.bufferView)) {
                accessor.bufferView = globalMapping.bufferViews[accessor.bufferView];
            }
        });
        ForEach.shader(gltf, function(shader) {
            var extensions = shader.extensions;
            if (defined(extensions)) {
                var binaryGltf = extensions.KHR_binary_glTF;
                if (defined(binaryGltf)) {
                    shader.bufferView = globalMapping.bufferViews[binaryGltf.bufferView];
                    delete extensions.KHR_binary_glTF;
                }
                if (Object.keys(extensions).length === 0) {
                    delete shader.extensions;
                }
            }
        });
        ForEach.program(gltf, function(program) {
            if (defined(program.vertexShader)) {
                program.vertexShader = globalMapping.shaders[program.vertexShader];
            }
            if (defined(program.fragmentShader)) {
                program.fragmentShader = globalMapping.shaders[program.fragmentShader];
            }
        });
        ForEach.technique(gltf, function(technique) {
            if (defined(technique.program)) {
                technique.program = globalMapping.programs[technique.program];
            }
            ForEach.techniqueParameter(technique, function(parameter) {
                if (defined(parameter.node)) {
                    parameter.node = globalMapping.nodes[parameter.node];
                }
                var value = parameter.value;
                if (typeof value === 'string') {
                    parameter.value = {
                        index: globalMapping.textures[value]
                    };
                }
            });
        });
        ForEach.mesh(gltf, function(mesh) {
            ForEach.meshPrimitive(mesh, function(primitive) {
                if (defined(primitive.indices)) {
                    primitive.indices = globalMapping.accessors[primitive.indices];
                }
                ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) {
                    primitive.attributes[semantic] = globalMapping.accessors[accessorId];
                });
                if (defined(primitive.material)) {
                    primitive.material = globalMapping.materials[primitive.material];
                }
            });
        });
        ForEach.node(gltf, function(node) {
            var children = node.children;
            if (defined(children)) {
                var childrenLength = children.length;
                for (i = 0; i < childrenLength; ++i) {
                    children[i] = globalMapping.nodes[children[i]];
                }
            }
            if (defined(node.meshes)) {
                // Split out meshes on nodes
                var meshes = node.meshes;
                var meshesLength = meshes.length;
                if (meshesLength > 0) {
                    node.mesh = globalMapping.meshes[meshes[0]];
                    for (i = 1; i < meshesLength; ++i) {
                        var meshNode = {
                            mesh: globalMapping.meshes[meshes[i]]
                        };
                        var meshNodeId = addToArray(gltf.nodes, meshNode);
                        if (!defined(children)) {
                            children = [];
                            node.children = children;
                        }
                        children.push(meshNodeId);
                    }
                }
                delete node.meshes;
            }
            if (defined(node.camera)) {
                node.camera = globalMapping.cameras[node.camera];
            }
            if (defined(node.skin)) {
                node.skin = globalMapping.skins[node.skin];
            }
            if (defined(node.skeletons)) {
                // Assign skeletons to skins
                var skeletons = node.skeletons;
                var skeletonsLength = skeletons.length;
                if ((skeletonsLength > 0) && defined(node.skin)) {
                    var skin = gltf.skins[node.skin];
                    skin.skeleton = globalMapping.nodes[skeletons[0]];
                }
                delete node.skeletons;
            }
            if (defined(node.jointName)) {
                delete node.jointName;
            }
        });
        ForEach.skin(gltf, function(skin) {
            if (defined(skin.inverseBindMatrices)) {
                skin.inverseBindMatrices = globalMapping.accessors[skin.inverseBindMatrices];
            }
            var jointNames = skin.jointNames;
            if (defined(jointNames)) {
                var joints = [];
                var jointNamesLength = jointNames.length;
                for (i = 0; i < jointNamesLength; ++i) {
                    joints[i] = jointNameToId[jointNames[i]];
                }
                skin.joints = joints;
                delete skin.jointNames;
            }
        });
        ForEach.scene(gltf, function(scene) {
            var sceneNodes = scene.nodes;
            if (defined(sceneNodes)) {
                var sceneNodesLength = sceneNodes.length;
                for (i = 0; i < sceneNodesLength; ++i) {
                    sceneNodes[i] = globalMapping.nodes[sceneNodes[i]];
                }
            }
        });
        ForEach.animation(gltf, function(animation) {
            var samplerMapping = {};
            animation.samplers = objectToArray(animation.samplers, samplerMapping);
            ForEach.animationSampler(animation, function(sampler) {
                sampler.input = globalMapping.accessors[sampler.input];
                sampler.output = globalMapping.accessors[sampler.output];
            });
            ForEach.animationChannel(animation, function(channel) {
                channel.sampler = samplerMapping[channel.sampler];
                var target = channel.target;
                if (defined(target)) {
                    target.node = globalMapping.nodes[target.id];
                    delete target.id;
                }
            });
        });
        ForEach.material(gltf, function(material) {
            if (defined(material.technique)) {
                material.technique = globalMapping.techniques[material.technique];
            }
            ForEach.materialValue(material, function(value, name) {
                if (typeof value === 'string') {
                    material.values[name] = {
                        index: globalMapping.textures[value]
                    };
                }
            });
            var extensions = material.extensions;
            if (defined(extensions)) {
                var materialsCommon = extensions.KHR_materials_common;
                if (defined(materialsCommon)) {
                    ForEach.materialValue(materialsCommon, function(value, name) {
                        if (typeof value === 'string') {
                            materialsCommon.values[name] = {
                                index: globalMapping.textures[value]
                            };
                        }
                    });
                }
            }
        });
        ForEach.image(gltf, function(image) {
            var extensions = image.extensions;
            if (defined(extensions)) {
                var binaryGltf = extensions.KHR_binary_glTF;
                if (defined(binaryGltf)) {
                    image.bufferView = globalMapping.bufferViews[binaryGltf.bufferView];
                    image.mimeType = binaryGltf.mimeType;
                    delete extensions.KHR_binary_glTF;
                }
                if (Object.keys(extensions).length === 0) {
                    delete image.extensions;
                }
            }
            ForEach.compressedImage(image, function(compressedImage) {
                var compressedExtensions = compressedImage.extensions;
                if (defined(compressedExtensions)) {
                    var compressedBinaryGltf = compressedExtensions.KHR_binary_glTF;
                    if (defined(compressedBinaryGltf)) {
                        compressedImage.bufferView = globalMapping.bufferViews[compressedBinaryGltf.bufferView];
                        compressedImage.mimeType = compressedBinaryGltf.mimeType;
                        delete compressedExtensions.KHR_binary_glTF;
                    }
                    if (Object.keys(extensions).length === 0) {
                        delete compressedImage.extensions;
                    }
                }
            });
        });
        ForEach.texture(gltf, function(texture) {
            if (defined(texture.sampler)) {
                texture.sampler = globalMapping.samplers[texture.sampler];
            }
            if (defined(texture.source)) {
                texture.source = globalMapping.images[texture.source];
            }
        });
    }

    function removeAnimationSamplerNames(gltf) {
        ForEach.animation(gltf, function(animation) {
            ForEach.animationSampler(animation, function(sampler) {
                delete sampler.name;
            });
        });
    }

    function removeEmptyArrays(gltf) {
        for (var topLevelId in gltf) {
            if (Object.prototype.hasOwnProperty.call(gltf, topLevelId)) {
                var array = gltf[topLevelId];
                if (Array.isArray(array) && array.length === 0) {
                    delete gltf[topLevelId];
                }
            }
        }
        ForEach.node(gltf, function(node) {
            if (defined(node.children) && node.children.length === 0) {
                delete node.children;
            }
        });
    }

    function stripAsset(gltf) {
        var asset = gltf.asset;
        delete asset.profile;
        delete asset.premultipliedAlpha;
    }

    var knownExtensions = {
        CESIUM_RTC: true,
        KHR_materials_common: true,
        WEB3D_quantized_attributes: true
    };
    function requireKnownExtensions(gltf) {
        var extensionsUsed = gltf.extensionsUsed;
        gltf.extensionsRequired = defaultValue(gltf.extensionsRequired, []);
        if (defined(extensionsUsed)) {
            var extensionsUsedLength = extensionsUsed.length;
            for (var i = 0; i < extensionsUsedLength; ++i) {
                var extension = extensionsUsed[i];
                if (defined(knownExtensions[extension])) {
                    gltf.extensionsRequired.push(extension);
                }
            }
        }
    }

    function removeBufferType(gltf) {
        ForEach.buffer(gltf, function(buffer) {
            delete buffer.type;
        });
    }

    function removeTextureProperties(gltf) {
        ForEach.texture(gltf, function(texture) {
            delete texture.format;
            delete texture.internalFormat;
            delete texture.target;
            delete texture.type;
        });
    }

    function requireAttributeSetIndex(gltf) {
        ForEach.mesh(gltf, function(mesh) {
            ForEach.meshPrimitive(mesh, function(primitive) {
                ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) {
                    if (semantic === 'TEXCOORD') {
                        primitive.attributes.TEXCOORD_0 = accessorId;
                    } else if (semantic === 'COLOR') {
                        primitive.attributes.COLOR_0 = accessorId;
                    }
                });
                delete primitive.attributes.TEXCOORD;
                delete primitive.attributes.COLOR;
            });
        });
        ForEach.technique(gltf, function(technique) {
            ForEach.techniqueParameter(technique, function(parameter) {
                var semantic = parameter.semantic;
                if (defined(semantic)) {
                    if (semantic === 'TEXCOORD') {
                        parameter.semantic = 'TEXCOORD_0';
                    } else if (semantic === 'COLOR') {
                        parameter.semantic = 'COLOR_0';
                    }
                }
            });
        });
    }

    var knownSemantics = {
        POSITION: true,
        NORMAL: true,
        TANGENT: true
    };
    var indexedSemantics = {
        COLOR: 'COLOR',
        JOINT : 'JOINTS',
        JOINTS: 'JOINTS',
        TEXCOORD: 'TEXCOORD',
        WEIGHT: 'WEIGHTS',
        WEIGHTS: 'WEIGHTS'
    };
    function underscoreApplicationSpecificSemantics(gltf) {
        var mappedSemantics = {};
        ForEach.mesh(gltf, function(mesh) {
            ForEach.meshPrimitive(mesh, function(primitive) {
                /*eslint-disable no-unused-vars*/
                ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) {
                    if (semantic.charAt(0) !== '_') {
                        var setIndex = semantic.search(/_[0-9]+/g);
                        var strippedSemantic = semantic;
                        var suffix = '_0';
                        if (setIndex >= 0) {
                            strippedSemantic = semantic.substring(0, setIndex);
                            suffix = semantic.substring(setIndex);
                        }
                        var newSemantic;
                        var indexedSemantic = indexedSemantics[strippedSemantic];
                        if (defined(indexedSemantic)) {
                            newSemantic = indexedSemantic + suffix;
                            mappedSemantics[semantic] = newSemantic;
                        } else if (!defined(knownSemantics[strippedSemantic])) {
                            newSemantic = '_' + semantic;
                            mappedSemantics[semantic] = newSemantic;
                        }
                    }
                });
                for (var semantic in mappedSemantics) {
                    if (Object.prototype.hasOwnProperty.call(mappedSemantics, semantic)) {
                        var mappedSemantic = mappedSemantics[semantic];
                        var accessorId = primitive.attributes[semantic];
                        if (defined(accessorId)) {
                            delete primitive.attributes[semantic];
                            primitive.attributes[mappedSemantic] = accessorId;
                        }
                    }
                }
            });
        });
        ForEach.technique(gltf, function(technique) {
            ForEach.techniqueParameter(technique, function(parameter) {
                var mappedSemantic = mappedSemantics[parameter.semantic];
                if (defined(mappedSemantic)) {
                    parameter.semantic = mappedSemantic;
                }
            });
        });
    }

    function clampCameraParameters(gltf) {
        ForEach.camera(gltf, function(camera) {
            var perspective = camera.perspective;
            if (defined(perspective)) {
                var aspectRatio = perspective.aspectRatio;
                if (defined(aspectRatio) && aspectRatio === 0.0) {
                    delete perspective.aspectRatio;
                }
                var yfov = perspective.yfov;
                if (defined(yfov) && yfov === 0.0) {
                    perspective.yfov = 1.0;
                }
            }
        });
    }

    function computeAccessorByteStride(gltf, accessor) {
        return (defined(accessor.byteStride) && accessor.byteStride !== 0) ? accessor.byteStride : getAccessorByteStride(gltf, accessor);
    }

    function requireByteLength(gltf) {
        ForEach.buffer(gltf, function(buffer) {
            if (!defined(buffer.byteLength)) {
                buffer.byteLength = buffer.extras._pipeline.source.length;
            }
        });
        ForEach.accessor(gltf, function(accessor) {
            var bufferViewId = accessor.bufferView;
            if (defined(bufferViewId)) {
                var bufferView = gltf.bufferViews[bufferViewId];
                var accessorByteStride = computeAccessorByteStride(gltf, accessor);
                var accessorByteEnd = accessor.byteOffset + accessor.count * accessorByteStride;
                bufferView.byteLength = Math.max(defaultValue(bufferView.byteLength, 0), accessorByteEnd);
            }
        });
    }

    function moveByteStrideToBufferView(gltf) {
        var i;
        var j;
        var bufferView;
        var bufferViews = gltf.bufferViews;

        var bufferViewHasVertexAttributes = {};
        ForEach.accessorContainingVertexAttributeData(gltf, function(accessorId) {
            var accessor = gltf.accessors[accessorId];
            if (defined(accessor.bufferView)) {
                bufferViewHasVertexAttributes[accessor.bufferView] = true;
            }
        });

        // Map buffer views to a list of accessors
        var bufferViewMap = {};
        ForEach.accessor(gltf, function(accessor) {
            if (defined(accessor.bufferView)) {
                bufferViewMap[accessor.bufferView] = defaultValue(bufferViewMap[accessor.bufferView], []);
                bufferViewMap[accessor.bufferView].push(accessor);
            }
        });

        // Split accessors with different byte strides
        for (var bufferViewId in bufferViewMap) {
            if (Object.prototype.hasOwnProperty.call(bufferViewMap, bufferViewId)) {
                bufferView = bufferViews[bufferViewId];
                var accessors = bufferViewMap[bufferViewId];
                accessors.sort(function(a, b) {
                    return a.byteOffset - b.byteOffset;
                });
                var currentByteOffset = 0;
                var currentIndex = 0;
                var accessorsLength = accessors.length;
                for (i = 0; i < accessorsLength; ++i) {
                    var accessor = accessors[i];
                    var accessorByteStride = computeAccessorByteStride(gltf, accessor);
                    var accessorByteOffset = accessor.byteOffset;
                    var accessorByteLength = accessor.count * accessorByteStride;
                    delete accessor.byteStride;

                    var hasNextAccessor = (i < accessorsLength - 1);
                    var nextAccessorByteStride = hasNextAccessor ? computeAccessorByteStride(gltf, accessors[i + 1]) : undefined;
                    if (accessorByteStride !== nextAccessorByteStride) {
                        var newBufferView = clone(bufferView, true);
                        if (bufferViewHasVertexAttributes[bufferViewId]) {
                            newBufferView.byteStride = accessorByteStride;
                        }
                        newBufferView.byteOffset += currentByteOffset;
                        newBufferView.byteLength = accessorByteOffset + accessorByteLength - currentByteOffset;
                        var newBufferViewId = addToArray(bufferViews, newBufferView);
                        for (j = currentIndex; j <= i; ++j) {
                            accessor = accessors[j];
                            accessor.bufferView = newBufferViewId;
                            accessor.byteOffset = accessor.byteOffset - currentByteOffset;
                        }
                        // Set current byte offset to next accessor's byte offset
                        currentByteOffset = hasNextAccessor ? accessors[i + 1].byteOffset : undefined;
                        currentIndex = i + 1;
                    }
                }
            }
        }

        // Remove unused buffer views
        removeUnusedElements(gltf, ['accessor', 'bufferView', 'buffer']);
    }

    function requirePositionAccessorMinMax(gltf) {
        ForEach.accessorWithSemantic(gltf, 'POSITION', function(accessorId) {
            var accessor = gltf.accessors[accessorId];
            if (!defined(accessor.min) || !defined(accessor.max)) {
                var minMax = findAccessorMinMax(gltf, accessor);
                accessor.min = minMax.min;
                accessor.max = minMax.max;
            }
        });
    }

    function isNodeEmpty(node) {
        return (!defined(node.children) || node.children.length === 0) &&
            (!defined(node.meshes) || node.meshes.length === 0) &&
            !defined(node.camera) && !defined(node.skin) && !defined(node.skeletons) && !defined(node.jointName) &&
            (!defined(node.translation) || Cartesian3.fromArray(node.translation).equals(Cartesian3.ZERO)) &&
            (!defined(node.scale) || Cartesian3.fromArray(node.scale).equals(new Cartesian3(1.0, 1.0, 1.0))) &&
            (!defined(node.rotation) || Cartesian4.fromArray(node.rotation).equals(new Cartesian4(0.0, 0.0, 0.0, 1.0))) &&
            (!defined(node.matrix) || Matrix4.fromColumnMajorArray(node.matrix).equals(Matrix4.IDENTITY)) &&
            !defined(node.extensions) && !defined(node.extras);
    }

    function deleteNode(gltf, nodeId) {
        // Remove from list of nodes in scene
        ForEach.scene(gltf, function(scene) {
            var sceneNodes = scene.nodes;
            if (defined(sceneNodes)) {
                var sceneNodesLength = sceneNodes.length;
                for (var i = sceneNodesLength; i >= 0; --i) {
                    if (sceneNodes[i] === nodeId) {
                        sceneNodes.splice(i, 1);
                        return;
                    }
                }
            }
        });

        // Remove parent node's reference to this node, and delete the parent if also empty
        ForEach.node(gltf, function(parentNode, parentNodeId) {
            if (defined(parentNode.children)) {
                var index = parentNode.children.indexOf(nodeId);
                if (index > -1) {
                    parentNode.children.splice(index, 1);

                    if (isNodeEmpty(parentNode)) {
                        deleteNode(gltf, parentNodeId);
                    }
                }
            }
        });

        delete gltf.nodes[nodeId];
    }

    function removeEmptyNodes(gltf) {
        ForEach.node(gltf, function(node, nodeId) {
            if (isNodeEmpty(node)) {
                deleteNode(gltf, nodeId);
            }
        });

        return gltf;
    }

    function requireAnimationAccessorMinMax(gltf) {
        ForEach.animation(gltf, function(animation) {
            ForEach.animationSampler(animation, function(sampler) {
                var accessor = gltf.accessors[sampler.input];
                if (!defined(accessor.min) || !defined(accessor.max)) {
                    var minMax = findAccessorMinMax(gltf, accessor);
                    accessor.min = minMax.min;
                    accessor.max = minMax.max;
                }
            });
        });
    }

    function glTF10to20(gltf) {
        gltf.asset = defaultValue(gltf.asset, {});
        gltf.asset.version = '2.0';
        // material.instanceTechnique properties should be directly on the material. instanceTechnique is a gltf 0.8 property but is seen in some 1.0 models.
        updateInstanceTechniques(gltf);
        // animation.samplers now refers directly to accessors and animation.parameters should be removed
        removeAnimationSamplersIndirection(gltf);
        // Remove empty nodes and re-assign referencing indices
        removeEmptyNodes(gltf);
        // Top-level objects are now arrays referenced by index instead of id
        objectsToArrays(gltf);
        // Animation.sampler objects cannot have names
        removeAnimationSamplerNames(gltf);
        // asset.profile no longer exists
        stripAsset(gltf);
        // Move known extensions from extensionsUsed to extensionsRequired
        requireKnownExtensions(gltf);
        // bufferView.byteLength and buffer.byteLength are required
        requireByteLength(gltf);
        // byteStride moved from accessor to bufferView
        moveByteStrideToBufferView(gltf);
        // accessor.min and accessor.max must be defined for accessors containing POSITION attributes
        requirePositionAccessorMinMax(gltf);
        // An animation sampler's input accessor must have min and max properties defined
        requireAnimationAccessorMinMax(gltf);
        // buffer.type is unnecessary and should be removed
        removeBufferType(gltf);
        // Remove format, internalFormat, target, and type
        removeTextureProperties(gltf);
        // TEXCOORD and COLOR attributes must be written with a set index (TEXCOORD_#)
        requireAttributeSetIndex(gltf);
        // Add underscores to application-specific parameters
        underscoreApplicationSpecificSemantics(gltf);
        // Accessors referenced by JOINTS_0 and WEIGHTS_0 attributes must have correct component types
        updateAccessorComponentTypes(gltf);
        // Clamp camera parameters
        clampCameraParameters(gltf);
        // Move legacy technique render states to material properties and add KHR_blend extension blending functions
        moveTechniqueRenderStates(gltf);
        // Add material techniques to KHR_techniques_webgl extension, removing shaders, programs, and techniques
        moveTechniquesToExtension(gltf);
        // Remove empty arrays
        removeEmptyArrays(gltf);
    }

    export default updateVersion;