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";
|
|
const 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);
|
const targetVersion = options.targetVersion;
|
let 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";
|
}
|
}
|
|
let 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) {
|
const materials = gltf.materials;
|
for (const materialId in materials) {
|
if (Object.prototype.hasOwnProperty.call(materials, materialId)) {
|
const material = materials[materialId];
|
const instanceTechnique = material.instanceTechnique;
|
if (defined(instanceTechnique)) {
|
material.technique = instanceTechnique.technique;
|
material.values = instanceTechnique.values;
|
delete material.instanceTechnique;
|
}
|
}
|
}
|
}
|
|
function setPrimitiveModes(gltf) {
|
const meshes = gltf.meshes;
|
for (const meshId in meshes) {
|
if (Object.prototype.hasOwnProperty.call(meshes, meshId)) {
|
const mesh = meshes[meshId];
|
const primitives = mesh.primitives;
|
if (defined(primitives)) {
|
const primitivesLength = primitives.length;
|
for (let i = 0; i < primitivesLength; ++i) {
|
const primitive = primitives[i];
|
const defaultMode = defaultValue(
|
primitive.primitive,
|
WebGLConstants.TRIANGLES
|
);
|
primitive.mode = defaultValue(primitive.mode, defaultMode);
|
delete primitive.primitive;
|
}
|
}
|
}
|
}
|
}
|
|
function updateNodes(gltf) {
|
const nodes = gltf.nodes;
|
const axis = new Cartesian3();
|
const quat = new Quaternion();
|
for (const nodeId in nodes) {
|
if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) {
|
const node = nodes[nodeId];
|
if (defined(node.rotation)) {
|
const rotation = node.rotation;
|
Cartesian3.fromArray(rotation, 0, axis);
|
Quaternion.fromAxisAngle(axis, rotation[3], quat);
|
node.rotation = [quat.x, quat.y, quat.z, quat.w];
|
}
|
const instanceSkin = node.instanceSkin;
|
if (defined(instanceSkin)) {
|
node.skeletons = instanceSkin.skeletons;
|
node.skin = instanceSkin.skin;
|
node.meshes = instanceSkin.meshes;
|
delete node.instanceSkin;
|
}
|
}
|
}
|
}
|
|
function updateAnimations(gltf) {
|
const animations = gltf.animations;
|
const accessors = gltf.accessors;
|
const bufferViews = gltf.bufferViews;
|
const buffers = gltf.buffers;
|
const updatedAccessors = {};
|
const axis = new Cartesian3();
|
const quat = new Quaternion();
|
for (const animationId in animations) {
|
if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
|
const animation = animations[animationId];
|
const channels = animation.channels;
|
const parameters = animation.parameters;
|
const samplers = animation.samplers;
|
if (defined(channels)) {
|
const channelsLength = channels.length;
|
for (let i = 0; i < channelsLength; ++i) {
|
const channel = channels[i];
|
if (channel.target.path === "rotation") {
|
const accessorId = parameters[samplers[channel.sampler].output];
|
if (defined(updatedAccessors[accessorId])) {
|
continue;
|
}
|
updatedAccessors[accessorId] = true;
|
const accessor = accessors[accessorId];
|
const bufferView = bufferViews[accessor.bufferView];
|
const buffer = buffers[bufferView.buffer];
|
const source = buffer.extras._pipeline.source;
|
const byteOffset =
|
source.byteOffset + bufferView.byteOffset + accessor.byteOffset;
|
const componentType = accessor.componentType;
|
const count = accessor.count;
|
const componentsLength = numberOfComponentsForType(accessor.type);
|
const length = accessor.count * componentsLength;
|
const typedArray = ComponentDatatype.createArrayBufferView(
|
componentType,
|
source.buffer,
|
byteOffset,
|
length
|
);
|
|
for (let j = 0; j < count; j++) {
|
const offset = j * componentsLength;
|
Cartesian3.unpack(typedArray, offset, axis);
|
const angle = typedArray[offset + 3];
|
Quaternion.fromAxisAngle(axis, angle, quat);
|
Quaternion.pack(quat, typedArray, offset);
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
function removeTechniquePasses(gltf) {
|
const techniques = gltf.techniques;
|
for (const techniqueId in techniques) {
|
if (Object.prototype.hasOwnProperty.call(techniques, techniqueId)) {
|
const technique = techniques[techniqueId];
|
const passes = technique.passes;
|
if (defined(passes)) {
|
const passName = defaultValue(technique.pass, "defaultPass");
|
if (Object.prototype.hasOwnProperty.call(passes, passName)) {
|
const pass = passes[passName];
|
const 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 = {};
|
}
|
const asset = gltf.asset;
|
asset.version = "1.0";
|
// Profile should be an object, not a string
|
if (typeof asset.profile === "string") {
|
const 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)) {
|
const extensions = defaultValue(gltf.extensions, {});
|
gltf.extensions = extensions;
|
const 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) {
|
const animations = gltf.animations;
|
for (const animationId in animations) {
|
if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
|
const animation = animations[animationId];
|
const parameters = animation.parameters;
|
if (defined(parameters)) {
|
const samplers = animation.samplers;
|
for (const samplerId in samplers) {
|
if (Object.prototype.hasOwnProperty.call(samplers, samplerId)) {
|
const sampler = samplers[samplerId];
|
sampler.input = parameters[sampler.input];
|
sampler.output = parameters[sampler.output];
|
}
|
}
|
delete animation.parameters;
|
}
|
}
|
}
|
}
|
|
function objectToArray(object, mapping) {
|
const array = [];
|
for (const id in object) {
|
if (Object.prototype.hasOwnProperty.call(object, id)) {
|
const value = object[id];
|
mapping[id] = array.length;
|
array.push(value);
|
if (!defined(value.name)) {
|
value.name = id;
|
}
|
}
|
}
|
return array;
|
}
|
|
function objectsToArrays(gltf) {
|
let i;
|
const globalMapping = {
|
accessors: {},
|
animations: {},
|
buffers: {},
|
bufferViews: {},
|
cameras: {},
|
images: {},
|
materials: {},
|
meshes: {},
|
nodes: {},
|
programs: {},
|
samplers: {},
|
scenes: {},
|
shaders: {},
|
skins: {},
|
textures: {},
|
techniques: {},
|
};
|
|
// Map joint names to id names
|
let jointName;
|
const jointNameToId = {};
|
const nodes = gltf.nodes;
|
for (const 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 (const topLevelId in gltf) {
|
if (
|
Object.prototype.hasOwnProperty.call(gltf, topLevelId) &&
|
defined(globalMapping[topLevelId])
|
) {
|
const objectMapping = {};
|
const 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) {
|
const extensions = shader.extensions;
|
if (defined(extensions)) {
|
const 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];
|
}
|
const 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) {
|
let children = node.children;
|
if (defined(children)) {
|
const childrenLength = children.length;
|
for (i = 0; i < childrenLength; ++i) {
|
children[i] = globalMapping.nodes[children[i]];
|
}
|
}
|
if (defined(node.meshes)) {
|
// Split out meshes on nodes
|
const meshes = node.meshes;
|
const meshesLength = meshes.length;
|
if (meshesLength > 0) {
|
node.mesh = globalMapping.meshes[meshes[0]];
|
for (i = 1; i < meshesLength; ++i) {
|
const meshNode = {
|
mesh: globalMapping.meshes[meshes[i]],
|
};
|
const 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
|
const skeletons = node.skeletons;
|
const skeletonsLength = skeletons.length;
|
if (skeletonsLength > 0 && defined(node.skin)) {
|
const 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];
|
}
|
const jointNames = skin.jointNames;
|
if (defined(jointNames)) {
|
const joints = [];
|
const 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) {
|
const sceneNodes = scene.nodes;
|
if (defined(sceneNodes)) {
|
const sceneNodesLength = sceneNodes.length;
|
for (i = 0; i < sceneNodesLength; ++i) {
|
sceneNodes[i] = globalMapping.nodes[sceneNodes[i]];
|
}
|
}
|
});
|
ForEach.animation(gltf, function (animation) {
|
const 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];
|
const 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],
|
};
|
}
|
});
|
const extensions = material.extensions;
|
if (defined(extensions)) {
|
const 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) {
|
const extensions = image.extensions;
|
if (defined(extensions)) {
|
const 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.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 (const topLevelId in gltf) {
|
if (Object.prototype.hasOwnProperty.call(gltf, topLevelId)) {
|
const 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) {
|
const asset = gltf.asset;
|
delete asset.profile;
|
delete asset.premultipliedAlpha;
|
}
|
|
const knownExtensions = {
|
CESIUM_RTC: true,
|
KHR_materials_common: true,
|
WEB3D_quantized_attributes: true,
|
};
|
function requireKnownExtensions(gltf) {
|
const extensionsUsed = gltf.extensionsUsed;
|
gltf.extensionsRequired = defaultValue(gltf.extensionsRequired, []);
|
if (defined(extensionsUsed)) {
|
const extensionsUsedLength = extensionsUsed.length;
|
for (let i = 0; i < extensionsUsedLength; ++i) {
|
const 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) {
|
const semantic = parameter.semantic;
|
if (defined(semantic)) {
|
if (semantic === "TEXCOORD") {
|
parameter.semantic = "TEXCOORD_0";
|
} else if (semantic === "COLOR") {
|
parameter.semantic = "COLOR_0";
|
}
|
}
|
});
|
});
|
}
|
|
const knownSemantics = {
|
POSITION: true,
|
NORMAL: true,
|
TANGENT: true,
|
};
|
const indexedSemantics = {
|
COLOR: "COLOR",
|
JOINT: "JOINTS",
|
JOINTS: "JOINTS",
|
TEXCOORD: "TEXCOORD",
|
WEIGHT: "WEIGHTS",
|
WEIGHTS: "WEIGHTS",
|
};
|
function underscoreApplicationSpecificSemantics(gltf) {
|
const 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) !== "_") {
|
const setIndex = semantic.search(/_[0-9]+/g);
|
let strippedSemantic = semantic;
|
let suffix = "_0";
|
if (setIndex >= 0) {
|
strippedSemantic = semantic.substring(0, setIndex);
|
suffix = semantic.substring(setIndex);
|
}
|
let newSemantic;
|
const indexedSemantic = indexedSemantics[strippedSemantic];
|
if (defined(indexedSemantic)) {
|
newSemantic = indexedSemantic + suffix;
|
mappedSemantics[semantic] = newSemantic;
|
} else if (!defined(knownSemantics[strippedSemantic])) {
|
newSemantic = "_" + semantic;
|
mappedSemantics[semantic] = newSemantic;
|
}
|
}
|
}
|
);
|
for (const semantic in mappedSemantics) {
|
if (Object.prototype.hasOwnProperty.call(mappedSemantics, semantic)) {
|
const mappedSemantic = mappedSemantics[semantic];
|
const 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) {
|
const mappedSemantic = mappedSemantics[parameter.semantic];
|
if (defined(mappedSemantic)) {
|
parameter.semantic = mappedSemantic;
|
}
|
});
|
});
|
}
|
|
function clampCameraParameters(gltf) {
|
ForEach.camera(gltf, function (camera) {
|
const perspective = camera.perspective;
|
if (defined(perspective)) {
|
const aspectRatio = perspective.aspectRatio;
|
if (defined(aspectRatio) && aspectRatio === 0.0) {
|
delete perspective.aspectRatio;
|
}
|
const 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) {
|
const bufferViewId = accessor.bufferView;
|
if (defined(bufferViewId)) {
|
const bufferView = gltf.bufferViews[bufferViewId];
|
const accessorByteStride = computeAccessorByteStride(gltf, accessor);
|
const accessorByteEnd =
|
accessor.byteOffset + accessor.count * accessorByteStride;
|
bufferView.byteLength = Math.max(
|
defaultValue(bufferView.byteLength, 0),
|
accessorByteEnd
|
);
|
}
|
});
|
}
|
|
function moveByteStrideToBufferView(gltf) {
|
let i;
|
let j;
|
let bufferView;
|
const bufferViews = gltf.bufferViews;
|
|
const bufferViewHasVertexAttributes = {};
|
ForEach.accessorContainingVertexAttributeData(gltf, function (accessorId) {
|
const accessor = gltf.accessors[accessorId];
|
if (defined(accessor.bufferView)) {
|
bufferViewHasVertexAttributes[accessor.bufferView] = true;
|
}
|
});
|
|
// Map buffer views to a list of accessors
|
const 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 (const bufferViewId in bufferViewMap) {
|
if (Object.prototype.hasOwnProperty.call(bufferViewMap, bufferViewId)) {
|
bufferView = bufferViews[bufferViewId];
|
const accessors = bufferViewMap[bufferViewId];
|
accessors.sort(function (a, b) {
|
return a.byteOffset - b.byteOffset;
|
});
|
let currentByteOffset = 0;
|
let currentIndex = 0;
|
const accessorsLength = accessors.length;
|
for (i = 0; i < accessorsLength; ++i) {
|
let accessor = accessors[i];
|
const accessorByteStride = computeAccessorByteStride(gltf, accessor);
|
const accessorByteOffset = accessor.byteOffset;
|
const accessorByteLength = accessor.count * accessorByteStride;
|
delete accessor.byteStride;
|
|
const hasNextAccessor = i < accessorsLength - 1;
|
const nextAccessorByteStride = hasNextAccessor
|
? computeAccessorByteStride(gltf, accessors[i + 1])
|
: undefined;
|
if (accessorByteStride !== nextAccessorByteStride) {
|
const newBufferView = clone(bufferView, true);
|
if (bufferViewHasVertexAttributes[bufferViewId]) {
|
newBufferView.byteStride = accessorByteStride;
|
}
|
newBufferView.byteOffset += currentByteOffset;
|
newBufferView.byteLength =
|
accessorByteOffset + accessorByteLength - currentByteOffset;
|
const 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) {
|
const accessor = gltf.accessors[accessorId];
|
if (!defined(accessor.min) || !defined(accessor.max)) {
|
const 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) {
|
const sceneNodes = scene.nodes;
|
if (defined(sceneNodes)) {
|
const sceneNodesLength = sceneNodes.length;
|
for (let 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)) {
|
const 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) {
|
const accessor = gltf.accessors[sampler.input];
|
if (!defined(accessor.min) || !defined(accessor.max)) {
|
const 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;
|