| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417 | import AttributeCompression from "../Core/AttributeCompression.js";import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian2 from "../Core/Cartesian2.js";import Cartesian3 from "../Core/Cartesian3.js";import Check from "../Core/Check.js";import Color from "../Core/Color.js";import ComponentDatatype from "../Core/ComponentDatatype.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import EncodedCartesian3 from "../Core/EncodedCartesian3.js";import IndexDatatype from "../Core/IndexDatatype.js";import CesiumMath from "../Core/Math.js";import Matrix4 from "../Core/Matrix4.js";import WebGLConstants from "../Core/WebGLConstants.js";import Buffer from "../Renderer/Buffer.js";import BufferUsage from "../Renderer/BufferUsage.js";import ContextLimits from "../Renderer/ContextLimits.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 VertexArrayFacade from "../Renderer/VertexArrayFacade.js";import BillboardCollectionFS from "../Shaders/BillboardCollectionFS.js";import BillboardCollectionVS from "../Shaders/BillboardCollectionVS.js";import Billboard from "./Billboard.js";import BlendingState from "./BlendingState.js";import BlendOption from "./BlendOption.js";import HeightReference from "./HeightReference.js";import HorizontalOrigin from "./HorizontalOrigin.js";import SceneMode from "./SceneMode.js";import SDFSettings from "./SDFSettings.js";import TextureAtlas from "./TextureAtlas.js";import VerticalOrigin from "./VerticalOrigin.js";const SHOW_INDEX = Billboard.SHOW_INDEX;const POSITION_INDEX = Billboard.POSITION_INDEX;const PIXEL_OFFSET_INDEX = Billboard.PIXEL_OFFSET_INDEX;const EYE_OFFSET_INDEX = Billboard.EYE_OFFSET_INDEX;const HORIZONTAL_ORIGIN_INDEX = Billboard.HORIZONTAL_ORIGIN_INDEX;const VERTICAL_ORIGIN_INDEX = Billboard.VERTICAL_ORIGIN_INDEX;const SCALE_INDEX = Billboard.SCALE_INDEX;const IMAGE_INDEX_INDEX = Billboard.IMAGE_INDEX_INDEX;const COLOR_INDEX = Billboard.COLOR_INDEX;const ROTATION_INDEX = Billboard.ROTATION_INDEX;const ALIGNED_AXIS_INDEX = Billboard.ALIGNED_AXIS_INDEX;const SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX;const TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX;const PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX =  Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX;const DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION;const DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE;const TEXTURE_COORDINATE_BOUNDS = Billboard.TEXTURE_COORDINATE_BOUNDS;const SDF_INDEX = Billboard.SDF_INDEX;const NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES;let attributeLocations;const attributeLocationsBatched = {  positionHighAndScale: 0,  positionLowAndRotation: 1,  compressedAttribute0: 2, // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates  compressedAttribute1: 3, // aligned axis, translucency by distance, image width  compressedAttribute2: 4, // image height, color, pick color, size in meters, valid aligned axis, 13 bits free  eyeOffset: 5, // 4 bytes free  scaleByDistance: 6,  pixelOffsetScaleByDistance: 7,  compressedAttribute3: 8,  textureCoordinateBoundsOrLabelTranslate: 9,  a_batchId: 10,  sdf: 11,};const attributeLocationsInstanced = {  direction: 0,  positionHighAndScale: 1,  positionLowAndRotation: 2, // texture offset in w  compressedAttribute0: 3,  compressedAttribute1: 4,  compressedAttribute2: 5,  eyeOffset: 6, // texture range in w  scaleByDistance: 7,  pixelOffsetScaleByDistance: 8,  compressedAttribute3: 9,  textureCoordinateBoundsOrLabelTranslate: 10,  a_batchId: 11,  sdf: 12,};/** * A renderable collection of billboards.  Billboards are viewport-aligned * images positioned in the 3D scene. * <br /><br /> * <div align='center'> * <img src='Images/Billboard.png' width='400' height='300' /><br /> * Example billboards * </div> * <br /><br /> * Billboards are added and removed from the collection using {@link BillboardCollection#add} * and {@link BillboardCollection#remove}.  Billboards in a collection automatically share textures * for images with the same identifier. * * @alias BillboardCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each billboard from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Scene} [options.scene] Must be passed in for billboards that use the height reference property or will be depth tested against the globe. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The billboard blending option. The default * is used for rendering both opaque and translucent billboards. However, if either all of the billboards are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x. * @param {Boolean} [options.show=true] Determines if the billboards in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many billboards, to * many collections with only a few billboards each.  Organize collections so that billboards * with the same update frequency are in the same collection, i.e., billboards that do not * change should be in one collection; billboards that change every frame should be in another * collection; and so on. * * @see BillboardCollection#add * @see BillboardCollection#remove * @see Billboard * @see LabelCollection * * @demo {@link https://sandcastle.cesium.com/index.html?src=Billboards.html|Cesium Sandcastle Billboard Demo} * * @example * // Create a billboard collection with two billboards * const billboards = scene.primitives.add(new Cesium.BillboardCollection()); * billboards.add({ *   position : new Cesium.Cartesian3(1.0, 2.0, 3.0), *   image : 'url/to/image' * }); * billboards.add({ *   position : new Cesium.Cartesian3(4.0, 5.0, 6.0), *   image : 'url/to/another/image' * }); */function BillboardCollection(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  this._scene = options.scene;  this._batchTable = options.batchTable;  this._textureAtlas = undefined;  this._textureAtlasGUID = undefined;  this._destroyTextureAtlas = true;  this._sp = undefined;  this._spTranslucent = undefined;  this._rsOpaque = undefined;  this._rsTranslucent = undefined;  this._vaf = undefined;  this._billboards = [];  this._billboardsToUpdate = [];  this._billboardsToUpdateIndex = 0;  this._billboardsRemoved = false;  this._createVertexArray = false;  this._shaderRotation = false;  this._compiledShaderRotation = false;  this._shaderAlignedAxis = false;  this._compiledShaderAlignedAxis = false;  this._shaderScaleByDistance = false;  this._compiledShaderScaleByDistance = false;  this._shaderTranslucencyByDistance = false;  this._compiledShaderTranslucencyByDistance = false;  this._shaderPixelOffsetScaleByDistance = false;  this._compiledShaderPixelOffsetScaleByDistance = false;  this._shaderDistanceDisplayCondition = false;  this._compiledShaderDistanceDisplayCondition = false;  this._shaderDisableDepthDistance = false;  this._compiledShaderDisableDepthDistance = false;  this._shaderClampToGround = false;  this._compiledShaderClampToGround = false;  this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);  this._maxSize = 0.0;  this._maxEyeOffset = 0.0;  this._maxScale = 1.0;  this._maxPixelOffset = 0.0;  this._allHorizontalCenter = true;  this._allVerticalCenter = true;  this._allSizedInMeters = true;  this._baseVolume = new BoundingSphere();  this._baseVolumeWC = new BoundingSphere();  this._baseVolume2D = new BoundingSphere();  this._boundingVolume = new BoundingSphere();  this._boundingVolumeDirty = false;  this._colorCommands = [];  /**   * Determines if billboards in this collection will be shown.   *   * @type {Boolean}   * @default true   */  this.show = defaultValue(options.show, true);  /**   * The 4x4 transformation matrix that transforms each billboard in this collection from model to world coordinates.   * When this is the identity matrix, the billboards are 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 center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);   * billboards.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);   * billboards.add({   *   image : 'url/to/image',   *   position : new Cesium.Cartesian3(0.0, 0.0, 0.0) // center   * });   * billboards.add({   *   image : 'url/to/image',   *   position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0) // east   * });   * billboards.add({   *   image : 'url/to/image',   *   position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0) // north   * });   * billboards.add({   *   image : 'url/to/image',   *   position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0) // up   * });   *   * @see Transforms.eastNorthUpToFixedFrame   */  this.modelMatrix = Matrix4.clone(    defaultValue(options.modelMatrix, Matrix4.IDENTITY)  );  this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);  /**   * 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 texture atlas for this BillboardCollection as a fullscreen quad.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowTextureAtlas = defaultValue(    options.debugShowTextureAtlas,    false  );  /**   * The billboard blending option. The default is used for rendering both opaque and translucent billboards.   * However, if either all of the billboards are completely opaque or all are completely translucent,   * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve   * performance by up to 2x.   * @type {BlendOption}   * @default BlendOption.OPAQUE_AND_TRANSLUCENT   */  this.blendOption = defaultValue(    options.blendOption,    BlendOption.OPAQUE_AND_TRANSLUCENT  );  this._blendOption = undefined;  this._mode = SceneMode.SCENE3D;  // The buffer usage for each attribute is determined based on the usage of the attribute over time.  this._buffersUsage = [    BufferUsage.STATIC_DRAW, // SHOW_INDEX    BufferUsage.STATIC_DRAW, // POSITION_INDEX    BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_INDEX    BufferUsage.STATIC_DRAW, // EYE_OFFSET_INDEX    BufferUsage.STATIC_DRAW, // HORIZONTAL_ORIGIN_INDEX    BufferUsage.STATIC_DRAW, // VERTICAL_ORIGIN_INDEX    BufferUsage.STATIC_DRAW, // SCALE_INDEX    BufferUsage.STATIC_DRAW, // IMAGE_INDEX_INDEX    BufferUsage.STATIC_DRAW, // COLOR_INDEX    BufferUsage.STATIC_DRAW, // ROTATION_INDEX    BufferUsage.STATIC_DRAW, // ALIGNED_AXIS_INDEX    BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX    BufferUsage.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX    BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX    BufferUsage.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX    BufferUsage.STATIC_DRAW, // TEXTURE_COORDINATE_BOUNDS  ];  this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints  const that = this;  this._uniforms = {    u_atlas: function () {      return that._textureAtlas.texture;    },    u_highlightColor: function () {      return that._highlightColor;    },  };  const scene = this._scene;  if (defined(scene) && defined(scene.terrainProviderChanged)) {    this._removeCallbackFunc = scene.terrainProviderChanged.addEventListener(      function () {        const billboards = this._billboards;        const length = billboards.length;        for (let i = 0; i < length; ++i) {          if (defined(billboards[i])) {            billboards[i]._updateClamping();          }        }      },      this    );  }}Object.defineProperties(BillboardCollection.prototype, {  /**   * Returns the number of billboards in this collection.  This is commonly used with   * {@link BillboardCollection#get} to iterate over all the billboards   * in the collection.   * @memberof BillboardCollection.prototype   * @type {Number}   */  length: {    get: function () {      removeBillboards(this);      return this._billboards.length;    },  },  /**   * Gets or sets the textureAtlas.   * @memberof BillboardCollection.prototype   * @type {TextureAtlas}   * @private   */  textureAtlas: {    get: function () {      return this._textureAtlas;    },    set: function (value) {      if (this._textureAtlas !== value) {        this._textureAtlas =          this._destroyTextureAtlas &&          this._textureAtlas &&          this._textureAtlas.destroy();        this._textureAtlas = value;        this._createVertexArray = true; // New per-billboard texture coordinates      }    },  },  /**   * Gets or sets a value which determines if the texture atlas is   * destroyed when the collection is destroyed.   *   * If the texture atlas is used by more than one collection, set this to <code>false</code>,   * and explicitly destroy the atlas to avoid attempting to destroy it multiple times.   *   * @memberof BillboardCollection.prototype   * @type {Boolean}   * @private   *   * @example   * // Set destroyTextureAtlas   * // Destroy a billboard collection but not its texture atlas.   *   * const atlas = new TextureAtlas({   *   scene : scene,   *   images : images   * });   * billboards.textureAtlas = atlas;   * billboards.destroyTextureAtlas = false;   * billboards = billboards.destroy();   * console.log(atlas.isDestroyed()); // False   */  destroyTextureAtlas: {    get: function () {      return this._destroyTextureAtlas;    },    set: function (value) {      this._destroyTextureAtlas = value;    },  },});function destroyBillboards(billboards) {  const length = billboards.length;  for (let i = 0; i < length; ++i) {    if (billboards[i]) {      billboards[i]._destroy();    }  }}/** * Creates and adds a billboard with the specified initial properties to the collection. * The added billboard is returned so it can be modified or removed from the collection later. * * @param {Object}[options] A template describing the billboard's properties as shown in Example 1. * @returns {Billboard} The billboard that was added to the collection. * * @performance Calling <code>add</code> is expected constant time.  However, the collection's vertex buffer * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead.  For * best performance, add as many billboards as possible before calling <code>update</code>. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Example 1:  Add a billboard, specifying all the default values. * const b = billboards.add({ *   show : true, *   position : Cesium.Cartesian3.ZERO, *   pixelOffset : Cesium.Cartesian2.ZERO, *   eyeOffset : Cesium.Cartesian3.ZERO, *   heightReference : Cesium.HeightReference.NONE, *   horizontalOrigin : Cesium.HorizontalOrigin.CENTER, *   verticalOrigin : Cesium.VerticalOrigin.CENTER, *   scale : 1.0, *   image : 'url/to/image', *   imageSubRegion : undefined, *   color : Cesium.Color.WHITE, *   id : undefined, *   rotation : 0.0, *   alignedAxis : Cesium.Cartesian3.ZERO, *   width : undefined, *   height : undefined, *   scaleByDistance : undefined, *   translucencyByDistance : undefined, *   pixelOffsetScaleByDistance : undefined, *   sizeInMeters : false, *   distanceDisplayCondition : undefined * }); * * @example * // Example 2:  Specify only the billboard's cartographic position. * const b = billboards.add({ *   position : Cesium.Cartesian3.fromDegrees(longitude, latitude, height) * }); * * @see BillboardCollection#remove * @see BillboardCollection#removeAll */BillboardCollection.prototype.add = function (options) {  const billboard = new Billboard(options, this);  billboard._index = this._billboards.length;  this._billboards.push(billboard);  this._createVertexArray = true;  return billboard;};/** * Removes a billboard from the collection. * * @param {Billboard} billboard The billboard to remove. * @returns {Boolean} <code>true</code> if the billboard was removed; <code>false</code> if the billboard was not found in the collection. * * @performance Calling <code>remove</code> is expected constant time.  However, the collection's vertex buffer * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead.  For * best performance, remove as many billboards as possible before calling <code>update</code>. * If you intend to temporarily hide a billboard, it is usually more efficient to call * {@link Billboard#show} instead of removing and re-adding the billboard. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * const b = billboards.add(...); * billboards.remove(b);  // Returns true * * @see BillboardCollection#add * @see BillboardCollection#removeAll * @see Billboard#show */BillboardCollection.prototype.remove = function (billboard) {  if (this.contains(billboard)) {    this._billboards[billboard._index] = undefined; // Removed later    this._billboardsRemoved = true;    this._createVertexArray = true;    billboard._destroy();    return true;  }  return false;};/** * Removes all billboards from the collection. * * @performance <code>O(n)</code>.  It is more efficient to remove all the billboards * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * billboards.add(...); * billboards.add(...); * billboards.removeAll(); * * @see BillboardCollection#add * @see BillboardCollection#remove */BillboardCollection.prototype.removeAll = function () {  destroyBillboards(this._billboards);  this._billboards = [];  this._billboardsToUpdate = [];  this._billboardsToUpdateIndex = 0;  this._billboardsRemoved = false;  this._createVertexArray = true;};function removeBillboards(billboardCollection) {  if (billboardCollection._billboardsRemoved) {    billboardCollection._billboardsRemoved = false;    const newBillboards = [];    const billboards = billboardCollection._billboards;    const length = billboards.length;    for (let i = 0, j = 0; i < length; ++i) {      const billboard = billboards[i];      if (defined(billboard)) {        billboard._index = j++;        newBillboards.push(billboard);      }    }    billboardCollection._billboards = newBillboards;  }}BillboardCollection.prototype._updateBillboard = function (  billboard,  propertyChanged) {  if (!billboard._dirty) {    this._billboardsToUpdate[this._billboardsToUpdateIndex++] = billboard;  }  ++this._propertiesChanged[propertyChanged];};/** * Check whether this collection contains a given billboard. * * @param {Billboard} [billboard] The billboard to check for. * @returns {Boolean} true if this collection contains the billboard, false otherwise. * * @see BillboardCollection#get */BillboardCollection.prototype.contains = function (billboard) {  return defined(billboard) && billboard._billboardCollection === this;};/** * Returns the billboard in the collection at the specified index.  Indices are zero-based * and increase as billboards are added.  Removing a billboard shifts all billboards after * it to the left, changing their indices.  This function is commonly used with * {@link BillboardCollection#length} to iterate over all the billboards * in the collection. * * @param {Number} index The zero-based index of the billboard. * @returns {Billboard} The billboard at the specified index. * * @performance Expected constant time.  If billboards were removed from the collection and * {@link BillboardCollection#update} was not called, an implicit <code>O(n)</code> * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Toggle the show property of every billboard in the collection * const len = billboards.length; * for (let i = 0; i < len; ++i) { *   const b = billboards.get(i); *   b.show = !b.show; * } * * @see BillboardCollection#length */BillboardCollection.prototype.get = function (index) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.number("index", index);  //>>includeEnd('debug');  removeBillboards(this);  return this._billboards[index];};let getIndexBuffer;function getIndexBufferBatched(context) {  const sixteenK = 16 * 1024;  let indexBuffer = context.cache.billboardCollection_indexBufferBatched;  if (defined(indexBuffer)) {    return indexBuffer;  }  // Subtract 6 because the last index is reserverd for primitive restart.  // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18  const length = sixteenK * 6 - 6;  const indices = new Uint16Array(length);  for (let i = 0, j = 0; i < length; i += 6, j += 4) {    indices[i] = j;    indices[i + 1] = j + 1;    indices[i + 2] = j + 2;    indices[i + 3] = j + 0;    indices[i + 4] = j + 2;    indices[i + 5] = j + 3;  }  // PERFORMANCE_IDEA:  Should we reference count billboard collections, and eventually delete this?  // Is this too much memory to allocate up front?  Should we dynamically grow it?  indexBuffer = Buffer.createIndexBuffer({    context: context,    typedArray: indices,    usage: BufferUsage.STATIC_DRAW,    indexDatatype: IndexDatatype.UNSIGNED_SHORT,  });  indexBuffer.vertexArrayDestroyable = false;  context.cache.billboardCollection_indexBufferBatched = indexBuffer;  return indexBuffer;}function getIndexBufferInstanced(context) {  let indexBuffer = context.cache.billboardCollection_indexBufferInstanced;  if (defined(indexBuffer)) {    return indexBuffer;  }  indexBuffer = Buffer.createIndexBuffer({    context: context,    typedArray: new Uint16Array([0, 1, 2, 0, 2, 3]),    usage: BufferUsage.STATIC_DRAW,    indexDatatype: IndexDatatype.UNSIGNED_SHORT,  });  indexBuffer.vertexArrayDestroyable = false;  context.cache.billboardCollection_indexBufferInstanced = indexBuffer;  return indexBuffer;}function getVertexBufferInstanced(context) {  let vertexBuffer = context.cache.billboardCollection_vertexBufferInstanced;  if (defined(vertexBuffer)) {    return vertexBuffer;  }  vertexBuffer = Buffer.createVertexBuffer({    context: context,    typedArray: new Float32Array([0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]),    usage: BufferUsage.STATIC_DRAW,  });  vertexBuffer.vertexArrayDestroyable = false;  context.cache.billboardCollection_vertexBufferInstanced = vertexBuffer;  return vertexBuffer;}BillboardCollection.prototype.computeNewBuffersUsage = function () {  const buffersUsage = this._buffersUsage;  let usageChanged = false;  const properties = this._propertiesChanged;  for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {    const newUsage =      properties[k] === 0 ? BufferUsage.STATIC_DRAW : BufferUsage.STREAM_DRAW;    usageChanged = usageChanged || buffersUsage[k] !== newUsage;    buffersUsage[k] = newUsage;  }  return usageChanged;};function createVAF(  context,  numberOfBillboards,  buffersUsage,  instanced,  batchTable,  sdf) {  const attributes = [    {      index: attributeLocations.positionHighAndScale,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[POSITION_INDEX],    },    {      index: attributeLocations.positionLowAndRotation,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[POSITION_INDEX],    },    {      index: attributeLocations.compressedAttribute0,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[PIXEL_OFFSET_INDEX],    },    {      index: attributeLocations.compressedAttribute1,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX],    },    {      index: attributeLocations.compressedAttribute2,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[COLOR_INDEX],    },    {      index: attributeLocations.eyeOffset,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[EYE_OFFSET_INDEX],    },    {      index: attributeLocations.scaleByDistance,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[SCALE_BY_DISTANCE_INDEX],    },    {      index: attributeLocations.pixelOffsetScaleByDistance,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX],    },    {      index: attributeLocations.compressedAttribute3,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX],    },    {      index: attributeLocations.textureCoordinateBoundsOrLabelTranslate,      componentsPerAttribute: 4,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[TEXTURE_COORDINATE_BOUNDS],    },  ];  // Instancing requires one non-instanced attribute.  if (instanced) {    attributes.push({      index: attributeLocations.direction,      componentsPerAttribute: 2,      componentDatatype: ComponentDatatype.FLOAT,      vertexBuffer: getVertexBufferInstanced(context),    });  }  if (defined(batchTable)) {    attributes.push({      index: attributeLocations.a_batchId,      componentsPerAttribute: 1,      componentDatatype: ComponentDatatype.FLOAT,      bufferUsage: BufferUsage.STATIC_DRAW,    });  }  if (sdf) {    attributes.push({      index: attributeLocations.sdf,      componentsPerAttribute: 2,      componentDatatype: ComponentDatatype.FLOAT,      usage: buffersUsage[SDF_INDEX],    });  }  // When instancing is enabled, only one vertex is needed for each billboard.  const sizeInVertices = instanced    ? numberOfBillboards    : 4 * numberOfBillboards;  return new VertexArrayFacade(context, attributes, sizeInVertices, instanced);}///////////////////////////////////////////////////////////////////////////// Four vertices per billboard.  Each has the same position, etc., but a different screen-space direction vector.// PERFORMANCE_IDEA:  Save memory if a property is the same for all billboards, use a latched attribute state,// instead of storing it in a vertex buffer.const writePositionScratch = new EncodedCartesian3();function writePositionScaleAndRotation(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const positionHighWriter =    vafWriters[attributeLocations.positionHighAndScale];  const positionLowWriter =    vafWriters[attributeLocations.positionLowAndRotation];  const position = billboard._getActualPosition();  if (billboardCollection._mode === SceneMode.SCENE3D) {    BoundingSphere.expand(      billboardCollection._baseVolume,      position,      billboardCollection._baseVolume    );    billboardCollection._boundingVolumeDirty = true;  }  EncodedCartesian3.fromCartesian(position, writePositionScratch);  const scale = billboard.scale;  const rotation = billboard.rotation;  if (rotation !== 0.0) {    billboardCollection._shaderRotation = true;  }  billboardCollection._maxScale = Math.max(    billboardCollection._maxScale,    scale  );  const high = writePositionScratch.high;  const low = writePositionScratch.low;  if (billboardCollection._instanced) {    i = billboard._index;    positionHighWriter(i, high.x, high.y, high.z, scale);    positionLowWriter(i, low.x, low.y, low.z, rotation);  } else {    i = billboard._index * 4;    positionHighWriter(i + 0, high.x, high.y, high.z, scale);    positionHighWriter(i + 1, high.x, high.y, high.z, scale);    positionHighWriter(i + 2, high.x, high.y, high.z, scale);    positionHighWriter(i + 3, high.x, high.y, high.z, scale);    positionLowWriter(i + 0, low.x, low.y, low.z, rotation);    positionLowWriter(i + 1, low.x, low.y, low.z, rotation);    positionLowWriter(i + 2, low.x, low.y, low.z, rotation);    positionLowWriter(i + 3, low.x, low.y, low.z, rotation);  }}const scratchCartesian2 = new Cartesian2();const UPPER_BOUND = 32768.0; // 2^15const LEFT_SHIFT16 = 65536.0; // 2^16const LEFT_SHIFT12 = 4096.0; // 2^12const LEFT_SHIFT8 = 256.0; // 2^8const LEFT_SHIFT7 = 128.0;const LEFT_SHIFT5 = 32.0;const LEFT_SHIFT3 = 8.0;const LEFT_SHIFT2 = 4.0;const RIGHT_SHIFT8 = 1.0 / 256.0;const LOWER_LEFT = 0.0;const LOWER_RIGHT = 2.0;const UPPER_RIGHT = 3.0;const UPPER_LEFT = 1.0;function writeCompressedAttrib0(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.compressedAttribute0];  const pixelOffset = billboard.pixelOffset;  const pixelOffsetX = pixelOffset.x;  const pixelOffsetY = pixelOffset.y;  const translate = billboard._translate;  const translateX = translate.x;  const translateY = translate.y;  billboardCollection._maxPixelOffset = Math.max(    billboardCollection._maxPixelOffset,    Math.abs(pixelOffsetX + translateX),    Math.abs(-pixelOffsetY + translateY)  );  const horizontalOrigin = billboard.horizontalOrigin;  let verticalOrigin = billboard._verticalOrigin;  let show = billboard.show && billboard.clusterShow;  // If the color alpha is zero, do not show this billboard.  This lets us avoid providing  // color during the pick pass and also eliminates a discard in the fragment shader.  if (billboard.color.alpha === 0.0) {    show = false;  }  // Raw billboards don't distinguish between BASELINE and BOTTOM, only LabelCollection does that.  if (verticalOrigin === VerticalOrigin.BASELINE) {    verticalOrigin = VerticalOrigin.BOTTOM;  }  billboardCollection._allHorizontalCenter =    billboardCollection._allHorizontalCenter &&    horizontalOrigin === HorizontalOrigin.CENTER;  billboardCollection._allVerticalCenter =    billboardCollection._allVerticalCenter &&    verticalOrigin === VerticalOrigin.CENTER;  let bottomLeftX = 0;  let bottomLeftY = 0;  let width = 0;  let height = 0;  const index = billboard._imageIndex;  if (index !== -1) {    const imageRectangle = textureAtlasCoordinates[index];    //>>includeStart('debug', pragmas.debug);    if (!defined(imageRectangle)) {      throw new DeveloperError(`Invalid billboard image index: ${index}`);    }    //>>includeEnd('debug');    bottomLeftX = imageRectangle.x;    bottomLeftY = imageRectangle.y;    width = imageRectangle.width;    height = imageRectangle.height;  }  const topRightX = bottomLeftX + width;  const topRightY = bottomLeftY + height;  let compressed0 =    Math.floor(      CesiumMath.clamp(pixelOffsetX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND    ) * LEFT_SHIFT7;  compressed0 += (horizontalOrigin + 1.0) * LEFT_SHIFT5;  compressed0 += (verticalOrigin + 1.0) * LEFT_SHIFT3;  compressed0 += (show ? 1.0 : 0.0) * LEFT_SHIFT2;  let compressed1 =    Math.floor(      CesiumMath.clamp(pixelOffsetY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND    ) * LEFT_SHIFT8;  let compressed2 =    Math.floor(      CesiumMath.clamp(translateX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND    ) * LEFT_SHIFT8;  const tempTanslateY =    (CesiumMath.clamp(translateY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND) *    RIGHT_SHIFT8;  const upperTranslateY = Math.floor(tempTanslateY);  const lowerTranslateY = Math.floor(    (tempTanslateY - upperTranslateY) * LEFT_SHIFT8  );  compressed1 += upperTranslateY;  compressed2 += lowerTranslateY;  scratchCartesian2.x = bottomLeftX;  scratchCartesian2.y = bottomLeftY;  const compressedTexCoordsLL = AttributeCompression.compressTextureCoordinates(    scratchCartesian2  );  scratchCartesian2.x = topRightX;  const compressedTexCoordsLR = AttributeCompression.compressTextureCoordinates(    scratchCartesian2  );  scratchCartesian2.y = topRightY;  const compressedTexCoordsUR = AttributeCompression.compressTextureCoordinates(    scratchCartesian2  );  scratchCartesian2.x = bottomLeftX;  const compressedTexCoordsUL = AttributeCompression.compressTextureCoordinates(    scratchCartesian2  );  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, compressed0, compressed1, compressed2, compressedTexCoordsLL);  } else {    i = billboard._index * 4;    writer(      i + 0,      compressed0 + LOWER_LEFT,      compressed1,      compressed2,      compressedTexCoordsLL    );    writer(      i + 1,      compressed0 + LOWER_RIGHT,      compressed1,      compressed2,      compressedTexCoordsLR    );    writer(      i + 2,      compressed0 + UPPER_RIGHT,      compressed1,      compressed2,      compressedTexCoordsUR    );    writer(      i + 3,      compressed0 + UPPER_LEFT,      compressed1,      compressed2,      compressedTexCoordsUL    );  }}function writeCompressedAttrib1(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.compressedAttribute1];  const alignedAxis = billboard.alignedAxis;  if (!Cartesian3.equals(alignedAxis, Cartesian3.ZERO)) {    billboardCollection._shaderAlignedAxis = true;  }  let near = 0.0;  let nearValue = 1.0;  let far = 1.0;  let farValue = 1.0;  const translucency = billboard.translucencyByDistance;  if (defined(translucency)) {    near = translucency.near;    nearValue = translucency.nearValue;    far = translucency.far;    farValue = translucency.farValue;    if (nearValue !== 1.0 || farValue !== 1.0) {      // translucency by distance calculation in shader need not be enabled      // until a billboard with near and far !== 1.0 is found      billboardCollection._shaderTranslucencyByDistance = true;    }  }  let width = 0;  const index = billboard._imageIndex;  if (index !== -1) {    const imageRectangle = textureAtlasCoordinates[index];    //>>includeStart('debug', pragmas.debug);    if (!defined(imageRectangle)) {      throw new DeveloperError(`Invalid billboard image index: ${index}`);    }    //>>includeEnd('debug');    width = imageRectangle.width;  }  const textureWidth = billboardCollection._textureAtlas.texture.width;  const imageWidth = Math.round(    defaultValue(billboard.width, textureWidth * width)  );  billboardCollection._maxSize = Math.max(    billboardCollection._maxSize,    imageWidth  );  let compressed0 = CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT16);  let compressed1 = 0.0;  if (    Math.abs(Cartesian3.magnitudeSquared(alignedAxis) - 1.0) <    CesiumMath.EPSILON6  ) {    compressed1 = AttributeCompression.octEncodeFloat(alignedAxis);  }  nearValue = CesiumMath.clamp(nearValue, 0.0, 1.0);  nearValue = nearValue === 1.0 ? 255.0 : (nearValue * 255.0) | 0;  compressed0 = compressed0 * LEFT_SHIFT8 + nearValue;  farValue = CesiumMath.clamp(farValue, 0.0, 1.0);  farValue = farValue === 1.0 ? 255.0 : (farValue * 255.0) | 0;  compressed1 = compressed1 * LEFT_SHIFT8 + farValue;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, compressed0, compressed1, near, far);  } else {    i = billboard._index * 4;    writer(i + 0, compressed0, compressed1, near, far);    writer(i + 1, compressed0, compressed1, near, far);    writer(i + 2, compressed0, compressed1, near, far);    writer(i + 3, compressed0, compressed1, near, far);  }}function writeCompressedAttrib2(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.compressedAttribute2];  const color = billboard.color;  const pickColor = !defined(billboardCollection._batchTable)    ? billboard.getPickId(frameState.context).color    : Color.WHITE;  const sizeInMeters = billboard.sizeInMeters ? 1.0 : 0.0;  const validAlignedAxis =    Math.abs(Cartesian3.magnitudeSquared(billboard.alignedAxis) - 1.0) <    CesiumMath.EPSILON6      ? 1.0      : 0.0;  billboardCollection._allSizedInMeters =    billboardCollection._allSizedInMeters && sizeInMeters === 1.0;  let height = 0;  const index = billboard._imageIndex;  if (index !== -1) {    const imageRectangle = textureAtlasCoordinates[index];    //>>includeStart('debug', pragmas.debug);    if (!defined(imageRectangle)) {      throw new DeveloperError(`Invalid billboard image index: ${index}`);    }    //>>includeEnd('debug');    height = imageRectangle.height;  }  const dimensions = billboardCollection._textureAtlas.texture.dimensions;  const imageHeight = Math.round(    defaultValue(billboard.height, dimensions.y * height)  );  billboardCollection._maxSize = Math.max(    billboardCollection._maxSize,    imageHeight  );  let labelHorizontalOrigin = defaultValue(    billboard._labelHorizontalOrigin,    -2  );  labelHorizontalOrigin += 2;  const compressed3 = imageHeight * LEFT_SHIFT2 + labelHorizontalOrigin;  let red = Color.floatToByte(color.red);  let green = Color.floatToByte(color.green);  let blue = Color.floatToByte(color.blue);  const compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;  red = Color.floatToByte(pickColor.red);  green = Color.floatToByte(pickColor.green);  blue = Color.floatToByte(pickColor.blue);  const compressed1 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;  let compressed2 =    Color.floatToByte(color.alpha) * LEFT_SHIFT16 +    Color.floatToByte(pickColor.alpha) * LEFT_SHIFT8;  compressed2 += sizeInMeters * 2.0 + validAlignedAxis;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, compressed0, compressed1, compressed2, compressed3);  } else {    i = billboard._index * 4;    writer(i + 0, compressed0, compressed1, compressed2, compressed3);    writer(i + 1, compressed0, compressed1, compressed2, compressed3);    writer(i + 2, compressed0, compressed1, compressed2, compressed3);    writer(i + 3, compressed0, compressed1, compressed2, compressed3);  }}function writeEyeOffset(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.eyeOffset];  const eyeOffset = billboard.eyeOffset;  // For billboards that are clamped to ground, move it slightly closer to the camera  let eyeOffsetZ = eyeOffset.z;  if (billboard._heightReference !== HeightReference.NONE) {    eyeOffsetZ *= 1.005;  }  billboardCollection._maxEyeOffset = Math.max(    billboardCollection._maxEyeOffset,    Math.abs(eyeOffset.x),    Math.abs(eyeOffset.y),    Math.abs(eyeOffsetZ)  );  if (billboardCollection._instanced) {    let width = 0;    let height = 0;    const index = billboard._imageIndex;    if (index !== -1) {      const imageRectangle = textureAtlasCoordinates[index];      //>>includeStart('debug', pragmas.debug);      if (!defined(imageRectangle)) {        throw new DeveloperError(`Invalid billboard image index: ${index}`);      }      //>>includeEnd('debug');      width = imageRectangle.width;      height = imageRectangle.height;    }    scratchCartesian2.x = width;    scratchCartesian2.y = height;    const compressedTexCoordsRange = AttributeCompression.compressTextureCoordinates(      scratchCartesian2    );    i = billboard._index;    writer(i, eyeOffset.x, eyeOffset.y, eyeOffsetZ, compressedTexCoordsRange);  } else {    i = billboard._index * 4;    writer(i + 0, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);    writer(i + 1, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);    writer(i + 2, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);    writer(i + 3, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);  }}function writeScaleByDistance(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.scaleByDistance];  let near = 0.0;  let nearValue = 1.0;  let far = 1.0;  let farValue = 1.0;  const scale = billboard.scaleByDistance;  if (defined(scale)) {    near = scale.near;    nearValue = scale.nearValue;    far = scale.far;    farValue = scale.farValue;    if (nearValue !== 1.0 || farValue !== 1.0) {      // scale by distance calculation in shader need not be enabled      // until a billboard with near and far !== 1.0 is found      billboardCollection._shaderScaleByDistance = true;    }  }  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, near, nearValue, far, farValue);  } else {    i = billboard._index * 4;    writer(i + 0, near, nearValue, far, farValue);    writer(i + 1, near, nearValue, far, farValue);    writer(i + 2, near, nearValue, far, farValue);    writer(i + 3, near, nearValue, far, farValue);  }}function writePixelOffsetScaleByDistance(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.pixelOffsetScaleByDistance];  let near = 0.0;  let nearValue = 1.0;  let far = 1.0;  let farValue = 1.0;  const pixelOffsetScale = billboard.pixelOffsetScaleByDistance;  if (defined(pixelOffsetScale)) {    near = pixelOffsetScale.near;    nearValue = pixelOffsetScale.nearValue;    far = pixelOffsetScale.far;    farValue = pixelOffsetScale.farValue;    if (nearValue !== 1.0 || farValue !== 1.0) {      // pixelOffsetScale by distance calculation in shader need not be enabled      // until a billboard with near and far !== 1.0 is found      billboardCollection._shaderPixelOffsetScaleByDistance = true;    }  }  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, near, nearValue, far, farValue);  } else {    i = billboard._index * 4;    writer(i + 0, near, nearValue, far, farValue);    writer(i + 1, near, nearValue, far, farValue);    writer(i + 2, near, nearValue, far, farValue);    writer(i + 3, near, nearValue, far, farValue);  }}function writeCompressedAttribute3(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  let i;  const writer = vafWriters[attributeLocations.compressedAttribute3];  let near = 0.0;  let far = Number.MAX_VALUE;  const distanceDisplayCondition = billboard.distanceDisplayCondition;  if (defined(distanceDisplayCondition)) {    near = distanceDisplayCondition.near;    far = distanceDisplayCondition.far;    near *= near;    far *= far;    billboardCollection._shaderDistanceDisplayCondition = true;  }  let disableDepthTestDistance = billboard.disableDepthTestDistance;  const clampToGround =    billboard.heightReference === HeightReference.CLAMP_TO_GROUND &&    frameState.context.depthTexture;  if (!defined(disableDepthTestDistance)) {    disableDepthTestDistance = clampToGround ? 5000.0 : 0.0;  }  disableDepthTestDistance *= disableDepthTestDistance;  if (clampToGround || disableDepthTestDistance > 0.0) {    billboardCollection._shaderDisableDepthDistance = true;    if (disableDepthTestDistance === Number.POSITIVE_INFINITY) {      disableDepthTestDistance = -1.0;    }  }  let imageHeight;  let imageWidth;  if (!defined(billboard._labelDimensions)) {    let height = 0;    let width = 0;    const index = billboard._imageIndex;    if (index !== -1) {      const imageRectangle = textureAtlasCoordinates[index];      //>>includeStart('debug', pragmas.debug);      if (!defined(imageRectangle)) {        throw new DeveloperError(`Invalid billboard image index: ${index}`);      }      //>>includeEnd('debug');      height = imageRectangle.height;      width = imageRectangle.width;    }    imageHeight = Math.round(      defaultValue(        billboard.height,        billboardCollection._textureAtlas.texture.dimensions.y * height      )    );    const textureWidth = billboardCollection._textureAtlas.texture.width;    imageWidth = Math.round(      defaultValue(billboard.width, textureWidth * width)    );  } else {    imageWidth = billboard._labelDimensions.x;    imageHeight = billboard._labelDimensions.y;  }  const w = Math.floor(CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT12));  const h = Math.floor(CesiumMath.clamp(imageHeight, 0.0, LEFT_SHIFT12));  const dimensions = w * LEFT_SHIFT12 + h;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, near, far, disableDepthTestDistance, dimensions);  } else {    i = billboard._index * 4;    writer(i + 0, near, far, disableDepthTestDistance, dimensions);    writer(i + 1, near, far, disableDepthTestDistance, dimensions);    writer(i + 2, near, far, disableDepthTestDistance, dimensions);    writer(i + 3, near, far, disableDepthTestDistance, dimensions);  }}function writeTextureCoordinateBoundsOrLabelTranslate(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) {    const scene = billboardCollection._scene;    const context = frameState.context;    const globeTranslucent = frameState.globeTranslucencyState.translucent;    const depthTestAgainstTerrain =      defined(scene.globe) && scene.globe.depthTestAgainstTerrain;    // Only do manual depth test if the globe is opaque and writes depth    billboardCollection._shaderClampToGround =      context.depthTexture && !globeTranslucent && depthTestAgainstTerrain;  }  let i;  const writer =    vafWriters[attributeLocations.textureCoordinateBoundsOrLabelTranslate];  if (ContextLimits.maximumVertexTextureImageUnits > 0) {    //write _labelTranslate, used by depth testing in the vertex shader    let translateX = 0;    let translateY = 0;    if (defined(billboard._labelTranslate)) {      translateX = billboard._labelTranslate.x;      translateY = billboard._labelTranslate.y;    }    if (billboardCollection._instanced) {      i = billboard._index;      writer(i, translateX, translateY, 0.0, 0.0);    } else {      i = billboard._index * 4;      writer(i + 0, translateX, translateY, 0.0, 0.0);      writer(i + 1, translateX, translateY, 0.0, 0.0);      writer(i + 2, translateX, translateY, 0.0, 0.0);      writer(i + 3, translateX, translateY, 0.0, 0.0);    }    return;  }  //write texture coordinate bounds, used by depth testing in fragment shader  let minX = 0;  let minY = 0;  let width = 0;  let height = 0;  const index = billboard._imageIndex;  if (index !== -1) {    const imageRectangle = textureAtlasCoordinates[index];    //>>includeStart('debug', pragmas.debug);    if (!defined(imageRectangle)) {      throw new DeveloperError(`Invalid billboard image index: ${index}`);    }    //>>includeEnd('debug');    minX = imageRectangle.x;    minY = imageRectangle.y;    width = imageRectangle.width;    height = imageRectangle.height;  }  const maxX = minX + width;  const maxY = minY + height;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, minX, minY, maxX, maxY);  } else {    i = billboard._index * 4;    writer(i + 0, minX, minY, maxX, maxY);    writer(i + 1, minX, minY, maxX, maxY);    writer(i + 2, minX, minY, maxX, maxY);    writer(i + 3, minX, minY, maxX, maxY);  }}function writeBatchId(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  if (!defined(billboardCollection._batchTable)) {    return;  }  const writer = vafWriters[attributeLocations.a_batchId];  const id = billboard._batchIndex;  let i;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, id);  } else {    i = billboard._index * 4;    writer(i + 0, id);    writer(i + 1, id);    writer(i + 2, id);    writer(i + 3, id);  }}function writeSDF(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  if (!billboardCollection._sdf) {    return;  }  let i;  const writer = vafWriters[attributeLocations.sdf];  const outlineColor = billboard.outlineColor;  const outlineWidth = billboard.outlineWidth;  const red = Color.floatToByte(outlineColor.red);  const green = Color.floatToByte(outlineColor.green);  const blue = Color.floatToByte(outlineColor.blue);  const compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;  // Compute the relative outline distance  const outlineDistance = outlineWidth / SDFSettings.RADIUS;  const compressed1 =    Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT16 +    Color.floatToByte(outlineDistance) * LEFT_SHIFT8;  if (billboardCollection._instanced) {    i = billboard._index;    writer(i, compressed0, compressed1);  } else {    i = billboard._index * 4;    writer(i + 0, compressed0 + LOWER_LEFT, compressed1);    writer(i + 1, compressed0 + LOWER_RIGHT, compressed1);    writer(i + 2, compressed0 + UPPER_RIGHT, compressed1);    writer(i + 3, compressed0 + UPPER_LEFT, compressed1);  }}function writeBillboard(  billboardCollection,  frameState,  textureAtlasCoordinates,  vafWriters,  billboard) {  writePositionScaleAndRotation(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeCompressedAttrib0(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeCompressedAttrib1(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeCompressedAttrib2(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeEyeOffset(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeScaleByDistance(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writePixelOffsetScaleByDistance(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeCompressedAttribute3(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeTextureCoordinateBoundsOrLabelTranslate(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeBatchId(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );  writeSDF(    billboardCollection,    frameState,    textureAtlasCoordinates,    vafWriters,    billboard  );}function recomputeActualPositions(  billboardCollection,  billboards,  length,  frameState,  modelMatrix,  recomputeBoundingVolume) {  let boundingVolume;  if (frameState.mode === SceneMode.SCENE3D) {    boundingVolume = billboardCollection._baseVolume;    billboardCollection._boundingVolumeDirty = true;  } else {    boundingVolume = billboardCollection._baseVolume2D;  }  const positions = [];  for (let i = 0; i < length; ++i) {    const billboard = billboards[i];    const position = billboard.position;    const actualPosition = Billboard._computeActualPosition(      billboard,      position,      frameState,      modelMatrix    );    if (defined(actualPosition)) {      billboard._setActualPosition(actualPosition);      if (recomputeBoundingVolume) {        positions.push(actualPosition);      } else {        BoundingSphere.expand(boundingVolume, actualPosition, boundingVolume);      }    }  }  if (recomputeBoundingVolume) {    BoundingSphere.fromPoints(positions, boundingVolume);  }}function updateMode(billboardCollection, frameState) {  const mode = frameState.mode;  const billboards = billboardCollection._billboards;  const billboardsToUpdate = billboardCollection._billboardsToUpdate;  const modelMatrix = billboardCollection._modelMatrix;  if (    billboardCollection._createVertexArray ||    billboardCollection._mode !== mode ||    (mode !== SceneMode.SCENE3D &&      !Matrix4.equals(modelMatrix, billboardCollection.modelMatrix))  ) {    billboardCollection._mode = mode;    Matrix4.clone(billboardCollection.modelMatrix, modelMatrix);    billboardCollection._createVertexArray = true;    if (      mode === SceneMode.SCENE3D ||      mode === SceneMode.SCENE2D ||      mode === SceneMode.COLUMBUS_VIEW    ) {      recomputeActualPositions(        billboardCollection,        billboards,        billboards.length,        frameState,        modelMatrix,        true      );    }  } else if (mode === SceneMode.MORPHING) {    recomputeActualPositions(      billboardCollection,      billboards,      billboards.length,      frameState,      modelMatrix,      true    );  } else if (mode === SceneMode.SCENE2D || mode === SceneMode.COLUMBUS_VIEW) {    recomputeActualPositions(      billboardCollection,      billboardsToUpdate,      billboardCollection._billboardsToUpdateIndex,      frameState,      modelMatrix,      false    );  }}function updateBoundingVolume(collection, frameState, boundingVolume) {  let pixelScale = 1.0;  if (!collection._allSizedInMeters || collection._maxPixelOffset !== 0.0) {    pixelScale = frameState.camera.getPixelSize(      boundingVolume,      frameState.context.drawingBufferWidth,      frameState.context.drawingBufferHeight    );  }  let size = pixelScale * collection._maxScale * collection._maxSize * 2.0;  if (collection._allHorizontalCenter && collection._allVerticalCenter) {    size *= 0.5;  }  const offset =    pixelScale * collection._maxPixelOffset + collection._maxEyeOffset;  boundingVolume.radius += size + offset;}function createDebugCommand(billboardCollection, context) {  const fs =    "uniform sampler2D billboard_texture; \n" +    "varying vec2 v_textureCoordinates; \n" +    "void main() \n" +    "{ \n" +    "    gl_FragColor = texture2D(billboard_texture, v_textureCoordinates); \n" +    "} \n";  const drawCommand = context.createViewportQuadCommand(fs, {    uniformMap: {      billboard_texture: function () {        return billboardCollection._textureAtlas.texture;      },    },  });  drawCommand.pass = Pass.OVERLAY;  return drawCommand;}const scratchWriterArray = [];/** * 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 {RuntimeError} image with id must be in the atlas. */BillboardCollection.prototype.update = function (frameState) {  removeBillboards(this);  if (!this.show) {    return;  }  let billboards = this._billboards;  let billboardsLength = billboards.length;  const context = frameState.context;  this._instanced = context.instancedArrays;  attributeLocations = this._instanced    ? attributeLocationsInstanced    : attributeLocationsBatched;  getIndexBuffer = this._instanced    ? getIndexBufferInstanced    : getIndexBufferBatched;  let textureAtlas = this._textureAtlas;  if (!defined(textureAtlas)) {    textureAtlas = this._textureAtlas = new TextureAtlas({      context: context,    });    for (let ii = 0; ii < billboardsLength; ++ii) {      billboards[ii]._loadImage();    }  }  const textureAtlasCoordinates = textureAtlas.textureCoordinates;  if (textureAtlasCoordinates.length === 0) {    // Can't write billboard vertices until we have texture coordinates    // provided by a texture atlas    return;  }  updateMode(this, frameState);  billboards = this._billboards;  billboardsLength = billboards.length;  const billboardsToUpdate = this._billboardsToUpdate;  const billboardsToUpdateLength = this._billboardsToUpdateIndex;  const properties = this._propertiesChanged;  const textureAtlasGUID = textureAtlas.guid;  const createVertexArray =    this._createVertexArray || this._textureAtlasGUID !== textureAtlasGUID;  this._textureAtlasGUID = textureAtlasGUID;  let vafWriters;  const pass = frameState.passes;  const picking = pass.pick;  // PERFORMANCE_IDEA: Round robin multiple buffers.  if (createVertexArray || (!picking && this.computeNewBuffersUsage())) {    this._createVertexArray = false;    for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {      properties[k] = 0;    }    this._vaf = this._vaf && this._vaf.destroy();    if (billboardsLength > 0) {      // PERFORMANCE_IDEA:  Instead of creating a new one, resize like std::vector.      this._vaf = createVAF(        context,        billboardsLength,        this._buffersUsage,        this._instanced,        this._batchTable,        this._sdf      );      vafWriters = this._vaf.writers;      // Rewrite entire buffer if billboards were added or removed.      for (let i = 0; i < billboardsLength; ++i) {        const billboard = this._billboards[i];        billboard._dirty = false; // In case it needed an update.        writeBillboard(          this,          frameState,          textureAtlasCoordinates,          vafWriters,          billboard        );      }      // Different billboard collections share the same index buffer.      this._vaf.commit(getIndexBuffer(context));    }    this._billboardsToUpdateIndex = 0;  } else if (billboardsToUpdateLength > 0) {    // Billboards were modified, but none were added or removed.    const writers = scratchWriterArray;    writers.length = 0;    if (      properties[POSITION_INDEX] ||      properties[ROTATION_INDEX] ||      properties[SCALE_INDEX]    ) {      writers.push(writePositionScaleAndRotation);    }    if (      properties[IMAGE_INDEX_INDEX] ||      properties[PIXEL_OFFSET_INDEX] ||      properties[HORIZONTAL_ORIGIN_INDEX] ||      properties[VERTICAL_ORIGIN_INDEX] ||      properties[SHOW_INDEX]    ) {      writers.push(writeCompressedAttrib0);      if (this._instanced) {        writers.push(writeEyeOffset);      }    }    if (      properties[IMAGE_INDEX_INDEX] ||      properties[ALIGNED_AXIS_INDEX] ||      properties[TRANSLUCENCY_BY_DISTANCE_INDEX]    ) {      writers.push(writeCompressedAttrib1);      writers.push(writeCompressedAttrib2);    }    if (properties[IMAGE_INDEX_INDEX] || properties[COLOR_INDEX]) {      writers.push(writeCompressedAttrib2);    }    if (properties[EYE_OFFSET_INDEX]) {      writers.push(writeEyeOffset);    }    if (properties[SCALE_BY_DISTANCE_INDEX]) {      writers.push(writeScaleByDistance);    }    if (properties[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX]) {      writers.push(writePixelOffsetScaleByDistance);    }    if (      properties[DISTANCE_DISPLAY_CONDITION_INDEX] ||      properties[DISABLE_DEPTH_DISTANCE] ||      properties[IMAGE_INDEX_INDEX] ||      properties[POSITION_INDEX]    ) {      writers.push(writeCompressedAttribute3);    }    if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) {      writers.push(writeTextureCoordinateBoundsOrLabelTranslate);    }    if (properties[SDF_INDEX]) {      writers.push(writeSDF);    }    const numWriters = writers.length;    vafWriters = this._vaf.writers;    if (billboardsToUpdateLength / billboardsLength > 0.1) {      // If more than 10% of billboard change, rewrite the entire buffer.      // PERFORMANCE_IDEA:  I totally made up 10% :).      for (let m = 0; m < billboardsToUpdateLength; ++m) {        const b = billboardsToUpdate[m];        b._dirty = false;        for (let n = 0; n < numWriters; ++n) {          writers[n](this, frameState, textureAtlasCoordinates, vafWriters, b);        }      }      this._vaf.commit(getIndexBuffer(context));    } else {      for (let h = 0; h < billboardsToUpdateLength; ++h) {        const bb = billboardsToUpdate[h];        bb._dirty = false;        for (let o = 0; o < numWriters; ++o) {          writers[o](this, frameState, textureAtlasCoordinates, vafWriters, bb);        }        if (this._instanced) {          this._vaf.subCommit(bb._index, 1);        } else {          this._vaf.subCommit(bb._index * 4, 4);        }      }      this._vaf.endSubCommits();    }    this._billboardsToUpdateIndex = 0;  }  // If the number of total billboards ever shrinks considerably  // Truncate billboardsToUpdate so that we free memory that we're  // not going to be using.  if (billboardsToUpdateLength > billboardsLength * 1.5) {    billboardsToUpdate.length = billboardsLength;  }  if (!defined(this._vaf) || !defined(this._vaf.va)) {    return;  }  if (this._boundingVolumeDirty) {    this._boundingVolumeDirty = false;    BoundingSphere.transform(      this._baseVolume,      this.modelMatrix,      this._baseVolumeWC    );  }  let boundingVolume;  let modelMatrix = Matrix4.IDENTITY;  if (frameState.mode === SceneMode.SCENE3D) {    modelMatrix = this.modelMatrix;    boundingVolume = BoundingSphere.clone(      this._baseVolumeWC,      this._boundingVolume    );  } else {    boundingVolume = BoundingSphere.clone(      this._baseVolume2D,      this._boundingVolume    );  }  updateBoundingVolume(this, frameState, boundingVolume);  const blendOptionChanged = this._blendOption !== this.blendOption;  this._blendOption = this.blendOption;  if (blendOptionChanged) {    if (      this._blendOption === BlendOption.OPAQUE ||      this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT    ) {      this._rsOpaque = RenderState.fromCache({        depthTest: {          enabled: true,          func: WebGLConstants.LESS,        },        depthMask: true,      });    } else {      this._rsOpaque = undefined;    }    // If OPAQUE_AND_TRANSLUCENT is in use, only the opaque pass gets the benefit of the depth buffer,    // not the translucent pass.  Otherwise, if the TRANSLUCENT pass is on its own, it turns on    // a depthMask in lieu of full depth sorting (because it has opaque-ish fragments that look bad in OIT).    // When the TRANSLUCENT depth mask is in use, label backgrounds require the depth func to be LEQUAL.    const useTranslucentDepthMask =      this._blendOption === BlendOption.TRANSLUCENT;    if (      this._blendOption === BlendOption.TRANSLUCENT ||      this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT    ) {      this._rsTranslucent = RenderState.fromCache({        depthTest: {          enabled: true,          func: useTranslucentDepthMask            ? WebGLConstants.LEQUAL            : WebGLConstants.LESS,        },        depthMask: useTranslucentDepthMask,        blending: BlendingState.ALPHA_BLEND,      });    } else {      this._rsTranslucent = undefined;    }  }  this._shaderDisableDepthDistance =    this._shaderDisableDepthDistance ||    frameState.minimumDisableDepthTestDistance !== 0.0;  let vsSource;  let fsSource;  let vs;  let fs;  let vertDefines;  const supportVSTextureReads =    ContextLimits.maximumVertexTextureImageUnits > 0;  if (    blendOptionChanged ||    this._shaderRotation !== this._compiledShaderRotation ||    this._shaderAlignedAxis !== this._compiledShaderAlignedAxis ||    this._shaderScaleByDistance !== this._compiledShaderScaleByDistance ||    this._shaderTranslucencyByDistance !==      this._compiledShaderTranslucencyByDistance ||    this._shaderPixelOffsetScaleByDistance !==      this._compiledShaderPixelOffsetScaleByDistance ||    this._shaderDistanceDisplayCondition !==      this._compiledShaderDistanceDisplayCondition ||    this._shaderDisableDepthDistance !==      this._compiledShaderDisableDepthDistance ||    this._shaderClampToGround !== this._compiledShaderClampToGround ||    this._sdf !== this._compiledSDF  ) {    vsSource = BillboardCollectionVS;    fsSource = BillboardCollectionFS;    vertDefines = [];    if (defined(this._batchTable)) {      vertDefines.push("VECTOR_TILE");      vsSource = this._batchTable.getVertexShaderCallback(        false,        "a_batchId",        undefined      )(vsSource);      fsSource = this._batchTable.getFragmentShaderCallback(        false,        undefined      )(fsSource);    }    vs = new ShaderSource({      defines: vertDefines,      sources: [vsSource],    });    if (this._instanced) {      vs.defines.push("INSTANCED");    }    if (this._shaderRotation) {      vs.defines.push("ROTATION");    }    if (this._shaderAlignedAxis) {      vs.defines.push("ALIGNED_AXIS");    }    if (this._shaderScaleByDistance) {      vs.defines.push("EYE_DISTANCE_SCALING");    }    if (this._shaderTranslucencyByDistance) {      vs.defines.push("EYE_DISTANCE_TRANSLUCENCY");    }    if (this._shaderPixelOffsetScaleByDistance) {      vs.defines.push("EYE_DISTANCE_PIXEL_OFFSET");    }    if (this._shaderDistanceDisplayCondition) {      vs.defines.push("DISTANCE_DISPLAY_CONDITION");    }    if (this._shaderDisableDepthDistance) {      vs.defines.push("DISABLE_DEPTH_DISTANCE");    }    if (this._shaderClampToGround) {      if (supportVSTextureReads) {        vs.defines.push("VERTEX_DEPTH_CHECK");      } else {        vs.defines.push("FRAGMENT_DEPTH_CHECK");      }    }    const sdfEdge = 1.0 - SDFSettings.CUTOFF;    if (this._sdf) {      vs.defines.push("SDF");    }    const vectorFragDefine = defined(this._batchTable) ? "VECTOR_TILE" : "";    if (this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT) {      fs = new ShaderSource({        defines: ["OPAQUE", vectorFragDefine],        sources: [fsSource],      });      if (this._shaderClampToGround) {        if (supportVSTextureReads) {          fs.defines.push("VERTEX_DEPTH_CHECK");        } else {          fs.defines.push("FRAGMENT_DEPTH_CHECK");        }      }      if (this._sdf) {        fs.defines.push("SDF");        fs.defines.push(`SDF_EDGE ${sdfEdge}`);      }      this._sp = ShaderProgram.replaceCache({        context: context,        shaderProgram: this._sp,        vertexShaderSource: vs,        fragmentShaderSource: fs,        attributeLocations: attributeLocations,      });      fs = new ShaderSource({        defines: ["TRANSLUCENT", vectorFragDefine],        sources: [fsSource],      });      if (this._shaderClampToGround) {        if (supportVSTextureReads) {          fs.defines.push("VERTEX_DEPTH_CHECK");        } else {          fs.defines.push("FRAGMENT_DEPTH_CHECK");        }      }      if (this._sdf) {        fs.defines.push("SDF");        fs.defines.push(`SDF_EDGE ${sdfEdge}`);      }      this._spTranslucent = ShaderProgram.replaceCache({        context: context,        shaderProgram: this._spTranslucent,        vertexShaderSource: vs,        fragmentShaderSource: fs,        attributeLocations: attributeLocations,      });    }    if (this._blendOption === BlendOption.OPAQUE) {      fs = new ShaderSource({        defines: [vectorFragDefine],        sources: [fsSource],      });      if (this._shaderClampToGround) {        if (supportVSTextureReads) {          fs.defines.push("VERTEX_DEPTH_CHECK");        } else {          fs.defines.push("FRAGMENT_DEPTH_CHECK");        }      }      if (this._sdf) {        fs.defines.push("SDF");        fs.defines.push(`SDF_EDGE ${sdfEdge}`);      }      this._sp = ShaderProgram.replaceCache({        context: context,        shaderProgram: this._sp,        vertexShaderSource: vs,        fragmentShaderSource: fs,        attributeLocations: attributeLocations,      });    }    if (this._blendOption === BlendOption.TRANSLUCENT) {      fs = new ShaderSource({        defines: [vectorFragDefine],        sources: [fsSource],      });      if (this._shaderClampToGround) {        if (supportVSTextureReads) {          fs.defines.push("VERTEX_DEPTH_CHECK");        } else {          fs.defines.push("FRAGMENT_DEPTH_CHECK");        }      }      if (this._sdf) {        fs.defines.push("SDF");        fs.defines.push(`SDF_EDGE ${sdfEdge}`);      }      this._spTranslucent = ShaderProgram.replaceCache({        context: context,        shaderProgram: this._spTranslucent,        vertexShaderSource: vs,        fragmentShaderSource: fs,        attributeLocations: attributeLocations,      });    }    this._compiledShaderRotation = this._shaderRotation;    this._compiledShaderAlignedAxis = this._shaderAlignedAxis;    this._compiledShaderScaleByDistance = this._shaderScaleByDistance;    this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance;    this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance;    this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition;    this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance;    this._compiledShaderClampToGround = this._shaderClampToGround;    this._compiledSDF = this._sdf;  }  const commandList = frameState.commandList;  if (pass.render || pass.pick) {    const colorList = this._colorCommands;    const opaque = this._blendOption === BlendOption.OPAQUE;    const opaqueAndTranslucent =      this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT;    const va = this._vaf.va;    const vaLength = va.length;    let uniforms = this._uniforms;    let pickId;    if (defined(this._batchTable)) {      uniforms = this._batchTable.getUniformMapCallback()(uniforms);      pickId = this._batchTable.getPickId();    } else {      pickId = "v_pickColor";    }    colorList.length = vaLength;    const totalLength = opaqueAndTranslucent ? vaLength * 2 : vaLength;    for (let j = 0; j < totalLength; ++j) {      let command = colorList[j];      if (!defined(command)) {        command = colorList[j] = new DrawCommand();      }      const opaqueCommand = opaque || (opaqueAndTranslucent && j % 2 === 0);      command.pass =        opaqueCommand || !opaqueAndTranslucent ? Pass.OPAQUE : Pass.TRANSLUCENT;      command.owner = this;      const index = opaqueAndTranslucent ? Math.floor(j / 2.0) : j;      command.boundingVolume = boundingVolume;      command.modelMatrix = modelMatrix;      command.count = va[index].indicesCount;      command.shaderProgram = opaqueCommand ? this._sp : this._spTranslucent;      command.uniformMap = uniforms;      command.vertexArray = va[index].va;      command.renderState = opaqueCommand        ? this._rsOpaque        : this._rsTranslucent;      command.debugShowBoundingVolume = this.debugShowBoundingVolume;      command.pickId = pickId;      if (this._instanced) {        command.count = 6;        command.instanceCount = billboardsLength;      }      commandList.push(command);    }    if (this.debugShowTextureAtlas) {      if (!defined(this.debugCommand)) {        this.debugCommand = createDebugCommand(this, frameState.context);      }      commandList.push(this.debugCommand);    }  }};/** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * 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. * * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see BillboardCollection#destroy */BillboardCollection.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. * <br /><br /> * 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. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * billboards = billboards && billboards.destroy(); * * @see BillboardCollection#isDestroyed */BillboardCollection.prototype.destroy = function () {  if (defined(this._removeCallbackFunc)) {    this._removeCallbackFunc();    this._removeCallbackFunc = undefined;  }  this._textureAtlas =    this._destroyTextureAtlas &&    this._textureAtlas &&    this._textureAtlas.destroy();  this._sp = this._sp && this._sp.destroy();  this._spTranslucent = this._spTranslucent && this._spTranslucent.destroy();  this._vaf = this._vaf && this._vaf.destroy();  destroyBillboards(this._billboards);  return destroyObject(this);};export default BillboardCollection;
 |