| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296 | 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 "./GltfPipeline/addToArray.js";import ForEach from "./GltfPipeline/ForEach.js";import usesExtension from "./GltfPipeline/usesExtension.js";import AttributeType from "./AttributeType.js";import Axis from "./Axis.js";/** * @private */const 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) {  const 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) {  const accessors = gltf.accessors;  const materials = gltf.materials;  const primitiveInfoByMaterial = {};  ForEach.mesh(gltf, function (mesh) {    ForEach.meshPrimitive(mesh, function (primitive) {      let materialIndex = primitive.material;      const material = materials[materialIndex];      const jointAccessorId = primitive.attributes.JOINTS_0;      let componentType;      let accessorType;      if (defined(jointAccessorId)) {        const jointAccessor = accessors[jointAccessorId];        componentType = jointAccessor.componentType;        accessorType = jointAccessor.type;      }      const isSkinned = defined(jointAccessorId) && accessorType === "VEC4";      const hasVertexColors = defined(primitive.attributes.COLOR_0);      const hasMorphTargets = defined(primitive.targets);      const hasNormals = defined(primitive.attributes.NORMAL);      const hasTangents = defined(primitive.attributes.TANGENT);      const hasTexCoords = defined(primitive.attributes.TEXCOORD_0);      const hasTexCoord1 =        hasTexCoords && defined(primitive.attributes.TEXCOORD_1);      const hasOutline =        defined(primitive.extensions) &&        defined(primitive.extensions.CESIUM_primitive_outline);      const 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.        const 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;    let message = `Failed to load ${type}: ${path}`;    if (defined(error)) {      message += `\n${error.message}`;    }    model._readyPromise.reject(new RuntimeError(message));  };};ModelUtility.parseBuffers = function (model, bufferLoad) {  const 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)) {      const bufferResource = model._resource.getDerivedResource({        url: buffer.uri,      });      ++loadResources.pendingBufferLoads;      bufferResource        .fetchArrayBuffer()        .then(bufferLoad(model, bufferViewId))        .catch(          ModelUtility.getFailedLoadFunction(            model,            "buffer",            bufferResource.url          )        );    }  });};const aMinScratch = new Cartesian3();const aMaxScratch = new Cartesian3();ModelUtility.computeBoundingSphere = function (model) {  const gltf = model.gltf;  const gltfNodes = gltf.nodes;  const gltfMeshes = gltf.meshes;  const rootNodes = gltf.scenes[gltf.scene].nodes;  const rootNodesLength = rootNodes.length;  const nodeStack = [];  const min = new Cartesian3(    Number.MAX_VALUE,    Number.MAX_VALUE,    Number.MAX_VALUE  );  const max = new Cartesian3(    -Number.MAX_VALUE,    -Number.MAX_VALUE,    -Number.MAX_VALUE  );  for (let i = 0; i < rootNodesLength; ++i) {    let n = gltfNodes[rootNodes[i]];    n._transformToRoot = ModelUtility.getTransform(n);    nodeStack.push(n);    while (nodeStack.length > 0) {      n = nodeStack.pop();      const transformToRoot = n._transformToRoot;      const meshId = n.mesh;      if (defined(meshId)) {        const mesh = gltfMeshes[meshId];        const primitives = mesh.primitives;        const primitivesLength = primitives.length;        for (let m = 0; m < primitivesLength; ++m) {          const positionAccessor = primitives[m].attributes.POSITION;          if (defined(positionAccessor)) {            const minMax = ModelUtility.getAccessorMinMax(              gltf,              positionAccessor            );            if (defined(minMax.min) && defined(minMax.max)) {              const aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);              const 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);            }          }        }      }      const children = n.children;      if (defined(children)) {        const childrenLength = children.length;        for (let k = 0; k < childrenLength; ++k) {          const child = gltfNodes[children[k]];          child._transformToRoot = ModelUtility.getTransform(child);          Matrix4.multiplyTransformation(            transformToRoot,            child._transformToRoot,            child._transformToRoot          );          nodeStack.push(child);        }      }      delete n._transformToRoot;    }  }  const 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) {  const accessors = gltf.accessors;  const materials = gltf.materials;  const techniquesWebgl = gltf.extensions.KHR_techniques_webgl;  const techniques = techniquesWebgl.techniques;  const programs = techniquesWebgl.programs;  const shaders = techniquesWebgl.shaders;  const targets = primitive.targets;  const attributes = primitive.attributes;  for (const target in targets) {    if (targets.hasOwnProperty(target)) {      const targetAttributes = targets[target];      for (const attribute in targetAttributes) {        if (attribute !== "extras") {          attributes[`${attribute}_${target}`] = targetAttributes[attribute];        }      }    }  }  const material = materials[primitive.material];  const technique =    techniques[material.extensions.KHR_techniques_webgl.technique];  const program = programs[technique.program];  const vertexShader = shaders[program.vertexShader];  for (const semantic in attributes) {    if (attributes.hasOwnProperty(semantic)) {      if (!defined(techniqueAttributeForSemantic(technique, semantic))) {        const accessorId = attributes[semantic];        const accessor = accessors[accessorId];        let lowerCase = semantic.toLowerCase();        if (lowerCase.charAt(0) === "_") {          lowerCase = lowerCase.slice(1);        }        const attributeName = `a_${lowerCase}`;        technique.attributes[attributeName] = {          semantic: semantic,          type: accessor.componentType,        };        const pipelineExtras = vertexShader.extras._pipeline;        let 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) {  const attributeLocations = {};  let hasIndex0 = false;  let 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 (const attributeName in precreatedAttributes) {      if (precreatedAttributes.hasOwnProperty(attributeName)) {        attributeLocations[attributeName] = i++;      }    }  }  return attributeLocations;};ModelUtility.getAccessorMinMax = function (gltf, accessorId) {  const accessor = gltf.accessors[accessorId];  const extensions = accessor.extensions;  let accessorMin = accessor.min;  let accessorMax = accessor.max;  // If this accessor is quantized, we should use the decoded min and max  if (defined(extensions)) {    const 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 (usesExtension(gltf, "KHR_techniques_webgl")) {    return function (attributeOrUniform, attributeOrUniformName) {      if (        attributeOrUniform.semantic === semantic &&        (!ignoreNodes || !defined(attributeOrUniform.node))      ) {        return attributeOrUniformName;      }    };  }  return function (parameterName, attributeOrUniformName) {    const 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;    }    const 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) {  let diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(    gltf,    "COLOR_0",    programId  );  if (!defined(diffuseUniformName)) {    diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(      gltf,      "_3DTILESDIFFUSE",      programId    );  }  return diffuseUniformName;};const nodeTranslationScratch = new Cartesian3();const nodeQuaternionScratch = new Quaternion();const 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) {  const extensionsUsed = gltf.extensionsUsed;  const cachedExtensionsUsed = {};  if (defined(extensionsUsed)) {    const extensionsUsedLength = extensionsUsed.length;    for (let i = 0; i < extensionsUsedLength; i++) {      const extension = extensionsUsed[i];      cachedExtensionsUsed[extension] = true;    }  }  return cachedExtensionsUsed;};ModelUtility.getRequiredExtensions = function (gltf) {  const extensionsRequired = gltf.extensionsRequired;  const cachedExtensionsRequired = {};  if (defined(extensionsRequired)) {    const extensionsRequiredLength = extensionsRequired.length;    for (let i = 0; i < extensionsRequiredLength; i++) {      const 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_texture_basisu: 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 (const 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)) {    const glExtensionsUsedLength = extensionsUsed.length;    for (let i = 0; i < glExtensionsUsedLength; i++) {      const 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");  const index = string.search(find);  return string.replace(find, function (match, offset) {    return index === offset ? match : replace;  });}function getQuantizedAttributes(gltf, accessorId) {  const accessor = gltf.accessors[accessorId];  const extensions = accessor.extensions;  if (defined(extensions)) {    return extensions.WEB3D_quantized_attributes;  }  return undefined;}function getAttributeVariableName(gltf, primitive, attributeSemantic) {  const materialId = primitive.material;  const material = gltf.materials[materialId];  if (    !usesExtension(gltf, "KHR_techniques_webgl") ||    !defined(material.extensions) ||    !defined(material.extensions.KHR_techniques_webgl)  ) {    return;  }  const techniqueId = material.extensions.KHR_techniques_webgl.technique;  const techniquesWebgl = gltf.extensions.KHR_techniques_webgl;  const technique = techniquesWebgl.techniques[techniqueId];  return ForEach.techniqueAttribute(technique, function (    attribute,    attributeName  ) {    const semantic = attribute.semantic;    if (semantic === attributeSemantic) {      return attributeName;    }  });}ModelUtility.modifyShaderForDracoQuantizedAttributes = function (  gltf,  primitive,  shader,  decodedAttributes) {  const quantizedUniforms = {};  for (let attributeSemantic in decodedAttributes) {    if (decodedAttributes.hasOwnProperty(attributeSemantic)) {      const attribute = decodedAttributes[attributeSemantic];      const quantization = attribute.quantization;      if (!defined(quantization)) {        continue;      }      const attributeVarName = getAttributeVariableName(        gltf,        primitive,        attributeSemantic      );      if (attributeSemantic.charAt(0) === "_") {        attributeSemantic = attributeSemantic.substring(1);      }      const decodeUniformVarName = `gltf_u_dec_${attributeSemantic.toLowerCase()}`;      if (!defined(quantizedUniforms[decodeUniformVarName])) {        const newMain = `gltf_decoded_${attributeSemantic}`;        const decodedAttributeVarName = attributeVarName.replace(          "a_",          "gltf_a_dec_"        );        const 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        let 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        const vec3Color = size === 3 && attributeSemantic === "COLOR_0";        if (vec3Color) {          shader = replaceAllButFirstInString(            shader,            decodedAttributeVarName,            `vec4(${decodedAttributeVarName}, 1.0)`          );        }        // splice decode function into the shader        let decode = "";        if (quantization.octEncoded) {          const 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 {          const decodeUniformVarNameNormConstant = `${decodeUniformVarName}_normConstant`;          const decodeUniformVarNameMin = `${decodeUniformVarName}_min`;          shader =            `uniform float ${decodeUniformVarNameNormConstant};\n` +            `uniform ${variableType} ${decodeUniformVarNameMin};\n${shader}`;          const 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) {  const quantizedUniforms = {};  const attributes = primitive.attributes;  for (let attributeSemantic in attributes) {    if (attributes.hasOwnProperty(attributeSemantic)) {      const attributeVarName = getAttributeVariableName(        gltf,        primitive,        attributeSemantic      );      const accessorId = primitive.attributes[attributeSemantic];      if (attributeSemantic.charAt(0) === "_") {        attributeSemantic = attributeSemantic.substring(1);      }      const decodeUniformVarName = `gltf_u_dec_${attributeSemantic.toLowerCase()}`;      const decodeUniformVarNameScale = `${decodeUniformVarName}_scale`;      const decodeUniformVarNameTranslate = `${decodeUniformVarName}_translate`;      if (        !defined(quantizedUniforms[decodeUniformVarName]) &&        !defined(quantizedUniforms[decodeUniformVarNameScale])      ) {        const quantizedAttributes = getQuantizedAttributes(gltf, accessorId);        if (defined(quantizedAttributes)) {          const decodeMatrix = quantizedAttributes.decodeMatrix;          const newMain = `gltf_decoded_${attributeSemantic}`;          const decodedAttributeVarName = attributeVarName.replace(            "a_",            "gltf_a_dec_"          );          const 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          let 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)          let 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) {  const that = {    value: value,    clone: function (source, result) {      return source;    },    func: function () {      return that.value;    },  };  return that;}function getVec2UniformFunction(value) {  const that = {    value: Cartesian2.fromArray(value),    clone: Cartesian2.clone,    func: function () {      return that.value;    },  };  return that;}function getVec3UniformFunction(value) {  const that = {    value: Cartesian3.fromArray(value),    clone: Cartesian3.clone,    func: function () {      return that.value;    },  };  return that;}function getVec4UniformFunction(value) {  const that = {    value: Cartesian4.fromArray(value),    clone: Cartesian4.clone,    func: function () {      return that.value;    },  };  return that;}function getMat2UniformFunction(value) {  const that = {    value: Matrix2.fromColumnMajorArray(value),    clone: Matrix2.clone,    func: function () {      return that.value;    },  };  return that;}function getMat3UniformFunction(value) {  const that = {    value: Matrix3.fromColumnMajorArray(value),    clone: Matrix3.clone,    func: function () {      return that.value;    },  };  return that;}function getMat4UniformFunction(value) {  const 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)) {        const 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) {  const 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;}const 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/40ModelUtility.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) {  const uniformMap = {};  for (let attribute in decodedAttributes) {    if (decodedAttributes.hasOwnProperty(attribute)) {      const decodedData = decodedAttributes[attribute];      const quantization = decodedData.quantization;      if (!defined(quantization)) {        continue;      }      if (attribute.charAt(0) === "_") {        attribute = attribute.substring(1);      }      const uniformVarName = `gltf_u_dec_${attribute.toLowerCase()}`;      if (quantization.octEncoded) {        const uniformVarNameRangeConstant = `${uniformVarName}_rangeConstant`;        const rangeConstant = (1 << quantization.quantizationBits) - 1.0;        uniformMap[uniformVarNameRangeConstant] = getScalarUniformFunction(          rangeConstant        ).func;        continue;      }      const uniformVarNameNormConstant = `${uniformVarName}_normConstant`;      const normConstant =        quantization.range / (1 << quantization.quantizationBits);      uniformMap[uniformVarNameNormConstant] = getScalarUniformFunction(        normConstant      ).func;      const 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) {  const accessors = gltf.accessors;  const setUniforms = {};  const uniformMap = {};  const attributes = primitive.attributes;  for (let attribute in attributes) {    if (attributes.hasOwnProperty(attribute)) {      const accessorId = attributes[attribute];      const a = accessors[accessorId];      const extensions = a.extensions;      if (attribute.charAt(0) === "_") {        attribute = attribute.substring(1);      }      if (defined(extensions)) {        const quantizedAttributes = extensions.WEB3D_quantized_attributes;        if (defined(quantizedAttributes)) {          const decodeMatrix = quantizedAttributes.decodeMatrix;          const uniformVariable = `gltf_u_dec_${attribute.toLowerCase()}`;          let uniformVariableScale;          let uniformVariableTranslate;          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              uniformVariableScale = `${uniformVariable}_scale`;              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 (const quantizedUniform in quantizedUniforms) {    if (quantizedUniforms.hasOwnProperty(quantizedUniform)) {      if (!setUniforms[quantizedUniform]) {        const 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.const scratchTranslationRtc = new Cartesian3();const 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    const 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;
 |