gltf-pipeline
Version:
Content pipeline tools for optimizing glTF assets.
240 lines (213 loc) • 7.53 kB
JavaScript
;
const Cesium = require("cesium");
const addToArray = require("./addToArray");
const ForEach = require("./ForEach");
const getAccessorByteStride = require("./getAccessorByteStride");
const defaultValue = Cesium.defaultValue;
const defined = Cesium.defined;
const WebGLConstants = Cesium.WebGLConstants;
module.exports = addDefaults;
/**
* Adds default glTF values if they don't exist.
*
* @param {Object} gltf A javascript object containing a glTF asset.
* @returns {Object} The modified glTF.
*
* @private
*/
function addDefaults(gltf) {
ForEach.accessor(gltf, function (accessor) {
if (defined(accessor.bufferView)) {
accessor.byteOffset = defaultValue(accessor.byteOffset, 0);
}
});
ForEach.bufferView(gltf, function (bufferView) {
if (defined(bufferView.buffer)) {
bufferView.byteOffset = defaultValue(bufferView.byteOffset, 0);
}
});
ForEach.mesh(gltf, function (mesh) {
ForEach.meshPrimitive(mesh, function (primitive) {
primitive.mode = defaultValue(primitive.mode, WebGLConstants.TRIANGLES);
if (!defined(primitive.material)) {
if (!defined(gltf.materials)) {
gltf.materials = [];
}
const defaultMaterial = {
name: "default",
};
primitive.material = addToArray(gltf.materials, defaultMaterial);
}
});
});
ForEach.accessorContainingVertexAttributeData(gltf, function (accessorId) {
const accessor = gltf.accessors[accessorId];
const bufferViewId = accessor.bufferView;
accessor.normalized = defaultValue(accessor.normalized, false);
if (defined(bufferViewId)) {
const bufferView = gltf.bufferViews[bufferViewId];
bufferView.byteStride = getAccessorByteStride(gltf, accessor);
bufferView.target = WebGLConstants.ARRAY_BUFFER;
}
});
ForEach.accessorContainingIndexData(gltf, function (accessorId) {
const accessor = gltf.accessors[accessorId];
const bufferViewId = accessor.bufferView;
if (defined(bufferViewId)) {
const bufferView = gltf.bufferViews[bufferViewId];
bufferView.target = WebGLConstants.ELEMENT_ARRAY_BUFFER;
}
});
ForEach.material(gltf, function (material) {
const extensions = defaultValue(
material.extensions,
defaultValue.EMPTY_OBJECT
);
const materialsCommon = extensions.KHR_materials_common;
if (defined(materialsCommon)) {
const technique = materialsCommon.technique;
const values = defined(materialsCommon.values)
? materialsCommon.values
: {};
materialsCommon.values = values;
values.ambient = defined(values.ambient)
? values.ambient
: [0.0, 0.0, 0.0, 1.0];
values.emission = defined(values.emission)
? values.emission
: [0.0, 0.0, 0.0, 1.0];
values.transparency = defaultValue(values.transparency, 1.0);
if (technique !== "CONSTANT") {
values.diffuse = defined(values.diffuse)
? values.diffuse
: [0.0, 0.0, 0.0, 1.0];
if (technique !== "LAMBERT") {
values.specular = defined(values.specular)
? values.specular
: [0.0, 0.0, 0.0, 1.0];
values.shininess = defaultValue(values.shininess, 0.0);
}
}
// These actually exist on the extension object, not the values object despite what's shown in the spec
materialsCommon.transparent = defaultValue(
materialsCommon.transparent,
false
);
materialsCommon.doubleSided = defaultValue(
materialsCommon.doubleSided,
false
);
return;
}
material.emissiveFactor = defaultValue(
material.emissiveFactor,
[0.0, 0.0, 0.0]
);
material.alphaMode = defaultValue(material.alphaMode, "OPAQUE");
material.doubleSided = defaultValue(material.doubleSided, false);
if (material.alphaMode === "MASK") {
material.alphaCutoff = defaultValue(material.alphaCutoff, 0.5);
}
const techniquesExtension = extensions.KHR_techniques_webgl;
if (defined(techniquesExtension)) {
ForEach.materialValue(material, function (materialValue) {
// Check if material value is a TextureInfo object
if (defined(materialValue.index)) {
addTextureDefaults(materialValue);
}
});
}
addTextureDefaults(material.emissiveTexture);
addTextureDefaults(material.normalTexture);
addTextureDefaults(material.occlusionTexture);
const pbrMetallicRoughness = material.pbrMetallicRoughness;
if (defined(pbrMetallicRoughness)) {
pbrMetallicRoughness.baseColorFactor = defaultValue(
pbrMetallicRoughness.baseColorFactor,
[1.0, 1.0, 1.0, 1.0]
);
pbrMetallicRoughness.metallicFactor = defaultValue(
pbrMetallicRoughness.metallicFactor,
1.0
);
pbrMetallicRoughness.roughnessFactor = defaultValue(
pbrMetallicRoughness.roughnessFactor,
1.0
);
addTextureDefaults(pbrMetallicRoughness.baseColorTexture);
addTextureDefaults(pbrMetallicRoughness.metallicRoughnessTexture);
}
const pbrSpecularGlossiness =
extensions.KHR_materials_pbrSpecularGlossiness;
if (defined(pbrSpecularGlossiness)) {
pbrSpecularGlossiness.diffuseFactor = defaultValue(
pbrSpecularGlossiness.diffuseFactor,
[1.0, 1.0, 1.0, 1.0]
);
pbrSpecularGlossiness.specularFactor = defaultValue(
pbrSpecularGlossiness.specularFactor,
[1.0, 1.0, 1.0]
);
pbrSpecularGlossiness.glossinessFactor = defaultValue(
pbrSpecularGlossiness.glossinessFactor,
1.0
);
addTextureDefaults(pbrSpecularGlossiness.specularGlossinessTexture);
}
});
ForEach.animation(gltf, function (animation) {
ForEach.animationSampler(animation, function (sampler) {
sampler.interpolation = defaultValue(sampler.interpolation, "LINEAR");
});
});
const animatedNodes = getAnimatedNodes(gltf);
ForEach.node(gltf, function (node, id) {
const animated = defined(animatedNodes[id]);
if (
animated ||
defined(node.translation) ||
defined(node.rotation) ||
defined(node.scale)
) {
node.translation = defaultValue(node.translation, [0.0, 0.0, 0.0]);
node.rotation = defaultValue(node.rotation, [0.0, 0.0, 0.0, 1.0]);
node.scale = defaultValue(node.scale, [1.0, 1.0, 1.0]);
} else {
node.matrix = defaultValue(
node.matrix,
[
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 1.0,
]
);
}
});
ForEach.sampler(gltf, function (sampler) {
sampler.wrapS = defaultValue(sampler.wrapS, WebGLConstants.REPEAT);
sampler.wrapT = defaultValue(sampler.wrapT, WebGLConstants.REPEAT);
});
if (defined(gltf.scenes) && !defined(gltf.scene)) {
gltf.scene = 0;
}
return gltf;
}
function getAnimatedNodes(gltf) {
const nodes = {};
ForEach.animation(gltf, function (animation) {
ForEach.animationChannel(animation, function (channel) {
const target = channel.target;
const nodeId = target.node;
const path = target.path;
// Ignore animations that target 'weights'
if (path === "translation" || path === "rotation" || path === "scale") {
nodes[nodeId] = true;
}
});
});
return nodes;
}
function addTextureDefaults(texture) {
if (defined(texture)) {
texture.texCoord = defaultValue(texture.texCoord, 0);
}
}