| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150 |
- import AttributeCompression from "../Core/AttributeCompression.js";
- import BoundingRectangle from "../Core/BoundingRectangle.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 Frozen from "../Core/Frozen.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.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 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, { isHeightReferenceClamp } from "./HeightReference.js";
- import HorizontalOrigin from "./HorizontalOrigin.js";
- import SceneMode from "./SceneMode.js";
- import SDFSettings from "./SDFSettings.js";
- import TextureAtlas from "../Renderer/TextureAtlas.js";
- import VerticalOrigin from "./VerticalOrigin.js";
- import Ellipsoid from "../Core/Ellipsoid.js";
- import WebGLConstants from "../Core/WebGLConstants.js";
- import DeveloperError from "../Core/DeveloperError.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 SPLIT_DIRECTION_INDEX = Billboard.SPLIT_DIRECTION_INDEX;
- const NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES;
-
- const attributeLocations = {
- 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,
- splitDirection: 13,
- };
-
- /**
- * 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.
- * @param {number} [options.coarseDepthTestDistance] The distance from the camera, beyond which, billboards are depth-tested against an approximation of the globe ellipsoid rather than against the full globe depth buffer. If unspecified, the default value is determined relative to the value of {@link Ellipsoid.default}.
- * @param {number} [options.threePointDepthTestDistance] The distance from the camera, within which, billboards with a {@link Billboard#heightReference} value of {@link HeightReference.CLAMP_TO_GROUND} or {@link HeightReference.CLAMP_TO_TERRAIN} are depth tested against three key points. This ensures that if any key point of the billboard is visible, the whole billboard will be visible. If unspecified, the default value is determined relative to the value of {@link Ellipsoid.default}.
- * @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?id=billboards|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 = options ?? Frozen.EMPTY_OBJECT;
-
- this._scene = options.scene;
- this._batchTable = options.batchTable;
-
- let textureAtlas = options.textureAtlas; // Hidden option for internal use
- if (!defined(textureAtlas)) {
- textureAtlas = new TextureAtlas();
- }
- this._textureAtlas = textureAtlas;
- this._textureAtlasGUID = textureAtlas.guid;
- this._destroyTextureAtlas = true;
- this._billboardTextureCache = new Map();
-
- 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 = [];
-
- this._allBillboardsReady = false;
-
- /**
- * Determines if billboards in this collection will be shown.
- *
- * @type {boolean}
- * @default true
- */
- this.show = 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(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 = 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 = 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 = 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
- BufferUsage.STATIC_DRAW, // SPLIT_DIRECTION_INDEX
- ];
-
- this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints
- this._coarseDepthTestDistance =
- options.coarseDepthTestDistance ?? Ellipsoid.default.minimumRadius / 10.0;
- this._threePointDepthTestDistance =
- options.threePointDepthTestDistance ??
- Ellipsoid.default.minimumRadius / 1000.0;
-
- this._uniforms = {
- u_atlas: () => {
- return this.textureAtlas.texture;
- },
- u_highlightColor: () => {
- return this._highlightColor;
- },
- u_coarseDepthTestDistance: () => {
- return this._coarseDepthTestDistance;
- },
- u_threePointDepthTestDistance: () => {
- return this._threePointDepthTestDistance;
- },
- };
-
- 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}
- * @readonly
- */
- 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) {
- //>>includeStart('debug', pragmas.debug);
- Check.defined("textureAtlas", value);
- //>>includeEnd('debug');
-
- if (this._textureAtlas !== value) {
- this._textureAtlas =
- this._destroyTextureAtlas &&
- this._textureAtlas &&
- this._textureAtlas.destroy();
- this._textureAtlas = value;
- }
- },
- },
-
- /**
- * 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();
- * 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;
- },
- },
-
- /**
- * Returns the size in bytes of the WebGL texture resources.
- * @private
- * @memberof BillboardCollection.prototype
- * @type {number}
- * @readonly
- */
- sizeInBytes: {
- get: function () {
- return this._textureAtlas.sizeInBytes;
- },
- },
-
- /**
- * True when all billboards currently in the collection are ready for rendering.
- * @private
- * @memberof BillboardCollection.prototype
- * @type {boolean}
- * @readonly
- */
- ready: {
- get: function () {
- return this._allBillboardsReady;
- },
- },
-
- /**
- * Cache of loaded billboard images.
- * @private
- * @memberof BillboardCollection.prototype
- * @type {Map<string, BillboardTexture>}
- * @readonly
- */
- billboardTextureCache: {
- get: function () {
- return this._billboardTextureCache;
- },
- },
-
- /**
- * The distance from the camera, beyond which, billboards are depth-tested against an approximation of
- * the globe ellipsoid rather than against the full globe depth buffer. When set to <code>0</code>, the
- * approximate depth test is always applied. When set to <code>Number.POSITIVE_INFINITY</code>, the
- * approximate depth test is never applied.
- * <br/><br/>
- * This setting only applies when a billboard's {@link Billboard#disableDepthTestDistance} value would
- * otherwise allow depth testing—i.e., distance from the camera to the billboard is less than a
- * billboard's {@link Billboard#disableDepthTestDistance} value.
- * @memberof BillboardCollection.prototype
- * @type {number}
- */
- coarseDepthTestDistance: {
- get: function () {
- return this._coarseDepthTestDistance;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number("coarseDepthTestDistance", value);
- //>>includeEnd('debug');
- this._coarseDepthTestDistance = value;
- },
- },
-
- /**
- * The distance from the camera, within which, billboards with a {@link Billboard#heightReference} value
- * of {@link HeightReference.CLAMP_TO_GROUND} or {@link HeightReference.CLAMP_TO_TERRAIN} are depth tested
- * against three key points. This ensures that if any key point of the billboard is visible, the whole
- * billboard will be visible. When set to <code>0</code>, this feature is disabled and portions of a
- * billboards behind terrain be clipped.
- * <br/><br/>
- * This setting only applies when a billboard's {@link Billboard#disableDepthTestDistance} value would
- * otherwise allow depth testing—i.e., distance from the camera to the billboard is less than a
- * billboard's {@link Billboard#disableDepthTestDistance} value.
- * @see {@link https://cesium.com/blog/2018/07/30/billboards-on-terrain-improvements/|Billboards and Labels on Terrain Improvements}
- * @memberof BillboardCollection.prototype
- * @type {number}
- */
- threePointDepthTestDistance: {
- get: function () {
- return this._threePointDepthTestDistance;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number("threePointDepthTestDistance", value);
- //>>includeEnd('debug');
- this._threePointDepthTestDistance = 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 {Billboard.ConstructorOptions}[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];
- };
-
- function getIndexBuffer(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, 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],
- },
- {
- index: attributeLocations.splitDirection,
- componentsPerAttribute: 1,
- componentDatatype: ComponentDatatype.FLOAT,
- usage: buffersUsage[SPLIT_DIRECTION_INDEX],
- },
- // Instancing requires one non-instanced attribute.
- {
- 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],
- });
- }
-
- // One vertex is needed for each (instanced) billboard.
- return new VertexArrayFacade(context, attributes, numberOfBillboards, true);
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- // 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,
- vafWriters,
- billboard,
- ) {
- 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;
-
- positionHighWriter(billboard._index, high.x, high.y, high.z, scale);
- positionLowWriter(billboard._index, low.x, low.y, low.z, rotation);
- }
-
- const scratchCartesian2 = new Cartesian2();
-
- const UPPER_BOUND = 32768.0; // 2^15
-
- const LEFT_SHIFT16 = 65536.0; // 2^16
- const LEFT_SHIFT12 = 4096.0; // 2^12
- const LEFT_SHIFT8 = 256.0; // 2^8
- const 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 scratchBoundingRectangle = new BoundingRectangle();
-
- function writeCompressedAttrib0(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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;
- if (billboard.ready) {
- const imageRectangle = billboard.computeTextureCoordinates(
- scratchBoundingRectangle,
- );
-
- bottomLeftX = imageRectangle.x;
- bottomLeftY = imageRectangle.y;
- }
-
- 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;
-
- // We scale `translate` by LEFT_SHIFT2 before encoding it (and unscale after decoding in the shader)
- // to preserve some subpixel precision (1 / 4 = 0.25 pixels). This mitigates rounding errors in aligning glyphs.
- // The cost of increasing this scaling factor is that it decreases the range of representable `translate` values
- // by the same scaling factor. Value must be kept in sync with the shader.
- let compressed2 =
- Math.floor(
- CesiumMath.clamp(translateX * LEFT_SHIFT2, -UPPER_BOUND, UPPER_BOUND) +
- UPPER_BOUND,
- ) * LEFT_SHIFT8;
-
- const tempTanslateY =
- (CesiumMath.clamp(translateY * LEFT_SHIFT2, -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);
-
- writer(
- billboard._index,
- compressed0,
- compressed1,
- compressed2,
- compressedTexCoordsLL,
- );
- }
-
- function writeCompressedAttrib1(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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;
- }
- }
-
- const imageWidth = Math.round(billboard.width ?? 0);
- 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;
-
- writer(billboard._index, compressed0, compressed1, near, far);
- }
-
- function writeCompressedAttrib2(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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;
-
- const imageHeight = billboard.height ?? 0;
- billboardCollection._maxSize = Math.max(
- billboardCollection._maxSize,
- imageHeight,
- );
- let labelHorizontalOrigin = billboard._labelHorizontalOrigin ?? -2;
- labelHorizontalOrigin += 2;
-
- const compressed0 = AttributeCompression.encodeRGB8(color);
- const compressed1 = AttributeCompression.encodeRGB8(pickColor);
- const compressed2 =
- Color.floatToByte(color.alpha) * LEFT_SHIFT16 +
- Color.floatToByte(pickColor.alpha) * LEFT_SHIFT8 +
- (sizeInMeters * 2.0 + validAlignedAxis);
- const compressed3 = imageHeight * LEFT_SHIFT2 + labelHorizontalOrigin;
-
- writer(billboard._index, compressed0, compressed1, compressed2, compressed3);
- }
-
- function writeEyeOffset(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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),
- );
-
- scratchCartesian2.x = 0;
- scratchCartesian2.y = 0;
-
- if (billboard.ready) {
- const imageRectangle = billboard.computeTextureCoordinates(
- scratchBoundingRectangle,
- );
-
- scratchCartesian2.x = imageRectangle.width;
- scratchCartesian2.y = imageRectangle.height;
- }
-
- const compressedTexCoordsRange =
- AttributeCompression.compressTextureCoordinates(scratchCartesian2);
-
- writer(
- billboard._index,
- eyeOffset.x,
- eyeOffset.y,
- eyeOffsetZ,
- compressedTexCoordsRange,
- );
- }
-
- function writeScaleByDistance(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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;
- }
- }
-
- writer(billboard._index, near, nearValue, far, farValue);
- }
-
- function writePixelOffsetScaleByDistance(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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;
- }
- }
-
- writer(billboard._index, near, nearValue, far, farValue);
- }
-
- function writeCompressedAttribute3(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- 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 =
- isHeightReferenceClamp(billboard.heightReference) &&
- frameState.context.depthTexture;
-
- 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)) {
- imageWidth = billboard.width ?? 0;
- imageHeight = billboard.height ?? 0;
- } 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;
-
- writer(billboard._index, near, far, disableDepthTestDistance, dimensions);
- }
-
- function writeTextureCoordinateBoundsOrLabelTranslate(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- if (isHeightReferenceClamp(billboard.heightReference)) {
- 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;
- }
- const writer =
- vafWriters[attributeLocations.textureCoordinateBoundsOrLabelTranslate];
-
- //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;
- }
-
- writer(billboard._index, translateX, translateY, 0.0, 0.0);
- }
-
- function writeBatchId(billboardCollection, frameState, vafWriters, billboard) {
- if (!defined(billboardCollection._batchTable)) {
- return;
- }
-
- const writer = vafWriters[attributeLocations.a_batchId];
- const id = billboard._batchIndex;
-
- writer(billboard._index, id);
- }
-
- function writeSDF(billboardCollection, frameState, vafWriters, billboard) {
- if (!billboardCollection._sdf) {
- return;
- }
-
- const writer = vafWriters[attributeLocations.sdf];
-
- const outlineColor = billboard.outlineColor;
- const outlineWidth = billboard.outlineWidth;
-
- const compressed0 = AttributeCompression.encodeRGB8(outlineColor);
-
- // Compute the relative outline distance
- const outlineDistance = outlineWidth / SDFSettings.RADIUS;
- const compressed1 =
- Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT16 +
- Color.floatToByte(outlineDistance) * LEFT_SHIFT8;
-
- writer(billboard._index, compressed0, compressed1);
- }
-
- function writeSplitDirection(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- const writer = vafWriters[attributeLocations.splitDirection];
- let direction = 0.0;
-
- const split = billboard.splitDirection;
- if (defined(split)) {
- direction = split;
- }
-
- writer(billboard._index, direction);
- }
-
- function writeBillboard(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- ) {
- writePositionScaleAndRotation(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeCompressedAttrib0(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeCompressedAttrib1(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeCompressedAttrib2(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeEyeOffset(billboardCollection, frameState, vafWriters, billboard);
- writeScaleByDistance(billboardCollection, frameState, vafWriters, billboard);
- writePixelOffsetScaleByDistance(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeCompressedAttribute3(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeTextureCoordinateBoundsOrLabelTranslate(
- billboardCollection,
- frameState,
- vafWriters,
- billboard,
- );
- writeBatchId(billboardCollection, frameState, vafWriters, billboard);
- writeSDF(billboardCollection, frameState, vafWriters, billboard);
- writeSplitDirection(billboardCollection, frameState, 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" +
- "in vec2 v_textureCoordinates; \n" +
- "void main() \n" +
- "{ \n" +
- " out_FragColor = texture(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;
- }
-
- const context = frameState.context;
-
- if (
- !context.instancedArrays ||
- !(ContextLimits.maximumVertexTextureImageUnits > 0)
- ) {
- throw new DeveloperError(
- "Beginning in CesiumJS 1.140, billboards and labels require device support for WebGL 2, " +
- "or WebGL 1 with ANGLE_instanced_arrays and MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0. For more " +
- "information or to share feedback, see: https://github.com/CesiumGS/cesium/issues/13053",
- );
- }
-
- let billboards = this._billboards;
- let billboardsLength = billboards.length;
- let allBillboardsReady = true;
- for (let i = 0; i < billboardsLength; ++i) {
- const billboard = billboards[i];
- if (defined(billboard.loadError)) {
- console.error(
- `Error loading image for billboard: ${billboard.loadError}`,
- );
- billboard.image = undefined;
- }
-
- if (billboard.textureDirty) {
- this._updateBillboard(billboard, IMAGE_INDEX_INDEX);
- }
-
- if (billboard.show) {
- allBillboardsReady = allBillboardsReady && billboard.ready;
- }
- }
-
- // Queue any texture resource updates for after the frame is rendered
- const textureAtlas = this._textureAtlas;
- frameState.afterRender.push(() => {
- if (this.isDestroyed()) {
- return;
- }
-
- return textureAtlas.update(frameState.context);
- });
-
- if (!defined(textureAtlas.texture)) {
- // Can't write billboard vertices until the texture atlas
- // has been updated once
- 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._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.
- billboard.textureDirty = false;
- writeBillboard(this, frameState, 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);
- 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[IMAGE_INDEX_INDEX] || 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);
- }
-
- if (properties[SPLIT_DIRECTION_INDEX]) {
- writers.push(writeSplitDirection);
- }
-
- 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;
- b.textureDirty = false;
-
- for (let n = 0; n < numWriters; ++n) {
- writers[n](this, frameState, vafWriters, b);
- }
- }
- this._vaf.commit(getIndexBuffer(context));
- } else {
- for (let h = 0; h < billboardsToUpdateLength; ++h) {
- const bb = billboardsToUpdate[h];
- bb._dirty = false;
- bb.textureDirty = false;
-
- for (let o = 0; o < numWriters; ++o) {
- writers[o](this, frameState, vafWriters, bb);
- }
-
- this._vaf.subCommit(bb._index, 1);
- }
- 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).
- 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;
-
- 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 = ["INSTANCED"];
- 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._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) {
- vs.defines.push("VS_THREE_POINT_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) {
- fs.defines.push("VS_THREE_POINT_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) {
- fs.defines.push("VS_THREE_POINT_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) {
- fs.defines.push("VS_THREE_POINT_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) {
- fs.defines.push("VS_THREE_POINT_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;
-
- 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);
- }
- }
-
- this._allBillboardsReady = allBillboardsReady;
- };
-
- /**
- * 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;
|