| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421 | import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";import combine from "../Core/combine.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 GeometryInstance from "../Core/GeometryInstance.js";import DrawCommand from "../Renderer/DrawCommand.js";import Pass from "../Renderer/Pass.js";import RenderState from "../Renderer/RenderState.js";import ShaderProgram from "../Renderer/ShaderProgram.js";import ShaderSource from "../Renderer/ShaderSource.js";import ShadowVolumeAppearanceVS from "../Shaders/ShadowVolumeAppearanceVS.js";import ShadowVolumeFS from "../Shaders/ShadowVolumeFS.js";import BlendingState from "./BlendingState.js";import ClassificationType from "./ClassificationType.js";import DepthFunction from "./DepthFunction.js";import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";import Primitive from "./Primitive.js";import SceneMode from "./SceneMode.js";import ShadowVolumeAppearance from "./ShadowVolumeAppearance.js";import StencilConstants from "./StencilConstants.js";import StencilFunction from "./StencilFunction.js";import StencilOperation from "./StencilOperation.js";/** * A classification primitive represents a volume enclosing geometry in the {@link Scene} to be highlighted. * <p> * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including * {@link Material} and {@link RenderState}.  Roughly, the geometry instance defines the structure and placement, * and the appearance defines the visual characteristics.  Decoupling geometry and appearance allows us to mix * and match most of them and add a new geometry or appearance independently of each other. * Only {@link PerInstanceColorAppearance} with the same color across all instances is supported at this time when using * ClassificationPrimitive directly. * For full {@link Appearance} support when classifying terrain or 3D Tiles use {@link GroundPrimitive} instead. * </p> * <p> * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there * will be rendering artifacts for some viewing angles. * </p> * <p> * Valid geometries are {@link BoxGeometry}, {@link CylinderGeometry}, {@link EllipsoidGeometry}, {@link PolylineVolumeGeometry}, and {@link SphereGeometry}. * </p> * <p> * Geometries that follow the surface of the ellipsoid, such as {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}, * are also valid if they are extruded volumes; otherwise, they will not be rendered. * </p> * * @alias ClassificationPrimitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. This can either be a single instance or an array of length one. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to PerInstanceColorAppearance when GeometryInstances have a color attribute. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory. * @param {Boolean} [options.releaseGeometryInstances=true] When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}.  When <code>false</code>, GPU memory is saved. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be <code>true</code> on *                  creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be <code>false</code>. * * @see Primitive * @see GroundPrimitive * @see GeometryInstance * @see Appearance */function ClassificationPrimitive(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const geometryInstances = options.geometryInstances;  /**   * The geometry instance rendered with this primitive.  This may   * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>   * is <code>true</code> when the primitive is constructed.   * <p>   * Changing this property after the primitive is rendered has no effect.   * </p>   * <p>   * Because of the rendering technique used, all geometry instances must be the same color.   * If there is an instance with a differing color, a <code>DeveloperError</code> will be thrown   * on the first attempt to render.   * </p>   *   * @readonly   * @type {Array|GeometryInstance}   *   * @default undefined   */  this.geometryInstances = geometryInstances;  /**   * Determines if the primitive will be shown.  This affects all geometry   * instances in the primitive.   *   * @type {Boolean}   *   * @default true   */  this.show = defaultValue(options.show, true);  /**   * Determines whether terrain, 3D Tiles or both will be classified.   *   * @type {ClassificationType}   *   * @default ClassificationType.BOTH   */  this.classificationType = defaultValue(    options.classificationType,    ClassificationType.BOTH  );  /**   * 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 primitive.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowBoundingVolume = defaultValue(    options.debugShowBoundingVolume,    false  );  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the shadow volume for each geometry in the primitive.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowShadowVolume = defaultValue(    options.debugShowShadowVolume,    false  );  this._debugShowShadowVolume = false;  // These are used by GroundPrimitive to augment the shader and uniform map.  this._extruded = defaultValue(options._extruded, false);  this._uniformMap = options._uniformMap;  this._sp = undefined;  this._spStencil = undefined;  this._spPick = undefined;  this._spColor = undefined;  this._spPick2D = undefined; // only derived if necessary  this._spColor2D = undefined; // only derived if necessary  this._rsStencilDepthPass = undefined;  this._rsStencilDepthPass3DTiles = undefined;  this._rsColorPass = undefined;  this._rsPickPass = undefined;  this._commandsIgnoreShow = [];  this._ready = false;  this._readyPromise = defer();  this._primitive = undefined;  this._pickPrimitive = options._pickPrimitive;  // Set in update  this._hasSphericalExtentsAttribute = false;  this._hasPlanarExtentsAttributes = false;  this._hasPerColorAttribute = false;  this.appearance = options.appearance;  this._createBoundingVolumeFunction = options._createBoundingVolumeFunction;  this._updateAndQueueCommandsFunction =    options._updateAndQueueCommandsFunction;  this._usePickOffsets = false;  this._primitiveOptions = {    geometryInstances: undefined,    appearance: undefined,    vertexCacheOptimize: defaultValue(options.vertexCacheOptimize, false),    interleave: defaultValue(options.interleave, false),    releaseGeometryInstances: defaultValue(      options.releaseGeometryInstances,      true    ),    allowPicking: defaultValue(options.allowPicking, true),    asynchronous: defaultValue(options.asynchronous, true),    compressVertices: defaultValue(options.compressVertices, true),    _createBoundingVolumeFunction: undefined,    _createRenderStatesFunction: undefined,    _createShaderProgramFunction: undefined,    _createCommandsFunction: undefined,    _updateAndQueueCommandsFunction: undefined,    _createPickOffsets: true,  };}Object.defineProperties(ClassificationPrimitive.prototype, {  /**   * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  vertexCacheOptimize: {    get: function () {      return this._primitiveOptions.vertexCacheOptimize;    },  },  /**   * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default false   */  interleave: {    get: function () {      return this._primitiveOptions.interleave;    },  },  /**   * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  releaseGeometryInstances: {    get: function () {      return this._primitiveOptions.releaseGeometryInstances;    },  },  /**   * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}.  When <code>false</code>, GPU memory is saved.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  allowPicking: {    get: function () {      return this._primitiveOptions.allowPicking;    },  },  /**   * Determines if the geometry instances will be created and batched on a web worker.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  asynchronous: {    get: function () {      return this._primitiveOptions.asynchronous;    },  },  /**   * When <code>true</code>, geometry vertices are compressed, which will save memory.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  compressVertices: {    get: function () {      return this._primitiveOptions.compressVertices;    },  },  /**   * Determines if the primitive is complete and ready to render.  If this property is   * true, the primitive will be rendered the next time that {@link ClassificationPrimitive#update}   * is called.   *   * @memberof ClassificationPrimitive.prototype   *   * @type {Boolean}   * @readonly   */  ready: {    get: function () {      return this._ready;    },  },  /**   * Gets a promise that resolves when the primitive is ready to render.   * @memberof ClassificationPrimitive.prototype   * @type {Promise.<ClassificationPrimitive>}   * @readonly   */  readyPromise: {    get: function () {      return this._readyPromise.promise;    },  },  /**   * Returns true if the ClassificationPrimitive needs a separate shader and commands for 2D.   * This is because texture coordinates on ClassificationPrimitives are computed differently,   * and are used for culling when multiple GeometryInstances are batched in one ClassificationPrimitive.   * @memberof ClassificationPrimitive.prototype   * @type {Boolean}   * @readonly   * @private   */  _needs2DShader: {    get: function () {      return (        this._hasPlanarExtentsAttributes || this._hasSphericalExtentsAttribute      );    },  },});/** * Determines if ClassificationPrimitive rendering is supported. * * @param {Scene} scene The scene. * @returns {Boolean} <code>true</code> if ClassificationPrimitives are supported; otherwise, returns <code>false</code> */ClassificationPrimitive.isSupported = function (scene) {  return scene.context.stencilBuffer;};function getStencilDepthRenderState(enableStencil, mask3DTiles) {  const stencilFunction = mask3DTiles    ? StencilFunction.EQUAL    : StencilFunction.ALWAYS;  return {    colorMask: {      red: false,      green: false,      blue: false,      alpha: false,    },    stencilTest: {      enabled: enableStencil,      frontFunction: stencilFunction,      frontOperation: {        fail: StencilOperation.KEEP,        zFail: StencilOperation.DECREMENT_WRAP,        zPass: StencilOperation.KEEP,      },      backFunction: stencilFunction,      backOperation: {        fail: StencilOperation.KEEP,        zFail: StencilOperation.INCREMENT_WRAP,        zPass: StencilOperation.KEEP,      },      reference: StencilConstants.CESIUM_3D_TILE_MASK,      mask: StencilConstants.CESIUM_3D_TILE_MASK,    },    stencilMask: StencilConstants.CLASSIFICATION_MASK,    depthTest: {      enabled: true,      func: DepthFunction.LESS_OR_EQUAL,    },    depthMask: false,  };}function getColorRenderState(enableStencil) {  return {    stencilTest: {      enabled: enableStencil,      frontFunction: StencilFunction.NOT_EQUAL,      frontOperation: {        fail: StencilOperation.ZERO,        zFail: StencilOperation.ZERO,        zPass: StencilOperation.ZERO,      },      backFunction: StencilFunction.NOT_EQUAL,      backOperation: {        fail: StencilOperation.ZERO,        zFail: StencilOperation.ZERO,        zPass: StencilOperation.ZERO,      },      reference: 0,      mask: StencilConstants.CLASSIFICATION_MASK,    },    stencilMask: StencilConstants.CLASSIFICATION_MASK,    depthTest: {      enabled: false,    },    depthMask: false,    blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,  };}const pickRenderState = {  stencilTest: {    enabled: true,    frontFunction: StencilFunction.NOT_EQUAL,    frontOperation: {      fail: StencilOperation.ZERO,      zFail: StencilOperation.ZERO,      zPass: StencilOperation.ZERO,    },    backFunction: StencilFunction.NOT_EQUAL,    backOperation: {      fail: StencilOperation.ZERO,      zFail: StencilOperation.ZERO,      zPass: StencilOperation.ZERO,    },    reference: 0,    mask: StencilConstants.CLASSIFICATION_MASK,  },  stencilMask: StencilConstants.CLASSIFICATION_MASK,  depthTest: {    enabled: false,  },  depthMask: false,};function createRenderStates(  classificationPrimitive,  context,  appearance,  twoPasses) {  if (defined(classificationPrimitive._rsStencilDepthPass)) {    return;  }  const stencilEnabled = !classificationPrimitive.debugShowShadowVolume;  classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(    getStencilDepthRenderState(stencilEnabled, false)  );  classificationPrimitive._rsStencilDepthPass3DTiles = RenderState.fromCache(    getStencilDepthRenderState(stencilEnabled, true)  );  classificationPrimitive._rsColorPass = RenderState.fromCache(    getColorRenderState(stencilEnabled, false)  );  classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState);}function modifyForEncodedNormals(primitive, vertexShaderSource) {  if (!primitive.compressVertices) {    return vertexShaderSource;  }  if (    vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1  ) {    const attributeName = "compressedAttributes";    //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes    const attributeDecl = `attribute vec2 ${attributeName};`;    const globalDecl = "vec3 extrudeDirection;\n";    const decode = `    extrudeDirection = czm_octDecode(${attributeName}, 65535.0);\n`;    let modifiedVS = vertexShaderSource;    modifiedVS = modifiedVS.replace(      /attribute\s+vec3\s+extrudeDirection;/g,      ""    );    modifiedVS = ShaderSource.replaceMain(      modifiedVS,      "czm_non_compressed_main"    );    const compressedMain =      `${"void main() \n" + "{ \n"}${decode}    czm_non_compressed_main(); \n` +      `}`;    return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n");  }}function createShaderProgram(classificationPrimitive, frameState) {  const context = frameState.context;  const primitive = classificationPrimitive._primitive;  let vs = ShadowVolumeAppearanceVS;  vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(    vs  );  vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);  vs = Primitive._modifyShaderPosition(    classificationPrimitive,    vs,    frameState.scene3DOnly  );  vs = Primitive._updateColorAttribute(primitive, vs);  const planarExtents = classificationPrimitive._hasPlanarExtentsAttributes;  const cullFragmentsUsingExtents =    planarExtents || classificationPrimitive._hasSphericalExtentsAttribute;  if (classificationPrimitive._extruded) {    vs = modifyForEncodedNormals(primitive, vs);  }  const extrudedDefine = classificationPrimitive._extruded    ? "EXTRUDED_GEOMETRY"    : "";  let vsSource = new ShaderSource({    defines: [extrudedDefine],    sources: [vs],  });  const fsSource = new ShaderSource({    sources: [ShadowVolumeFS],  });  const attributeLocations =    classificationPrimitive._primitive._attributeLocations;  const shadowVolumeAppearance = new ShadowVolumeAppearance(    cullFragmentsUsingExtents,    planarExtents,    classificationPrimitive.appearance  );  classificationPrimitive._spStencil = ShaderProgram.replaceCache({    context: context,    shaderProgram: classificationPrimitive._spStencil,    vertexShaderSource: vsSource,    fragmentShaderSource: fsSource,    attributeLocations: attributeLocations,  });  if (classificationPrimitive._primitive.allowPicking) {    let vsPick = ShaderSource.createPickVertexShaderSource(vs);    vsPick = Primitive._appendShowToShader(primitive, vsPick);    vsPick = Primitive._updatePickColorAttribute(vsPick);    const pickFS3D = shadowVolumeAppearance.createPickFragmentShader(false);    const pickVS3D = shadowVolumeAppearance.createPickVertexShader(      [extrudedDefine],      vsPick,      false,      frameState.mapProjection    );    classificationPrimitive._spPick = ShaderProgram.replaceCache({      context: context,      shaderProgram: classificationPrimitive._spPick,      vertexShaderSource: pickVS3D,      fragmentShaderSource: pickFS3D,      attributeLocations: attributeLocations,    });    // Derive a 2D pick shader if the primitive uses texture coordinate-based fragment culling,    // since texture coordinates are computed differently in 2D.    if (cullFragmentsUsingExtents) {      let pickProgram2D = context.shaderCache.getDerivedShaderProgram(        classificationPrimitive._spPick,        "2dPick"      );      if (!defined(pickProgram2D)) {        const pickFS2D = shadowVolumeAppearance.createPickFragmentShader(true);        const pickVS2D = shadowVolumeAppearance.createPickVertexShader(          [extrudedDefine],          vsPick,          true,          frameState.mapProjection        );        pickProgram2D = context.shaderCache.createDerivedShaderProgram(          classificationPrimitive._spPick,          "2dPick",          {            vertexShaderSource: pickVS2D,            fragmentShaderSource: pickFS2D,            attributeLocations: attributeLocations,          }        );      }      classificationPrimitive._spPick2D = pickProgram2D;    }  } else {    classificationPrimitive._spPick = ShaderProgram.fromCache({      context: context,      vertexShaderSource: vsSource,      fragmentShaderSource: fsSource,      attributeLocations: attributeLocations,    });  }  vs = Primitive._appendShowToShader(primitive, vs);  vsSource = new ShaderSource({    defines: [extrudedDefine],    sources: [vs],  });  classificationPrimitive._sp = ShaderProgram.replaceCache({    context: context,    shaderProgram: classificationPrimitive._sp,    vertexShaderSource: vsSource,    fragmentShaderSource: fsSource,    attributeLocations: attributeLocations,  });  // Create a fragment shader that computes only required material hookups using screen space techniques  const fsColorSource = shadowVolumeAppearance.createFragmentShader(false);  const vsColorSource = shadowVolumeAppearance.createVertexShader(    [extrudedDefine],    vs,    false,    frameState.mapProjection  );  classificationPrimitive._spColor = ShaderProgram.replaceCache({    context: context,    shaderProgram: classificationPrimitive._spColor,    vertexShaderSource: vsColorSource,    fragmentShaderSource: fsColorSource,    attributeLocations: attributeLocations,  });  // Derive a 2D shader if the primitive uses texture coordinate-based fragment culling,  // since texture coordinates are computed differently in 2D.  // Any material that uses texture coordinates will also equip texture coordinate-based fragment culling.  if (cullFragmentsUsingExtents) {    let colorProgram2D = context.shaderCache.getDerivedShaderProgram(      classificationPrimitive._spColor,      "2dColor"    );    if (!defined(colorProgram2D)) {      const fsColorSource2D = shadowVolumeAppearance.createFragmentShader(true);      const vsColorSource2D = shadowVolumeAppearance.createVertexShader(        [extrudedDefine],        vs,        true,        frameState.mapProjection      );      colorProgram2D = context.shaderCache.createDerivedShaderProgram(        classificationPrimitive._spColor,        "2dColor",        {          vertexShaderSource: vsColorSource2D,          fragmentShaderSource: fsColorSource2D,          attributeLocations: attributeLocations,        }      );    }    classificationPrimitive._spColor2D = colorProgram2D;  }}function createColorCommands(classificationPrimitive, colorCommands) {  const primitive = classificationPrimitive._primitive;  let length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill  colorCommands.length = length;  let i;  let command;  let derivedCommand;  let vaIndex = 0;  let uniformMap = primitive._batchTable.getUniformMapCallback()(    classificationPrimitive._uniformMap  );  const needs2DShader = classificationPrimitive._needs2DShader;  for (i = 0; i < length; i += 2) {    const vertexArray = primitive._va[vaIndex++];    // Stencil depth command    command = colorCommands[i];    if (!defined(command)) {      command = colorCommands[i] = new DrawCommand({        owner: classificationPrimitive,        primitiveType: primitive._primitiveType,      });    }    command.vertexArray = vertexArray;    command.renderState = classificationPrimitive._rsStencilDepthPass;    command.shaderProgram = classificationPrimitive._sp;    command.uniformMap = uniformMap;    command.pass = Pass.TERRAIN_CLASSIFICATION;    derivedCommand = DrawCommand.shallowClone(      command,      command.derivedCommands.tileset    );    derivedCommand.renderState =      classificationPrimitive._rsStencilDepthPass3DTiles;    derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;    command.derivedCommands.tileset = derivedCommand;    // Color command    command = colorCommands[i + 1];    if (!defined(command)) {      command = colorCommands[i + 1] = new DrawCommand({        owner: classificationPrimitive,        primitiveType: primitive._primitiveType,      });    }    command.vertexArray = vertexArray;    command.renderState = classificationPrimitive._rsColorPass;    command.shaderProgram = classificationPrimitive._spColor;    command.pass = Pass.TERRAIN_CLASSIFICATION;    const appearance = classificationPrimitive.appearance;    const material = appearance.material;    if (defined(material)) {      uniformMap = combine(uniformMap, material._uniforms);    }    command.uniformMap = uniformMap;    derivedCommand = DrawCommand.shallowClone(      command,      command.derivedCommands.tileset    );    derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;    command.derivedCommands.tileset = derivedCommand;    // Derive for 2D if texture coordinates are ever computed    if (needs2DShader) {      // First derive from the terrain command      let derived2DCommand = DrawCommand.shallowClone(        command,        command.derivedCommands.appearance2D      );      derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;      command.derivedCommands.appearance2D = derived2DCommand;      // Then derive from the 3D Tiles command      derived2DCommand = DrawCommand.shallowClone(        derivedCommand,        derivedCommand.derivedCommands.appearance2D      );      derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;      derivedCommand.derivedCommands.appearance2D = derived2DCommand;    }  }  const commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow;  const spStencil = classificationPrimitive._spStencil;  let commandIndex = 0;  length = commandsIgnoreShow.length = length / 2;  for (let j = 0; j < length; ++j) {    const commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone(      colorCommands[commandIndex],      commandsIgnoreShow[j]    ));    commandIgnoreShow.shaderProgram = spStencil;    commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;    commandIndex += 2;  }}function createPickCommands(classificationPrimitive, pickCommands) {  const usePickOffsets = classificationPrimitive._usePickOffsets;  const primitive = classificationPrimitive._primitive;  let length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill  // Fallback for batching same-color geometry instances  let pickOffsets;  let pickIndex = 0;  let pickOffset;  if (usePickOffsets) {    pickOffsets = primitive._pickOffsets;    length = pickOffsets.length * 2;  }  pickCommands.length = length;  let j;  let command;  let derivedCommand;  let vaIndex = 0;  const uniformMap = primitive._batchTable.getUniformMapCallback()(    classificationPrimitive._uniformMap  );  const needs2DShader = classificationPrimitive._needs2DShader;  for (j = 0; j < length; j += 2) {    let vertexArray = primitive._va[vaIndex++];    if (usePickOffsets) {      pickOffset = pickOffsets[pickIndex++];      vertexArray = primitive._va[pickOffset.index];    }    // Stencil depth command    command = pickCommands[j];    if (!defined(command)) {      command = pickCommands[j] = new DrawCommand({        owner: classificationPrimitive,        primitiveType: primitive._primitiveType,        pickOnly: true,      });    }    command.vertexArray = vertexArray;    command.renderState = classificationPrimitive._rsStencilDepthPass;    command.shaderProgram = classificationPrimitive._sp;    command.uniformMap = uniformMap;    command.pass = Pass.TERRAIN_CLASSIFICATION;    if (usePickOffsets) {      command.offset = pickOffset.offset;      command.count = pickOffset.count;    }    // Derive for 3D Tiles classification    derivedCommand = DrawCommand.shallowClone(      command,      command.derivedCommands.tileset    );    derivedCommand.renderState =      classificationPrimitive._rsStencilDepthPass3DTiles;    derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;    command.derivedCommands.tileset = derivedCommand;    // Pick color command    command = pickCommands[j + 1];    if (!defined(command)) {      command = pickCommands[j + 1] = new DrawCommand({        owner: classificationPrimitive,        primitiveType: primitive._primitiveType,        pickOnly: true,      });    }    command.vertexArray = vertexArray;    command.renderState = classificationPrimitive._rsPickPass;    command.shaderProgram = classificationPrimitive._spPick;    command.uniformMap = uniformMap;    command.pass = Pass.TERRAIN_CLASSIFICATION;    if (usePickOffsets) {      command.offset = pickOffset.offset;      command.count = pickOffset.count;    }    derivedCommand = DrawCommand.shallowClone(      command,      command.derivedCommands.tileset    );    derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;    command.derivedCommands.tileset = derivedCommand;    // Derive for 2D if texture coordinates are ever computed    if (needs2DShader) {      // First derive from the terrain command      let derived2DCommand = DrawCommand.shallowClone(        command,        command.derivedCommands.pick2D      );      derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;      command.derivedCommands.pick2D = derived2DCommand;      // Then derive from the 3D Tiles command      derived2DCommand = DrawCommand.shallowClone(        derivedCommand,        derivedCommand.derivedCommands.pick2D      );      derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;      derivedCommand.derivedCommands.pick2D = derived2DCommand;    }  }}function createCommands(  classificationPrimitive,  appearance,  material,  translucent,  twoPasses,  colorCommands,  pickCommands) {  createColorCommands(classificationPrimitive, colorCommands);  createPickCommands(classificationPrimitive, pickCommands);}function boundingVolumeIndex(commandIndex, length) {  return Math.floor((commandIndex % length) / 2);}function updateAndQueueRenderCommand(  command,  frameState,  modelMatrix,  cull,  boundingVolume,  debugShowBoundingVolume) {  command.modelMatrix = modelMatrix;  command.boundingVolume = boundingVolume;  command.cull = cull;  command.debugShowBoundingVolume = debugShowBoundingVolume;  frameState.commandList.push(command);}function updateAndQueuePickCommand(  command,  frameState,  modelMatrix,  cull,  boundingVolume) {  command.modelMatrix = modelMatrix;  command.boundingVolume = boundingVolume;  command.cull = cull;  frameState.commandList.push(command);}function updateAndQueueCommands(  classificationPrimitive,  frameState,  colorCommands,  pickCommands,  modelMatrix,  cull,  debugShowBoundingVolume,  twoPasses) {  const primitive = classificationPrimitive._primitive;  Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);  let boundingVolumes;  if (frameState.mode === SceneMode.SCENE3D) {    boundingVolumes = primitive._boundingSphereWC;  } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {    boundingVolumes = primitive._boundingSphereCV;  } else if (    frameState.mode === SceneMode.SCENE2D &&    defined(primitive._boundingSphere2D)  ) {    boundingVolumes = primitive._boundingSphere2D;  } else if (defined(primitive._boundingSphereMorph)) {    boundingVolumes = primitive._boundingSphereMorph;  }  const classificationType = classificationPrimitive.classificationType;  const queueTerrainCommands =    classificationType !== ClassificationType.CESIUM_3D_TILE;  const queue3DTilesCommands =    classificationType !== ClassificationType.TERRAIN;  const passes = frameState.passes;  let i;  let boundingVolume;  let command;  if (passes.render) {    const colorLength = colorCommands.length;    for (i = 0; i < colorLength; ++i) {      boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)];      if (queueTerrainCommands) {        command = colorCommands[i];        updateAndQueueRenderCommand(          command,          frameState,          modelMatrix,          cull,          boundingVolume,          debugShowBoundingVolume        );      }      if (queue3DTilesCommands) {        command = colorCommands[i].derivedCommands.tileset;        updateAndQueueRenderCommand(          command,          frameState,          modelMatrix,          cull,          boundingVolume,          debugShowBoundingVolume        );      }    }    if (frameState.invertClassification) {      const ignoreShowCommands = classificationPrimitive._commandsIgnoreShow;      const ignoreShowCommandsLength = ignoreShowCommands.length;      for (i = 0; i < ignoreShowCommandsLength; ++i) {        boundingVolume = boundingVolumes[i];        command = ignoreShowCommands[i];        updateAndQueueRenderCommand(          command,          frameState,          modelMatrix,          cull,          boundingVolume,          debugShowBoundingVolume        );      }    }  }  if (passes.pick) {    const pickLength = pickCommands.length;    const pickOffsets = primitive._pickOffsets;    for (i = 0; i < pickLength; ++i) {      const pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];      boundingVolume = boundingVolumes[pickOffset.index];      if (queueTerrainCommands) {        command = pickCommands[i];        updateAndQueuePickCommand(          command,          frameState,          modelMatrix,          cull,          boundingVolume        );      }      if (queue3DTilesCommands) {        command = pickCommands[i].derivedCommands.tileset;        updateAndQueuePickCommand(          command,          frameState,          modelMatrix,          cull,          boundingVolume        );      }    }  }}/** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. * <p> * Do not call this function directly.  This is documented just to * list the exceptions that may be propagated when the scene is rendered: * </p> * * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. * @exception {DeveloperError} Not all of the geometry instances have the same color attribute. */ClassificationPrimitive.prototype.update = function (frameState) {  if (!defined(this._primitive) && !defined(this.geometryInstances)) {    return;  }  let appearance = this.appearance;  if (defined(appearance) && defined(appearance.material)) {    appearance.material.update(frameState.context);  }  const that = this;  const primitiveOptions = this._primitiveOptions;  if (!defined(this._primitive)) {    const instances = Array.isArray(this.geometryInstances)      ? this.geometryInstances      : [this.geometryInstances];    const length = instances.length;    let i;    let instance;    let attributes;    let hasPerColorAttribute = false;    let allColorsSame = true;    let firstColor;    let hasSphericalExtentsAttribute = false;    let hasPlanarExtentsAttributes = false;    if (length > 0) {      attributes = instances[0].attributes;      // Not expecting these to be set by users, should only be set via GroundPrimitive.      // So don't check for mismatch.      hasSphericalExtentsAttribute = ShadowVolumeAppearance.hasAttributesForSphericalExtents(        attributes      );      hasPlanarExtentsAttributes = ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(        attributes      );      firstColor = attributes.color;    }    for (i = 0; i < length; i++) {      instance = instances[i];      const color = instance.attributes.color;      if (defined(color)) {        hasPerColorAttribute = true;      }      //>>includeStart('debug', pragmas.debug);      else if (hasPerColorAttribute) {        throw new DeveloperError(          "All GeometryInstances must have color attributes to use per-instance color."        );      }      //>>includeEnd('debug');      allColorsSame =        allColorsSame &&        defined(color) &&        ColorGeometryInstanceAttribute.equals(firstColor, color);    }    // If no attributes exist for computing spherical extents or fragment culling,    // throw if the colors aren't all the same.    if (      !allColorsSame &&      !hasSphericalExtentsAttribute &&      !hasPlanarExtentsAttributes    ) {      throw new DeveloperError(        "All GeometryInstances must have the same color attribute except via GroundPrimitives"      );    }    // default to a color appearance    if (hasPerColorAttribute && !defined(appearance)) {      appearance = new PerInstanceColorAppearance({        flat: true,      });      this.appearance = appearance;    }    //>>includeStart('debug', pragmas.debug);    if (      !hasPerColorAttribute &&      appearance instanceof PerInstanceColorAppearance    ) {      throw new DeveloperError(        "PerInstanceColorAppearance requires color GeometryInstanceAttributes on all GeometryInstances"      );    }    if (      defined(appearance.material) &&      !hasSphericalExtentsAttribute &&      !hasPlanarExtentsAttributes    ) {      throw new DeveloperError(        "Materials on ClassificationPrimitives are not supported except via GroundPrimitives"      );    }    //>>includeEnd('debug');    this._usePickOffsets =      !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes;    this._hasSphericalExtentsAttribute = hasSphericalExtentsAttribute;    this._hasPlanarExtentsAttributes = hasPlanarExtentsAttributes;    this._hasPerColorAttribute = hasPerColorAttribute;    const geometryInstances = new Array(length);    for (i = 0; i < length; ++i) {      instance = instances[i];      geometryInstances[i] = new GeometryInstance({        geometry: instance.geometry,        attributes: instance.attributes,        modelMatrix: instance.modelMatrix,        id: instance.id,        pickPrimitive: defaultValue(this._pickPrimitive, that),      });    }    primitiveOptions.appearance = appearance;    primitiveOptions.geometryInstances = geometryInstances;    if (defined(this._createBoundingVolumeFunction)) {      primitiveOptions._createBoundingVolumeFunction = function (        frameState,        geometry      ) {        that._createBoundingVolumeFunction(frameState, geometry);      };    }    primitiveOptions._createRenderStatesFunction = function (      primitive,      context,      appearance,      twoPasses    ) {      createRenderStates(that, context);    };    primitiveOptions._createShaderProgramFunction = function (      primitive,      frameState,      appearance    ) {      createShaderProgram(that, frameState);    };    primitiveOptions._createCommandsFunction = function (      primitive,      appearance,      material,      translucent,      twoPasses,      colorCommands,      pickCommands    ) {      createCommands(        that,        undefined,        undefined,        true,        false,        colorCommands,        pickCommands      );    };    if (defined(this._updateAndQueueCommandsFunction)) {      primitiveOptions._updateAndQueueCommandsFunction = function (        primitive,        frameState,        colorCommands,        pickCommands,        modelMatrix,        cull,        debugShowBoundingVolume,        twoPasses      ) {        that._updateAndQueueCommandsFunction(          primitive,          frameState,          colorCommands,          pickCommands,          modelMatrix,          cull,          debugShowBoundingVolume,          twoPasses        );      };    } else {      primitiveOptions._updateAndQueueCommandsFunction = function (        primitive,        frameState,        colorCommands,        pickCommands,        modelMatrix,        cull,        debugShowBoundingVolume,        twoPasses      ) {        updateAndQueueCommands(          that,          frameState,          colorCommands,          pickCommands,          modelMatrix,          cull,          debugShowBoundingVolume,          twoPasses        );      };    }    this._primitive = new Primitive(primitiveOptions);    this._primitive.readyPromise.then(function (primitive) {      that._ready = true;      if (that.releaseGeometryInstances) {        that.geometryInstances = undefined;      }      const error = primitive._error;      if (!defined(error)) {        that._readyPromise.resolve(that);      } else {        that._readyPromise.reject(error);      }    });  }  if (    this.debugShowShadowVolume &&    !this._debugShowShadowVolume &&    this._ready  ) {    this._debugShowShadowVolume = true;    this._rsStencilDepthPass = RenderState.fromCache(      getStencilDepthRenderState(false, false)    );    this._rsStencilDepthPass3DTiles = RenderState.fromCache(      getStencilDepthRenderState(false, true)    );    this._rsColorPass = RenderState.fromCache(getColorRenderState(false));  } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) {    this._debugShowShadowVolume = false;    this._rsStencilDepthPass = RenderState.fromCache(      getStencilDepthRenderState(true, false)    );    this._rsStencilDepthPass3DTiles = RenderState.fromCache(      getStencilDepthRenderState(true, true)    );    this._rsColorPass = RenderState.fromCache(getColorRenderState(true));  }  // Update primitive appearance  if (this._primitive.appearance !== appearance) {    //>>includeStart('debug', pragmas.debug);    // Check if the appearance is supported by the geometry attributes    if (      !this._hasSphericalExtentsAttribute &&      !this._hasPlanarExtentsAttributes &&      defined(appearance.material)    ) {      throw new DeveloperError(        "Materials on ClassificationPrimitives are not supported except via GroundPrimitive"      );    }    if (      !this._hasPerColorAttribute &&      appearance instanceof PerInstanceColorAppearance    ) {      throw new DeveloperError(        "PerInstanceColorAppearance requires color GeometryInstanceAttribute"      );    }    //>>includeEnd('debug');    this._primitive.appearance = appearance;  }  this._primitive.show = this.show;  this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;  this._primitive.update(frameState);};/** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {*} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * const attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); */ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function (  id) {  //>>includeStart('debug', pragmas.debug);  if (!defined(this._primitive)) {    throw new DeveloperError(      "must call update before calling getGeometryInstanceAttributes"    );  }  //>>includeEnd('debug');  return this._primitive.getGeometryInstanceAttributes(id);};/** * Returns true if this object was destroyed; otherwise, false. * <p> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * </p> * * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see ClassificationPrimitive#destroy */ClassificationPrimitive.prototype.isDestroyed = function () {  return false;};/** * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <p> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * </p> * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * e = e && e.destroy(); * * @see ClassificationPrimitive#isDestroyed */ClassificationPrimitive.prototype.destroy = function () {  this._primitive = this._primitive && this._primitive.destroy();  this._sp = this._sp && this._sp.destroy();  this._spPick = this._spPick && this._spPick.destroy();  this._spColor = this._spColor && this._spColor.destroy();  // Derived programs, destroyed above if they existed.  this._spPick2D = undefined;  this._spColor2D = undefined;  return destroyObject(this);};export default ClassificationPrimitive;
 |