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 type;
|
if (defined(jointAccessorId)) {
|
var jointAccessor = accessors[jointAccessorId];
|
componentType = jointAccessor.componentType;
|
type = jointAccessor.type;
|
}
|
var isSkinned = defined(jointAccessorId);
|
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 primitiveInfo = primitiveInfoByMaterial[materialIndex];
|
if (!defined(primitiveInfo)) {
|
primitiveInfoByMaterial[materialIndex] = {
|
skinning: {
|
skinned: isSkinned,
|
componentType: componentType,
|
type: type
|
},
|
hasVertexColors: hasVertexColors,
|
hasMorphTargets: hasMorphTargets,
|
hasNormals: hasNormals,
|
hasTangents: hasTangents,
|
hasTexCoords: hasTexCoords
|
};
|
} else if ((primitiveInfo.skinning.skinned !== isSkinned) ||
|
(primitiveInfo.skinning.type !== type) ||
|
(primitiveInfo.hasVertexColors !== hasVertexColors) ||
|
(primitiveInfo.hasMorphTargets !== hasMorphTargets) ||
|
(primitiveInfo.hasNormals !== hasNormals) ||
|
(primitiveInfo.hasTangents !== hasTangents) ||
|
(primitiveInfo.hasTexCoords !== hasTexCoords)) {
|
// 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
|
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,
|
type: type
|
},
|
hasVertexColors: hasVertexColors,
|
hasMorphTargets: hasMorphTargets,
|
hasNormals: hasNormals,
|
hasTangents: hasTangents,
|
hasTexCoords: hasTexCoords
|
};
|
}
|
});
|
});
|
|
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);
|
var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
|
var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
|
if (defined(min) && defined(max)) {
|
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
|
};
|
};
|
|
ModelUtility.toClipCoordinatesGLSL = function(gltf, shader) {
|
var positionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'POSITION');
|
var decodedPositionName = positionName.replace('a_', 'gltf_a_dec_');
|
if (shader.indexOf(decodedPositionName) !== -1) {
|
positionName = decodedPositionName;
|
}
|
|
var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEWPROJECTION', undefined, true);
|
if (!defined(modelViewProjectionName) || shader.indexOf(modelViewProjectionName) === -1) {
|
var projectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'PROJECTION', undefined, true);
|
var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEW', undefined, true);
|
if (shader.indexOf('czm_instanced_modelView ') !== -1) {
|
modelViewName = 'czm_instanced_modelView';
|
} else if (!defined(modelViewName)) {
|
modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'CESIUM_RTC_MODELVIEW', undefined, true);
|
}
|
modelViewProjectionName = projectionName + ' * ' + modelViewName;
|
}
|
|
return modelViewProjectionName + ' * vec4(' + positionName + '.xyz, 1.0)';
|
};
|
|
ModelUtility.modifyFragmentShaderForLogDepth = function(shader) {
|
shader = ShaderSource.replaceMain(shader, 'czm_depth_main');
|
shader +=
|
'\n' +
|
'void main() \n' +
|
'{ \n' +
|
' czm_depth_main(); \n' +
|
' czm_writeLogDepth(); \n' +
|
'} \n';
|
|
return shader;
|
};
|
|
ModelUtility.modifyVertexShaderForLogDepth = function(shader, toClipCoordinatesGLSL) {
|
shader = ShaderSource.replaceMain(shader, 'czm_depth_main');
|
shader +=
|
'\n' +
|
'void main() \n' +
|
'{ \n' +
|
' czm_depth_main(); \n' +
|
' czm_vertexLogDepth(' + toClipCoordinatesGLSL + '); \n' +
|
'} \n';
|
|
return shader;
|
};
|
|
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;
|