BillboardCollection.js 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399
  1. import AttributeCompression from "../Core/AttributeCompression.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Cartesian2 from "../Core/Cartesian2.js";
  4. import Cartesian3 from "../Core/Cartesian3.js";
  5. import Color from "../Core/Color.js";
  6. import ComponentDatatype from "../Core/ComponentDatatype.js";
  7. import defaultValue from "../Core/defaultValue.js";
  8. import defined from "../Core/defined.js";
  9. import destroyObject from "../Core/destroyObject.js";
  10. import DeveloperError from "../Core/DeveloperError.js";
  11. import EncodedCartesian3 from "../Core/EncodedCartesian3.js";
  12. import IndexDatatype from "../Core/IndexDatatype.js";
  13. import CesiumMath from "../Core/Math.js";
  14. import Matrix4 from "../Core/Matrix4.js";
  15. import WebGLConstants from "../Core/WebGLConstants.js";
  16. import Buffer from "../Renderer/Buffer.js";
  17. import BufferUsage from "../Renderer/BufferUsage.js";
  18. import ContextLimits from "../Renderer/ContextLimits.js";
  19. import DrawCommand from "../Renderer/DrawCommand.js";
  20. import Pass from "../Renderer/Pass.js";
  21. import RenderState from "../Renderer/RenderState.js";
  22. import ShaderProgram from "../Renderer/ShaderProgram.js";
  23. import ShaderSource from "../Renderer/ShaderSource.js";
  24. import VertexArrayFacade from "../Renderer/VertexArrayFacade.js";
  25. import BillboardCollectionFS from "../Shaders/BillboardCollectionFS.js";
  26. import BillboardCollectionVS from "../Shaders/BillboardCollectionVS.js";
  27. import Billboard from "./Billboard.js";
  28. import BlendingState from "./BlendingState.js";
  29. import BlendOption from "./BlendOption.js";
  30. import HeightReference from "./HeightReference.js";
  31. import HorizontalOrigin from "./HorizontalOrigin.js";
  32. import SceneMode from "./SceneMode.js";
  33. import SDFSettings from "./SDFSettings.js";
  34. import TextureAtlas from "./TextureAtlas.js";
  35. import VerticalOrigin from "./VerticalOrigin.js";
  36. var SHOW_INDEX = Billboard.SHOW_INDEX;
  37. var POSITION_INDEX = Billboard.POSITION_INDEX;
  38. var PIXEL_OFFSET_INDEX = Billboard.PIXEL_OFFSET_INDEX;
  39. var EYE_OFFSET_INDEX = Billboard.EYE_OFFSET_INDEX;
  40. var HORIZONTAL_ORIGIN_INDEX = Billboard.HORIZONTAL_ORIGIN_INDEX;
  41. var VERTICAL_ORIGIN_INDEX = Billboard.VERTICAL_ORIGIN_INDEX;
  42. var SCALE_INDEX = Billboard.SCALE_INDEX;
  43. var IMAGE_INDEX_INDEX = Billboard.IMAGE_INDEX_INDEX;
  44. var COLOR_INDEX = Billboard.COLOR_INDEX;
  45. var ROTATION_INDEX = Billboard.ROTATION_INDEX;
  46. var ALIGNED_AXIS_INDEX = Billboard.ALIGNED_AXIS_INDEX;
  47. var SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX;
  48. var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX;
  49. var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX =
  50. Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX;
  51. var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION;
  52. var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE;
  53. var TEXTURE_COORDINATE_BOUNDS = Billboard.TEXTURE_COORDINATE_BOUNDS;
  54. var SDF_INDEX = Billboard.SDF_INDEX;
  55. var NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES;
  56. var attributeLocations;
  57. var attributeLocationsBatched = {
  58. positionHighAndScale: 0,
  59. positionLowAndRotation: 1,
  60. compressedAttribute0: 2, // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates
  61. compressedAttribute1: 3, // aligned axis, translucency by distance, image width
  62. compressedAttribute2: 4, // image height, color, pick color, size in meters, valid aligned axis, 13 bits free
  63. eyeOffset: 5, // 4 bytes free
  64. scaleByDistance: 6,
  65. pixelOffsetScaleByDistance: 7,
  66. compressedAttribute3: 8,
  67. textureCoordinateBoundsOrLabelTranslate: 9,
  68. a_batchId: 10,
  69. sdf: 11,
  70. };
  71. var attributeLocationsInstanced = {
  72. direction: 0,
  73. positionHighAndScale: 1,
  74. positionLowAndRotation: 2, // texture offset in w
  75. compressedAttribute0: 3,
  76. compressedAttribute1: 4,
  77. compressedAttribute2: 5,
  78. eyeOffset: 6, // texture range in w
  79. scaleByDistance: 7,
  80. pixelOffsetScaleByDistance: 8,
  81. compressedAttribute3: 9,
  82. textureCoordinateBoundsOrLabelTranslate: 10,
  83. a_batchId: 11,
  84. sdf: 12,
  85. };
  86. /**
  87. * A renderable collection of billboards. Billboards are viewport-aligned
  88. * images positioned in the 3D scene.
  89. * <br /><br />
  90. * <div align='center'>
  91. * <img src='Images/Billboard.png' width='400' height='300' /><br />
  92. * Example billboards
  93. * </div>
  94. * <br /><br />
  95. * Billboards are added and removed from the collection using {@link BillboardCollection#add}
  96. * and {@link BillboardCollection#remove}. Billboards in a collection automatically share textures
  97. * for images with the same identifier.
  98. *
  99. * @alias BillboardCollection
  100. * @constructor
  101. *
  102. * @param {Object} [options] Object with the following properties:
  103. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each billboard from model to world coordinates.
  104. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  105. * @param {Scene} [options.scene] Must be passed in for billboards that use the height reference property or will be depth tested against the globe.
  106. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The billboard blending option. The default
  107. * is used for rendering both opaque and translucent billboards. However, if either all of the billboards are completely opaque or all are completely translucent,
  108. * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x.
  109. *
  110. * @performance For best performance, prefer a few collections, each with many billboards, to
  111. * many collections with only a few billboards each. Organize collections so that billboards
  112. * with the same update frequency are in the same collection, i.e., billboards that do not
  113. * change should be in one collection; billboards that change every frame should be in another
  114. * collection; and so on.
  115. *
  116. * @see BillboardCollection#add
  117. * @see BillboardCollection#remove
  118. * @see Billboard
  119. * @see LabelCollection
  120. *
  121. * @demo {@link https://sandcastle.cesium.com/index.html?src=Billboards.html|Cesium Sandcastle Billboard Demo}
  122. *
  123. * @example
  124. * // Create a billboard collection with two billboards
  125. * var billboards = scene.primitives.add(new Cesium.BillboardCollection());
  126. * billboards.add({
  127. * position : new Cesium.Cartesian3(1.0, 2.0, 3.0),
  128. * image : 'url/to/image'
  129. * });
  130. * billboards.add({
  131. * position : new Cesium.Cartesian3(4.0, 5.0, 6.0),
  132. * image : 'url/to/another/image'
  133. * });
  134. */
  135. function BillboardCollection(options) {
  136. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  137. this._scene = options.scene;
  138. this._batchTable = options.batchTable;
  139. this._textureAtlas = undefined;
  140. this._textureAtlasGUID = undefined;
  141. this._destroyTextureAtlas = true;
  142. this._sp = undefined;
  143. this._spTranslucent = undefined;
  144. this._rsOpaque = undefined;
  145. this._rsTranslucent = undefined;
  146. this._vaf = undefined;
  147. this._billboards = [];
  148. this._billboardsToUpdate = [];
  149. this._billboardsToUpdateIndex = 0;
  150. this._billboardsRemoved = false;
  151. this._createVertexArray = false;
  152. this._shaderRotation = false;
  153. this._compiledShaderRotation = false;
  154. this._shaderAlignedAxis = false;
  155. this._compiledShaderAlignedAxis = false;
  156. this._shaderScaleByDistance = false;
  157. this._compiledShaderScaleByDistance = false;
  158. this._shaderTranslucencyByDistance = false;
  159. this._compiledShaderTranslucencyByDistance = false;
  160. this._shaderPixelOffsetScaleByDistance = false;
  161. this._compiledShaderPixelOffsetScaleByDistance = false;
  162. this._shaderDistanceDisplayCondition = false;
  163. this._compiledShaderDistanceDisplayCondition = false;
  164. this._shaderDisableDepthDistance = false;
  165. this._compiledShaderDisableDepthDistance = false;
  166. this._shaderClampToGround = false;
  167. this._compiledShaderClampToGround = false;
  168. this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);
  169. this._maxSize = 0.0;
  170. this._maxEyeOffset = 0.0;
  171. this._maxScale = 1.0;
  172. this._maxPixelOffset = 0.0;
  173. this._allHorizontalCenter = true;
  174. this._allVerticalCenter = true;
  175. this._allSizedInMeters = true;
  176. this._baseVolume = new BoundingSphere();
  177. this._baseVolumeWC = new BoundingSphere();
  178. this._baseVolume2D = new BoundingSphere();
  179. this._boundingVolume = new BoundingSphere();
  180. this._boundingVolumeDirty = false;
  181. this._colorCommands = [];
  182. /**
  183. * The 4x4 transformation matrix that transforms each billboard in this collection from model to world coordinates.
  184. * When this is the identity matrix, the billboards are drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  185. * Local reference frames can be used by providing a different transformation matrix, like that returned
  186. * by {@link Transforms.eastNorthUpToFixedFrame}.
  187. *
  188. * @type {Matrix4}
  189. * @default {@link Matrix4.IDENTITY}
  190. *
  191. *
  192. * @example
  193. * var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);
  194. * billboards.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  195. * billboards.add({
  196. * image : 'url/to/image',
  197. * position : new Cesium.Cartesian3(0.0, 0.0, 0.0) // center
  198. * });
  199. * billboards.add({
  200. * image : 'url/to/image',
  201. * position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0) // east
  202. * });
  203. * billboards.add({
  204. * image : 'url/to/image',
  205. * position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0) // north
  206. * });
  207. * billboards.add({
  208. * image : 'url/to/image',
  209. * position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0) // up
  210. * });
  211. *
  212. * @see Transforms.eastNorthUpToFixedFrame
  213. */
  214. this.modelMatrix = Matrix4.clone(
  215. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  216. );
  217. this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  218. /**
  219. * This property is for debugging only; it is not for production use nor is it optimized.
  220. * <p>
  221. * Draws the bounding sphere for each draw command in the primitive.
  222. * </p>
  223. *
  224. * @type {Boolean}
  225. *
  226. * @default false
  227. */
  228. this.debugShowBoundingVolume = defaultValue(
  229. options.debugShowBoundingVolume,
  230. false
  231. );
  232. /**
  233. * This property is for debugging only; it is not for production use nor is it optimized.
  234. * <p>
  235. * Draws the texture atlas for this BillboardCollection as a fullscreen quad.
  236. * </p>
  237. *
  238. * @type {Boolean}
  239. *
  240. * @default false
  241. */
  242. this.debugShowTextureAtlas = defaultValue(
  243. options.debugShowTextureAtlas,
  244. false
  245. );
  246. /**
  247. * The billboard blending option. The default is used for rendering both opaque and translucent billboards.
  248. * However, if either all of the billboards are completely opaque or all are completely translucent,
  249. * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve
  250. * performance by up to 2x.
  251. * @type {BlendOption}
  252. * @default BlendOption.OPAQUE_AND_TRANSLUCENT
  253. */
  254. this.blendOption = defaultValue(
  255. options.blendOption,
  256. BlendOption.OPAQUE_AND_TRANSLUCENT
  257. );
  258. this._blendOption = undefined;
  259. this._mode = SceneMode.SCENE3D;
  260. // The buffer usage for each attribute is determined based on the usage of the attribute over time.
  261. this._buffersUsage = [
  262. BufferUsage.STATIC_DRAW, // SHOW_INDEX
  263. BufferUsage.STATIC_DRAW, // POSITION_INDEX
  264. BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_INDEX
  265. BufferUsage.STATIC_DRAW, // EYE_OFFSET_INDEX
  266. BufferUsage.STATIC_DRAW, // HORIZONTAL_ORIGIN_INDEX
  267. BufferUsage.STATIC_DRAW, // VERTICAL_ORIGIN_INDEX
  268. BufferUsage.STATIC_DRAW, // SCALE_INDEX
  269. BufferUsage.STATIC_DRAW, // IMAGE_INDEX_INDEX
  270. BufferUsage.STATIC_DRAW, // COLOR_INDEX
  271. BufferUsage.STATIC_DRAW, // ROTATION_INDEX
  272. BufferUsage.STATIC_DRAW, // ALIGNED_AXIS_INDEX
  273. BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX
  274. BufferUsage.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX
  275. BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX
  276. BufferUsage.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX
  277. BufferUsage.STATIC_DRAW, // TEXTURE_COORDINATE_BOUNDS
  278. ];
  279. this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints
  280. var that = this;
  281. this._uniforms = {
  282. u_atlas: function () {
  283. return that._textureAtlas.texture;
  284. },
  285. u_highlightColor: function () {
  286. return that._highlightColor;
  287. },
  288. };
  289. var scene = this._scene;
  290. if (defined(scene) && defined(scene.terrainProviderChanged)) {
  291. this._removeCallbackFunc = scene.terrainProviderChanged.addEventListener(
  292. function () {
  293. var billboards = this._billboards;
  294. var length = billboards.length;
  295. for (var i = 0; i < length; ++i) {
  296. if (defined(billboards[i])) {
  297. billboards[i]._updateClamping();
  298. }
  299. }
  300. },
  301. this
  302. );
  303. }
  304. }
  305. Object.defineProperties(BillboardCollection.prototype, {
  306. /**
  307. * Returns the number of billboards in this collection. This is commonly used with
  308. * {@link BillboardCollection#get} to iterate over all the billboards
  309. * in the collection.
  310. * @memberof BillboardCollection.prototype
  311. * @type {Number}
  312. */
  313. length: {
  314. get: function () {
  315. removeBillboards(this);
  316. return this._billboards.length;
  317. },
  318. },
  319. /**
  320. * Gets or sets the textureAtlas.
  321. * @memberof BillboardCollection.prototype
  322. * @type {TextureAtlas}
  323. * @private
  324. */
  325. textureAtlas: {
  326. get: function () {
  327. return this._textureAtlas;
  328. },
  329. set: function (value) {
  330. if (this._textureAtlas !== value) {
  331. this._textureAtlas =
  332. this._destroyTextureAtlas &&
  333. this._textureAtlas &&
  334. this._textureAtlas.destroy();
  335. this._textureAtlas = value;
  336. this._createVertexArray = true; // New per-billboard texture coordinates
  337. }
  338. },
  339. },
  340. /**
  341. * Gets or sets a value which determines if the texture atlas is
  342. * destroyed when the collection is destroyed.
  343. *
  344. * If the texture atlas is used by more than one collection, set this to <code>false</code>,
  345. * and explicitly destroy the atlas to avoid attempting to destroy it multiple times.
  346. *
  347. * @memberof BillboardCollection.prototype
  348. * @type {Boolean}
  349. * @private
  350. *
  351. * @example
  352. * // Set destroyTextureAtlas
  353. * // Destroy a billboard collection but not its texture atlas.
  354. *
  355. * var atlas = new TextureAtlas({
  356. * scene : scene,
  357. * images : images
  358. * });
  359. * billboards.textureAtlas = atlas;
  360. * billboards.destroyTextureAtlas = false;
  361. * billboards = billboards.destroy();
  362. * console.log(atlas.isDestroyed()); // False
  363. */
  364. destroyTextureAtlas: {
  365. get: function () {
  366. return this._destroyTextureAtlas;
  367. },
  368. set: function (value) {
  369. this._destroyTextureAtlas = value;
  370. },
  371. },
  372. });
  373. function destroyBillboards(billboards) {
  374. var length = billboards.length;
  375. for (var i = 0; i < length; ++i) {
  376. if (billboards[i]) {
  377. billboards[i]._destroy();
  378. }
  379. }
  380. }
  381. /**
  382. * Creates and adds a billboard with the specified initial properties to the collection.
  383. * The added billboard is returned so it can be modified or removed from the collection later.
  384. *
  385. * @param {Object}[options] A template describing the billboard's properties as shown in Example 1.
  386. * @returns {Billboard} The billboard that was added to the collection.
  387. *
  388. * @performance Calling <code>add</code> is expected constant time. However, the collection's vertex buffer
  389. * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead. For
  390. * best performance, add as many billboards as possible before calling <code>update</code>.
  391. *
  392. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  393. *
  394. *
  395. * @example
  396. * // Example 1: Add a billboard, specifying all the default values.
  397. * var b = billboards.add({
  398. * show : true,
  399. * position : Cesium.Cartesian3.ZERO,
  400. * pixelOffset : Cesium.Cartesian2.ZERO,
  401. * eyeOffset : Cesium.Cartesian3.ZERO,
  402. * heightReference : Cesium.HeightReference.NONE,
  403. * horizontalOrigin : Cesium.HorizontalOrigin.CENTER,
  404. * verticalOrigin : Cesium.VerticalOrigin.CENTER,
  405. * scale : 1.0,
  406. * image : 'url/to/image',
  407. * imageSubRegion : undefined,
  408. * color : Cesium.Color.WHITE,
  409. * id : undefined,
  410. * rotation : 0.0,
  411. * alignedAxis : Cesium.Cartesian3.ZERO,
  412. * width : undefined,
  413. * height : undefined,
  414. * scaleByDistance : undefined,
  415. * translucencyByDistance : undefined,
  416. * pixelOffsetScaleByDistance : undefined,
  417. * sizeInMeters : false,
  418. * distanceDisplayCondition : undefined
  419. * });
  420. *
  421. * @example
  422. * // Example 2: Specify only the billboard's cartographic position.
  423. * var b = billboards.add({
  424. * position : Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
  425. * });
  426. *
  427. * @see BillboardCollection#remove
  428. * @see BillboardCollection#removeAll
  429. */
  430. BillboardCollection.prototype.add = function (options) {
  431. var b = new Billboard(options, this);
  432. b._index = this._billboards.length;
  433. this._billboards.push(b);
  434. this._createVertexArray = true;
  435. return b;
  436. };
  437. /**
  438. * Removes a billboard from the collection.
  439. *
  440. * @param {Billboard} billboard The billboard to remove.
  441. * @returns {Boolean} <code>true</code> if the billboard was removed; <code>false</code> if the billboard was not found in the collection.
  442. *
  443. * @performance Calling <code>remove</code> is expected constant time. However, the collection's vertex buffer
  444. * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead. For
  445. * best performance, remove as many billboards as possible before calling <code>update</code>.
  446. * If you intend to temporarily hide a billboard, it is usually more efficient to call
  447. * {@link Billboard#show} instead of removing and re-adding the billboard.
  448. *
  449. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  450. *
  451. *
  452. * @example
  453. * var b = billboards.add(...);
  454. * billboards.remove(b); // Returns true
  455. *
  456. * @see BillboardCollection#add
  457. * @see BillboardCollection#removeAll
  458. * @see Billboard#show
  459. */
  460. BillboardCollection.prototype.remove = function (billboard) {
  461. if (this.contains(billboard)) {
  462. this._billboards[billboard._index] = null; // Removed later
  463. this._billboardsRemoved = true;
  464. this._createVertexArray = true;
  465. billboard._destroy();
  466. return true;
  467. }
  468. return false;
  469. };
  470. /**
  471. * Removes all billboards from the collection.
  472. *
  473. * @performance <code>O(n)</code>. It is more efficient to remove all the billboards
  474. * from a collection and then add new ones than to create a new collection entirely.
  475. *
  476. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  477. *
  478. *
  479. * @example
  480. * billboards.add(...);
  481. * billboards.add(...);
  482. * billboards.removeAll();
  483. *
  484. * @see BillboardCollection#add
  485. * @see BillboardCollection#remove
  486. */
  487. BillboardCollection.prototype.removeAll = function () {
  488. destroyBillboards(this._billboards);
  489. this._billboards = [];
  490. this._billboardsToUpdate = [];
  491. this._billboardsToUpdateIndex = 0;
  492. this._billboardsRemoved = false;
  493. this._createVertexArray = true;
  494. };
  495. function removeBillboards(billboardCollection) {
  496. if (billboardCollection._billboardsRemoved) {
  497. billboardCollection._billboardsRemoved = false;
  498. var newBillboards = [];
  499. var billboards = billboardCollection._billboards;
  500. var length = billboards.length;
  501. for (var i = 0, j = 0; i < length; ++i) {
  502. var billboard = billboards[i];
  503. if (billboard) {
  504. billboard._index = j++;
  505. newBillboards.push(billboard);
  506. }
  507. }
  508. billboardCollection._billboards = newBillboards;
  509. }
  510. }
  511. BillboardCollection.prototype._updateBillboard = function (
  512. billboard,
  513. propertyChanged
  514. ) {
  515. if (!billboard._dirty) {
  516. this._billboardsToUpdate[this._billboardsToUpdateIndex++] = billboard;
  517. }
  518. ++this._propertiesChanged[propertyChanged];
  519. };
  520. /**
  521. * Check whether this collection contains a given billboard.
  522. *
  523. * @param {Billboard} [billboard] The billboard to check for.
  524. * @returns {Boolean} true if this collection contains the billboard, false otherwise.
  525. *
  526. * @see BillboardCollection#get
  527. */
  528. BillboardCollection.prototype.contains = function (billboard) {
  529. return defined(billboard) && billboard._billboardCollection === this;
  530. };
  531. /**
  532. * Returns the billboard in the collection at the specified index. Indices are zero-based
  533. * and increase as billboards are added. Removing a billboard shifts all billboards after
  534. * it to the left, changing their indices. This function is commonly used with
  535. * {@link BillboardCollection#length} to iterate over all the billboards
  536. * in the collection.
  537. *
  538. * @param {Number} index The zero-based index of the billboard.
  539. * @returns {Billboard} The billboard at the specified index.
  540. *
  541. * @performance Expected constant time. If billboards were removed from the collection and
  542. * {@link BillboardCollection#update} was not called, an implicit <code>O(n)</code>
  543. * operation is performed.
  544. *
  545. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  546. *
  547. *
  548. * @example
  549. * // Toggle the show property of every billboard in the collection
  550. * var len = billboards.length;
  551. * for (var i = 0; i < len; ++i) {
  552. * var b = billboards.get(i);
  553. * b.show = !b.show;
  554. * }
  555. *
  556. * @see BillboardCollection#length
  557. */
  558. BillboardCollection.prototype.get = function (index) {
  559. //>>includeStart('debug', pragmas.debug);
  560. if (!defined(index)) {
  561. throw new DeveloperError("index is required.");
  562. }
  563. //>>includeEnd('debug');
  564. removeBillboards(this);
  565. return this._billboards[index];
  566. };
  567. var getIndexBuffer;
  568. function getIndexBufferBatched(context) {
  569. var sixteenK = 16 * 1024;
  570. var indexBuffer = context.cache.billboardCollection_indexBufferBatched;
  571. if (defined(indexBuffer)) {
  572. return indexBuffer;
  573. }
  574. // Subtract 6 because the last index is reserverd for primitive restart.
  575. // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18
  576. var length = sixteenK * 6 - 6;
  577. var indices = new Uint16Array(length);
  578. for (var i = 0, j = 0; i < length; i += 6, j += 4) {
  579. indices[i] = j;
  580. indices[i + 1] = j + 1;
  581. indices[i + 2] = j + 2;
  582. indices[i + 3] = j + 0;
  583. indices[i + 4] = j + 2;
  584. indices[i + 5] = j + 3;
  585. }
  586. // PERFORMANCE_IDEA: Should we reference count billboard collections, and eventually delete this?
  587. // Is this too much memory to allocate up front? Should we dynamically grow it?
  588. indexBuffer = Buffer.createIndexBuffer({
  589. context: context,
  590. typedArray: indices,
  591. usage: BufferUsage.STATIC_DRAW,
  592. indexDatatype: IndexDatatype.UNSIGNED_SHORT,
  593. });
  594. indexBuffer.vertexArrayDestroyable = false;
  595. context.cache.billboardCollection_indexBufferBatched = indexBuffer;
  596. return indexBuffer;
  597. }
  598. function getIndexBufferInstanced(context) {
  599. var indexBuffer = context.cache.billboardCollection_indexBufferInstanced;
  600. if (defined(indexBuffer)) {
  601. return indexBuffer;
  602. }
  603. indexBuffer = Buffer.createIndexBuffer({
  604. context: context,
  605. typedArray: new Uint16Array([0, 1, 2, 0, 2, 3]),
  606. usage: BufferUsage.STATIC_DRAW,
  607. indexDatatype: IndexDatatype.UNSIGNED_SHORT,
  608. });
  609. indexBuffer.vertexArrayDestroyable = false;
  610. context.cache.billboardCollection_indexBufferInstanced = indexBuffer;
  611. return indexBuffer;
  612. }
  613. function getVertexBufferInstanced(context) {
  614. var vertexBuffer = context.cache.billboardCollection_vertexBufferInstanced;
  615. if (defined(vertexBuffer)) {
  616. return vertexBuffer;
  617. }
  618. vertexBuffer = Buffer.createVertexBuffer({
  619. context: context,
  620. typedArray: new Float32Array([0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]),
  621. usage: BufferUsage.STATIC_DRAW,
  622. });
  623. vertexBuffer.vertexArrayDestroyable = false;
  624. context.cache.billboardCollection_vertexBufferInstanced = vertexBuffer;
  625. return vertexBuffer;
  626. }
  627. BillboardCollection.prototype.computeNewBuffersUsage = function () {
  628. var buffersUsage = this._buffersUsage;
  629. var usageChanged = false;
  630. var properties = this._propertiesChanged;
  631. for (var k = 0; k < NUMBER_OF_PROPERTIES; ++k) {
  632. var newUsage =
  633. properties[k] === 0 ? BufferUsage.STATIC_DRAW : BufferUsage.STREAM_DRAW;
  634. usageChanged = usageChanged || buffersUsage[k] !== newUsage;
  635. buffersUsage[k] = newUsage;
  636. }
  637. return usageChanged;
  638. };
  639. function createVAF(
  640. context,
  641. numberOfBillboards,
  642. buffersUsage,
  643. instanced,
  644. batchTable,
  645. sdf
  646. ) {
  647. var attributes = [
  648. {
  649. index: attributeLocations.positionHighAndScale,
  650. componentsPerAttribute: 4,
  651. componentDatatype: ComponentDatatype.FLOAT,
  652. usage: buffersUsage[POSITION_INDEX],
  653. },
  654. {
  655. index: attributeLocations.positionLowAndRotation,
  656. componentsPerAttribute: 4,
  657. componentDatatype: ComponentDatatype.FLOAT,
  658. usage: buffersUsage[POSITION_INDEX],
  659. },
  660. {
  661. index: attributeLocations.compressedAttribute0,
  662. componentsPerAttribute: 4,
  663. componentDatatype: ComponentDatatype.FLOAT,
  664. usage: buffersUsage[PIXEL_OFFSET_INDEX],
  665. },
  666. {
  667. index: attributeLocations.compressedAttribute1,
  668. componentsPerAttribute: 4,
  669. componentDatatype: ComponentDatatype.FLOAT,
  670. usage: buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX],
  671. },
  672. {
  673. index: attributeLocations.compressedAttribute2,
  674. componentsPerAttribute: 4,
  675. componentDatatype: ComponentDatatype.FLOAT,
  676. usage: buffersUsage[COLOR_INDEX],
  677. },
  678. {
  679. index: attributeLocations.eyeOffset,
  680. componentsPerAttribute: 4,
  681. componentDatatype: ComponentDatatype.FLOAT,
  682. usage: buffersUsage[EYE_OFFSET_INDEX],
  683. },
  684. {
  685. index: attributeLocations.scaleByDistance,
  686. componentsPerAttribute: 4,
  687. componentDatatype: ComponentDatatype.FLOAT,
  688. usage: buffersUsage[SCALE_BY_DISTANCE_INDEX],
  689. },
  690. {
  691. index: attributeLocations.pixelOffsetScaleByDistance,
  692. componentsPerAttribute: 4,
  693. componentDatatype: ComponentDatatype.FLOAT,
  694. usage: buffersUsage[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX],
  695. },
  696. {
  697. index: attributeLocations.compressedAttribute3,
  698. componentsPerAttribute: 4,
  699. componentDatatype: ComponentDatatype.FLOAT,
  700. usage: buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX],
  701. },
  702. {
  703. index: attributeLocations.textureCoordinateBoundsOrLabelTranslate,
  704. componentsPerAttribute: 4,
  705. componentDatatype: ComponentDatatype.FLOAT,
  706. usage: buffersUsage[TEXTURE_COORDINATE_BOUNDS],
  707. },
  708. ];
  709. // Instancing requires one non-instanced attribute.
  710. if (instanced) {
  711. attributes.push({
  712. index: attributeLocations.direction,
  713. componentsPerAttribute: 2,
  714. componentDatatype: ComponentDatatype.FLOAT,
  715. vertexBuffer: getVertexBufferInstanced(context),
  716. });
  717. }
  718. if (defined(batchTable)) {
  719. attributes.push({
  720. index: attributeLocations.a_batchId,
  721. componentsPerAttribute: 1,
  722. componentDatatype: ComponentDatatype.FLOAT,
  723. bufferUsage: BufferUsage.STATIC_DRAW,
  724. });
  725. }
  726. if (sdf) {
  727. attributes.push({
  728. index: attributeLocations.sdf,
  729. componentsPerAttribute: 2,
  730. componentDatatype: ComponentDatatype.FLOAT,
  731. usage: buffersUsage[SDF_INDEX],
  732. });
  733. }
  734. // When instancing is enabled, only one vertex is needed for each billboard.
  735. var sizeInVertices = instanced ? numberOfBillboards : 4 * numberOfBillboards;
  736. return new VertexArrayFacade(context, attributes, sizeInVertices, instanced);
  737. }
  738. ///////////////////////////////////////////////////////////////////////////
  739. // Four vertices per billboard. Each has the same position, etc., but a different screen-space direction vector.
  740. // PERFORMANCE_IDEA: Save memory if a property is the same for all billboards, use a latched attribute state,
  741. // instead of storing it in a vertex buffer.
  742. var writePositionScratch = new EncodedCartesian3();
  743. function writePositionScaleAndRotation(
  744. billboardCollection,
  745. frameState,
  746. textureAtlasCoordinates,
  747. vafWriters,
  748. billboard
  749. ) {
  750. var i;
  751. var positionHighWriter = vafWriters[attributeLocations.positionHighAndScale];
  752. var positionLowWriter = vafWriters[attributeLocations.positionLowAndRotation];
  753. var position = billboard._getActualPosition();
  754. if (billboardCollection._mode === SceneMode.SCENE3D) {
  755. BoundingSphere.expand(
  756. billboardCollection._baseVolume,
  757. position,
  758. billboardCollection._baseVolume
  759. );
  760. billboardCollection._boundingVolumeDirty = true;
  761. }
  762. EncodedCartesian3.fromCartesian(position, writePositionScratch);
  763. var scale = billboard.scale;
  764. var rotation = billboard.rotation;
  765. if (rotation !== 0.0) {
  766. billboardCollection._shaderRotation = true;
  767. }
  768. billboardCollection._maxScale = Math.max(
  769. billboardCollection._maxScale,
  770. scale
  771. );
  772. var high = writePositionScratch.high;
  773. var low = writePositionScratch.low;
  774. if (billboardCollection._instanced) {
  775. i = billboard._index;
  776. positionHighWriter(i, high.x, high.y, high.z, scale);
  777. positionLowWriter(i, low.x, low.y, low.z, rotation);
  778. } else {
  779. i = billboard._index * 4;
  780. positionHighWriter(i + 0, high.x, high.y, high.z, scale);
  781. positionHighWriter(i + 1, high.x, high.y, high.z, scale);
  782. positionHighWriter(i + 2, high.x, high.y, high.z, scale);
  783. positionHighWriter(i + 3, high.x, high.y, high.z, scale);
  784. positionLowWriter(i + 0, low.x, low.y, low.z, rotation);
  785. positionLowWriter(i + 1, low.x, low.y, low.z, rotation);
  786. positionLowWriter(i + 2, low.x, low.y, low.z, rotation);
  787. positionLowWriter(i + 3, low.x, low.y, low.z, rotation);
  788. }
  789. }
  790. var scratchCartesian2 = new Cartesian2();
  791. var UPPER_BOUND = 32768.0; // 2^15
  792. var LEFT_SHIFT16 = 65536.0; // 2^16
  793. var LEFT_SHIFT12 = 4096.0; // 2^12
  794. var LEFT_SHIFT8 = 256.0; // 2^8
  795. var LEFT_SHIFT7 = 128.0;
  796. var LEFT_SHIFT5 = 32.0;
  797. var LEFT_SHIFT3 = 8.0;
  798. var LEFT_SHIFT2 = 4.0;
  799. var RIGHT_SHIFT8 = 1.0 / 256.0;
  800. var LOWER_LEFT = 0.0;
  801. var LOWER_RIGHT = 2.0;
  802. var UPPER_RIGHT = 3.0;
  803. var UPPER_LEFT = 1.0;
  804. function writeCompressedAttrib0(
  805. billboardCollection,
  806. frameState,
  807. textureAtlasCoordinates,
  808. vafWriters,
  809. billboard
  810. ) {
  811. var i;
  812. var writer = vafWriters[attributeLocations.compressedAttribute0];
  813. var pixelOffset = billboard.pixelOffset;
  814. var pixelOffsetX = pixelOffset.x;
  815. var pixelOffsetY = pixelOffset.y;
  816. var translate = billboard._translate;
  817. var translateX = translate.x;
  818. var translateY = translate.y;
  819. billboardCollection._maxPixelOffset = Math.max(
  820. billboardCollection._maxPixelOffset,
  821. Math.abs(pixelOffsetX + translateX),
  822. Math.abs(-pixelOffsetY + translateY)
  823. );
  824. var horizontalOrigin = billboard.horizontalOrigin;
  825. var verticalOrigin = billboard._verticalOrigin;
  826. var show = billboard.show && billboard.clusterShow;
  827. // If the color alpha is zero, do not show this billboard. This lets us avoid providing
  828. // color during the pick pass and also eliminates a discard in the fragment shader.
  829. if (billboard.color.alpha === 0.0) {
  830. show = false;
  831. }
  832. // Raw billboards don't distinguish between BASELINE and BOTTOM, only LabelCollection does that.
  833. if (verticalOrigin === VerticalOrigin.BASELINE) {
  834. verticalOrigin = VerticalOrigin.BOTTOM;
  835. }
  836. billboardCollection._allHorizontalCenter =
  837. billboardCollection._allHorizontalCenter &&
  838. horizontalOrigin === HorizontalOrigin.CENTER;
  839. billboardCollection._allVerticalCenter =
  840. billboardCollection._allVerticalCenter &&
  841. verticalOrigin === VerticalOrigin.CENTER;
  842. var bottomLeftX = 0;
  843. var bottomLeftY = 0;
  844. var width = 0;
  845. var height = 0;
  846. var index = billboard._imageIndex;
  847. if (index !== -1) {
  848. var imageRectangle = textureAtlasCoordinates[index];
  849. //>>includeStart('debug', pragmas.debug);
  850. if (!defined(imageRectangle)) {
  851. throw new DeveloperError("Invalid billboard image index: " + index);
  852. }
  853. //>>includeEnd('debug');
  854. bottomLeftX = imageRectangle.x;
  855. bottomLeftY = imageRectangle.y;
  856. width = imageRectangle.width;
  857. height = imageRectangle.height;
  858. }
  859. var topRightX = bottomLeftX + width;
  860. var topRightY = bottomLeftY + height;
  861. var compressed0 =
  862. Math.floor(
  863. CesiumMath.clamp(pixelOffsetX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND
  864. ) * LEFT_SHIFT7;
  865. compressed0 += (horizontalOrigin + 1.0) * LEFT_SHIFT5;
  866. compressed0 += (verticalOrigin + 1.0) * LEFT_SHIFT3;
  867. compressed0 += (show ? 1.0 : 0.0) * LEFT_SHIFT2;
  868. var compressed1 =
  869. Math.floor(
  870. CesiumMath.clamp(pixelOffsetY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND
  871. ) * LEFT_SHIFT8;
  872. var compressed2 =
  873. Math.floor(
  874. CesiumMath.clamp(translateX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND
  875. ) * LEFT_SHIFT8;
  876. var tempTanslateY =
  877. (CesiumMath.clamp(translateY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND) *
  878. RIGHT_SHIFT8;
  879. var upperTranslateY = Math.floor(tempTanslateY);
  880. var lowerTranslateY = Math.floor(
  881. (tempTanslateY - upperTranslateY) * LEFT_SHIFT8
  882. );
  883. compressed1 += upperTranslateY;
  884. compressed2 += lowerTranslateY;
  885. scratchCartesian2.x = bottomLeftX;
  886. scratchCartesian2.y = bottomLeftY;
  887. var compressedTexCoordsLL = AttributeCompression.compressTextureCoordinates(
  888. scratchCartesian2
  889. );
  890. scratchCartesian2.x = topRightX;
  891. var compressedTexCoordsLR = AttributeCompression.compressTextureCoordinates(
  892. scratchCartesian2
  893. );
  894. scratchCartesian2.y = topRightY;
  895. var compressedTexCoordsUR = AttributeCompression.compressTextureCoordinates(
  896. scratchCartesian2
  897. );
  898. scratchCartesian2.x = bottomLeftX;
  899. var compressedTexCoordsUL = AttributeCompression.compressTextureCoordinates(
  900. scratchCartesian2
  901. );
  902. if (billboardCollection._instanced) {
  903. i = billboard._index;
  904. writer(i, compressed0, compressed1, compressed2, compressedTexCoordsLL);
  905. } else {
  906. i = billboard._index * 4;
  907. writer(
  908. i + 0,
  909. compressed0 + LOWER_LEFT,
  910. compressed1,
  911. compressed2,
  912. compressedTexCoordsLL
  913. );
  914. writer(
  915. i + 1,
  916. compressed0 + LOWER_RIGHT,
  917. compressed1,
  918. compressed2,
  919. compressedTexCoordsLR
  920. );
  921. writer(
  922. i + 2,
  923. compressed0 + UPPER_RIGHT,
  924. compressed1,
  925. compressed2,
  926. compressedTexCoordsUR
  927. );
  928. writer(
  929. i + 3,
  930. compressed0 + UPPER_LEFT,
  931. compressed1,
  932. compressed2,
  933. compressedTexCoordsUL
  934. );
  935. }
  936. }
  937. function writeCompressedAttrib1(
  938. billboardCollection,
  939. frameState,
  940. textureAtlasCoordinates,
  941. vafWriters,
  942. billboard
  943. ) {
  944. var i;
  945. var writer = vafWriters[attributeLocations.compressedAttribute1];
  946. var alignedAxis = billboard.alignedAxis;
  947. if (!Cartesian3.equals(alignedAxis, Cartesian3.ZERO)) {
  948. billboardCollection._shaderAlignedAxis = true;
  949. }
  950. var near = 0.0;
  951. var nearValue = 1.0;
  952. var far = 1.0;
  953. var farValue = 1.0;
  954. var translucency = billboard.translucencyByDistance;
  955. if (defined(translucency)) {
  956. near = translucency.near;
  957. nearValue = translucency.nearValue;
  958. far = translucency.far;
  959. farValue = translucency.farValue;
  960. if (nearValue !== 1.0 || farValue !== 1.0) {
  961. // translucency by distance calculation in shader need not be enabled
  962. // until a billboard with near and far !== 1.0 is found
  963. billboardCollection._shaderTranslucencyByDistance = true;
  964. }
  965. }
  966. var width = 0;
  967. var index = billboard._imageIndex;
  968. if (index !== -1) {
  969. var imageRectangle = textureAtlasCoordinates[index];
  970. //>>includeStart('debug', pragmas.debug);
  971. if (!defined(imageRectangle)) {
  972. throw new DeveloperError("Invalid billboard image index: " + index);
  973. }
  974. //>>includeEnd('debug');
  975. width = imageRectangle.width;
  976. }
  977. var textureWidth = billboardCollection._textureAtlas.texture.width;
  978. var imageWidth = Math.round(
  979. defaultValue(billboard.width, textureWidth * width)
  980. );
  981. billboardCollection._maxSize = Math.max(
  982. billboardCollection._maxSize,
  983. imageWidth
  984. );
  985. var compressed0 = CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT16);
  986. var compressed1 = 0.0;
  987. if (
  988. Math.abs(Cartesian3.magnitudeSquared(alignedAxis) - 1.0) <
  989. CesiumMath.EPSILON6
  990. ) {
  991. compressed1 = AttributeCompression.octEncodeFloat(alignedAxis);
  992. }
  993. nearValue = CesiumMath.clamp(nearValue, 0.0, 1.0);
  994. nearValue = nearValue === 1.0 ? 255.0 : (nearValue * 255.0) | 0;
  995. compressed0 = compressed0 * LEFT_SHIFT8 + nearValue;
  996. farValue = CesiumMath.clamp(farValue, 0.0, 1.0);
  997. farValue = farValue === 1.0 ? 255.0 : (farValue * 255.0) | 0;
  998. compressed1 = compressed1 * LEFT_SHIFT8 + farValue;
  999. if (billboardCollection._instanced) {
  1000. i = billboard._index;
  1001. writer(i, compressed0, compressed1, near, far);
  1002. } else {
  1003. i = billboard._index * 4;
  1004. writer(i + 0, compressed0, compressed1, near, far);
  1005. writer(i + 1, compressed0, compressed1, near, far);
  1006. writer(i + 2, compressed0, compressed1, near, far);
  1007. writer(i + 3, compressed0, compressed1, near, far);
  1008. }
  1009. }
  1010. function writeCompressedAttrib2(
  1011. billboardCollection,
  1012. frameState,
  1013. textureAtlasCoordinates,
  1014. vafWriters,
  1015. billboard
  1016. ) {
  1017. var i;
  1018. var writer = vafWriters[attributeLocations.compressedAttribute2];
  1019. var color = billboard.color;
  1020. var pickColor = !defined(billboardCollection._batchTable)
  1021. ? billboard.getPickId(frameState.context).color
  1022. : Color.WHITE;
  1023. var sizeInMeters = billboard.sizeInMeters ? 1.0 : 0.0;
  1024. var validAlignedAxis =
  1025. Math.abs(Cartesian3.magnitudeSquared(billboard.alignedAxis) - 1.0) <
  1026. CesiumMath.EPSILON6
  1027. ? 1.0
  1028. : 0.0;
  1029. billboardCollection._allSizedInMeters =
  1030. billboardCollection._allSizedInMeters && sizeInMeters === 1.0;
  1031. var height = 0;
  1032. var index = billboard._imageIndex;
  1033. if (index !== -1) {
  1034. var imageRectangle = textureAtlasCoordinates[index];
  1035. //>>includeStart('debug', pragmas.debug);
  1036. if (!defined(imageRectangle)) {
  1037. throw new DeveloperError("Invalid billboard image index: " + index);
  1038. }
  1039. //>>includeEnd('debug');
  1040. height = imageRectangle.height;
  1041. }
  1042. var dimensions = billboardCollection._textureAtlas.texture.dimensions;
  1043. var imageHeight = Math.round(
  1044. defaultValue(billboard.height, dimensions.y * height)
  1045. );
  1046. billboardCollection._maxSize = Math.max(
  1047. billboardCollection._maxSize,
  1048. imageHeight
  1049. );
  1050. var labelHorizontalOrigin = defaultValue(
  1051. billboard._labelHorizontalOrigin,
  1052. -2
  1053. );
  1054. labelHorizontalOrigin += 2;
  1055. var compressed3 = imageHeight * LEFT_SHIFT2 + labelHorizontalOrigin;
  1056. var red = Color.floatToByte(color.red);
  1057. var green = Color.floatToByte(color.green);
  1058. var blue = Color.floatToByte(color.blue);
  1059. var compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  1060. red = Color.floatToByte(pickColor.red);
  1061. green = Color.floatToByte(pickColor.green);
  1062. blue = Color.floatToByte(pickColor.blue);
  1063. var compressed1 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  1064. var compressed2 =
  1065. Color.floatToByte(color.alpha) * LEFT_SHIFT16 +
  1066. Color.floatToByte(pickColor.alpha) * LEFT_SHIFT8;
  1067. compressed2 += sizeInMeters * 2.0 + validAlignedAxis;
  1068. if (billboardCollection._instanced) {
  1069. i = billboard._index;
  1070. writer(i, compressed0, compressed1, compressed2, compressed3);
  1071. } else {
  1072. i = billboard._index * 4;
  1073. writer(i + 0, compressed0, compressed1, compressed2, compressed3);
  1074. writer(i + 1, compressed0, compressed1, compressed2, compressed3);
  1075. writer(i + 2, compressed0, compressed1, compressed2, compressed3);
  1076. writer(i + 3, compressed0, compressed1, compressed2, compressed3);
  1077. }
  1078. }
  1079. function writeEyeOffset(
  1080. billboardCollection,
  1081. frameState,
  1082. textureAtlasCoordinates,
  1083. vafWriters,
  1084. billboard
  1085. ) {
  1086. var i;
  1087. var writer = vafWriters[attributeLocations.eyeOffset];
  1088. var eyeOffset = billboard.eyeOffset;
  1089. // For billboards that are clamped to ground, move it slightly closer to the camera
  1090. var eyeOffsetZ = eyeOffset.z;
  1091. if (billboard._heightReference !== HeightReference.NONE) {
  1092. eyeOffsetZ *= 1.005;
  1093. }
  1094. billboardCollection._maxEyeOffset = Math.max(
  1095. billboardCollection._maxEyeOffset,
  1096. Math.abs(eyeOffset.x),
  1097. Math.abs(eyeOffset.y),
  1098. Math.abs(eyeOffsetZ)
  1099. );
  1100. if (billboardCollection._instanced) {
  1101. var width = 0;
  1102. var height = 0;
  1103. var index = billboard._imageIndex;
  1104. if (index !== -1) {
  1105. var imageRectangle = textureAtlasCoordinates[index];
  1106. //>>includeStart('debug', pragmas.debug);
  1107. if (!defined(imageRectangle)) {
  1108. throw new DeveloperError("Invalid billboard image index: " + index);
  1109. }
  1110. //>>includeEnd('debug');
  1111. width = imageRectangle.width;
  1112. height = imageRectangle.height;
  1113. }
  1114. scratchCartesian2.x = width;
  1115. scratchCartesian2.y = height;
  1116. var compressedTexCoordsRange = AttributeCompression.compressTextureCoordinates(
  1117. scratchCartesian2
  1118. );
  1119. i = billboard._index;
  1120. writer(i, eyeOffset.x, eyeOffset.y, eyeOffsetZ, compressedTexCoordsRange);
  1121. } else {
  1122. i = billboard._index * 4;
  1123. writer(i + 0, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);
  1124. writer(i + 1, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);
  1125. writer(i + 2, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);
  1126. writer(i + 3, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);
  1127. }
  1128. }
  1129. function writeScaleByDistance(
  1130. billboardCollection,
  1131. frameState,
  1132. textureAtlasCoordinates,
  1133. vafWriters,
  1134. billboard
  1135. ) {
  1136. var i;
  1137. var writer = vafWriters[attributeLocations.scaleByDistance];
  1138. var near = 0.0;
  1139. var nearValue = 1.0;
  1140. var far = 1.0;
  1141. var farValue = 1.0;
  1142. var scale = billboard.scaleByDistance;
  1143. if (defined(scale)) {
  1144. near = scale.near;
  1145. nearValue = scale.nearValue;
  1146. far = scale.far;
  1147. farValue = scale.farValue;
  1148. if (nearValue !== 1.0 || farValue !== 1.0) {
  1149. // scale by distance calculation in shader need not be enabled
  1150. // until a billboard with near and far !== 1.0 is found
  1151. billboardCollection._shaderScaleByDistance = true;
  1152. }
  1153. }
  1154. if (billboardCollection._instanced) {
  1155. i = billboard._index;
  1156. writer(i, near, nearValue, far, farValue);
  1157. } else {
  1158. i = billboard._index * 4;
  1159. writer(i + 0, near, nearValue, far, farValue);
  1160. writer(i + 1, near, nearValue, far, farValue);
  1161. writer(i + 2, near, nearValue, far, farValue);
  1162. writer(i + 3, near, nearValue, far, farValue);
  1163. }
  1164. }
  1165. function writePixelOffsetScaleByDistance(
  1166. billboardCollection,
  1167. frameState,
  1168. textureAtlasCoordinates,
  1169. vafWriters,
  1170. billboard
  1171. ) {
  1172. var i;
  1173. var writer = vafWriters[attributeLocations.pixelOffsetScaleByDistance];
  1174. var near = 0.0;
  1175. var nearValue = 1.0;
  1176. var far = 1.0;
  1177. var farValue = 1.0;
  1178. var pixelOffsetScale = billboard.pixelOffsetScaleByDistance;
  1179. if (defined(pixelOffsetScale)) {
  1180. near = pixelOffsetScale.near;
  1181. nearValue = pixelOffsetScale.nearValue;
  1182. far = pixelOffsetScale.far;
  1183. farValue = pixelOffsetScale.farValue;
  1184. if (nearValue !== 1.0 || farValue !== 1.0) {
  1185. // pixelOffsetScale by distance calculation in shader need not be enabled
  1186. // until a billboard with near and far !== 1.0 is found
  1187. billboardCollection._shaderPixelOffsetScaleByDistance = true;
  1188. }
  1189. }
  1190. if (billboardCollection._instanced) {
  1191. i = billboard._index;
  1192. writer(i, near, nearValue, far, farValue);
  1193. } else {
  1194. i = billboard._index * 4;
  1195. writer(i + 0, near, nearValue, far, farValue);
  1196. writer(i + 1, near, nearValue, far, farValue);
  1197. writer(i + 2, near, nearValue, far, farValue);
  1198. writer(i + 3, near, nearValue, far, farValue);
  1199. }
  1200. }
  1201. function writeCompressedAttribute3(
  1202. billboardCollection,
  1203. frameState,
  1204. textureAtlasCoordinates,
  1205. vafWriters,
  1206. billboard
  1207. ) {
  1208. var i;
  1209. var writer = vafWriters[attributeLocations.compressedAttribute3];
  1210. var near = 0.0;
  1211. var far = Number.MAX_VALUE;
  1212. var distanceDisplayCondition = billboard.distanceDisplayCondition;
  1213. if (defined(distanceDisplayCondition)) {
  1214. near = distanceDisplayCondition.near;
  1215. far = distanceDisplayCondition.far;
  1216. near *= near;
  1217. far *= far;
  1218. billboardCollection._shaderDistanceDisplayCondition = true;
  1219. }
  1220. var disableDepthTestDistance = billboard.disableDepthTestDistance;
  1221. var clampToGround =
  1222. billboard.heightReference === HeightReference.CLAMP_TO_GROUND &&
  1223. frameState.context.depthTexture;
  1224. if (!defined(disableDepthTestDistance)) {
  1225. disableDepthTestDistance = clampToGround ? 5000.0 : 0.0;
  1226. }
  1227. disableDepthTestDistance *= disableDepthTestDistance;
  1228. if (clampToGround || disableDepthTestDistance > 0.0) {
  1229. billboardCollection._shaderDisableDepthDistance = true;
  1230. if (disableDepthTestDistance === Number.POSITIVE_INFINITY) {
  1231. disableDepthTestDistance = -1.0;
  1232. }
  1233. }
  1234. var imageHeight;
  1235. var imageWidth;
  1236. if (!defined(billboard._labelDimensions)) {
  1237. var height = 0;
  1238. var width = 0;
  1239. var index = billboard._imageIndex;
  1240. if (index !== -1) {
  1241. var imageRectangle = textureAtlasCoordinates[index];
  1242. //>>includeStart('debug', pragmas.debug);
  1243. if (!defined(imageRectangle)) {
  1244. throw new DeveloperError("Invalid billboard image index: " + index);
  1245. }
  1246. //>>includeEnd('debug');
  1247. height = imageRectangle.height;
  1248. width = imageRectangle.width;
  1249. }
  1250. imageHeight = Math.round(
  1251. defaultValue(
  1252. billboard.height,
  1253. billboardCollection._textureAtlas.texture.dimensions.y * height
  1254. )
  1255. );
  1256. var textureWidth = billboardCollection._textureAtlas.texture.width;
  1257. imageWidth = Math.round(
  1258. defaultValue(billboard.width, textureWidth * width)
  1259. );
  1260. } else {
  1261. imageWidth = billboard._labelDimensions.x;
  1262. imageHeight = billboard._labelDimensions.y;
  1263. }
  1264. var w = Math.floor(CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT12));
  1265. var h = Math.floor(CesiumMath.clamp(imageHeight, 0.0, LEFT_SHIFT12));
  1266. var dimensions = w * LEFT_SHIFT12 + h;
  1267. if (billboardCollection._instanced) {
  1268. i = billboard._index;
  1269. writer(i, near, far, disableDepthTestDistance, dimensions);
  1270. } else {
  1271. i = billboard._index * 4;
  1272. writer(i + 0, near, far, disableDepthTestDistance, dimensions);
  1273. writer(i + 1, near, far, disableDepthTestDistance, dimensions);
  1274. writer(i + 2, near, far, disableDepthTestDistance, dimensions);
  1275. writer(i + 3, near, far, disableDepthTestDistance, dimensions);
  1276. }
  1277. }
  1278. function writeTextureCoordinateBoundsOrLabelTranslate(
  1279. billboardCollection,
  1280. frameState,
  1281. textureAtlasCoordinates,
  1282. vafWriters,
  1283. billboard
  1284. ) {
  1285. if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) {
  1286. var scene = billboardCollection._scene;
  1287. var context = frameState.context;
  1288. var globeTranslucent = frameState.globeTranslucencyState.translucent;
  1289. var depthTestAgainstTerrain =
  1290. defined(scene.globe) && scene.globe.depthTestAgainstTerrain;
  1291. // Only do manual depth test if the globe is opaque and writes depth
  1292. billboardCollection._shaderClampToGround =
  1293. context.depthTexture && !globeTranslucent && depthTestAgainstTerrain;
  1294. }
  1295. var i;
  1296. var writer =
  1297. vafWriters[attributeLocations.textureCoordinateBoundsOrLabelTranslate];
  1298. if (ContextLimits.maximumVertexTextureImageUnits > 0) {
  1299. //write _labelTranslate, used by depth testing in the vertex shader
  1300. var translateX = 0;
  1301. var translateY = 0;
  1302. if (defined(billboard._labelTranslate)) {
  1303. translateX = billboard._labelTranslate.x;
  1304. translateY = billboard._labelTranslate.y;
  1305. }
  1306. if (billboardCollection._instanced) {
  1307. i = billboard._index;
  1308. writer(i, translateX, translateY, 0.0, 0.0);
  1309. } else {
  1310. i = billboard._index * 4;
  1311. writer(i + 0, translateX, translateY, 0.0, 0.0);
  1312. writer(i + 1, translateX, translateY, 0.0, 0.0);
  1313. writer(i + 2, translateX, translateY, 0.0, 0.0);
  1314. writer(i + 3, translateX, translateY, 0.0, 0.0);
  1315. }
  1316. return;
  1317. }
  1318. //write texture coordinate bounds, used by depth testing in fragment shader
  1319. var minX = 0;
  1320. var minY = 0;
  1321. var width = 0;
  1322. var height = 0;
  1323. var index = billboard._imageIndex;
  1324. if (index !== -1) {
  1325. var imageRectangle = textureAtlasCoordinates[index];
  1326. //>>includeStart('debug', pragmas.debug);
  1327. if (!defined(imageRectangle)) {
  1328. throw new DeveloperError("Invalid billboard image index: " + index);
  1329. }
  1330. //>>includeEnd('debug');
  1331. minX = imageRectangle.x;
  1332. minY = imageRectangle.y;
  1333. width = imageRectangle.width;
  1334. height = imageRectangle.height;
  1335. }
  1336. var maxX = minX + width;
  1337. var maxY = minY + height;
  1338. if (billboardCollection._instanced) {
  1339. i = billboard._index;
  1340. writer(i, minX, minY, maxX, maxY);
  1341. } else {
  1342. i = billboard._index * 4;
  1343. writer(i + 0, minX, minY, maxX, maxY);
  1344. writer(i + 1, minX, minY, maxX, maxY);
  1345. writer(i + 2, minX, minY, maxX, maxY);
  1346. writer(i + 3, minX, minY, maxX, maxY);
  1347. }
  1348. }
  1349. function writeBatchId(
  1350. billboardCollection,
  1351. frameState,
  1352. textureAtlasCoordinates,
  1353. vafWriters,
  1354. billboard
  1355. ) {
  1356. if (!defined(billboardCollection._batchTable)) {
  1357. return;
  1358. }
  1359. var writer = vafWriters[attributeLocations.a_batchId];
  1360. var id = billboard._batchIndex;
  1361. var i;
  1362. if (billboardCollection._instanced) {
  1363. i = billboard._index;
  1364. writer(i, id);
  1365. } else {
  1366. i = billboard._index * 4;
  1367. writer(i + 0, id);
  1368. writer(i + 1, id);
  1369. writer(i + 2, id);
  1370. writer(i + 3, id);
  1371. }
  1372. }
  1373. function writeSDF(
  1374. billboardCollection,
  1375. frameState,
  1376. textureAtlasCoordinates,
  1377. vafWriters,
  1378. billboard
  1379. ) {
  1380. if (!billboardCollection._sdf) {
  1381. return;
  1382. }
  1383. var i;
  1384. var writer = vafWriters[attributeLocations.sdf];
  1385. var outlineColor = billboard.outlineColor;
  1386. var outlineWidth = billboard.outlineWidth;
  1387. var red = Color.floatToByte(outlineColor.red);
  1388. var green = Color.floatToByte(outlineColor.green);
  1389. var blue = Color.floatToByte(outlineColor.blue);
  1390. var compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  1391. // Compute the relative outline distance
  1392. var outlineDistance = outlineWidth / SDFSettings.RADIUS;
  1393. var compressed1 =
  1394. Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT16 +
  1395. Color.floatToByte(outlineDistance) * LEFT_SHIFT8;
  1396. if (billboardCollection._instanced) {
  1397. i = billboard._index;
  1398. writer(i, compressed0, compressed1);
  1399. } else {
  1400. i = billboard._index * 4;
  1401. writer(i + 0, compressed0 + LOWER_LEFT, compressed1);
  1402. writer(i + 1, compressed0 + LOWER_RIGHT, compressed1);
  1403. writer(i + 2, compressed0 + UPPER_RIGHT, compressed1);
  1404. writer(i + 3, compressed0 + UPPER_LEFT, compressed1);
  1405. }
  1406. }
  1407. function writeBillboard(
  1408. billboardCollection,
  1409. frameState,
  1410. textureAtlasCoordinates,
  1411. vafWriters,
  1412. billboard
  1413. ) {
  1414. writePositionScaleAndRotation(
  1415. billboardCollection,
  1416. frameState,
  1417. textureAtlasCoordinates,
  1418. vafWriters,
  1419. billboard
  1420. );
  1421. writeCompressedAttrib0(
  1422. billboardCollection,
  1423. frameState,
  1424. textureAtlasCoordinates,
  1425. vafWriters,
  1426. billboard
  1427. );
  1428. writeCompressedAttrib1(
  1429. billboardCollection,
  1430. frameState,
  1431. textureAtlasCoordinates,
  1432. vafWriters,
  1433. billboard
  1434. );
  1435. writeCompressedAttrib2(
  1436. billboardCollection,
  1437. frameState,
  1438. textureAtlasCoordinates,
  1439. vafWriters,
  1440. billboard
  1441. );
  1442. writeEyeOffset(
  1443. billboardCollection,
  1444. frameState,
  1445. textureAtlasCoordinates,
  1446. vafWriters,
  1447. billboard
  1448. );
  1449. writeScaleByDistance(
  1450. billboardCollection,
  1451. frameState,
  1452. textureAtlasCoordinates,
  1453. vafWriters,
  1454. billboard
  1455. );
  1456. writePixelOffsetScaleByDistance(
  1457. billboardCollection,
  1458. frameState,
  1459. textureAtlasCoordinates,
  1460. vafWriters,
  1461. billboard
  1462. );
  1463. writeCompressedAttribute3(
  1464. billboardCollection,
  1465. frameState,
  1466. textureAtlasCoordinates,
  1467. vafWriters,
  1468. billboard
  1469. );
  1470. writeTextureCoordinateBoundsOrLabelTranslate(
  1471. billboardCollection,
  1472. frameState,
  1473. textureAtlasCoordinates,
  1474. vafWriters,
  1475. billboard
  1476. );
  1477. writeBatchId(
  1478. billboardCollection,
  1479. frameState,
  1480. textureAtlasCoordinates,
  1481. vafWriters,
  1482. billboard
  1483. );
  1484. writeSDF(
  1485. billboardCollection,
  1486. frameState,
  1487. textureAtlasCoordinates,
  1488. vafWriters,
  1489. billboard
  1490. );
  1491. }
  1492. function recomputeActualPositions(
  1493. billboardCollection,
  1494. billboards,
  1495. length,
  1496. frameState,
  1497. modelMatrix,
  1498. recomputeBoundingVolume
  1499. ) {
  1500. var boundingVolume;
  1501. if (frameState.mode === SceneMode.SCENE3D) {
  1502. boundingVolume = billboardCollection._baseVolume;
  1503. billboardCollection._boundingVolumeDirty = true;
  1504. } else {
  1505. boundingVolume = billboardCollection._baseVolume2D;
  1506. }
  1507. var positions = [];
  1508. for (var i = 0; i < length; ++i) {
  1509. var billboard = billboards[i];
  1510. var position = billboard.position;
  1511. var actualPosition = Billboard._computeActualPosition(
  1512. billboard,
  1513. position,
  1514. frameState,
  1515. modelMatrix
  1516. );
  1517. if (defined(actualPosition)) {
  1518. billboard._setActualPosition(actualPosition);
  1519. if (recomputeBoundingVolume) {
  1520. positions.push(actualPosition);
  1521. } else {
  1522. BoundingSphere.expand(boundingVolume, actualPosition, boundingVolume);
  1523. }
  1524. }
  1525. }
  1526. if (recomputeBoundingVolume) {
  1527. BoundingSphere.fromPoints(positions, boundingVolume);
  1528. }
  1529. }
  1530. function updateMode(billboardCollection, frameState) {
  1531. var mode = frameState.mode;
  1532. var billboards = billboardCollection._billboards;
  1533. var billboardsToUpdate = billboardCollection._billboardsToUpdate;
  1534. var modelMatrix = billboardCollection._modelMatrix;
  1535. if (
  1536. billboardCollection._createVertexArray ||
  1537. billboardCollection._mode !== mode ||
  1538. (mode !== SceneMode.SCENE3D &&
  1539. !Matrix4.equals(modelMatrix, billboardCollection.modelMatrix))
  1540. ) {
  1541. billboardCollection._mode = mode;
  1542. Matrix4.clone(billboardCollection.modelMatrix, modelMatrix);
  1543. billboardCollection._createVertexArray = true;
  1544. if (
  1545. mode === SceneMode.SCENE3D ||
  1546. mode === SceneMode.SCENE2D ||
  1547. mode === SceneMode.COLUMBUS_VIEW
  1548. ) {
  1549. recomputeActualPositions(
  1550. billboardCollection,
  1551. billboards,
  1552. billboards.length,
  1553. frameState,
  1554. modelMatrix,
  1555. true
  1556. );
  1557. }
  1558. } else if (mode === SceneMode.MORPHING) {
  1559. recomputeActualPositions(
  1560. billboardCollection,
  1561. billboards,
  1562. billboards.length,
  1563. frameState,
  1564. modelMatrix,
  1565. true
  1566. );
  1567. } else if (mode === SceneMode.SCENE2D || mode === SceneMode.COLUMBUS_VIEW) {
  1568. recomputeActualPositions(
  1569. billboardCollection,
  1570. billboardsToUpdate,
  1571. billboardCollection._billboardsToUpdateIndex,
  1572. frameState,
  1573. modelMatrix,
  1574. false
  1575. );
  1576. }
  1577. }
  1578. function updateBoundingVolume(collection, frameState, boundingVolume) {
  1579. var pixelScale = 1.0;
  1580. if (!collection._allSizedInMeters || collection._maxPixelOffset !== 0.0) {
  1581. pixelScale = frameState.camera.getPixelSize(
  1582. boundingVolume,
  1583. frameState.context.drawingBufferWidth,
  1584. frameState.context.drawingBufferHeight
  1585. );
  1586. }
  1587. var size = pixelScale * collection._maxScale * collection._maxSize * 2.0;
  1588. if (collection._allHorizontalCenter && collection._allVerticalCenter) {
  1589. size *= 0.5;
  1590. }
  1591. var offset =
  1592. pixelScale * collection._maxPixelOffset + collection._maxEyeOffset;
  1593. boundingVolume.radius += size + offset;
  1594. }
  1595. function createDebugCommand(billboardCollection, context) {
  1596. var fs;
  1597. fs =
  1598. "uniform sampler2D billboard_texture; \n" +
  1599. "varying vec2 v_textureCoordinates; \n" +
  1600. "void main() \n" +
  1601. "{ \n" +
  1602. " gl_FragColor = texture2D(billboard_texture, v_textureCoordinates); \n" +
  1603. "} \n";
  1604. var drawCommand = context.createViewportQuadCommand(fs, {
  1605. uniformMap: {
  1606. billboard_texture: function () {
  1607. return billboardCollection._textureAtlas.texture;
  1608. },
  1609. },
  1610. });
  1611. drawCommand.pass = Pass.OVERLAY;
  1612. return drawCommand;
  1613. }
  1614. var scratchWriterArray = [];
  1615. /**
  1616. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  1617. * get the draw commands needed to render this primitive.
  1618. * <p>
  1619. * Do not call this function directly. This is documented just to
  1620. * list the exceptions that may be propagated when the scene is rendered:
  1621. * </p>
  1622. *
  1623. * @exception {RuntimeError} image with id must be in the atlas.
  1624. */
  1625. BillboardCollection.prototype.update = function (frameState) {
  1626. removeBillboards(this);
  1627. var billboards = this._billboards;
  1628. var billboardsLength = billboards.length;
  1629. var context = frameState.context;
  1630. this._instanced = context.instancedArrays;
  1631. attributeLocations = this._instanced
  1632. ? attributeLocationsInstanced
  1633. : attributeLocationsBatched;
  1634. getIndexBuffer = this._instanced
  1635. ? getIndexBufferInstanced
  1636. : getIndexBufferBatched;
  1637. var textureAtlas = this._textureAtlas;
  1638. if (!defined(textureAtlas)) {
  1639. textureAtlas = this._textureAtlas = new TextureAtlas({
  1640. context: context,
  1641. });
  1642. for (var ii = 0; ii < billboardsLength; ++ii) {
  1643. billboards[ii]._loadImage();
  1644. }
  1645. }
  1646. var textureAtlasCoordinates = textureAtlas.textureCoordinates;
  1647. if (textureAtlasCoordinates.length === 0) {
  1648. // Can't write billboard vertices until we have texture coordinates
  1649. // provided by a texture atlas
  1650. return;
  1651. }
  1652. updateMode(this, frameState);
  1653. billboards = this._billboards;
  1654. billboardsLength = billboards.length;
  1655. var billboardsToUpdate = this._billboardsToUpdate;
  1656. var billboardsToUpdateLength = this._billboardsToUpdateIndex;
  1657. var properties = this._propertiesChanged;
  1658. var textureAtlasGUID = textureAtlas.guid;
  1659. var createVertexArray =
  1660. this._createVertexArray || this._textureAtlasGUID !== textureAtlasGUID;
  1661. this._textureAtlasGUID = textureAtlasGUID;
  1662. var vafWriters;
  1663. var pass = frameState.passes;
  1664. var picking = pass.pick;
  1665. // PERFORMANCE_IDEA: Round robin multiple buffers.
  1666. if (createVertexArray || (!picking && this.computeNewBuffersUsage())) {
  1667. this._createVertexArray = false;
  1668. for (var k = 0; k < NUMBER_OF_PROPERTIES; ++k) {
  1669. properties[k] = 0;
  1670. }
  1671. this._vaf = this._vaf && this._vaf.destroy();
  1672. if (billboardsLength > 0) {
  1673. // PERFORMANCE_IDEA: Instead of creating a new one, resize like std::vector.
  1674. this._vaf = createVAF(
  1675. context,
  1676. billboardsLength,
  1677. this._buffersUsage,
  1678. this._instanced,
  1679. this._batchTable,
  1680. this._sdf
  1681. );
  1682. vafWriters = this._vaf.writers;
  1683. // Rewrite entire buffer if billboards were added or removed.
  1684. for (var i = 0; i < billboardsLength; ++i) {
  1685. var billboard = this._billboards[i];
  1686. billboard._dirty = false; // In case it needed an update.
  1687. writeBillboard(
  1688. this,
  1689. frameState,
  1690. textureAtlasCoordinates,
  1691. vafWriters,
  1692. billboard
  1693. );
  1694. }
  1695. // Different billboard collections share the same index buffer.
  1696. this._vaf.commit(getIndexBuffer(context));
  1697. }
  1698. this._billboardsToUpdateIndex = 0;
  1699. } else if (billboardsToUpdateLength > 0) {
  1700. // Billboards were modified, but none were added or removed.
  1701. var writers = scratchWriterArray;
  1702. writers.length = 0;
  1703. if (
  1704. properties[POSITION_INDEX] ||
  1705. properties[ROTATION_INDEX] ||
  1706. properties[SCALE_INDEX]
  1707. ) {
  1708. writers.push(writePositionScaleAndRotation);
  1709. }
  1710. if (
  1711. properties[IMAGE_INDEX_INDEX] ||
  1712. properties[PIXEL_OFFSET_INDEX] ||
  1713. properties[HORIZONTAL_ORIGIN_INDEX] ||
  1714. properties[VERTICAL_ORIGIN_INDEX] ||
  1715. properties[SHOW_INDEX]
  1716. ) {
  1717. writers.push(writeCompressedAttrib0);
  1718. if (this._instanced) {
  1719. writers.push(writeEyeOffset);
  1720. }
  1721. }
  1722. if (
  1723. properties[IMAGE_INDEX_INDEX] ||
  1724. properties[ALIGNED_AXIS_INDEX] ||
  1725. properties[TRANSLUCENCY_BY_DISTANCE_INDEX]
  1726. ) {
  1727. writers.push(writeCompressedAttrib1);
  1728. writers.push(writeCompressedAttrib2);
  1729. }
  1730. if (properties[IMAGE_INDEX_INDEX] || properties[COLOR_INDEX]) {
  1731. writers.push(writeCompressedAttrib2);
  1732. }
  1733. if (properties[EYE_OFFSET_INDEX]) {
  1734. writers.push(writeEyeOffset);
  1735. }
  1736. if (properties[SCALE_BY_DISTANCE_INDEX]) {
  1737. writers.push(writeScaleByDistance);
  1738. }
  1739. if (properties[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX]) {
  1740. writers.push(writePixelOffsetScaleByDistance);
  1741. }
  1742. if (
  1743. properties[DISTANCE_DISPLAY_CONDITION_INDEX] ||
  1744. properties[DISABLE_DEPTH_DISTANCE] ||
  1745. properties[IMAGE_INDEX_INDEX] ||
  1746. properties[POSITION_INDEX]
  1747. ) {
  1748. writers.push(writeCompressedAttribute3);
  1749. }
  1750. if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) {
  1751. writers.push(writeTextureCoordinateBoundsOrLabelTranslate);
  1752. }
  1753. if (properties[SDF_INDEX]) {
  1754. writers.push(writeSDF);
  1755. }
  1756. var numWriters = writers.length;
  1757. vafWriters = this._vaf.writers;
  1758. if (billboardsToUpdateLength / billboardsLength > 0.1) {
  1759. // If more than 10% of billboard change, rewrite the entire buffer.
  1760. // PERFORMANCE_IDEA: I totally made up 10% :).
  1761. for (var m = 0; m < billboardsToUpdateLength; ++m) {
  1762. var b = billboardsToUpdate[m];
  1763. b._dirty = false;
  1764. for (var n = 0; n < numWriters; ++n) {
  1765. writers[n](this, frameState, textureAtlasCoordinates, vafWriters, b);
  1766. }
  1767. }
  1768. this._vaf.commit(getIndexBuffer(context));
  1769. } else {
  1770. for (var h = 0; h < billboardsToUpdateLength; ++h) {
  1771. var bb = billboardsToUpdate[h];
  1772. bb._dirty = false;
  1773. for (var o = 0; o < numWriters; ++o) {
  1774. writers[o](this, frameState, textureAtlasCoordinates, vafWriters, bb);
  1775. }
  1776. if (this._instanced) {
  1777. this._vaf.subCommit(bb._index, 1);
  1778. } else {
  1779. this._vaf.subCommit(bb._index * 4, 4);
  1780. }
  1781. }
  1782. this._vaf.endSubCommits();
  1783. }
  1784. this._billboardsToUpdateIndex = 0;
  1785. }
  1786. // If the number of total billboards ever shrinks considerably
  1787. // Truncate billboardsToUpdate so that we free memory that we're
  1788. // not going to be using.
  1789. if (billboardsToUpdateLength > billboardsLength * 1.5) {
  1790. billboardsToUpdate.length = billboardsLength;
  1791. }
  1792. if (!defined(this._vaf) || !defined(this._vaf.va)) {
  1793. return;
  1794. }
  1795. if (this._boundingVolumeDirty) {
  1796. this._boundingVolumeDirty = false;
  1797. BoundingSphere.transform(
  1798. this._baseVolume,
  1799. this.modelMatrix,
  1800. this._baseVolumeWC
  1801. );
  1802. }
  1803. var boundingVolume;
  1804. var modelMatrix = Matrix4.IDENTITY;
  1805. if (frameState.mode === SceneMode.SCENE3D) {
  1806. modelMatrix = this.modelMatrix;
  1807. boundingVolume = BoundingSphere.clone(
  1808. this._baseVolumeWC,
  1809. this._boundingVolume
  1810. );
  1811. } else {
  1812. boundingVolume = BoundingSphere.clone(
  1813. this._baseVolume2D,
  1814. this._boundingVolume
  1815. );
  1816. }
  1817. updateBoundingVolume(this, frameState, boundingVolume);
  1818. var blendOptionChanged = this._blendOption !== this.blendOption;
  1819. this._blendOption = this.blendOption;
  1820. if (blendOptionChanged) {
  1821. if (
  1822. this._blendOption === BlendOption.OPAQUE ||
  1823. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
  1824. ) {
  1825. this._rsOpaque = RenderState.fromCache({
  1826. depthTest: {
  1827. enabled: true,
  1828. func: WebGLConstants.LESS,
  1829. },
  1830. depthMask: true,
  1831. });
  1832. } else {
  1833. this._rsOpaque = undefined;
  1834. }
  1835. // If OPAQUE_AND_TRANSLUCENT is in use, only the opaque pass gets the benefit of the depth buffer,
  1836. // not the translucent pass. Otherwise, if the TRANSLUCENT pass is on its own, it turns on
  1837. // a depthMask in lieu of full depth sorting (because it has opaque-ish fragments that look bad in OIT).
  1838. // When the TRANSLUCENT depth mask is in use, label backgrounds require the depth func to be LEQUAL.
  1839. var useTranslucentDepthMask = this._blendOption === BlendOption.TRANSLUCENT;
  1840. if (
  1841. this._blendOption === BlendOption.TRANSLUCENT ||
  1842. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
  1843. ) {
  1844. this._rsTranslucent = RenderState.fromCache({
  1845. depthTest: {
  1846. enabled: true,
  1847. func: useTranslucentDepthMask
  1848. ? WebGLConstants.LEQUAL
  1849. : WebGLConstants.LESS,
  1850. },
  1851. depthMask: useTranslucentDepthMask,
  1852. blending: BlendingState.ALPHA_BLEND,
  1853. });
  1854. } else {
  1855. this._rsTranslucent = undefined;
  1856. }
  1857. }
  1858. this._shaderDisableDepthDistance =
  1859. this._shaderDisableDepthDistance ||
  1860. frameState.minimumDisableDepthTestDistance !== 0.0;
  1861. var vsSource;
  1862. var fsSource;
  1863. var vs;
  1864. var fs;
  1865. var vertDefines;
  1866. var supportVSTextureReads = ContextLimits.maximumVertexTextureImageUnits > 0;
  1867. if (
  1868. blendOptionChanged ||
  1869. this._shaderRotation !== this._compiledShaderRotation ||
  1870. this._shaderAlignedAxis !== this._compiledShaderAlignedAxis ||
  1871. this._shaderScaleByDistance !== this._compiledShaderScaleByDistance ||
  1872. this._shaderTranslucencyByDistance !==
  1873. this._compiledShaderTranslucencyByDistance ||
  1874. this._shaderPixelOffsetScaleByDistance !==
  1875. this._compiledShaderPixelOffsetScaleByDistance ||
  1876. this._shaderDistanceDisplayCondition !==
  1877. this._compiledShaderDistanceDisplayCondition ||
  1878. this._shaderDisableDepthDistance !==
  1879. this._compiledShaderDisableDepthDistance ||
  1880. this._shaderClampToGround !== this._compiledShaderClampToGround ||
  1881. this._sdf !== this._compiledSDF
  1882. ) {
  1883. vsSource = BillboardCollectionVS;
  1884. fsSource = BillboardCollectionFS;
  1885. vertDefines = [];
  1886. if (defined(this._batchTable)) {
  1887. vertDefines.push("VECTOR_TILE");
  1888. vsSource = this._batchTable.getVertexShaderCallback(
  1889. false,
  1890. "a_batchId",
  1891. undefined
  1892. )(vsSource);
  1893. fsSource = this._batchTable.getFragmentShaderCallback(
  1894. false,
  1895. undefined
  1896. )(fsSource);
  1897. }
  1898. vs = new ShaderSource({
  1899. defines: vertDefines,
  1900. sources: [vsSource],
  1901. });
  1902. if (this._instanced) {
  1903. vs.defines.push("INSTANCED");
  1904. }
  1905. if (this._shaderRotation) {
  1906. vs.defines.push("ROTATION");
  1907. }
  1908. if (this._shaderAlignedAxis) {
  1909. vs.defines.push("ALIGNED_AXIS");
  1910. }
  1911. if (this._shaderScaleByDistance) {
  1912. vs.defines.push("EYE_DISTANCE_SCALING");
  1913. }
  1914. if (this._shaderTranslucencyByDistance) {
  1915. vs.defines.push("EYE_DISTANCE_TRANSLUCENCY");
  1916. }
  1917. if (this._shaderPixelOffsetScaleByDistance) {
  1918. vs.defines.push("EYE_DISTANCE_PIXEL_OFFSET");
  1919. }
  1920. if (this._shaderDistanceDisplayCondition) {
  1921. vs.defines.push("DISTANCE_DISPLAY_CONDITION");
  1922. }
  1923. if (this._shaderDisableDepthDistance) {
  1924. vs.defines.push("DISABLE_DEPTH_DISTANCE");
  1925. }
  1926. if (this._shaderClampToGround) {
  1927. if (supportVSTextureReads) {
  1928. vs.defines.push("VERTEX_DEPTH_CHECK");
  1929. } else {
  1930. vs.defines.push("FRAGMENT_DEPTH_CHECK");
  1931. }
  1932. }
  1933. var sdfEdge = 1.0 - SDFSettings.CUTOFF;
  1934. if (this._sdf) {
  1935. vs.defines.push("SDF");
  1936. }
  1937. var vectorFragDefine = defined(this._batchTable) ? "VECTOR_TILE" : "";
  1938. if (this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT) {
  1939. fs = new ShaderSource({
  1940. defines: ["OPAQUE", vectorFragDefine],
  1941. sources: [fsSource],
  1942. });
  1943. if (this._shaderClampToGround) {
  1944. if (supportVSTextureReads) {
  1945. fs.defines.push("VERTEX_DEPTH_CHECK");
  1946. } else {
  1947. fs.defines.push("FRAGMENT_DEPTH_CHECK");
  1948. }
  1949. }
  1950. if (this._sdf) {
  1951. fs.defines.push("SDF");
  1952. fs.defines.push("SDF_EDGE " + sdfEdge);
  1953. }
  1954. this._sp = ShaderProgram.replaceCache({
  1955. context: context,
  1956. shaderProgram: this._sp,
  1957. vertexShaderSource: vs,
  1958. fragmentShaderSource: fs,
  1959. attributeLocations: attributeLocations,
  1960. });
  1961. fs = new ShaderSource({
  1962. defines: ["TRANSLUCENT", vectorFragDefine],
  1963. sources: [fsSource],
  1964. });
  1965. if (this._shaderClampToGround) {
  1966. if (supportVSTextureReads) {
  1967. fs.defines.push("VERTEX_DEPTH_CHECK");
  1968. } else {
  1969. fs.defines.push("FRAGMENT_DEPTH_CHECK");
  1970. }
  1971. }
  1972. if (this._sdf) {
  1973. fs.defines.push("SDF");
  1974. fs.defines.push("SDF_EDGE " + sdfEdge);
  1975. }
  1976. this._spTranslucent = ShaderProgram.replaceCache({
  1977. context: context,
  1978. shaderProgram: this._spTranslucent,
  1979. vertexShaderSource: vs,
  1980. fragmentShaderSource: fs,
  1981. attributeLocations: attributeLocations,
  1982. });
  1983. }
  1984. if (this._blendOption === BlendOption.OPAQUE) {
  1985. fs = new ShaderSource({
  1986. defines: [vectorFragDefine],
  1987. sources: [fsSource],
  1988. });
  1989. if (this._shaderClampToGround) {
  1990. if (supportVSTextureReads) {
  1991. fs.defines.push("VERTEX_DEPTH_CHECK");
  1992. } else {
  1993. fs.defines.push("FRAGMENT_DEPTH_CHECK");
  1994. }
  1995. }
  1996. if (this._sdf) {
  1997. fs.defines.push("SDF");
  1998. fs.defines.push("SDF_EDGE " + sdfEdge);
  1999. }
  2000. this._sp = ShaderProgram.replaceCache({
  2001. context: context,
  2002. shaderProgram: this._sp,
  2003. vertexShaderSource: vs,
  2004. fragmentShaderSource: fs,
  2005. attributeLocations: attributeLocations,
  2006. });
  2007. }
  2008. if (this._blendOption === BlendOption.TRANSLUCENT) {
  2009. fs = new ShaderSource({
  2010. defines: [vectorFragDefine],
  2011. sources: [fsSource],
  2012. });
  2013. if (this._shaderClampToGround) {
  2014. if (supportVSTextureReads) {
  2015. fs.defines.push("VERTEX_DEPTH_CHECK");
  2016. } else {
  2017. fs.defines.push("FRAGMENT_DEPTH_CHECK");
  2018. }
  2019. }
  2020. if (this._sdf) {
  2021. fs.defines.push("SDF");
  2022. fs.defines.push("SDF_EDGE " + sdfEdge);
  2023. }
  2024. this._spTranslucent = ShaderProgram.replaceCache({
  2025. context: context,
  2026. shaderProgram: this._spTranslucent,
  2027. vertexShaderSource: vs,
  2028. fragmentShaderSource: fs,
  2029. attributeLocations: attributeLocations,
  2030. });
  2031. }
  2032. this._compiledShaderRotation = this._shaderRotation;
  2033. this._compiledShaderAlignedAxis = this._shaderAlignedAxis;
  2034. this._compiledShaderScaleByDistance = this._shaderScaleByDistance;
  2035. this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance;
  2036. this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance;
  2037. this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition;
  2038. this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance;
  2039. this._compiledShaderClampToGround = this._shaderClampToGround;
  2040. this._compiledSDF = this._sdf;
  2041. }
  2042. var commandList = frameState.commandList;
  2043. if (pass.render || pass.pick) {
  2044. var colorList = this._colorCommands;
  2045. var opaque = this._blendOption === BlendOption.OPAQUE;
  2046. var opaqueAndTranslucent =
  2047. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT;
  2048. var va = this._vaf.va;
  2049. var vaLength = va.length;
  2050. var uniforms = this._uniforms;
  2051. var pickId;
  2052. if (defined(this._batchTable)) {
  2053. uniforms = this._batchTable.getUniformMapCallback()(uniforms);
  2054. pickId = this._batchTable.getPickId();
  2055. } else {
  2056. pickId = "v_pickColor";
  2057. }
  2058. colorList.length = vaLength;
  2059. var totalLength = opaqueAndTranslucent ? vaLength * 2 : vaLength;
  2060. for (var j = 0; j < totalLength; ++j) {
  2061. var command = colorList[j];
  2062. if (!defined(command)) {
  2063. command = colorList[j] = new DrawCommand();
  2064. }
  2065. var opaqueCommand = opaque || (opaqueAndTranslucent && j % 2 === 0);
  2066. command.pass =
  2067. opaqueCommand || !opaqueAndTranslucent ? Pass.OPAQUE : Pass.TRANSLUCENT;
  2068. command.owner = this;
  2069. var index = opaqueAndTranslucent ? Math.floor(j / 2.0) : j;
  2070. command.boundingVolume = boundingVolume;
  2071. command.modelMatrix = modelMatrix;
  2072. command.count = va[index].indicesCount;
  2073. command.shaderProgram = opaqueCommand ? this._sp : this._spTranslucent;
  2074. command.uniformMap = uniforms;
  2075. command.vertexArray = va[index].va;
  2076. command.renderState = opaqueCommand
  2077. ? this._rsOpaque
  2078. : this._rsTranslucent;
  2079. command.debugShowBoundingVolume = this.debugShowBoundingVolume;
  2080. command.pickId = pickId;
  2081. if (this._instanced) {
  2082. command.count = 6;
  2083. command.instanceCount = billboardsLength;
  2084. }
  2085. commandList.push(command);
  2086. }
  2087. if (this.debugShowTextureAtlas) {
  2088. if (!defined(this.debugCommand)) {
  2089. this.debugCommand = createDebugCommand(this, frameState.context);
  2090. }
  2091. commandList.push(this.debugCommand);
  2092. }
  2093. }
  2094. };
  2095. /**
  2096. * Returns true if this object was destroyed; otherwise, false.
  2097. * <br /><br />
  2098. * If this object was destroyed, it should not be used; calling any function other than
  2099. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  2100. *
  2101. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  2102. *
  2103. * @see BillboardCollection#destroy
  2104. */
  2105. BillboardCollection.prototype.isDestroyed = function () {
  2106. return false;
  2107. };
  2108. /**
  2109. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  2110. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  2111. * <br /><br />
  2112. * Once an object is destroyed, it should not be used; calling any function other than
  2113. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  2114. * assign the return value (<code>undefined</code>) to the object as done in the example.
  2115. *
  2116. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  2117. *
  2118. *
  2119. * @example
  2120. * billboards = billboards && billboards.destroy();
  2121. *
  2122. * @see BillboardCollection#isDestroyed
  2123. */
  2124. BillboardCollection.prototype.destroy = function () {
  2125. if (defined(this._removeCallbackFunc)) {
  2126. this._removeCallbackFunc();
  2127. this._removeCallbackFunc = undefined;
  2128. }
  2129. this._textureAtlas =
  2130. this._destroyTextureAtlas &&
  2131. this._textureAtlas &&
  2132. this._textureAtlas.destroy();
  2133. this._sp = this._sp && this._sp.destroy();
  2134. this._spTranslucent = this._spTranslucent && this._spTranslucent.destroy();
  2135. this._vaf = this._vaf && this._vaf.destroy();
  2136. destroyBillboards(this._billboards);
  2137. return destroyObject(this);
  2138. };
  2139. export default BillboardCollection;