| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198 | import arraySlice from "../Core/arraySlice.js";import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartesian4 from "../Core/Cartesian4.js";import Color from "../Core/Color.js";import combine from "../Core/combine.js";import ComponentDatatype from "../Core/ComponentDatatype.js";import defaultValue from "../Core/defaultValue.js";import defer from "../Core/defer.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import FeatureDetection from "../Core/FeatureDetection.js";import IndexDatatype from "../Core/IndexDatatype.js";import Matrix4 from "../Core/Matrix4.js";import PrimitiveType from "../Core/PrimitiveType.js";import RuntimeError from "../Core/RuntimeError.js";import Transforms from "../Core/Transforms.js";import WebGLConstants from "../Core/WebGLConstants.js";import addDefaults from "./GltfPipeline/addDefaults.js";import ForEach from "./GltfPipeline/ForEach.js";import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js";import numberOfComponentsForType from "./GltfPipeline/numberOfComponentsForType.js";import parseGlb from "./GltfPipeline/parseGlb.js";import updateVersion from "./GltfPipeline/updateVersion.js";import Axis from "./Axis.js";import ModelLoadResources from "./ModelLoadResources.js";import ModelUtility from "./ModelUtility.js";import processModelMaterialsCommon from "./processModelMaterialsCommon.js";import processPbrMaterials from "./processPbrMaterials.js";import SceneMode from "./SceneMode.js";import Vector3DTileBatch from "./Vector3DTileBatch.js";import Vector3DTilePrimitive from "./Vector3DTilePrimitive.js";const boundingSphereCartesian3Scratch = new Cartesian3();const ModelState = ModelUtility.ModelState;////////////////////////////////////////////////////////////////////////////** * A 3D model for classifying other 3D assets based on glTF, the runtime 3D asset format. * This is a special case when a model of a 3D tileset becomes a classifier when setting {@link Cesium3DTileset#classificationType}. * * @alias ClassificationModel * @constructor * * @private * * @param {Object} options Object with the following properties: * @param {ArrayBuffer|Uint8Array} options.gltf A binary glTF buffer. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {ClassificationType} [options.classificationType] What this model will classify. * * @exception {RuntimeError} Only binary glTF is supported. * @exception {RuntimeError} Buffer data must be embedded in the binary glTF. * @exception {RuntimeError} Only one node is supported for classification and it must have a mesh. * @exception {RuntimeError} Only one mesh is supported when using b3dm for classification. * @exception {RuntimeError} Only one primitive per mesh is supported when using b3dm for classification. * @exception {RuntimeError} The mesh must have a position attribute. * @exception {RuntimeError} The mesh must have a batch id attribute. */function ClassificationModel(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  let gltf = options.gltf;  if (gltf instanceof ArrayBuffer) {    gltf = new Uint8Array(gltf);  }  if (gltf instanceof Uint8Array) {    // Parse and update binary glTF    gltf = parseGlb(gltf);    updateVersion(gltf);    addDefaults(gltf);    processModelMaterialsCommon(gltf);    processPbrMaterials(gltf);  } else {    throw new RuntimeError("Only binary glTF is supported as a classifier.");  }  ForEach.buffer(gltf, function (buffer) {    if (!defined(buffer.extras._pipeline.source)) {      throw new RuntimeError(        "Buffer data must be embedded in the binary gltf."      );    }  });  const gltfNodes = gltf.nodes;  const gltfMeshes = gltf.meshes;  const gltfNode = gltfNodes[0];  const meshId = gltfNode.mesh;  if (gltfNodes.length !== 1 || !defined(meshId)) {    throw new RuntimeError(      "Only one node is supported for classification and it must have a mesh."    );  }  if (gltfMeshes.length !== 1) {    throw new RuntimeError(      "Only one mesh is supported when using b3dm for classification."    );  }  const gltfPrimitives = gltfMeshes[0].primitives;  if (gltfPrimitives.length !== 1) {    throw new RuntimeError(      "Only one primitive per mesh is supported when using b3dm for classification."    );  }  const gltfPositionAttribute = gltfPrimitives[0].attributes.POSITION;  if (!defined(gltfPositionAttribute)) {    throw new RuntimeError("The mesh must have a position attribute.");  }  const gltfBatchIdAttribute = gltfPrimitives[0].attributes._BATCHID;  if (!defined(gltfBatchIdAttribute)) {    throw new RuntimeError("The mesh must have a batch id attribute.");  }  this._gltf = gltf;  /**   * Determines if the model primitive will be shown.   *   * @type {Boolean}   *   * @default true   */  this.show = defaultValue(options.show, true);  /**   * The 4x4 transformation matrix that transforms the model from model to world coordinates.   * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.   * Local reference frames can be used by providing a different transformation matrix, like that returned   * by {@link Transforms.eastNorthUpToFixedFrame}.   *   * @type {Matrix4}   *   * @default {@link Matrix4.IDENTITY}   *   * @example   * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);   * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);   */  this.modelMatrix = Matrix4.clone(    defaultValue(options.modelMatrix, Matrix4.IDENTITY)  );  this._modelMatrix = Matrix4.clone(this.modelMatrix);  this._ready = false;  this._readyPromise = defer();  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the bounding sphere for each draw command in the model.  A glTF primitive corresponds   * to one draw command.  A glTF mesh has an array of primitives, often of length one.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowBoundingVolume = defaultValue(    options.debugShowBoundingVolume,    false  );  this._debugShowBoundingVolume = false;  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the model in wireframe.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugWireframe = defaultValue(options.debugWireframe, false);  this._debugWireframe = false;  this._classificationType = options.classificationType;  // Undocumented options  this._vertexShaderLoaded = options.vertexShaderLoaded;  this._classificationShaderLoaded = options.classificationShaderLoaded;  this._uniformMapLoaded = options.uniformMapLoaded;  this._pickIdLoaded = options.pickIdLoaded;  this._ignoreCommands = defaultValue(options.ignoreCommands, false);  this._upAxis = defaultValue(options.upAxis, Axis.Y);  this._batchTable = options.batchTable;  this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and axis  this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins  this._boundingSphere = undefined;  this._scaledBoundingSphere = new BoundingSphere();  this._state = ModelState.NEEDS_LOAD;  this._loadResources = undefined;  this._mode = undefined;  this._dirty = false; // true when the model was transformed this frame  this._nodeMatrix = new Matrix4();  this._primitive = undefined;  this._extensionsUsed = undefined; // Cached used glTF extensions  this._extensionsRequired = undefined; // Cached required glTF extensions  this._quantizedUniforms = undefined; // Quantized uniforms for WEB3D_quantized_attributes  this._buffers = {};  this._vertexArray = undefined;  this._shaderProgram = undefined;  this._uniformMap = undefined;  this._geometryByteLength = 0;  this._trianglesLength = 0;  // CESIUM_RTC extension  this._rtcCenter = undefined; // reference to either 3D or 2D  this._rtcCenterEye = undefined; // in eye coordinates  this._rtcCenter3D = undefined; // in world coordinates  this._rtcCenter2D = undefined; // in projected world coordinates}Object.defineProperties(ClassificationModel.prototype, {  /**   * The object for the glTF JSON, including properties with default values omitted   * from the JSON provided to this model.   *   * @memberof ClassificationModel.prototype   *   * @type {Object}   * @readonly   *   * @default undefined   */  gltf: {    get: function () {      return this._gltf;    },  },  /**   * The model's bounding sphere in its local coordinate system.   *   * @memberof ClassificationModel.prototype   *   * @type {BoundingSphere}   * @readonly   *   * @default undefined   *   * @exception {DeveloperError} The model is not loaded.  Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true.   *   * @example   * // Center in WGS84 coordinates   * const center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());   */  boundingSphere: {    get: function () {      //>>includeStart('debug', pragmas.debug);      if (this._state !== ModelState.LOADED) {        throw new DeveloperError(          "The model is not loaded.  Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true."        );      }      //>>includeEnd('debug');      const modelMatrix = this.modelMatrix;      const nonUniformScale = Matrix4.getScale(        modelMatrix,        boundingSphereCartesian3Scratch      );      const scaledBoundingSphere = this._scaledBoundingSphere;      scaledBoundingSphere.center = Cartesian3.multiplyComponents(        this._boundingSphere.center,        nonUniformScale,        scaledBoundingSphere.center      );      scaledBoundingSphere.radius =        Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;      if (defined(this._rtcCenter)) {        Cartesian3.add(          this._rtcCenter,          scaledBoundingSphere.center,          scaledBoundingSphere.center        );      }      return scaledBoundingSphere;    },  },  /**   * When <code>true</code>, this model is ready to render, i.e., the external binary, image,   * and shader files were downloaded and the WebGL resources were created.  This is set to   * <code>true</code> right before {@link ClassificationModel#readyPromise} is resolved.   *   * @memberof ClassificationModel.prototype   *   * @type {Boolean}   * @readonly   *   * @default false   */  ready: {    get: function () {      return this._ready;    },  },  /**   * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,   * and shader files were downloaded and the WebGL resources were created.   * <p>   * This promise is resolved at the end of the frame before the first frame the model is rendered in.   * </p>   *   * @memberof ClassificationModel.prototype   * @type {Promise.<ClassificationModel>}   * @readonly   *   * @see ClassificationModel#ready   */  readyPromise: {    get: function () {      return this._readyPromise.promise;    },  },  /**   * Returns true if the model was transformed this frame   *   * @memberof ClassificationModel.prototype   *   * @type {Boolean}   * @readonly   *   * @private   */  dirty: {    get: function () {      return this._dirty;    },  },  /**   * Returns an object with all of the glTF extensions used.   *   * @memberof ClassificationModel.prototype   *   * @type {Object}   * @readonly   */  extensionsUsed: {    get: function () {      if (!defined(this._extensionsUsed)) {        this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf);      }      return this._extensionsUsed;    },  },  /**   * Returns an object with all of the glTF extensions required.   *   * @memberof ClassificationModel.prototype   *   * @type {Object}   * @readonly   */  extensionsRequired: {    get: function () {      if (!defined(this._extensionsRequired)) {        this._extensionsRequired = ModelUtility.getRequiredExtensions(          this.gltf        );      }      return this._extensionsRequired;    },  },  /**   * Gets the model's up-axis.   * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.   *   * @memberof ClassificationModel.prototype   *   * @type {Number}   * @default Axis.Y   * @readonly   *   * @private   */  upAxis: {    get: function () {      return this._upAxis;    },  },  /**   * Gets the model's triangle count.   *   * @private   */  trianglesLength: {    get: function () {      return this._trianglesLength;    },  },  /**   * Gets the model's geometry memory in bytes. This includes all vertex and index buffers.   *   * @private   */  geometryByteLength: {    get: function () {      return this._geometryByteLength;    },  },  /**   * Gets the model's texture memory in bytes.   *   * @private   */  texturesByteLength: {    get: function () {      return 0;    },  },  /**   * Gets the model's classification type.   * @memberof ClassificationModel.prototype   * @type {ClassificationType}   */  classificationType: {    get: function () {      return this._classificationType;    },  },});///////////////////////////////////////////////////////////////////////////function addBuffersToLoadResources(model) {  const gltf = model.gltf;  const loadResources = model._loadResources;  ForEach.buffer(gltf, function (buffer, id) {    loadResources.buffers[id] = buffer.extras._pipeline.source;  });}function parseBufferViews(model) {  const bufferViews = model.gltf.bufferViews;  const vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate;  // Only ARRAY_BUFFER here.  ELEMENT_ARRAY_BUFFER created below.  ForEach.bufferView(model.gltf, function (bufferView, id) {    if (bufferView.target === WebGLConstants.ARRAY_BUFFER) {      vertexBuffersToCreate.enqueue(id);    }  });  const indexBuffersToCreate = model._loadResources.indexBuffersToCreate;  const indexBufferIds = {};  // The Cesium Renderer requires knowing the datatype for an index buffer  // at creation type, which is not part of the glTF bufferview so loop  // through glTF accessors to create the bufferview's index buffer.  ForEach.accessor(model.gltf, function (accessor) {    const bufferViewId = accessor.bufferView;    const bufferView = bufferViews[bufferViewId];    if (      bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER &&      !defined(indexBufferIds[bufferViewId])    ) {      indexBufferIds[bufferViewId] = true;      indexBuffersToCreate.enqueue({        id: bufferViewId,        componentType: accessor.componentType,      });    }  });}function createVertexBuffer(bufferViewId, model) {  const loadResources = model._loadResources;  const bufferViews = model.gltf.bufferViews;  const bufferView = bufferViews[bufferViewId];  const vertexBuffer = loadResources.getBuffer(bufferView);  model._buffers[bufferViewId] = vertexBuffer;  model._geometryByteLength += vertexBuffer.byteLength;}function createIndexBuffer(bufferViewId, componentType, model) {  const loadResources = model._loadResources;  const bufferViews = model.gltf.bufferViews;  const bufferView = bufferViews[bufferViewId];  const indexBuffer = {    typedArray: loadResources.getBuffer(bufferView),    indexDatatype: componentType,  };  model._buffers[bufferViewId] = indexBuffer;  model._geometryByteLength += indexBuffer.typedArray.byteLength;}function createBuffers(model) {  const loadResources = model._loadResources;  if (loadResources.pendingBufferLoads !== 0) {    return;  }  const vertexBuffersToCreate = loadResources.vertexBuffersToCreate;  const indexBuffersToCreate = loadResources.indexBuffersToCreate;  while (vertexBuffersToCreate.length > 0) {    createVertexBuffer(vertexBuffersToCreate.dequeue(), model);  }  while (indexBuffersToCreate.length > 0) {    const i = indexBuffersToCreate.dequeue();    createIndexBuffer(i.id, i.componentType, model);  }}function modifyShaderForQuantizedAttributes(shader, model) {  const primitive = model.gltf.meshes[0].primitives[0];  const result = ModelUtility.modifyShaderForQuantizedAttributes(    model.gltf,    primitive,    shader  );  model._quantizedUniforms = result.uniforms;  return result.shader;}function modifyShader(shader, callback) {  if (defined(callback)) {    shader = callback(shader);  }  return shader;}function createProgram(model) {  const gltf = model.gltf;  const positionName = ModelUtility.getAttributeOrUniformBySemantic(    gltf,    "POSITION"  );  const batchIdName = ModelUtility.getAttributeOrUniformBySemantic(    gltf,    "_BATCHID"  );  const attributeLocations = {};  attributeLocations[positionName] = 0;  attributeLocations[batchIdName] = 1;  const modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(    gltf,    "MODELVIEWPROJECTION"  );  let uniformDecl;  let toClip;  if (!defined(modelViewProjectionName)) {    const projectionName = ModelUtility.getAttributeOrUniformBySemantic(      gltf,      "PROJECTION"    );    let modelViewName = ModelUtility.getAttributeOrUniformBySemantic(      gltf,      "MODELVIEW"    );    if (!defined(modelViewName)) {      modelViewName = ModelUtility.getAttributeOrUniformBySemantic(        gltf,        "CESIUM_RTC_MODELVIEW"      );    }    uniformDecl =      `uniform mat4 ${modelViewName};\n` + `uniform mat4 ${projectionName};\n`;    toClip = `${projectionName} * ${modelViewName} * vec4(${positionName}, 1.0)`;  } else {    uniformDecl = `uniform mat4 ${modelViewProjectionName};\n`;    toClip = `${modelViewProjectionName} * vec4(${positionName}, 1.0)`;  }  const computePosition = `    vec4 positionInClipCoords = ${toClip};\n`;  let vs =    `attribute vec3 ${positionName};\n` +    `attribute float ${batchIdName};\n${uniformDecl}void main() {\n${computePosition}    gl_Position = czm_depthClamp(positionInClipCoords);\n` +    `}\n`;  const fs =    "#ifdef GL_EXT_frag_depth\n" +    "#extension GL_EXT_frag_depth : enable\n" +    "#endif\n" +    "void main() \n" +    "{ \n" +    "    gl_FragColor = vec4(1.0); \n" +    "    czm_writeDepthClamp();\n" +    "}\n";  if (model.extensionsUsed.WEB3D_quantized_attributes) {    vs = modifyShaderForQuantizedAttributes(vs, model);  }  const drawVS = modifyShader(vs, model._vertexShaderLoaded);  const drawFS = modifyShader(fs, model._classificationShaderLoaded);  model._shaderProgram = {    vertexShaderSource: drawVS,    fragmentShaderSource: drawFS,    attributeLocations: attributeLocations,  };}function getAttributeLocations() {  return {    POSITION: 0,    _BATCHID: 1,  };}function createVertexArray(model) {  const loadResources = model._loadResources;  if (!loadResources.finishedBuffersCreation() || defined(model._vertexArray)) {    return;  }  const rendererBuffers = model._buffers;  const gltf = model.gltf;  const accessors = gltf.accessors;  const meshes = gltf.meshes;  const primitives = meshes[0].primitives;  const primitive = primitives[0];  const attributeLocations = getAttributeLocations();  const attributes = {};  ForEach.meshPrimitiveAttribute(primitive, function (    accessorId,    attributeName  ) {    // Skip if the attribute is not used by the material, e.g., because the asset    // was exported with an attribute that wasn't used and the asset wasn't optimized.    const attributeLocation = attributeLocations[attributeName];    if (defined(attributeLocation)) {      const a = accessors[accessorId];      attributes[attributeName] = {        index: attributeLocation,        vertexBuffer: rendererBuffers[a.bufferView],        componentsPerAttribute: numberOfComponentsForType(a.type),        componentDatatype: a.componentType,        offsetInBytes: a.byteOffset,        strideInBytes: getAccessorByteStride(gltf, a),      };    }  });  let indexBuffer;  if (defined(primitive.indices)) {    const accessor = accessors[primitive.indices];    indexBuffer = rendererBuffers[accessor.bufferView];  }  model._vertexArray = {    attributes: attributes,    indexBuffer: indexBuffer,  };}const gltfSemanticUniforms = {  PROJECTION: function (uniformState, model) {    return ModelUtility.getGltfSemanticUniforms().PROJECTION(      uniformState,      model    );  },  MODELVIEW: function (uniformState, model) {    return ModelUtility.getGltfSemanticUniforms().MODELVIEW(      uniformState,      model    );  },  CESIUM_RTC_MODELVIEW: function (uniformState, model) {    return ModelUtility.getGltfSemanticUniforms().CESIUM_RTC_MODELVIEW(      uniformState,      model    );  },  MODELVIEWPROJECTION: function (uniformState, model) {    return ModelUtility.getGltfSemanticUniforms().MODELVIEWPROJECTION(      uniformState,      model    );  },};function createUniformMap(model, context) {  if (defined(model._uniformMap)) {    return;  }  const uniformMap = {};  ForEach.technique(model.gltf, function (technique) {    ForEach.techniqueUniform(technique, function (uniform, uniformName) {      if (        !defined(uniform.semantic) ||        !defined(gltfSemanticUniforms[uniform.semantic])      ) {        return;      }      uniformMap[uniformName] = gltfSemanticUniforms[uniform.semantic](        context.uniformState,        model      );    });  });  model._uniformMap = uniformMap;}function createUniformsForQuantizedAttributes(model, primitive) {  return ModelUtility.createUniformsForQuantizedAttributes(    model.gltf,    primitive,    model._quantizedUniforms  );}function triangleCountFromPrimitiveIndices(primitive, indicesCount) {  switch (primitive.mode) {    case PrimitiveType.TRIANGLES:      return indicesCount / 3;    case PrimitiveType.TRIANGLE_STRIP:    case PrimitiveType.TRIANGLE_FAN:      return Math.max(indicesCount - 2, 0);    default:      return 0;  }}function createPrimitive(model) {  const batchTable = model._batchTable;  let uniformMap = model._uniformMap;  const vertexArray = model._vertexArray;  const gltf = model.gltf;  const accessors = gltf.accessors;  const gltfMeshes = gltf.meshes;  const primitive = gltfMeshes[0].primitives[0];  const ix = accessors[primitive.indices];  const positionAccessor = primitive.attributes.POSITION;  const minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);  const boundingSphere = BoundingSphere.fromCornerPoints(    Cartesian3.fromArray(minMax.min),    Cartesian3.fromArray(minMax.max)  );  let offset;  let count;  if (defined(ix)) {    count = ix.count;    offset = ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType); // glTF has offset in bytes.  Cesium has offsets in indices  } else {    const positions = accessors[primitive.attributes.POSITION];    count = positions.count;    offset = 0;  }  // Update model triangle count using number of indices  model._trianglesLength += triangleCountFromPrimitiveIndices(primitive, count);  // Allow callback to modify the uniformMap  if (defined(model._uniformMapLoaded)) {    uniformMap = model._uniformMapLoaded(uniformMap);  }  // Add uniforms for decoding quantized attributes if used  if (model.extensionsUsed.WEB3D_quantized_attributes) {    const quantizedUniformMap = createUniformsForQuantizedAttributes(      model,      primitive    );    uniformMap = combine(uniformMap, quantizedUniformMap);  }  let attribute = vertexArray.attributes.POSITION;  let componentDatatype = attribute.componentDatatype;  let typedArray = attribute.vertexBuffer;  let byteOffset = typedArray.byteOffset;  let bufferLength =    typedArray.byteLength / ComponentDatatype.getSizeInBytes(componentDatatype);  let positionsBuffer = ComponentDatatype.createArrayBufferView(    componentDatatype,    typedArray.buffer,    byteOffset,    bufferLength  );  attribute = vertexArray.attributes._BATCHID;  componentDatatype = attribute.componentDatatype;  typedArray = attribute.vertexBuffer;  byteOffset = typedArray.byteOffset;  bufferLength =    typedArray.byteLength / ComponentDatatype.getSizeInBytes(componentDatatype);  let vertexBatchIds = ComponentDatatype.createArrayBufferView(    componentDatatype,    typedArray.buffer,    byteOffset,    bufferLength  );  const buffer = vertexArray.indexBuffer.typedArray;  let indices;  if (vertexArray.indexBuffer.indexDatatype === IndexDatatype.UNSIGNED_SHORT) {    indices = new Uint16Array(      buffer.buffer,      buffer.byteOffset,      buffer.byteLength / Uint16Array.BYTES_PER_ELEMENT    );  } else {    indices = new Uint32Array(      buffer.buffer,      buffer.byteOffset,      buffer.byteLength / Uint32Array.BYTES_PER_ELEMENT    );  }  positionsBuffer = arraySlice(positionsBuffer);  vertexBatchIds = arraySlice(vertexBatchIds);  indices = arraySlice(indices, offset, offset + count);  const batchIds = [];  const indexCounts = [];  const indexOffsets = [];  const batchedIndices = [];  let currentId = vertexBatchIds[indices[0]];  batchIds.push(currentId);  indexOffsets.push(0);  let batchId;  let indexOffset;  let indexCount;  const indicesLength = indices.length;  for (let j = 1; j < indicesLength; ++j) {    batchId = vertexBatchIds[indices[j]];    if (batchId !== currentId) {      indexOffset = indexOffsets[indexOffsets.length - 1];      indexCount = j - indexOffset;      batchIds.push(batchId);      indexCounts.push(indexCount);      indexOffsets.push(j);      batchedIndices.push(        new Vector3DTileBatch({          offset: indexOffset,          count: indexCount,          batchIds: [currentId],          color: Color.WHITE,        })      );      currentId = batchId;    }  }  indexOffset = indexOffsets[indexOffsets.length - 1];  indexCount = indicesLength - indexOffset;  indexCounts.push(indexCount);  batchedIndices.push(    new Vector3DTileBatch({      offset: indexOffset,      count: indexCount,      batchIds: [currentId],      color: Color.WHITE,    })  );  const shader = model._shaderProgram;  const vertexShaderSource = shader.vertexShaderSource;  const fragmentShaderSource = shader.fragmentShaderSource;  const attributeLocations = shader.attributeLocations;  const pickId = defined(model._pickIdLoaded)    ? model._pickIdLoaded()    : undefined;  model._primitive = new Vector3DTilePrimitive({    classificationType: model._classificationType,    positions: positionsBuffer,    indices: indices,    indexOffsets: indexOffsets,    indexCounts: indexCounts,    batchIds: batchIds,    vertexBatchIds: vertexBatchIds,    batchedIndices: batchedIndices,    batchTable: batchTable,    boundingVolume: new BoundingSphere(), // updated in update()    _vertexShaderSource: vertexShaderSource,    _fragmentShaderSource: fragmentShaderSource,    _attributeLocations: attributeLocations,    _uniformMap: uniformMap,    _pickId: pickId,    _modelMatrix: new Matrix4(), // updated in update()    _boundingSphere: boundingSphere, // used to update boundingVolume  });  // Release CPU resources  model._buffers = undefined;  model._vertexArray = undefined;  model._shaderProgram = undefined;  model._uniformMap = undefined;}function createRuntimeNodes(model) {  const loadResources = model._loadResources;  if (!loadResources.finished()) {    return;  }  if (defined(model._primitive)) {    return;  }  const gltf = model.gltf;  const nodes = gltf.nodes;  const gltfNode = nodes[0];  model._nodeMatrix = ModelUtility.getTransform(gltfNode, model._nodeMatrix);  createPrimitive(model);}function createResources(model, frameState) {  const context = frameState.context;  ModelUtility.checkSupportedGlExtensions(model.gltf.glExtensionsUsed, context);  createBuffers(model); // using glTF bufferViews  createProgram(model);  createVertexArray(model); // using glTF meshes  createUniformMap(model, context); // using glTF materials/techniques  createRuntimeNodes(model); // using glTF scene}///////////////////////////////////////////////////////////////////////////const scratchComputedTranslation = new Cartesian4();const scratchComputedMatrixIn2D = new Matrix4();function updateNodeModelMatrix(  model,  modelTransformChanged,  justLoaded,  projection) {  let computedModelMatrix = model._computedModelMatrix;  if (model._mode !== SceneMode.SCENE3D && !model._ignoreCommands) {    const translation = Matrix4.getColumn(      computedModelMatrix,      3,      scratchComputedTranslation    );    if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {      computedModelMatrix = Transforms.basisTo2D(        projection,        computedModelMatrix,        scratchComputedMatrixIn2D      );      model._rtcCenter = model._rtcCenter3D;    } else {      const center = model.boundingSphere.center;      const to2D = Transforms.wgs84To2DModelMatrix(        projection,        center,        scratchComputedMatrixIn2D      );      computedModelMatrix = Matrix4.multiply(        to2D,        computedModelMatrix,        scratchComputedMatrixIn2D      );      if (defined(model._rtcCenter)) {        Matrix4.setTranslation(          computedModelMatrix,          Cartesian4.UNIT_W,          computedModelMatrix        );        model._rtcCenter = model._rtcCenter2D;      }    }  }  const primitive = model._primitive;  if (modelTransformChanged || justLoaded) {    Matrix4.multiplyTransformation(      computedModelMatrix,      model._nodeMatrix,      primitive._modelMatrix    );    BoundingSphere.transform(      primitive._boundingSphere,      primitive._modelMatrix,      primitive._boundingVolume    );    if (defined(model._rtcCenter)) {      Cartesian3.add(        model._rtcCenter,        primitive._boundingVolume.center,        primitive._boundingVolume.center      );    }  }}///////////////////////////////////////////////////////////////////////////ClassificationModel.prototype.updateCommands = function (batchId, color) {  this._primitive.updateCommands(batchId, color);};ClassificationModel.prototype.update = function (frameState) {  if (frameState.mode === SceneMode.MORPHING) {    return;  }  if (!FeatureDetection.supportsWebP.initialized) {    FeatureDetection.supportsWebP.initialize();    return;  }  const supportsWebP = FeatureDetection.supportsWebP();  if (this._state === ModelState.NEEDS_LOAD && defined(this.gltf)) {    this._state = ModelState.LOADING;    if (this._state !== ModelState.FAILED) {      const extensions = this.gltf.extensions;      if (defined(extensions) && defined(extensions.CESIUM_RTC)) {        const center = Cartesian3.fromArray(extensions.CESIUM_RTC.center);        if (!Cartesian3.equals(center, Cartesian3.ZERO)) {          this._rtcCenter3D = center;          const projection = frameState.mapProjection;          const ellipsoid = projection.ellipsoid;          const cartographic = ellipsoid.cartesianToCartographic(            this._rtcCenter3D          );          const projectedCart = projection.project(cartographic);          Cartesian3.fromElements(            projectedCart.z,            projectedCart.x,            projectedCart.y,            projectedCart          );          this._rtcCenter2D = projectedCart;          this._rtcCenterEye = new Cartesian3();          this._rtcCenter = this._rtcCenter3D;        }      }      this._loadResources = new ModelLoadResources();      ModelUtility.parseBuffers(this);    }  }  const loadResources = this._loadResources;  let justLoaded = false;  if (this._state === ModelState.LOADING) {    // Transition from LOADING -> LOADED once resources are downloaded and created.    // Textures may continue to stream in while in the LOADED state.    if (loadResources.pendingBufferLoads === 0) {      ModelUtility.checkSupportedExtensions(        this.extensionsRequired,        supportsWebP      );      addBuffersToLoadResources(this);      parseBufferViews(this);      this._boundingSphere = ModelUtility.computeBoundingSphere(this);      this._initialRadius = this._boundingSphere.radius;      createResources(this, frameState);    }    if (loadResources.finished()) {      this._state = ModelState.LOADED;      justLoaded = true;    }  }  if (defined(loadResources) && this._state === ModelState.LOADED) {    if (!justLoaded) {      createResources(this, frameState);    }    if (loadResources.finished()) {      this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.    }  }  const show = this.show;  if ((show && this._state === ModelState.LOADED) || justLoaded) {    this._dirty = false;    const modelMatrix = this.modelMatrix;    const modeChanged = frameState.mode !== this._mode;    this._mode = frameState.mode;    // ClassificationModel's model matrix needs to be updated    const modelTransformChanged =      !Matrix4.equals(this._modelMatrix, modelMatrix) || modeChanged;    if (modelTransformChanged || justLoaded) {      Matrix4.clone(modelMatrix, this._modelMatrix);      const computedModelMatrix = this._computedModelMatrix;      Matrix4.clone(modelMatrix, computedModelMatrix);      if (this._upAxis === Axis.Y) {        Matrix4.multiplyTransformation(          computedModelMatrix,          Axis.Y_UP_TO_Z_UP,          computedModelMatrix        );      } else if (this._upAxis === Axis.X) {        Matrix4.multiplyTransformation(          computedModelMatrix,          Axis.X_UP_TO_Z_UP,          computedModelMatrix        );      }    }    // Update modelMatrix throughout the graph as needed    if (modelTransformChanged || justLoaded) {      updateNodeModelMatrix(        this,        modelTransformChanged,        justLoaded,        frameState.mapProjection      );      this._dirty = true;    }  }  if (justLoaded) {    // Called after modelMatrix update.    const model = this;    frameState.afterRender.push(function () {      model._ready = true;      model._readyPromise.resolve(model);    });    return;  }  if (show && !this._ignoreCommands) {    this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;    this._primitive.debugWireframe = this.debugWireframe;    this._primitive.update(frameState);  }};ClassificationModel.prototype.isDestroyed = function () {  return false;};ClassificationModel.prototype.destroy = function () {  this._primitive = this._primitive && this._primitive.destroy();  return destroyObject(this);};export default ClassificationModel;
 |