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.

1355 lines
40 KiB
JavaScript

import BoundingSphere from "../Core/BoundingSphere.js";
import Cartesian2 from "../Core/Cartesian2.js";
import Cartesian3 from "../Core/Cartesian3.js";
import Cartesian4 from "../Core/Cartesian4.js";
import clone from "../Core/clone.js";
import defined from "../Core/defined.js";
import Matrix2 from "../Core/Matrix2.js";
import Matrix3 from "../Core/Matrix3.js";
import Matrix4 from "../Core/Matrix4.js";
import Quaternion from "../Core/Quaternion.js";
import RuntimeError from "../Core/RuntimeError.js";
import WebGLConstants from "../Core/WebGLConstants.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import addToArray from "../ThirdParty/GltfPipeline/addToArray.js";
import ForEach from "../ThirdParty/GltfPipeline/ForEach.js";
import hasExtension from "../ThirdParty/GltfPipeline/hasExtension.js";
import AttributeType from "./AttributeType.js";
import Axis from "./Axis.js";
/**
* @private
*/
var ModelUtility = {};
/**
* Updates the model's forward axis if the model is not a 2.0 model.
*
* @param {Object} model The model to update.
*/
ModelUtility.updateForwardAxis = function (model) {
var cachedSourceVersion = model.gltf.extras.sourceVersion;
if (
(defined(cachedSourceVersion) && cachedSourceVersion !== "2.0") ||
ModelUtility.getAssetVersion(model.gltf) !== "2.0"
) {
model._gltfForwardAxis = Axis.X;
}
};
/**
* Gets the string representing the glTF asset version.
*
* @param {Object} gltf A javascript object containing a glTF asset.
* @returns {String} The glTF asset version string.
*/
ModelUtility.getAssetVersion = function (gltf) {
// In glTF 1.0 it was valid to omit the version number.
if (!defined(gltf.asset) || !defined(gltf.asset.version)) {
return "1.0";
}
return gltf.asset.version;
};
/**
* Splits primitive materials with values incompatible for generating techniques.
*
* @param {Object} gltf A javascript object containing a glTF asset.
* @returns {Object} The glTF asset with modified materials.
*/
ModelUtility.splitIncompatibleMaterials = function (gltf) {
var accessors = gltf.accessors;
var materials = gltf.materials;
var primitiveInfoByMaterial = {};
ForEach.mesh(gltf, function (mesh) {
ForEach.meshPrimitive(mesh, function (primitive) {
var materialIndex = primitive.material;
var material = materials[materialIndex];
var jointAccessorId = primitive.attributes.JOINTS_0;
var componentType;
var accessorType;
if (defined(jointAccessorId)) {
var jointAccessor = accessors[jointAccessorId];
componentType = jointAccessor.componentType;
accessorType = jointAccessor.type;
}
var isSkinned = defined(jointAccessorId) && accessorType === "VEC4";
var hasVertexColors = defined(primitive.attributes.COLOR_0);
var hasMorphTargets = defined(primitive.targets);
var hasNormals = defined(primitive.attributes.NORMAL);
var hasTangents = defined(primitive.attributes.TANGENT);
var hasTexCoords = defined(primitive.attributes.TEXCOORD_0);
var hasTexCoord1 =
hasTexCoords && defined(primitive.attributes.TEXCOORD_1);
var hasOutline =
defined(primitive.extensions) &&
defined(primitive.extensions.CESIUM_primitive_outline);
var primitiveInfo = primitiveInfoByMaterial[materialIndex];
if (!defined(primitiveInfo)) {
primitiveInfoByMaterial[materialIndex] = {
skinning: {
skinned: isSkinned,
componentType: componentType,
},
hasVertexColors: hasVertexColors,
hasMorphTargets: hasMorphTargets,
hasNormals: hasNormals,
hasTangents: hasTangents,
hasTexCoords: hasTexCoords,
hasTexCoord1: hasTexCoord1,
hasOutline: hasOutline,
};
} else if (
primitiveInfo.skinning.skinned !== isSkinned ||
primitiveInfo.hasVertexColors !== hasVertexColors ||
primitiveInfo.hasMorphTargets !== hasMorphTargets ||
primitiveInfo.hasNormals !== hasNormals ||
primitiveInfo.hasTangents !== hasTangents ||
primitiveInfo.hasTexCoords !== hasTexCoords ||
primitiveInfo.hasTexCoord1 !== hasTexCoord1 ||
primitiveInfo.hasOutline !== hasOutline
) {
// This primitive uses the same material as another one that either:
// * Isn't skinned
// * Uses a different type to store joints and weights
// * Doesn't have vertex colors, morph targets, normals, tangents, or texCoords
// * Doesn't have a CESIUM_primitive_outline extension.
var clonedMaterial = clone(material, true);
// Split this off as a separate material
materialIndex = addToArray(materials, clonedMaterial);
primitive.material = materialIndex;
primitiveInfoByMaterial[materialIndex] = {
skinning: {
skinned: isSkinned,
componentType: componentType,
},
hasVertexColors: hasVertexColors,
hasMorphTargets: hasMorphTargets,
hasNormals: hasNormals,
hasTangents: hasTangents,
hasTexCoords: hasTexCoords,
hasTexCoord1: hasTexCoord1,
hasOutline: hasOutline,
};
}
});
});
return primitiveInfoByMaterial;
};
ModelUtility.getShaderVariable = function (type) {
if (type === "SCALAR") {
return "float";
}
return type.toLowerCase();
};
ModelUtility.ModelState = {
NEEDS_LOAD: 0,
LOADING: 1,
LOADED: 2, // Renderable, but textures can still be pending when incrementallyLoadTextures is true.
FAILED: 3,
};
ModelUtility.getFailedLoadFunction = function (model, type, path) {
return function (error) {
model._state = ModelUtility.ModelState.FAILED;
var message = "Failed to load " + type + ": " + path;
if (defined(error)) {
message += "\n" + error.message;
}
model._readyPromise.reject(new RuntimeError(message));
};
};
ModelUtility.parseBuffers = function (model, bufferLoad) {
var loadResources = model._loadResources;
ForEach.buffer(model.gltf, function (buffer, bufferViewId) {
if (defined(buffer.extras._pipeline.source)) {
loadResources.buffers[bufferViewId] = buffer.extras._pipeline.source;
} else if (defined(bufferLoad)) {
var bufferResource = model._resource.getDerivedResource({
url: buffer.uri,
});
++loadResources.pendingBufferLoads;
bufferResource
.fetchArrayBuffer()
.then(bufferLoad(model, bufferViewId))
.otherwise(
ModelUtility.getFailedLoadFunction(
model,
"buffer",
bufferResource.url
)
);
}
});
};
var aMinScratch = new Cartesian3();
var aMaxScratch = new Cartesian3();
ModelUtility.computeBoundingSphere = function (model) {
var gltf = model.gltf;
var gltfNodes = gltf.nodes;
var gltfMeshes = gltf.meshes;
var rootNodes = gltf.scenes[gltf.scene].nodes;
var rootNodesLength = rootNodes.length;
var nodeStack = [];
var min = new Cartesian3(
Number.MAX_VALUE,
Number.MAX_VALUE,
Number.MAX_VALUE
);
var max = new Cartesian3(
-Number.MAX_VALUE,
-Number.MAX_VALUE,
-Number.MAX_VALUE
);
for (var i = 0; i < rootNodesLength; ++i) {
var n = gltfNodes[rootNodes[i]];
n._transformToRoot = ModelUtility.getTransform(n);
nodeStack.push(n);
while (nodeStack.length > 0) {
n = nodeStack.pop();
var transformToRoot = n._transformToRoot;
var meshId = n.mesh;
if (defined(meshId)) {
var mesh = gltfMeshes[meshId];
var primitives = mesh.primitives;
var primitivesLength = primitives.length;
for (var m = 0; m < primitivesLength; ++m) {
var positionAccessor = primitives[m].attributes.POSITION;
if (defined(positionAccessor)) {
var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);
if (defined(minMax.min) && defined(minMax.max)) {
var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
Cartesian3.minimumByComponent(min, aMin, min);
Cartesian3.maximumByComponent(max, aMax, max);
}
}
}
}
var children = n.children;
if (defined(children)) {
var childrenLength = children.length;
for (var k = 0; k < childrenLength; ++k) {
var child = gltfNodes[children[k]];
child._transformToRoot = ModelUtility.getTransform(child);
Matrix4.multiplyTransformation(
transformToRoot,
child._transformToRoot,
child._transformToRoot
);
nodeStack.push(child);
}
}
delete n._transformToRoot;
}
}
var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
if (model._forwardAxis === Axis.Z) {
// glTF 2.0 has a Z-forward convention that must be adapted here to X-forward.
BoundingSphere.transformWithoutScale(
boundingSphere,
Axis.Z_UP_TO_X_UP,
boundingSphere
);
}
if (model._upAxis === Axis.Y) {
BoundingSphere.transformWithoutScale(
boundingSphere,
Axis.Y_UP_TO_Z_UP,
boundingSphere
);
} else if (model._upAxis === Axis.X) {
BoundingSphere.transformWithoutScale(
boundingSphere,
Axis.X_UP_TO_Z_UP,
boundingSphere
);
}
return boundingSphere;
};
function techniqueAttributeForSemantic(technique, semantic) {
return ForEach.techniqueAttribute(technique, function (
attribute,
attributeName
) {
if (attribute.semantic === semantic) {
return attributeName;
}
});
}
function ensureSemanticExistenceForPrimitive(gltf, primitive) {
var accessors = gltf.accessors;
var materials = gltf.materials;
var techniquesWebgl = gltf.extensions.KHR_techniques_webgl;
var techniques = techniquesWebgl.techniques;
var programs = techniquesWebgl.programs;
var shaders = techniquesWebgl.shaders;
var targets = primitive.targets;
var attributes = primitive.attributes;
for (var target in targets) {
if (targets.hasOwnProperty(target)) {
var targetAttributes = targets[target];
for (var attribute in targetAttributes) {
if (attribute !== "extras") {
attributes[attribute + "_" + target] = targetAttributes[attribute];
}
}
}
}
var material = materials[primitive.material];
var technique =
techniques[material.extensions.KHR_techniques_webgl.technique];
var program = programs[technique.program];
var vertexShader = shaders[program.vertexShader];
for (var semantic in attributes) {
if (attributes.hasOwnProperty(semantic)) {
if (!defined(techniqueAttributeForSemantic(technique, semantic))) {
var accessorId = attributes[semantic];
var accessor = accessors[accessorId];
var lowerCase = semantic.toLowerCase();
if (lowerCase.charAt(0) === "_") {
lowerCase = lowerCase.slice(1);
}
var attributeName = "a_" + lowerCase;
technique.attributes[attributeName] = {
semantic: semantic,
type: accessor.componentType,
};
var pipelineExtras = vertexShader.extras._pipeline;
var shaderText = pipelineExtras.source;
shaderText =
"attribute " +
ModelUtility.getShaderVariable(accessor.type) +
" " +
attributeName +
";\n" +
shaderText;
pipelineExtras.source = shaderText;
}
}
}
}
/**
* Ensures all attributes present on the primitive are present in the technique and
* vertex shader.
*
* @param {Object} gltf A javascript object containing a glTF asset.
* @returns {Object} The glTF asset, including any additional attributes.
*/
ModelUtility.ensureSemanticExistence = function (gltf) {
ForEach.mesh(gltf, function (mesh) {
ForEach.meshPrimitive(mesh, function (primitive) {
ensureSemanticExistenceForPrimitive(gltf, primitive);
});
});
return gltf;
};
/**
* Creates attribute location for all attributes required by a technique.
*
* @param {Object} technique A glTF KHR_techniques_webgl technique object.
* @param {Object} precreatedAttributes A dictionary object of pre-created attributes for which to also create locations.
* @returns {Object} A dictionary object containing attribute names and their locations.
*/
ModelUtility.createAttributeLocations = function (
technique,
precreatedAttributes
) {
var attributeLocations = {};
var hasIndex0 = false;
var i = 1;
ForEach.techniqueAttribute(technique, function (attribute, attributeName) {
// Set the position attribute to the 0th index. In some WebGL implementations the shader
// will not work correctly if the 0th attribute is not active. For example, some glTF models
// list the normal attribute first but derived shaders like the cast-shadows shader do not use
// the normal attribute.
if (/pos/i.test(attributeName) && !hasIndex0) {
attributeLocations[attributeName] = 0;
hasIndex0 = true;
} else {
attributeLocations[attributeName] = i++;
}
});
if (defined(precreatedAttributes)) {
for (var attributeName in precreatedAttributes) {
if (precreatedAttributes.hasOwnProperty(attributeName)) {
attributeLocations[attributeName] = i++;
}
}
}
return attributeLocations;
};
ModelUtility.getAccessorMinMax = function (gltf, accessorId) {
var accessor = gltf.accessors[accessorId];
var extensions = accessor.extensions;
var accessorMin = accessor.min;
var accessorMax = accessor.max;
// If this accessor is quantized, we should use the decoded min and max
if (defined(extensions)) {
var quantizedAttributes = extensions.WEB3D_quantized_attributes;
if (defined(quantizedAttributes)) {
accessorMin = quantizedAttributes.decodedMin;
accessorMax = quantizedAttributes.decodedMax;
}
}
return {
min: accessorMin,
max: accessorMax,
};
};
function getTechniqueAttributeOrUniformFunction(
gltf,
technique,
semantic,
ignoreNodes
) {
if (hasExtension(gltf, "KHR_techniques_webgl")) {
return function (attributeOrUniform, attributeOrUniformName) {
if (
attributeOrUniform.semantic === semantic &&
(!ignoreNodes || !defined(attributeOrUniform.node))
) {
return attributeOrUniformName;
}
};
}
return function (parameterName, attributeOrUniformName) {
var attributeOrUniform = technique.parameters[parameterName];
if (
attributeOrUniform.semantic === semantic &&
(!ignoreNodes || !defined(attributeOrUniform.node))
) {
return attributeOrUniformName;
}
};
}
ModelUtility.getAttributeOrUniformBySemantic = function (
gltf,
semantic,
programId,
ignoreNodes
) {
return ForEach.technique(gltf, function (technique) {
if (defined(programId) && technique.program !== programId) {
return;
}
var value = ForEach.techniqueAttribute(
technique,
getTechniqueAttributeOrUniformFunction(
gltf,
technique,
semantic,
ignoreNodes
)
);
if (defined(value)) {
return value;
}
return ForEach.techniqueUniform(
technique,
getTechniqueAttributeOrUniformFunction(
gltf,
technique,
semantic,
ignoreNodes
)
);
});
};
ModelUtility.getDiffuseAttributeOrUniform = function (gltf, programId) {
var diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(
gltf,
"COLOR_0",
programId
);
if (!defined(diffuseUniformName)) {
diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(
gltf,
"_3DTILESDIFFUSE",
programId
);
}
return diffuseUniformName;
};
var nodeTranslationScratch = new Cartesian3();
var nodeQuaternionScratch = new Quaternion();
var nodeScaleScratch = new Cartesian3();
ModelUtility.getTransform = function (node, result) {
if (defined(node.matrix)) {
return Matrix4.fromColumnMajorArray(node.matrix, result);
}
return Matrix4.fromTranslationQuaternionRotationScale(
Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch),
Quaternion.unpack(node.rotation, 0, nodeQuaternionScratch),
Cartesian3.fromArray(node.scale, 0, nodeScaleScratch),
result
);
};
ModelUtility.getUsedExtensions = function (gltf) {
var extensionsUsed = gltf.extensionsUsed;
var cachedExtensionsUsed = {};
if (defined(extensionsUsed)) {
var extensionsUsedLength = extensionsUsed.length;
for (var i = 0; i < extensionsUsedLength; i++) {
var extension = extensionsUsed[i];
cachedExtensionsUsed[extension] = true;
}
}
return cachedExtensionsUsed;
};
ModelUtility.getRequiredExtensions = function (gltf) {
var extensionsRequired = gltf.extensionsRequired;
var cachedExtensionsRequired = {};
if (defined(extensionsRequired)) {
var extensionsRequiredLength = extensionsRequired.length;
for (var i = 0; i < extensionsRequiredLength; i++) {
var extension = extensionsRequired[i];
cachedExtensionsRequired[extension] = true;
}
}
return cachedExtensionsRequired;
};
ModelUtility.supportedExtensions = {
AGI_articulations: true,
CESIUM_RTC: true,
EXT_texture_webp: true,
KHR_blend: true,
KHR_binary_glTF: true,
KHR_draco_mesh_compression: true,
KHR_materials_common: true,
KHR_techniques_webgl: true,
KHR_materials_unlit: true,
KHR_materials_pbrSpecularGlossiness: true,
KHR_texture_transform: true,
WEB3D_quantized_attributes: true,
};
ModelUtility.checkSupportedExtensions = function (
extensionsRequired,
browserSupportsWebp
) {
for (var extension in extensionsRequired) {
if (extensionsRequired.hasOwnProperty(extension)) {
if (!ModelUtility.supportedExtensions[extension]) {
throw new RuntimeError("Unsupported glTF Extension: " + extension);
}
if (extension === "EXT_texture_webp" && browserSupportsWebp === false) {
throw new RuntimeError(
"Loaded model requires WebP but browser does not support it."
);
}
}
}
};
ModelUtility.checkSupportedGlExtensions = function (extensionsUsed, context) {
if (defined(extensionsUsed)) {
var glExtensionsUsedLength = extensionsUsed.length;
for (var i = 0; i < glExtensionsUsedLength; i++) {
var extension = extensionsUsed[i];
if (extension !== "OES_element_index_uint") {
throw new RuntimeError("Unsupported WebGL Extension: " + extension);
} else if (!context.elementIndexUint) {
throw new RuntimeError(
"OES_element_index_uint WebGL extension is not enabled."
);
}
}
}
};
function replaceAllButFirstInString(string, find, replace) {
// Limit search to strings that are not a subset of other tokens.
find += "(?!\\w)";
find = new RegExp(find, "g");
var index = string.search(find);
return string.replace(find, function (match, offset) {
return index === offset ? match : replace;
});
}
function getQuantizedAttributes(gltf, accessorId) {
var accessor = gltf.accessors[accessorId];
var extensions = accessor.extensions;
if (defined(extensions)) {
return extensions.WEB3D_quantized_attributes;
}
return undefined;
}
function getAttributeVariableName(gltf, primitive, attributeSemantic) {
var materialId = primitive.material;
var material = gltf.materials[materialId];
if (
!hasExtension(gltf, "KHR_techniques_webgl") ||
!defined(material.extensions) ||
!defined(material.extensions.KHR_techniques_webgl)
) {
return;
}
var techniqueId = material.extensions.KHR_techniques_webgl.technique;
var techniquesWebgl = gltf.extensions.KHR_techniques_webgl;
var technique = techniquesWebgl.techniques[techniqueId];
return ForEach.techniqueAttribute(technique, function (
attribute,
attributeName
) {
var semantic = attribute.semantic;
if (semantic === attributeSemantic) {
return attributeName;
}
});
}
ModelUtility.modifyShaderForDracoQuantizedAttributes = function (
gltf,
primitive,
shader,
decodedAttributes
) {
var quantizedUniforms = {};
for (var attributeSemantic in decodedAttributes) {
if (decodedAttributes.hasOwnProperty(attributeSemantic)) {
var attribute = decodedAttributes[attributeSemantic];
var quantization = attribute.quantization;
if (!defined(quantization)) {
continue;
}
var attributeVarName = getAttributeVariableName(
gltf,
primitive,
attributeSemantic
);
if (attributeSemantic.charAt(0) === "_") {
attributeSemantic = attributeSemantic.substring(1);
}
var decodeUniformVarName =
"gltf_u_dec_" + attributeSemantic.toLowerCase();
if (!defined(quantizedUniforms[decodeUniformVarName])) {
var newMain = "gltf_decoded_" + attributeSemantic;
var decodedAttributeVarName = attributeVarName.replace(
"a_",
"gltf_a_dec_"
);
var size = attribute.componentsPerAttribute;
// replace usages of the original attribute with the decoded version, but not the declaration
shader = replaceAllButFirstInString(
shader,
attributeVarName,
decodedAttributeVarName
);
// declare decoded attribute
var variableType;
if (quantization.octEncoded) {
variableType = "vec3";
} else if (size > 1) {
variableType = "vec" + size;
} else {
variableType = "float";
}
shader = variableType + " " + decodedAttributeVarName + ";\n" + shader;
// The gltf 2.0 COLOR_0 vertex attribute can be VEC4 or VEC3
var vec3Color = size === 3 && attributeSemantic === "COLOR_0";
if (vec3Color) {
shader = replaceAllButFirstInString(
shader,
decodedAttributeVarName,
"vec4(" + decodedAttributeVarName + ", 1.0)"
);
}
// splice decode function into the shader
var decode = "";
if (quantization.octEncoded) {
var decodeUniformVarNameRangeConstant =
decodeUniformVarName + "_rangeConstant";
shader =
"uniform float " +
decodeUniformVarNameRangeConstant +
";\n" +
shader;
decode =
"\n" +
"void main() {\n" +
// Draco oct-encoding decodes to zxy order
" " +
decodedAttributeVarName +
" = czm_octDecode(" +
attributeVarName +
".xy, " +
decodeUniformVarNameRangeConstant +
").zxy;\n" +
" " +
newMain +
"();\n" +
"}\n";
} else {
var decodeUniformVarNameNormConstant =
decodeUniformVarName + "_normConstant";
var decodeUniformVarNameMin = decodeUniformVarName + "_min";
shader =
"uniform float " +
decodeUniformVarNameNormConstant +
";\n" +
"uniform " +
variableType +
" " +
decodeUniformVarNameMin +
";\n" +
shader;
var attributeVarAccess = vec3Color ? ".xyz" : "";
decode =
"\n" +
"void main() {\n" +
" " +
decodedAttributeVarName +
" = " +
decodeUniformVarNameMin +
" + " +
attributeVarName +
attributeVarAccess +
" * " +
decodeUniformVarNameNormConstant +
";\n" +
" " +
newMain +
"();\n" +
"}\n";
}
shader = ShaderSource.replaceMain(shader, newMain);
shader += decode;
}
}
}
return {
shader: shader,
};
};
ModelUtility.modifyShaderForQuantizedAttributes = function (
gltf,
primitive,
shader
) {
var quantizedUniforms = {};
var attributes = primitive.attributes;
for (var attributeSemantic in attributes) {
if (attributes.hasOwnProperty(attributeSemantic)) {
var attributeVarName = getAttributeVariableName(
gltf,
primitive,
attributeSemantic
);
var accessorId = primitive.attributes[attributeSemantic];
if (attributeSemantic.charAt(0) === "_") {
attributeSemantic = attributeSemantic.substring(1);
}
var decodeUniformVarName =
"gltf_u_dec_" + attributeSemantic.toLowerCase();
var decodeUniformVarNameScale = decodeUniformVarName + "_scale";
var decodeUniformVarNameTranslate = decodeUniformVarName + "_translate";
if (
!defined(quantizedUniforms[decodeUniformVarName]) &&
!defined(quantizedUniforms[decodeUniformVarNameScale])
) {
var quantizedAttributes = getQuantizedAttributes(gltf, accessorId);
if (defined(quantizedAttributes)) {
var decodeMatrix = quantizedAttributes.decodeMatrix;
var newMain = "gltf_decoded_" + attributeSemantic;
var decodedAttributeVarName = attributeVarName.replace(
"a_",
"gltf_a_dec_"
);
var size = Math.floor(Math.sqrt(decodeMatrix.length));
// replace usages of the original attribute with the decoded version, but not the declaration
shader = replaceAllButFirstInString(
shader,
attributeVarName,
decodedAttributeVarName
);
// declare decoded attribute
var variableType;
if (size > 2) {
variableType = "vec" + (size - 1);
} else {
variableType = "float";
}
shader =
variableType + " " + decodedAttributeVarName + ";\n" + shader;
// splice decode function into the shader - attributes are pre-multiplied with the decode matrix
// uniform in the shader (32-bit floating point)
var decode = "";
if (size === 5) {
// separate scale and translate since glsl doesn't have mat5
shader =
"uniform mat4 " + decodeUniformVarNameScale + ";\n" + shader;
shader =
"uniform vec4 " + decodeUniformVarNameTranslate + ";\n" + shader;
decode =
"\n" +
"void main() {\n" +
" " +
decodedAttributeVarName +
" = " +
decodeUniformVarNameScale +
" * " +
attributeVarName +
" + " +
decodeUniformVarNameTranslate +
";\n" +
" " +
newMain +
"();\n" +
"}\n";
quantizedUniforms[decodeUniformVarNameScale] = { mat: 4 };
quantizedUniforms[decodeUniformVarNameTranslate] = { vec: 4 };
} else {
shader =
"uniform mat" +
size +
" " +
decodeUniformVarName +
";\n" +
shader;
decode =
"\n" +
"void main() {\n" +
" " +
decodedAttributeVarName +
" = " +
variableType +
"(" +
decodeUniformVarName +
" * vec" +
size +
"(" +
attributeVarName +
",1.0));\n" +
" " +
newMain +
"();\n" +
"}\n";
quantizedUniforms[decodeUniformVarName] = { mat: size };
}
shader = ShaderSource.replaceMain(shader, newMain);
shader += decode;
}
}
}
}
return {
shader: shader,
uniforms: quantizedUniforms,
};
};
function getScalarUniformFunction(value) {
var that = {
value: value,
clone: function (source, result) {
return source;
},
func: function () {
return that.value;
},
};
return that;
}
function getVec2UniformFunction(value) {
var that = {
value: Cartesian2.fromArray(value),
clone: Cartesian2.clone,
func: function () {
return that.value;
},
};
return that;
}
function getVec3UniformFunction(value) {
var that = {
value: Cartesian3.fromArray(value),
clone: Cartesian3.clone,
func: function () {
return that.value;
},
};
return that;
}
function getVec4UniformFunction(value) {
var that = {
value: Cartesian4.fromArray(value),
clone: Cartesian4.clone,
func: function () {
return that.value;
},
};
return that;
}
function getMat2UniformFunction(value) {
var that = {
value: Matrix2.fromColumnMajorArray(value),
clone: Matrix2.clone,
func: function () {
return that.value;
},
};
return that;
}
function getMat3UniformFunction(value) {
var that = {
value: Matrix3.fromColumnMajorArray(value),
clone: Matrix3.clone,
func: function () {
return that.value;
},
};
return that;
}
function getMat4UniformFunction(value) {
var that = {
value: Matrix4.fromColumnMajorArray(value),
clone: Matrix4.clone,
func: function () {
return that.value;
},
};
return that;
}
///////////////////////////////////////////////////////////////////////////
function DelayLoadedTextureUniform(value, textures, defaultTexture) {
this._value = undefined;
this._textureId = value.index;
this._textures = textures;
this._defaultTexture = defaultTexture;
}
Object.defineProperties(DelayLoadedTextureUniform.prototype, {
value: {
get: function () {
// Use the default texture (1x1 white) until the model's texture is loaded
if (!defined(this._value)) {
var texture = this._textures[this._textureId];
if (defined(texture)) {
this._value = texture;
} else {
return this._defaultTexture;
}
}
return this._value;
},
set: function (value) {
this._value = value;
},
},
});
DelayLoadedTextureUniform.prototype.clone = function (source) {
return source;
};
DelayLoadedTextureUniform.prototype.func = undefined;
///////////////////////////////////////////////////////////////////////////
function getTextureUniformFunction(value, textures, defaultTexture) {
var uniform = new DelayLoadedTextureUniform(value, textures, defaultTexture);
// Define function here to access closure since 'this' can't be
// used when the Renderer sets uniforms.
uniform.func = function () {
return uniform.value;
};
return uniform;
}
var gltfUniformFunctions = {};
gltfUniformFunctions[WebGLConstants.FLOAT] = getScalarUniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_VEC2] = getVec2UniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_VEC3] = getVec3UniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_VEC4] = getVec4UniformFunction;
gltfUniformFunctions[WebGLConstants.INT] = getScalarUniformFunction;
gltfUniformFunctions[WebGLConstants.INT_VEC2] = getVec2UniformFunction;
gltfUniformFunctions[WebGLConstants.INT_VEC3] = getVec3UniformFunction;
gltfUniformFunctions[WebGLConstants.INT_VEC4] = getVec4UniformFunction;
gltfUniformFunctions[WebGLConstants.BOOL] = getScalarUniformFunction;
gltfUniformFunctions[WebGLConstants.BOOL_VEC2] = getVec2UniformFunction;
gltfUniformFunctions[WebGLConstants.BOOL_VEC3] = getVec3UniformFunction;
gltfUniformFunctions[WebGLConstants.BOOL_VEC4] = getVec4UniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_MAT2] = getMat2UniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_MAT3] = getMat3UniformFunction;
gltfUniformFunctions[WebGLConstants.FLOAT_MAT4] = getMat4UniformFunction;
gltfUniformFunctions[WebGLConstants.SAMPLER_2D] = getTextureUniformFunction;
// GLTF_SPEC: Support SAMPLER_CUBE. https://github.com/KhronosGroup/glTF/issues/40
ModelUtility.createUniformFunction = function (
type,
value,
textures,
defaultTexture
) {
return gltfUniformFunctions[type](value, textures, defaultTexture);
};
function scaleFromMatrix5Array(matrix) {
return [
matrix[0],
matrix[1],
matrix[2],
matrix[3],
matrix[5],
matrix[6],
matrix[7],
matrix[8],
matrix[10],
matrix[11],
matrix[12],
matrix[13],
matrix[15],
matrix[16],
matrix[17],
matrix[18],
];
}
function translateFromMatrix5Array(matrix) {
return [matrix[20], matrix[21], matrix[22], matrix[23]];
}
ModelUtility.createUniformsForDracoQuantizedAttributes = function (
decodedAttributes
) {
var uniformMap = {};
for (var attribute in decodedAttributes) {
if (decodedAttributes.hasOwnProperty(attribute)) {
var decodedData = decodedAttributes[attribute];
var quantization = decodedData.quantization;
if (!defined(quantization)) {
continue;
}
if (attribute.charAt(0) === "_") {
attribute = attribute.substring(1);
}
var uniformVarName = "gltf_u_dec_" + attribute.toLowerCase();
if (quantization.octEncoded) {
var uniformVarNameRangeConstant = uniformVarName + "_rangeConstant";
var rangeConstant = (1 << quantization.quantizationBits) - 1.0;
uniformMap[uniformVarNameRangeConstant] = getScalarUniformFunction(
rangeConstant
).func;
continue;
}
var uniformVarNameNormConstant = uniformVarName + "_normConstant";
var normConstant =
quantization.range / (1 << quantization.quantizationBits);
uniformMap[uniformVarNameNormConstant] = getScalarUniformFunction(
normConstant
).func;
var uniformVarNameMin = uniformVarName + "_min";
switch (decodedData.componentsPerAttribute) {
case 1:
uniformMap[uniformVarNameMin] = getScalarUniformFunction(
quantization.minValues
).func;
break;
case 2:
uniformMap[uniformVarNameMin] = getVec2UniformFunction(
quantization.minValues
).func;
break;
case 3:
uniformMap[uniformVarNameMin] = getVec3UniformFunction(
quantization.minValues
).func;
break;
case 4:
uniformMap[uniformVarNameMin] = getVec4UniformFunction(
quantization.minValues
).func;
break;
}
}
}
return uniformMap;
};
ModelUtility.createUniformsForQuantizedAttributes = function (
gltf,
primitive,
quantizedUniforms
) {
var accessors = gltf.accessors;
var setUniforms = {};
var uniformMap = {};
var attributes = primitive.attributes;
for (var attribute in attributes) {
if (attributes.hasOwnProperty(attribute)) {
var accessorId = attributes[attribute];
var a = accessors[accessorId];
var extensions = a.extensions;
if (attribute.charAt(0) === "_") {
attribute = attribute.substring(1);
}
if (defined(extensions)) {
var quantizedAttributes = extensions.WEB3D_quantized_attributes;
if (defined(quantizedAttributes)) {
var decodeMatrix = quantizedAttributes.decodeMatrix;
var uniformVariable = "gltf_u_dec_" + attribute.toLowerCase();
switch (a.type) {
case AttributeType.SCALAR:
uniformMap[uniformVariable] = getMat2UniformFunction(
decodeMatrix
).func;
setUniforms[uniformVariable] = true;
break;
case AttributeType.VEC2:
uniformMap[uniformVariable] = getMat3UniformFunction(
decodeMatrix
).func;
setUniforms[uniformVariable] = true;
break;
case AttributeType.VEC3:
uniformMap[uniformVariable] = getMat4UniformFunction(
decodeMatrix
).func;
setUniforms[uniformVariable] = true;
break;
case AttributeType.VEC4:
// VEC4 attributes are split into scale and translate because there is no mat5 in GLSL
var uniformVariableScale = uniformVariable + "_scale";
var uniformVariableTranslate = uniformVariable + "_translate";
uniformMap[uniformVariableScale] = getMat4UniformFunction(
scaleFromMatrix5Array(decodeMatrix)
).func;
uniformMap[uniformVariableTranslate] = getVec4UniformFunction(
translateFromMatrix5Array(decodeMatrix)
).func;
setUniforms[uniformVariableScale] = true;
setUniforms[uniformVariableTranslate] = true;
break;
}
}
}
}
}
// If there are any unset quantized uniforms in this program, they should be set to the identity
for (var quantizedUniform in quantizedUniforms) {
if (quantizedUniforms.hasOwnProperty(quantizedUniform)) {
if (!setUniforms[quantizedUniform]) {
var properties = quantizedUniforms[quantizedUniform];
if (defined(properties.mat)) {
if (properties.mat === 2) {
uniformMap[quantizedUniform] = getMat2UniformFunction(
Matrix2.IDENTITY
).func;
} else if (properties.mat === 3) {
uniformMap[quantizedUniform] = getMat3UniformFunction(
Matrix3.IDENTITY
).func;
} else if (properties.mat === 4) {
uniformMap[quantizedUniform] = getMat4UniformFunction(
Matrix4.IDENTITY
).func;
}
}
if (defined(properties.vec)) {
if (properties.vec === 4) {
uniformMap[quantizedUniform] = getVec4UniformFunction([
0,
0,
0,
0,
]).func;
}
}
}
}
}
return uniformMap;
};
// This doesn't support LOCAL, which we could add if it is ever used.
var scratchTranslationRtc = new Cartesian3();
var gltfSemanticUniforms = {
MODEL: function (uniformState, model) {
return function () {
return uniformState.model;
};
},
VIEW: function (uniformState, model) {
return function () {
return uniformState.view;
};
},
PROJECTION: function (uniformState, model) {
return function () {
return uniformState.projection;
};
},
MODELVIEW: function (uniformState, model) {
return function () {
return uniformState.modelView;
};
},
CESIUM_RTC_MODELVIEW: function (uniformState, model) {
// CESIUM_RTC extension
var mvRtc = new Matrix4();
return function () {
if (defined(model._rtcCenter)) {
Matrix4.getTranslation(uniformState.model, scratchTranslationRtc);
Cartesian3.add(
scratchTranslationRtc,
model._rtcCenter,
scratchTranslationRtc
);
Matrix4.multiplyByPoint(
uniformState.view,
scratchTranslationRtc,
scratchTranslationRtc
);
return Matrix4.setTranslation(
uniformState.modelView,
scratchTranslationRtc,
mvRtc
);
}
return uniformState.modelView;
};
},
MODELVIEWPROJECTION: function (uniformState, model) {
return function () {
return uniformState.modelViewProjection;
};
},
MODELINVERSE: function (uniformState, model) {
return function () {
return uniformState.inverseModel;
};
},
VIEWINVERSE: function (uniformState, model) {
return function () {
return uniformState.inverseView;
};
},
PROJECTIONINVERSE: function (uniformState, model) {
return function () {
return uniformState.inverseProjection;
};
},
MODELVIEWINVERSE: function (uniformState, model) {
return function () {
return uniformState.inverseModelView;
};
},
MODELVIEWPROJECTIONINVERSE: function (uniformState, model) {
return function () {
return uniformState.inverseModelViewProjection;
};
},
MODELINVERSETRANSPOSE: function (uniformState, model) {
return function () {
return uniformState.inverseTransposeModel;
};
},
MODELVIEWINVERSETRANSPOSE: function (uniformState, model) {
return function () {
return uniformState.normal;
};
},
VIEWPORT: function (uniformState, model) {
return function () {
return uniformState.viewportCartesian4;
};
},
// JOINTMATRIX created in createCommand()
};
ModelUtility.getGltfSemanticUniforms = function () {
return gltfSemanticUniforms;
};
export default ModelUtility;