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^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 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;
|