| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 | import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartographic from "../Core/Cartographic.js";import Check from "../Core/Check.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import deprecationWarning from "../Core/deprecationWarning.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import GeometryInstance from "../Core/GeometryInstance.js";import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";import Rectangle from "../Core/Rectangle.js";import TerrainExaggeration from "../Core/TerrainExaggeration.js";import ClassificationPrimitive from "./ClassificationPrimitive.js";import ClassificationType from "./ClassificationType.js";import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";import SceneMode from "./SceneMode.js";import ShadowVolumeAppearance from "./ShadowVolumeAppearance.js";const GroundPrimitiveUniformMap = {  u_globeMinimumAltitude: function () {    return 55000.0;  },};/** * A ground primitive represents geometry draped over terrain or 3D Tiles in the {@link Scene}. * <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. * </p> * <p> * Support for the WEBGL_depth_texture extension is required to use GeometryInstances with different PerInstanceColors * or materials besides PerInstanceColorAppearance. * </p> * <p> * Textured GroundPrimitives were designed for notional patterns and are not meant for precisely mapping * textures to terrain - for that use case, use {@link SingleTileImageryProvider}. * </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 CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}. * </p> * * @alias GroundPrimitive * @constructor * * @param {object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to a flat 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>. * * @example * // Example 1: Create primitive with a single instance * const rectangleInstance = new Cesium.GeometryInstance({ *   geometry : new Cesium.RectangleGeometry({ *     rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0) *   }), *   id : 'rectangle', *   attributes : { *     color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5) *   } * }); * scene.primitives.add(new Cesium.GroundPrimitive({ *   geometryInstances : rectangleInstance * })); * * // Example 2: Batch instances * const color = new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5); // Both instances must have the same color. * const rectangleInstance = new Cesium.GeometryInstance({ *   geometry : new Cesium.RectangleGeometry({ *     rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0) *   }), *   id : 'rectangle', *   attributes : { *     color : color *   } * }); * const ellipseInstance = new Cesium.GeometryInstance({ *     geometry : new Cesium.EllipseGeometry({ *         center : Cesium.Cartesian3.fromDegrees(-105.0, 40.0), *         semiMinorAxis : 300000.0, *         semiMajorAxis : 400000.0 *     }), *     id : 'ellipse', *     attributes : { *         color : color *     } * }); * scene.primitives.add(new Cesium.GroundPrimitive({ *   geometryInstances : [rectangleInstance, ellipseInstance] * })); * * @see Primitive * @see ClassificationPrimitive * @see GeometryInstance * @see Appearance */function GroundPrimitive(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  let appearance = options.appearance;  const geometryInstances = options.geometryInstances;  if (!defined(appearance) && defined(geometryInstances)) {    const geometryInstancesArray = Array.isArray(geometryInstances)      ? geometryInstances      : [geometryInstances];    const geometryInstanceCount = geometryInstancesArray.length;    for (let i = 0; i < geometryInstanceCount; i++) {      const attributes = geometryInstancesArray[i].attributes;      if (defined(attributes) && defined(attributes.color)) {        appearance = new PerInstanceColorAppearance({          flat: true,        });        break;      }    }  }  /**   * The {@link Appearance} used to shade this primitive. Each geometry   * instance is shaded with the same appearance.  Some appearances, like   * {@link PerInstanceColorAppearance} allow giving each instance unique   * properties.   *   * @type Appearance   *   * @default undefined   */  this.appearance = appearance;  /**   * The geometry instances 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>   *   * @readonly   * @type {Array|GeometryInstance}   *   * @default undefined   */  this.geometryInstances = options.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._boundingVolumes = [];  this._boundingVolumes2D = [];  this._ready = false;  const groundPrimitive = this;  // This is here for backwards compatibility. This promise wrapper can be removed once readyPromise is removed.  this._readyPromise = new Promise((resolve, reject) => {    groundPrimitive._completeLoad = () => {      if (this._ready) {        return;      }      this._ready = true;      if (this.releaseGeometryInstances) {        this.geometryInstances = undefined;      }      const error = this._error;      if (!defined(error)) {        resolve(this);      } else {        reject(error);      }    };  });  this._primitive = undefined;  this._maxHeight = undefined;  this._minHeight = undefined;  this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;  this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;  this._boundingSpheresKeys = [];  this._boundingSpheres = [];  this._useFragmentCulling = false;  // Used when inserting in an OrderedPrimitiveCollection  this._zIndex = undefined;  const that = this;  this._classificationPrimitiveOptions = {    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,    _updateAndQueueCommandsFunction: undefined,    _pickPrimitive: that,    _extruded: true,    _uniformMap: GroundPrimitiveUniformMap,  };}Object.defineProperties(GroundPrimitive.prototype, {  /**   * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default true   */  vertexCacheOptimize: {    get: function () {      return this._classificationPrimitiveOptions.vertexCacheOptimize;    },  },  /**   * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default false   */  interleave: {    get: function () {      return this._classificationPrimitiveOptions.interleave;    },  },  /**   * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default true   */  releaseGeometryInstances: {    get: function () {      return this._classificationPrimitiveOptions.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 GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default true   */  allowPicking: {    get: function () {      return this._classificationPrimitiveOptions.allowPicking;    },  },  /**   * Determines if the geometry instances will be created and batched on a web worker.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default true   */  asynchronous: {    get: function () {      return this._classificationPrimitiveOptions.asynchronous;    },  },  /**   * When <code>true</code>, geometry vertices are compressed, which will save memory.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   *   * @default true   */  compressVertices: {    get: function () {      return this._classificationPrimitiveOptions.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 GroundPrimitive#update}   * is called.   *   * @memberof GroundPrimitive.prototype   *   * @type {boolean}   * @readonly   */  ready: {    get: function () {      return this._ready;    },  },  /**   * Gets a promise that resolves when the primitive is ready to render.   * @memberof GroundPrimitive.prototype   * @type {Promise<GroundPrimitive>}   * @readonly   * @deprecated   */  readyPromise: {    get: function () {      deprecationWarning(        "GroundPrimitive.readyPromise",        "GroundPrimitive.readyPromise was deprecated in CesiumJS 1.104. It will be removed in 1.107. Wait for GroundPrimitive.ready to return true instead."      );      return this._readyPromise;    },  },});/** * Determines if GroundPrimitive rendering is supported. * * @function * @param {Scene} scene The scene. * @returns {boolean} <code>true</code> if GroundPrimitives are supported; otherwise, returns <code>false</code> */GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;function getComputeMaximumHeightFunction(primitive) {  return function (granularity, ellipsoid) {    const r = ellipsoid.maximumRadius;    const delta = r / Math.cos(granularity * 0.5) - r;    return primitive._maxHeight + delta;  };}function getComputeMinimumHeightFunction(primitive) {  return function (granularity, ellipsoid) {    return primitive._minHeight;  };}const scratchBVCartesianHigh = new Cartesian3();const scratchBVCartesianLow = new Cartesian3();const scratchBVCartesian = new Cartesian3();const scratchBVCartographic = new Cartographic();const scratchBVRectangle = new Rectangle();function getRectangle(frameState, geometry) {  const ellipsoid = frameState.mapProjection.ellipsoid;  if (    !defined(geometry.attributes) ||    !defined(geometry.attributes.position3DHigh)  ) {    if (defined(geometry.rectangle)) {      return geometry.rectangle;    }    return undefined;  }  const highPositions = geometry.attributes.position3DHigh.values;  const lowPositions = geometry.attributes.position3DLow.values;  const length = highPositions.length;  let minLat = Number.POSITIVE_INFINITY;  let minLon = Number.POSITIVE_INFINITY;  let maxLat = Number.NEGATIVE_INFINITY;  let maxLon = Number.NEGATIVE_INFINITY;  for (let i = 0; i < length; i += 3) {    const highPosition = Cartesian3.unpack(      highPositions,      i,      scratchBVCartesianHigh    );    const lowPosition = Cartesian3.unpack(      lowPositions,      i,      scratchBVCartesianLow    );    const position = Cartesian3.add(      highPosition,      lowPosition,      scratchBVCartesian    );    const cartographic = ellipsoid.cartesianToCartographic(      position,      scratchBVCartographic    );    const latitude = cartographic.latitude;    const longitude = cartographic.longitude;    minLat = Math.min(minLat, latitude);    minLon = Math.min(minLon, longitude);    maxLat = Math.max(maxLat, latitude);    maxLon = Math.max(maxLon, longitude);  }  const rectangle = scratchBVRectangle;  rectangle.north = maxLat;  rectangle.south = minLat;  rectangle.east = maxLon;  rectangle.west = minLon;  return rectangle;}function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) {  const result = ApproximateTerrainHeights.getMinimumMaximumHeights(    rectangle,    ellipsoid  );  primitive._minTerrainHeight = result.minimumTerrainHeight;  primitive._maxTerrainHeight = result.maximumTerrainHeight;}function createBoundingVolume(groundPrimitive, frameState, geometry) {  const ellipsoid = frameState.mapProjection.ellipsoid;  const rectangle = getRectangle(frameState, geometry);  const obb = OrientedBoundingBox.fromRectangle(    rectangle,    groundPrimitive._minHeight,    groundPrimitive._maxHeight,    ellipsoid  );  groundPrimitive._boundingVolumes.push(obb);  if (!frameState.scene3DOnly) {    const projection = frameState.mapProjection;    const boundingVolume = BoundingSphere.fromRectangleWithHeights2D(      rectangle,      projection,      groundPrimitive._maxHeight,      groundPrimitive._minHeight    );    Cartesian3.fromElements(      boundingVolume.center.z,      boundingVolume.center.x,      boundingVolume.center.y,      boundingVolume.center    );    groundPrimitive._boundingVolumes2D.push(boundingVolume);  }}function boundingVolumeIndex(commandIndex, length) {  return Math.floor((commandIndex % length) / 2);}function updateAndQueueRenderCommand(  groundPrimitive,  command,  frameState,  modelMatrix,  cull,  boundingVolume,  debugShowBoundingVolume) {  // Use derived appearance command for 2D if needed  const classificationPrimitive = groundPrimitive._primitive;  if (    frameState.mode !== SceneMode.SCENE3D &&    command.shaderProgram === classificationPrimitive._spColor &&    classificationPrimitive._needs2DShader  ) {    command = command.derivedCommands.appearance2D;  }  command.owner = groundPrimitive;  command.modelMatrix = modelMatrix;  command.boundingVolume = boundingVolume;  command.cull = cull;  command.debugShowBoundingVolume = debugShowBoundingVolume;  frameState.commandList.push(command);}function updateAndQueuePickCommand(  groundPrimitive,  command,  frameState,  modelMatrix,  cull,  boundingVolume) {  // Use derived pick command for 2D if needed  const classificationPrimitive = groundPrimitive._primitive;  if (    frameState.mode !== SceneMode.SCENE3D &&    command.shaderProgram === classificationPrimitive._spPick &&    classificationPrimitive._needs2DShader  ) {    command = command.derivedCommands.pick2D;  }  command.owner = groundPrimitive;  command.modelMatrix = modelMatrix;  command.boundingVolume = boundingVolume;  command.cull = cull;  frameState.commandList.push(command);}function updateAndQueueCommands(  groundPrimitive,  frameState,  colorCommands,  pickCommands,  modelMatrix,  cull,  debugShowBoundingVolume,  twoPasses) {  let boundingVolumes;  if (frameState.mode === SceneMode.SCENE3D) {    boundingVolumes = groundPrimitive._boundingVolumes;  } else {    boundingVolumes = groundPrimitive._boundingVolumes2D;  }  const classificationType = groundPrimitive.classificationType;  const queueTerrainCommands =    classificationType !== ClassificationType.CESIUM_3D_TILE;  const queue3DTilesCommands =    classificationType !== ClassificationType.TERRAIN;  const passes = frameState.passes;  const classificationPrimitive = groundPrimitive._primitive;  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(          groundPrimitive,          command,          frameState,          modelMatrix,          cull,          boundingVolume,          debugShowBoundingVolume        );      }      if (queue3DTilesCommands) {        command = colorCommands[i].derivedCommands.tileset;        updateAndQueueRenderCommand(          groundPrimitive,          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(          groundPrimitive,          command,          frameState,          modelMatrix,          cull,          boundingVolume,          debugShowBoundingVolume        );      }    }  }  if (passes.pick) {    const pickLength = pickCommands.length;    let pickOffsets;    if (!groundPrimitive._useFragmentCulling) {      // Must be using pick offsets      pickOffsets = classificationPrimitive._primitive._pickOffsets;    }    for (i = 0; i < pickLength; ++i) {      boundingVolume = boundingVolumes[boundingVolumeIndex(i, pickLength)];      if (!groundPrimitive._useFragmentCulling) {        const pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];        boundingVolume = boundingVolumes[pickOffset.index];      }      if (queueTerrainCommands) {        command = pickCommands[i];        updateAndQueuePickCommand(          groundPrimitive,          command,          frameState,          modelMatrix,          cull,          boundingVolume        );      }      if (queue3DTilesCommands) {        command = pickCommands[i].derivedCommands.tileset;        updateAndQueuePickCommand(          groundPrimitive,          command,          frameState,          modelMatrix,          cull,          boundingVolume        );      }    }  }}/** * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the * GroundPrimitive synchronously. * * @returns {Promise<void>} A promise that will resolve once the terrain heights have been loaded. * */GroundPrimitive.initializeTerrainHeights = function () {  return ApproximateTerrainHeights.initialize();};/** * 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} For synchronous GroundPrimitive, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve. * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. */GroundPrimitive.prototype.update = function (frameState) {  if (!defined(this._primitive) && !defined(this.geometryInstances)) {    return;  }  if (!ApproximateTerrainHeights.initialized) {    //>>includeStart('debug', pragmas.debug);    if (!this.asynchronous) {      throw new DeveloperError(        "For synchronous GroundPrimitives, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve."      );    }    //>>includeEnd('debug');    GroundPrimitive.initializeTerrainHeights();    return;  }  const that = this;  const primitiveOptions = this._classificationPrimitiveOptions;  if (!defined(this._primitive)) {    const ellipsoid = frameState.mapProjection.ellipsoid;    let instance;    let geometry;    let instanceType;    const instances = Array.isArray(this.geometryInstances)      ? this.geometryInstances      : [this.geometryInstances];    const length = instances.length;    const groundInstances = new Array(length);    let i;    let rectangle;    for (i = 0; i < length; ++i) {      instance = instances[i];      geometry = instance.geometry;      const instanceRectangle = getRectangle(frameState, geometry);      if (!defined(rectangle)) {        rectangle = Rectangle.clone(instanceRectangle);      } else if (defined(instanceRectangle)) {        Rectangle.union(rectangle, instanceRectangle, rectangle);      }      const id = instance.id;      if (defined(id) && defined(instanceRectangle)) {        const boundingSphere = ApproximateTerrainHeights.getBoundingSphere(          instanceRectangle,          ellipsoid        );        this._boundingSpheresKeys.push(id);        this._boundingSpheres.push(boundingSphere);      }      instanceType = geometry.constructor;      if (!defined(instanceType) || !defined(instanceType.createShadowVolume)) {        //>>includeStart('debug', pragmas.debug);        throw new DeveloperError(          "Not all of the geometry instances have GroundPrimitive support."        );        //>>includeEnd('debug');      }    }    // Now compute the min/max heights for the primitive    setMinMaxTerrainHeights(this, rectangle, ellipsoid);    const exaggeration = frameState.terrainExaggeration;    const exaggerationRelativeHeight =      frameState.terrainExaggerationRelativeHeight;    this._minHeight = TerrainExaggeration.getHeight(      this._minTerrainHeight,      exaggeration,      exaggerationRelativeHeight    );    this._maxHeight = TerrainExaggeration.getHeight(      this._maxTerrainHeight,      exaggeration,      exaggerationRelativeHeight    );    const useFragmentCulling = GroundPrimitive._supportsMaterials(      frameState.context    );    this._useFragmentCulling = useFragmentCulling;    if (useFragmentCulling) {      // Determine whether to add spherical or planar extent attributes for computing texture coordinates.      // This depends on the size of the GeometryInstances.      let attributes;      let usePlanarExtents = true;      for (i = 0; i < length; ++i) {        instance = instances[i];        geometry = instance.geometry;        rectangle = getRectangle(frameState, geometry);        if (ShadowVolumeAppearance.shouldUseSphericalCoordinates(rectangle)) {          usePlanarExtents = false;          break;        }      }      for (i = 0; i < length; ++i) {        instance = instances[i];        geometry = instance.geometry;        instanceType = geometry.constructor;        const boundingRectangle = getRectangle(frameState, geometry);        const textureCoordinateRotationPoints =          geometry.textureCoordinateRotationPoints;        if (usePlanarExtents) {          attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(            boundingRectangle,            textureCoordinateRotationPoints,            ellipsoid,            frameState.mapProjection,            this._maxHeight          );        } else {          attributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(            boundingRectangle,            textureCoordinateRotationPoints,            ellipsoid,            frameState.mapProjection          );        }        const instanceAttributes = instance.attributes;        for (const attributeKey in instanceAttributes) {          if (instanceAttributes.hasOwnProperty(attributeKey)) {            attributes[attributeKey] = instanceAttributes[attributeKey];          }        }        groundInstances[i] = new GeometryInstance({          geometry: instanceType.createShadowVolume(            geometry,            getComputeMinimumHeightFunction(this),            getComputeMaximumHeightFunction(this)          ),          attributes: attributes,          id: instance.id,        });      }    } else {      // ClassificationPrimitive will check if the colors are all the same if it detects lack of fragment culling attributes      for (i = 0; i < length; ++i) {        instance = instances[i];        geometry = instance.geometry;        instanceType = geometry.constructor;        groundInstances[i] = new GeometryInstance({          geometry: instanceType.createShadowVolume(            geometry,            getComputeMinimumHeightFunction(this),            getComputeMaximumHeightFunction(this)          ),          attributes: instance.attributes,          id: instance.id,        });      }    }    primitiveOptions.geometryInstances = groundInstances;    primitiveOptions.appearance = this.appearance;    primitiveOptions._createBoundingVolumeFunction = function (      frameState,      geometry    ) {      createBoundingVolume(that, frameState, geometry);    };    primitiveOptions._updateAndQueueCommandsFunction = function (      primitive,      frameState,      colorCommands,      pickCommands,      modelMatrix,      cull,      debugShowBoundingVolume,      twoPasses    ) {      updateAndQueueCommands(        that,        frameState,        colorCommands,        pickCommands,        modelMatrix,        cull,        debugShowBoundingVolume,        twoPasses      );    };    this._primitive = new ClassificationPrimitive(primitiveOptions);  }  this._primitive.appearance = this.appearance;  this._primitive.show = this.show;  this._primitive.debugShowShadowVolume = this.debugShowShadowVolume;  this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;  this._primitive.update(frameState);  frameState.afterRender.push(() => {    if (!this._ready && defined(this._primitive) && this._primitive.ready) {      this._completeLoad();    }  });};/** * @private */GroundPrimitive.prototype.getBoundingSphere = function (id) {  const index = this._boundingSpheresKeys.indexOf(id);  if (index !== -1) {    return this._boundingSpheres[index];  }  return undefined;};/** * 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); */GroundPrimitive.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 GroundPrimitive#destroy */GroundPrimitive.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 GroundPrimitive#isDestroyed */GroundPrimitive.prototype.destroy = function () {  this._primitive = this._primitive && this._primitive.destroy();  return destroyObject(this);};/** * Exposed for testing. * * @param {Context} context Rendering context * @returns {boolean} Whether or not the current context supports materials on GroundPrimitives. * @private */GroundPrimitive._supportsMaterials = function (context) {  return context.depthTexture;};/** * Checks if the given Scene supports materials on GroundPrimitives. * Materials on GroundPrimitives require support for the WEBGL_depth_texture extension. * * @param {Scene} scene The current scene. * @returns {boolean} Whether or not the current scene supports materials on GroundPrimitives. */GroundPrimitive.supportsMaterials = function (scene) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("scene", scene);  //>>includeEnd('debug');  return GroundPrimitive._supportsMaterials(scene.frameState.context);};export default GroundPrimitive;
 |