PointPrimitiveCollection.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Color from "../Core/Color.js";
  3. import ComponentDatatype from "../Core/ComponentDatatype.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import EncodedCartesian3 from "../Core/EncodedCartesian3.js";
  9. import CesiumMath from "../Core/Math.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import PrimitiveType from "../Core/PrimitiveType.js";
  12. import WebGLConstants from "../Core/WebGLConstants.js";
  13. import BufferUsage from "../Renderer/BufferUsage.js";
  14. import ContextLimits from "../Renderer/ContextLimits.js";
  15. import DrawCommand from "../Renderer/DrawCommand.js";
  16. import Pass from "../Renderer/Pass.js";
  17. import RenderState from "../Renderer/RenderState.js";
  18. import ShaderProgram from "../Renderer/ShaderProgram.js";
  19. import ShaderSource from "../Renderer/ShaderSource.js";
  20. import VertexArrayFacade from "../Renderer/VertexArrayFacade.js";
  21. import PointPrimitiveCollectionFS from "../Shaders/PointPrimitiveCollectionFS.js";
  22. import PointPrimitiveCollectionVS from "../Shaders/PointPrimitiveCollectionVS.js";
  23. import BlendingState from "./BlendingState.js";
  24. import BlendOption from "./BlendOption.js";
  25. import PointPrimitive from "./PointPrimitive.js";
  26. import SceneMode from "./SceneMode.js";
  27. const SHOW_INDEX = PointPrimitive.SHOW_INDEX;
  28. const POSITION_INDEX = PointPrimitive.POSITION_INDEX;
  29. const COLOR_INDEX = PointPrimitive.COLOR_INDEX;
  30. const OUTLINE_COLOR_INDEX = PointPrimitive.OUTLINE_COLOR_INDEX;
  31. const OUTLINE_WIDTH_INDEX = PointPrimitive.OUTLINE_WIDTH_INDEX;
  32. const PIXEL_SIZE_INDEX = PointPrimitive.PIXEL_SIZE_INDEX;
  33. const SCALE_BY_DISTANCE_INDEX = PointPrimitive.SCALE_BY_DISTANCE_INDEX;
  34. const TRANSLUCENCY_BY_DISTANCE_INDEX =
  35. PointPrimitive.TRANSLUCENCY_BY_DISTANCE_INDEX;
  36. const DISTANCE_DISPLAY_CONDITION_INDEX =
  37. PointPrimitive.DISTANCE_DISPLAY_CONDITION_INDEX;
  38. const DISABLE_DEPTH_DISTANCE_INDEX =
  39. PointPrimitive.DISABLE_DEPTH_DISTANCE_INDEX;
  40. const NUMBER_OF_PROPERTIES = PointPrimitive.NUMBER_OF_PROPERTIES;
  41. const attributeLocations = {
  42. positionHighAndSize: 0,
  43. positionLowAndOutline: 1,
  44. compressedAttribute0: 2, // color, outlineColor, pick color
  45. compressedAttribute1: 3, // show, translucency by distance, some free space
  46. scaleByDistance: 4,
  47. distanceDisplayConditionAndDisableDepth: 5,
  48. };
  49. /**
  50. * A renderable collection of points.
  51. * <br /><br />
  52. * Points are added and removed from the collection using {@link PointPrimitiveCollection#add}
  53. * and {@link PointPrimitiveCollection#remove}.
  54. *
  55. * @alias PointPrimitiveCollection
  56. * @constructor
  57. *
  58. * @param {Object} [options] Object with the following properties:
  59. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each point from model to world coordinates.
  60. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  61. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The point blending option. The default
  62. * is used for rendering both opaque and translucent points. However, if either all of the points are completely opaque or all are completely translucent,
  63. * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x.
  64. * @param {Boolean} [options.show=true] Determines if the primitives in the collection will be shown.
  65. *
  66. * @performance For best performance, prefer a few collections, each with many points, to
  67. * many collections with only a few points each. Organize collections so that points
  68. * with the same update frequency are in the same collection, i.e., points that do not
  69. * change should be in one collection; points that change every frame should be in another
  70. * collection; and so on.
  71. *
  72. *
  73. * @example
  74. * // Create a pointPrimitive collection with two points
  75. * const points = scene.primitives.add(new Cesium.PointPrimitiveCollection());
  76. * points.add({
  77. * position : new Cesium.Cartesian3(1.0, 2.0, 3.0),
  78. * color : Cesium.Color.YELLOW
  79. * });
  80. * points.add({
  81. * position : new Cesium.Cartesian3(4.0, 5.0, 6.0),
  82. * color : Cesium.Color.CYAN
  83. * });
  84. *
  85. * @see PointPrimitiveCollection#add
  86. * @see PointPrimitiveCollection#remove
  87. * @see PointPrimitive
  88. */
  89. function PointPrimitiveCollection(options) {
  90. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  91. this._sp = undefined;
  92. this._spTranslucent = undefined;
  93. this._rsOpaque = undefined;
  94. this._rsTranslucent = undefined;
  95. this._vaf = undefined;
  96. this._pointPrimitives = [];
  97. this._pointPrimitivesToUpdate = [];
  98. this._pointPrimitivesToUpdateIndex = 0;
  99. this._pointPrimitivesRemoved = false;
  100. this._createVertexArray = false;
  101. this._shaderScaleByDistance = false;
  102. this._compiledShaderScaleByDistance = false;
  103. this._shaderTranslucencyByDistance = false;
  104. this._compiledShaderTranslucencyByDistance = false;
  105. this._shaderDistanceDisplayCondition = false;
  106. this._compiledShaderDistanceDisplayCondition = false;
  107. this._shaderDisableDepthDistance = false;
  108. this._compiledShaderDisableDepthDistance = false;
  109. this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);
  110. this._maxPixelSize = 1.0;
  111. this._baseVolume = new BoundingSphere();
  112. this._baseVolumeWC = new BoundingSphere();
  113. this._baseVolume2D = new BoundingSphere();
  114. this._boundingVolume = new BoundingSphere();
  115. this._boundingVolumeDirty = false;
  116. this._colorCommands = [];
  117. /**
  118. * Determines if primitives in this collection will be shown.
  119. *
  120. * @type {Boolean}
  121. * @default true
  122. */
  123. this.show = defaultValue(options.show, true);
  124. /**
  125. * The 4x4 transformation matrix that transforms each point in this collection from model to world coordinates.
  126. * When this is the identity matrix, the pointPrimitives are drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  127. * Local reference frames can be used by providing a different transformation matrix, like that returned
  128. * by {@link Transforms.eastNorthUpToFixedFrame}.
  129. *
  130. * @type {Matrix4}
  131. * @default {@link Matrix4.IDENTITY}
  132. *
  133. *
  134. * @example
  135. * const center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);
  136. * pointPrimitives.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  137. * pointPrimitives.add({
  138. * color : Cesium.Color.ORANGE,
  139. * position : new Cesium.Cartesian3(0.0, 0.0, 0.0) // center
  140. * });
  141. * pointPrimitives.add({
  142. * color : Cesium.Color.YELLOW,
  143. * position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0) // east
  144. * });
  145. * pointPrimitives.add({
  146. * color : Cesium.Color.GREEN,
  147. * position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0) // north
  148. * });
  149. * pointPrimitives.add({
  150. * color : Cesium.Color.CYAN,
  151. * position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0) // up
  152. * });
  153. *
  154. * @see Transforms.eastNorthUpToFixedFrame
  155. */
  156. this.modelMatrix = Matrix4.clone(
  157. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  158. );
  159. this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  160. /**
  161. * This property is for debugging only; it is not for production use nor is it optimized.
  162. * <p>
  163. * Draws the bounding sphere for each draw command in the primitive.
  164. * </p>
  165. *
  166. * @type {Boolean}
  167. *
  168. * @default false
  169. */
  170. this.debugShowBoundingVolume = defaultValue(
  171. options.debugShowBoundingVolume,
  172. false
  173. );
  174. /**
  175. * The point blending option. The default is used for rendering both opaque and translucent points.
  176. * However, if either all of the points are completely opaque or all are completely translucent,
  177. * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve
  178. * performance by up to 2x.
  179. * @type {BlendOption}
  180. * @default BlendOption.OPAQUE_AND_TRANSLUCENT
  181. */
  182. this.blendOption = defaultValue(
  183. options.blendOption,
  184. BlendOption.OPAQUE_AND_TRANSLUCENT
  185. );
  186. this._blendOption = undefined;
  187. this._mode = SceneMode.SCENE3D;
  188. this._maxTotalPointSize = 1;
  189. // The buffer usage for each attribute is determined based on the usage of the attribute over time.
  190. this._buffersUsage = [
  191. BufferUsage.STATIC_DRAW, // SHOW_INDEX
  192. BufferUsage.STATIC_DRAW, // POSITION_INDEX
  193. BufferUsage.STATIC_DRAW, // COLOR_INDEX
  194. BufferUsage.STATIC_DRAW, // OUTLINE_COLOR_INDEX
  195. BufferUsage.STATIC_DRAW, // OUTLINE_WIDTH_INDEX
  196. BufferUsage.STATIC_DRAW, // PIXEL_SIZE_INDEX
  197. BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX
  198. BufferUsage.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX
  199. BufferUsage.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX
  200. ];
  201. const that = this;
  202. this._uniforms = {
  203. u_maxTotalPointSize: function () {
  204. return that._maxTotalPointSize;
  205. },
  206. };
  207. }
  208. Object.defineProperties(PointPrimitiveCollection.prototype, {
  209. /**
  210. * Returns the number of points in this collection. This is commonly used with
  211. * {@link PointPrimitiveCollection#get} to iterate over all the points
  212. * in the collection.
  213. * @memberof PointPrimitiveCollection.prototype
  214. * @type {Number}
  215. */
  216. length: {
  217. get: function () {
  218. removePointPrimitives(this);
  219. return this._pointPrimitives.length;
  220. },
  221. },
  222. });
  223. function destroyPointPrimitives(pointPrimitives) {
  224. const length = pointPrimitives.length;
  225. for (let i = 0; i < length; ++i) {
  226. if (pointPrimitives[i]) {
  227. pointPrimitives[i]._destroy();
  228. }
  229. }
  230. }
  231. /**
  232. * Creates and adds a point with the specified initial properties to the collection.
  233. * The added point is returned so it can be modified or removed from the collection later.
  234. *
  235. * @param {Object}[options] A template describing the point's properties as shown in Example 1.
  236. * @returns {PointPrimitive} The point that was added to the collection.
  237. *
  238. * @performance Calling <code>add</code> is expected constant time. However, the collection's vertex buffer
  239. * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead. For
  240. * best performance, add as many pointPrimitives as possible before calling <code>update</code>.
  241. *
  242. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  243. *
  244. *
  245. * @example
  246. * // Example 1: Add a point, specifying all the default values.
  247. * const p = pointPrimitives.add({
  248. * show : true,
  249. * position : Cesium.Cartesian3.ZERO,
  250. * pixelSize : 10.0,
  251. * color : Cesium.Color.WHITE,
  252. * outlineColor : Cesium.Color.TRANSPARENT,
  253. * outlineWidth : 0.0,
  254. * id : undefined
  255. * });
  256. *
  257. * @example
  258. * // Example 2: Specify only the point's cartographic position.
  259. * const p = pointPrimitives.add({
  260. * position : Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
  261. * });
  262. *
  263. * @see PointPrimitiveCollection#remove
  264. * @see PointPrimitiveCollection#removeAll
  265. */
  266. PointPrimitiveCollection.prototype.add = function (options) {
  267. const p = new PointPrimitive(options, this);
  268. p._index = this._pointPrimitives.length;
  269. this._pointPrimitives.push(p);
  270. this._createVertexArray = true;
  271. return p;
  272. };
  273. /**
  274. * Removes a point from the collection.
  275. *
  276. * @param {PointPrimitive} pointPrimitive The point to remove.
  277. * @returns {Boolean} <code>true</code> if the point was removed; <code>false</code> if the point was not found in the collection.
  278. *
  279. * @performance Calling <code>remove</code> is expected constant time. However, the collection's vertex buffer
  280. * is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead. For
  281. * best performance, remove as many points as possible before calling <code>update</code>.
  282. * If you intend to temporarily hide a point, it is usually more efficient to call
  283. * {@link PointPrimitive#show} instead of removing and re-adding the point.
  284. *
  285. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  286. *
  287. *
  288. * @example
  289. * const p = pointPrimitives.add(...);
  290. * pointPrimitives.remove(p); // Returns true
  291. *
  292. * @see PointPrimitiveCollection#add
  293. * @see PointPrimitiveCollection#removeAll
  294. * @see PointPrimitive#show
  295. */
  296. PointPrimitiveCollection.prototype.remove = function (pointPrimitive) {
  297. if (this.contains(pointPrimitive)) {
  298. this._pointPrimitives[pointPrimitive._index] = null; // Removed later
  299. this._pointPrimitivesRemoved = true;
  300. this._createVertexArray = true;
  301. pointPrimitive._destroy();
  302. return true;
  303. }
  304. return false;
  305. };
  306. /**
  307. * Removes all points from the collection.
  308. *
  309. * @performance <code>O(n)</code>. It is more efficient to remove all the points
  310. * from a collection and then add new ones than to create a new collection entirely.
  311. *
  312. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  313. *
  314. *
  315. * @example
  316. * pointPrimitives.add(...);
  317. * pointPrimitives.add(...);
  318. * pointPrimitives.removeAll();
  319. *
  320. * @see PointPrimitiveCollection#add
  321. * @see PointPrimitiveCollection#remove
  322. */
  323. PointPrimitiveCollection.prototype.removeAll = function () {
  324. destroyPointPrimitives(this._pointPrimitives);
  325. this._pointPrimitives = [];
  326. this._pointPrimitivesToUpdate = [];
  327. this._pointPrimitivesToUpdateIndex = 0;
  328. this._pointPrimitivesRemoved = false;
  329. this._createVertexArray = true;
  330. };
  331. function removePointPrimitives(pointPrimitiveCollection) {
  332. if (pointPrimitiveCollection._pointPrimitivesRemoved) {
  333. pointPrimitiveCollection._pointPrimitivesRemoved = false;
  334. const newPointPrimitives = [];
  335. const pointPrimitives = pointPrimitiveCollection._pointPrimitives;
  336. const length = pointPrimitives.length;
  337. for (let i = 0, j = 0; i < length; ++i) {
  338. const pointPrimitive = pointPrimitives[i];
  339. if (pointPrimitive) {
  340. pointPrimitive._index = j++;
  341. newPointPrimitives.push(pointPrimitive);
  342. }
  343. }
  344. pointPrimitiveCollection._pointPrimitives = newPointPrimitives;
  345. }
  346. }
  347. PointPrimitiveCollection.prototype._updatePointPrimitive = function (
  348. pointPrimitive,
  349. propertyChanged
  350. ) {
  351. if (!pointPrimitive._dirty) {
  352. this._pointPrimitivesToUpdate[
  353. this._pointPrimitivesToUpdateIndex++
  354. ] = pointPrimitive;
  355. }
  356. ++this._propertiesChanged[propertyChanged];
  357. };
  358. /**
  359. * Check whether this collection contains a given point.
  360. *
  361. * @param {PointPrimitive} [pointPrimitive] The point to check for.
  362. * @returns {Boolean} true if this collection contains the point, false otherwise.
  363. *
  364. * @see PointPrimitiveCollection#get
  365. */
  366. PointPrimitiveCollection.prototype.contains = function (pointPrimitive) {
  367. return (
  368. defined(pointPrimitive) && pointPrimitive._pointPrimitiveCollection === this
  369. );
  370. };
  371. /**
  372. * Returns the point in the collection at the specified index. Indices are zero-based
  373. * and increase as points are added. Removing a point shifts all points after
  374. * it to the left, changing their indices. This function is commonly used with
  375. * {@link PointPrimitiveCollection#length} to iterate over all the points
  376. * in the collection.
  377. *
  378. * @param {Number} index The zero-based index of the point.
  379. * @returns {PointPrimitive} The point at the specified index.
  380. *
  381. * @performance Expected constant time. If points were removed from the collection and
  382. * {@link PointPrimitiveCollection#update} was not called, an implicit <code>O(n)</code>
  383. * operation is performed.
  384. *
  385. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  386. *
  387. *
  388. * @example
  389. * // Toggle the show property of every point in the collection
  390. * const len = pointPrimitives.length;
  391. * for (let i = 0; i < len; ++i) {
  392. * const p = pointPrimitives.get(i);
  393. * p.show = !p.show;
  394. * }
  395. *
  396. * @see PointPrimitiveCollection#length
  397. */
  398. PointPrimitiveCollection.prototype.get = function (index) {
  399. //>>includeStart('debug', pragmas.debug);
  400. if (!defined(index)) {
  401. throw new DeveloperError("index is required.");
  402. }
  403. //>>includeEnd('debug');
  404. removePointPrimitives(this);
  405. return this._pointPrimitives[index];
  406. };
  407. PointPrimitiveCollection.prototype.computeNewBuffersUsage = function () {
  408. const buffersUsage = this._buffersUsage;
  409. let usageChanged = false;
  410. const properties = this._propertiesChanged;
  411. for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {
  412. const newUsage =
  413. properties[k] === 0 ? BufferUsage.STATIC_DRAW : BufferUsage.STREAM_DRAW;
  414. usageChanged = usageChanged || buffersUsage[k] !== newUsage;
  415. buffersUsage[k] = newUsage;
  416. }
  417. return usageChanged;
  418. };
  419. function createVAF(context, numberOfPointPrimitives, buffersUsage) {
  420. return new VertexArrayFacade(
  421. context,
  422. [
  423. {
  424. index: attributeLocations.positionHighAndSize,
  425. componentsPerAttribute: 4,
  426. componentDatatype: ComponentDatatype.FLOAT,
  427. usage: buffersUsage[POSITION_INDEX],
  428. },
  429. {
  430. index: attributeLocations.positionLowAndShow,
  431. componentsPerAttribute: 4,
  432. componentDatatype: ComponentDatatype.FLOAT,
  433. usage: buffersUsage[POSITION_INDEX],
  434. },
  435. {
  436. index: attributeLocations.compressedAttribute0,
  437. componentsPerAttribute: 4,
  438. componentDatatype: ComponentDatatype.FLOAT,
  439. usage: buffersUsage[COLOR_INDEX],
  440. },
  441. {
  442. index: attributeLocations.compressedAttribute1,
  443. componentsPerAttribute: 4,
  444. componentDatatype: ComponentDatatype.FLOAT,
  445. usage: buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX],
  446. },
  447. {
  448. index: attributeLocations.scaleByDistance,
  449. componentsPerAttribute: 4,
  450. componentDatatype: ComponentDatatype.FLOAT,
  451. usage: buffersUsage[SCALE_BY_DISTANCE_INDEX],
  452. },
  453. {
  454. index: attributeLocations.distanceDisplayConditionAndDisableDepth,
  455. componentsPerAttribute: 3,
  456. componentDatatype: ComponentDatatype.FLOAT,
  457. usage: buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX],
  458. },
  459. ],
  460. numberOfPointPrimitives
  461. ); // 1 vertex per pointPrimitive
  462. }
  463. ///////////////////////////////////////////////////////////////////////////
  464. // PERFORMANCE_IDEA: Save memory if a property is the same for all pointPrimitives, use a latched attribute state,
  465. // instead of storing it in a vertex buffer.
  466. const writePositionScratch = new EncodedCartesian3();
  467. function writePositionSizeAndOutline(
  468. pointPrimitiveCollection,
  469. context,
  470. vafWriters,
  471. pointPrimitive
  472. ) {
  473. const i = pointPrimitive._index;
  474. const position = pointPrimitive._getActualPosition();
  475. if (pointPrimitiveCollection._mode === SceneMode.SCENE3D) {
  476. BoundingSphere.expand(
  477. pointPrimitiveCollection._baseVolume,
  478. position,
  479. pointPrimitiveCollection._baseVolume
  480. );
  481. pointPrimitiveCollection._boundingVolumeDirty = true;
  482. }
  483. EncodedCartesian3.fromCartesian(position, writePositionScratch);
  484. const pixelSize = pointPrimitive.pixelSize;
  485. const outlineWidth = pointPrimitive.outlineWidth;
  486. pointPrimitiveCollection._maxPixelSize = Math.max(
  487. pointPrimitiveCollection._maxPixelSize,
  488. pixelSize + outlineWidth
  489. );
  490. const positionHighWriter = vafWriters[attributeLocations.positionHighAndSize];
  491. const high = writePositionScratch.high;
  492. positionHighWriter(i, high.x, high.y, high.z, pixelSize);
  493. const positionLowWriter =
  494. vafWriters[attributeLocations.positionLowAndOutline];
  495. const low = writePositionScratch.low;
  496. positionLowWriter(i, low.x, low.y, low.z, outlineWidth);
  497. }
  498. const LEFT_SHIFT16 = 65536.0; // 2^16
  499. const LEFT_SHIFT8 = 256.0; // 2^8
  500. function writeCompressedAttrib0(
  501. pointPrimitiveCollection,
  502. context,
  503. vafWriters,
  504. pointPrimitive
  505. ) {
  506. const i = pointPrimitive._index;
  507. const color = pointPrimitive.color;
  508. const pickColor = pointPrimitive.getPickId(context).color;
  509. const outlineColor = pointPrimitive.outlineColor;
  510. let red = Color.floatToByte(color.red);
  511. let green = Color.floatToByte(color.green);
  512. let blue = Color.floatToByte(color.blue);
  513. const compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  514. red = Color.floatToByte(outlineColor.red);
  515. green = Color.floatToByte(outlineColor.green);
  516. blue = Color.floatToByte(outlineColor.blue);
  517. const compressed1 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  518. red = Color.floatToByte(pickColor.red);
  519. green = Color.floatToByte(pickColor.green);
  520. blue = Color.floatToByte(pickColor.blue);
  521. const compressed2 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue;
  522. const compressed3 =
  523. Color.floatToByte(color.alpha) * LEFT_SHIFT16 +
  524. Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT8 +
  525. Color.floatToByte(pickColor.alpha);
  526. const writer = vafWriters[attributeLocations.compressedAttribute0];
  527. writer(i, compressed0, compressed1, compressed2, compressed3);
  528. }
  529. function writeCompressedAttrib1(
  530. pointPrimitiveCollection,
  531. context,
  532. vafWriters,
  533. pointPrimitive
  534. ) {
  535. const i = pointPrimitive._index;
  536. let near = 0.0;
  537. let nearValue = 1.0;
  538. let far = 1.0;
  539. let farValue = 1.0;
  540. const translucency = pointPrimitive.translucencyByDistance;
  541. if (defined(translucency)) {
  542. near = translucency.near;
  543. nearValue = translucency.nearValue;
  544. far = translucency.far;
  545. farValue = translucency.farValue;
  546. if (nearValue !== 1.0 || farValue !== 1.0) {
  547. // translucency by distance calculation in shader need not be enabled
  548. // until a pointPrimitive with near and far !== 1.0 is found
  549. pointPrimitiveCollection._shaderTranslucencyByDistance = true;
  550. }
  551. }
  552. let show = pointPrimitive.show && pointPrimitive.clusterShow;
  553. // If the color alphas are zero, do not show this pointPrimitive. This lets us avoid providing
  554. // color during the pick pass and also eliminates a discard in the fragment shader.
  555. if (
  556. pointPrimitive.color.alpha === 0.0 &&
  557. pointPrimitive.outlineColor.alpha === 0.0
  558. ) {
  559. show = false;
  560. }
  561. nearValue = CesiumMath.clamp(nearValue, 0.0, 1.0);
  562. nearValue = nearValue === 1.0 ? 255.0 : (nearValue * 255.0) | 0;
  563. const compressed0 = (show ? 1.0 : 0.0) * LEFT_SHIFT8 + nearValue;
  564. farValue = CesiumMath.clamp(farValue, 0.0, 1.0);
  565. farValue = farValue === 1.0 ? 255.0 : (farValue * 255.0) | 0;
  566. const compressed1 = farValue;
  567. const writer = vafWriters[attributeLocations.compressedAttribute1];
  568. writer(i, compressed0, compressed1, near, far);
  569. }
  570. function writeScaleByDistance(
  571. pointPrimitiveCollection,
  572. context,
  573. vafWriters,
  574. pointPrimitive
  575. ) {
  576. const i = pointPrimitive._index;
  577. const writer = vafWriters[attributeLocations.scaleByDistance];
  578. let near = 0.0;
  579. let nearValue = 1.0;
  580. let far = 1.0;
  581. let farValue = 1.0;
  582. const scale = pointPrimitive.scaleByDistance;
  583. if (defined(scale)) {
  584. near = scale.near;
  585. nearValue = scale.nearValue;
  586. far = scale.far;
  587. farValue = scale.farValue;
  588. if (nearValue !== 1.0 || farValue !== 1.0) {
  589. // scale by distance calculation in shader need not be enabled
  590. // until a pointPrimitive with near and far !== 1.0 is found
  591. pointPrimitiveCollection._shaderScaleByDistance = true;
  592. }
  593. }
  594. writer(i, near, nearValue, far, farValue);
  595. }
  596. function writeDistanceDisplayConditionAndDepthDisable(
  597. pointPrimitiveCollection,
  598. context,
  599. vafWriters,
  600. pointPrimitive
  601. ) {
  602. const i = pointPrimitive._index;
  603. const writer =
  604. vafWriters[attributeLocations.distanceDisplayConditionAndDisableDepth];
  605. let near = 0.0;
  606. let far = Number.MAX_VALUE;
  607. const distanceDisplayCondition = pointPrimitive.distanceDisplayCondition;
  608. if (defined(distanceDisplayCondition)) {
  609. near = distanceDisplayCondition.near;
  610. far = distanceDisplayCondition.far;
  611. near *= near;
  612. far *= far;
  613. pointPrimitiveCollection._shaderDistanceDisplayCondition = true;
  614. }
  615. let disableDepthTestDistance = pointPrimitive.disableDepthTestDistance;
  616. disableDepthTestDistance *= disableDepthTestDistance;
  617. if (disableDepthTestDistance > 0.0) {
  618. pointPrimitiveCollection._shaderDisableDepthDistance = true;
  619. if (disableDepthTestDistance === Number.POSITIVE_INFINITY) {
  620. disableDepthTestDistance = -1.0;
  621. }
  622. }
  623. writer(i, near, far, disableDepthTestDistance);
  624. }
  625. function writePointPrimitive(
  626. pointPrimitiveCollection,
  627. context,
  628. vafWriters,
  629. pointPrimitive
  630. ) {
  631. writePositionSizeAndOutline(
  632. pointPrimitiveCollection,
  633. context,
  634. vafWriters,
  635. pointPrimitive
  636. );
  637. writeCompressedAttrib0(
  638. pointPrimitiveCollection,
  639. context,
  640. vafWriters,
  641. pointPrimitive
  642. );
  643. writeCompressedAttrib1(
  644. pointPrimitiveCollection,
  645. context,
  646. vafWriters,
  647. pointPrimitive
  648. );
  649. writeScaleByDistance(
  650. pointPrimitiveCollection,
  651. context,
  652. vafWriters,
  653. pointPrimitive
  654. );
  655. writeDistanceDisplayConditionAndDepthDisable(
  656. pointPrimitiveCollection,
  657. context,
  658. vafWriters,
  659. pointPrimitive
  660. );
  661. }
  662. function recomputeActualPositions(
  663. pointPrimitiveCollection,
  664. pointPrimitives,
  665. length,
  666. frameState,
  667. modelMatrix,
  668. recomputeBoundingVolume
  669. ) {
  670. let boundingVolume;
  671. if (frameState.mode === SceneMode.SCENE3D) {
  672. boundingVolume = pointPrimitiveCollection._baseVolume;
  673. pointPrimitiveCollection._boundingVolumeDirty = true;
  674. } else {
  675. boundingVolume = pointPrimitiveCollection._baseVolume2D;
  676. }
  677. const positions = [];
  678. for (let i = 0; i < length; ++i) {
  679. const pointPrimitive = pointPrimitives[i];
  680. const position = pointPrimitive.position;
  681. const actualPosition = PointPrimitive._computeActualPosition(
  682. position,
  683. frameState,
  684. modelMatrix
  685. );
  686. if (defined(actualPosition)) {
  687. pointPrimitive._setActualPosition(actualPosition);
  688. if (recomputeBoundingVolume) {
  689. positions.push(actualPosition);
  690. } else {
  691. BoundingSphere.expand(boundingVolume, actualPosition, boundingVolume);
  692. }
  693. }
  694. }
  695. if (recomputeBoundingVolume) {
  696. BoundingSphere.fromPoints(positions, boundingVolume);
  697. }
  698. }
  699. function updateMode(pointPrimitiveCollection, frameState) {
  700. const mode = frameState.mode;
  701. const pointPrimitives = pointPrimitiveCollection._pointPrimitives;
  702. const pointPrimitivesToUpdate =
  703. pointPrimitiveCollection._pointPrimitivesToUpdate;
  704. const modelMatrix = pointPrimitiveCollection._modelMatrix;
  705. if (
  706. pointPrimitiveCollection._createVertexArray ||
  707. pointPrimitiveCollection._mode !== mode ||
  708. (mode !== SceneMode.SCENE3D &&
  709. !Matrix4.equals(modelMatrix, pointPrimitiveCollection.modelMatrix))
  710. ) {
  711. pointPrimitiveCollection._mode = mode;
  712. Matrix4.clone(pointPrimitiveCollection.modelMatrix, modelMatrix);
  713. pointPrimitiveCollection._createVertexArray = true;
  714. if (
  715. mode === SceneMode.SCENE3D ||
  716. mode === SceneMode.SCENE2D ||
  717. mode === SceneMode.COLUMBUS_VIEW
  718. ) {
  719. recomputeActualPositions(
  720. pointPrimitiveCollection,
  721. pointPrimitives,
  722. pointPrimitives.length,
  723. frameState,
  724. modelMatrix,
  725. true
  726. );
  727. }
  728. } else if (mode === SceneMode.MORPHING) {
  729. recomputeActualPositions(
  730. pointPrimitiveCollection,
  731. pointPrimitives,
  732. pointPrimitives.length,
  733. frameState,
  734. modelMatrix,
  735. true
  736. );
  737. } else if (mode === SceneMode.SCENE2D || mode === SceneMode.COLUMBUS_VIEW) {
  738. recomputeActualPositions(
  739. pointPrimitiveCollection,
  740. pointPrimitivesToUpdate,
  741. pointPrimitiveCollection._pointPrimitivesToUpdateIndex,
  742. frameState,
  743. modelMatrix,
  744. false
  745. );
  746. }
  747. }
  748. function updateBoundingVolume(collection, frameState, boundingVolume) {
  749. const pixelSize = frameState.camera.getPixelSize(
  750. boundingVolume,
  751. frameState.context.drawingBufferWidth,
  752. frameState.context.drawingBufferHeight
  753. );
  754. const size = pixelSize * collection._maxPixelSize;
  755. boundingVolume.radius += size;
  756. }
  757. const scratchWriterArray = [];
  758. /**
  759. * @private
  760. */
  761. PointPrimitiveCollection.prototype.update = function (frameState) {
  762. removePointPrimitives(this);
  763. if (!this.show) {
  764. return;
  765. }
  766. this._maxTotalPointSize = ContextLimits.maximumAliasedPointSize;
  767. updateMode(this, frameState);
  768. const pointPrimitives = this._pointPrimitives;
  769. const pointPrimitivesLength = pointPrimitives.length;
  770. const pointPrimitivesToUpdate = this._pointPrimitivesToUpdate;
  771. const pointPrimitivesToUpdateLength = this._pointPrimitivesToUpdateIndex;
  772. const properties = this._propertiesChanged;
  773. const createVertexArray = this._createVertexArray;
  774. let vafWriters;
  775. const context = frameState.context;
  776. const pass = frameState.passes;
  777. const picking = pass.pick;
  778. // PERFORMANCE_IDEA: Round robin multiple buffers.
  779. if (createVertexArray || (!picking && this.computeNewBuffersUsage())) {
  780. this._createVertexArray = false;
  781. for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {
  782. properties[k] = 0;
  783. }
  784. this._vaf = this._vaf && this._vaf.destroy();
  785. if (pointPrimitivesLength > 0) {
  786. // PERFORMANCE_IDEA: Instead of creating a new one, resize like std::vector.
  787. this._vaf = createVAF(context, pointPrimitivesLength, this._buffersUsage);
  788. vafWriters = this._vaf.writers;
  789. // Rewrite entire buffer if pointPrimitives were added or removed.
  790. for (let i = 0; i < pointPrimitivesLength; ++i) {
  791. const pointPrimitive = this._pointPrimitives[i];
  792. pointPrimitive._dirty = false; // In case it needed an update.
  793. writePointPrimitive(this, context, vafWriters, pointPrimitive);
  794. }
  795. this._vaf.commit();
  796. }
  797. this._pointPrimitivesToUpdateIndex = 0;
  798. } else if (pointPrimitivesToUpdateLength > 0) {
  799. // PointPrimitives were modified, but none were added or removed.
  800. const writers = scratchWriterArray;
  801. writers.length = 0;
  802. if (
  803. properties[POSITION_INDEX] ||
  804. properties[OUTLINE_WIDTH_INDEX] ||
  805. properties[PIXEL_SIZE_INDEX]
  806. ) {
  807. writers.push(writePositionSizeAndOutline);
  808. }
  809. if (properties[COLOR_INDEX] || properties[OUTLINE_COLOR_INDEX]) {
  810. writers.push(writeCompressedAttrib0);
  811. }
  812. if (properties[SHOW_INDEX] || properties[TRANSLUCENCY_BY_DISTANCE_INDEX]) {
  813. writers.push(writeCompressedAttrib1);
  814. }
  815. if (properties[SCALE_BY_DISTANCE_INDEX]) {
  816. writers.push(writeScaleByDistance);
  817. }
  818. if (
  819. properties[DISTANCE_DISPLAY_CONDITION_INDEX] ||
  820. properties[DISABLE_DEPTH_DISTANCE_INDEX]
  821. ) {
  822. writers.push(writeDistanceDisplayConditionAndDepthDisable);
  823. }
  824. const numWriters = writers.length;
  825. vafWriters = this._vaf.writers;
  826. if (pointPrimitivesToUpdateLength / pointPrimitivesLength > 0.1) {
  827. // If more than 10% of pointPrimitive change, rewrite the entire buffer.
  828. // PERFORMANCE_IDEA: I totally made up 10% :).
  829. for (let m = 0; m < pointPrimitivesToUpdateLength; ++m) {
  830. const b = pointPrimitivesToUpdate[m];
  831. b._dirty = false;
  832. for (let n = 0; n < numWriters; ++n) {
  833. writers[n](this, context, vafWriters, b);
  834. }
  835. }
  836. this._vaf.commit();
  837. } else {
  838. for (let h = 0; h < pointPrimitivesToUpdateLength; ++h) {
  839. const bb = pointPrimitivesToUpdate[h];
  840. bb._dirty = false;
  841. for (let o = 0; o < numWriters; ++o) {
  842. writers[o](this, context, vafWriters, bb);
  843. }
  844. this._vaf.subCommit(bb._index, 1);
  845. }
  846. this._vaf.endSubCommits();
  847. }
  848. this._pointPrimitivesToUpdateIndex = 0;
  849. }
  850. // If the number of total pointPrimitives ever shrinks considerably
  851. // Truncate pointPrimitivesToUpdate so that we free memory that we're
  852. // not going to be using.
  853. if (pointPrimitivesToUpdateLength > pointPrimitivesLength * 1.5) {
  854. pointPrimitivesToUpdate.length = pointPrimitivesLength;
  855. }
  856. if (!defined(this._vaf) || !defined(this._vaf.va)) {
  857. return;
  858. }
  859. if (this._boundingVolumeDirty) {
  860. this._boundingVolumeDirty = false;
  861. BoundingSphere.transform(
  862. this._baseVolume,
  863. this.modelMatrix,
  864. this._baseVolumeWC
  865. );
  866. }
  867. let boundingVolume;
  868. let modelMatrix = Matrix4.IDENTITY;
  869. if (frameState.mode === SceneMode.SCENE3D) {
  870. modelMatrix = this.modelMatrix;
  871. boundingVolume = BoundingSphere.clone(
  872. this._baseVolumeWC,
  873. this._boundingVolume
  874. );
  875. } else {
  876. boundingVolume = BoundingSphere.clone(
  877. this._baseVolume2D,
  878. this._boundingVolume
  879. );
  880. }
  881. updateBoundingVolume(this, frameState, boundingVolume);
  882. const blendOptionChanged = this._blendOption !== this.blendOption;
  883. this._blendOption = this.blendOption;
  884. if (blendOptionChanged) {
  885. if (
  886. this._blendOption === BlendOption.OPAQUE ||
  887. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
  888. ) {
  889. this._rsOpaque = RenderState.fromCache({
  890. depthTest: {
  891. enabled: true,
  892. func: WebGLConstants.LEQUAL,
  893. },
  894. depthMask: true,
  895. });
  896. } else {
  897. this._rsOpaque = undefined;
  898. }
  899. if (
  900. this._blendOption === BlendOption.TRANSLUCENT ||
  901. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
  902. ) {
  903. this._rsTranslucent = RenderState.fromCache({
  904. depthTest: {
  905. enabled: true,
  906. func: WebGLConstants.LEQUAL,
  907. },
  908. depthMask: false,
  909. blending: BlendingState.ALPHA_BLEND,
  910. });
  911. } else {
  912. this._rsTranslucent = undefined;
  913. }
  914. }
  915. this._shaderDisableDepthDistance =
  916. this._shaderDisableDepthDistance ||
  917. frameState.minimumDisableDepthTestDistance !== 0.0;
  918. let vs;
  919. let fs;
  920. if (
  921. blendOptionChanged ||
  922. (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance) ||
  923. (this._shaderTranslucencyByDistance &&
  924. !this._compiledShaderTranslucencyByDistance) ||
  925. (this._shaderDistanceDisplayCondition &&
  926. !this._compiledShaderDistanceDisplayCondition) ||
  927. this._shaderDisableDepthDistance !==
  928. this._compiledShaderDisableDepthDistance
  929. ) {
  930. vs = new ShaderSource({
  931. sources: [PointPrimitiveCollectionVS],
  932. });
  933. if (this._shaderScaleByDistance) {
  934. vs.defines.push("EYE_DISTANCE_SCALING");
  935. }
  936. if (this._shaderTranslucencyByDistance) {
  937. vs.defines.push("EYE_DISTANCE_TRANSLUCENCY");
  938. }
  939. if (this._shaderDistanceDisplayCondition) {
  940. vs.defines.push("DISTANCE_DISPLAY_CONDITION");
  941. }
  942. if (this._shaderDisableDepthDistance) {
  943. vs.defines.push("DISABLE_DEPTH_DISTANCE");
  944. }
  945. if (this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT) {
  946. fs = new ShaderSource({
  947. defines: ["OPAQUE"],
  948. sources: [PointPrimitiveCollectionFS],
  949. });
  950. this._sp = ShaderProgram.replaceCache({
  951. context: context,
  952. shaderProgram: this._sp,
  953. vertexShaderSource: vs,
  954. fragmentShaderSource: fs,
  955. attributeLocations: attributeLocations,
  956. });
  957. fs = new ShaderSource({
  958. defines: ["TRANSLUCENT"],
  959. sources: [PointPrimitiveCollectionFS],
  960. });
  961. this._spTranslucent = ShaderProgram.replaceCache({
  962. context: context,
  963. shaderProgram: this._spTranslucent,
  964. vertexShaderSource: vs,
  965. fragmentShaderSource: fs,
  966. attributeLocations: attributeLocations,
  967. });
  968. }
  969. if (this._blendOption === BlendOption.OPAQUE) {
  970. fs = new ShaderSource({
  971. sources: [PointPrimitiveCollectionFS],
  972. });
  973. this._sp = ShaderProgram.replaceCache({
  974. context: context,
  975. shaderProgram: this._sp,
  976. vertexShaderSource: vs,
  977. fragmentShaderSource: fs,
  978. attributeLocations: attributeLocations,
  979. });
  980. }
  981. if (this._blendOption === BlendOption.TRANSLUCENT) {
  982. fs = new ShaderSource({
  983. sources: [PointPrimitiveCollectionFS],
  984. });
  985. this._spTranslucent = ShaderProgram.replaceCache({
  986. context: context,
  987. shaderProgram: this._spTranslucent,
  988. vertexShaderSource: vs,
  989. fragmentShaderSource: fs,
  990. attributeLocations: attributeLocations,
  991. });
  992. }
  993. this._compiledShaderScaleByDistance = this._shaderScaleByDistance;
  994. this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance;
  995. this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition;
  996. this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance;
  997. }
  998. let va;
  999. let vaLength;
  1000. let command;
  1001. let j;
  1002. const commandList = frameState.commandList;
  1003. if (pass.render || picking) {
  1004. const colorList = this._colorCommands;
  1005. const opaque = this._blendOption === BlendOption.OPAQUE;
  1006. const opaqueAndTranslucent =
  1007. this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT;
  1008. va = this._vaf.va;
  1009. vaLength = va.length;
  1010. colorList.length = vaLength;
  1011. const totalLength = opaqueAndTranslucent ? vaLength * 2 : vaLength;
  1012. for (j = 0; j < totalLength; ++j) {
  1013. const opaqueCommand = opaque || (opaqueAndTranslucent && j % 2 === 0);
  1014. command = colorList[j];
  1015. if (!defined(command)) {
  1016. command = colorList[j] = new DrawCommand();
  1017. }
  1018. command.primitiveType = PrimitiveType.POINTS;
  1019. command.pass =
  1020. opaqueCommand || !opaqueAndTranslucent ? Pass.OPAQUE : Pass.TRANSLUCENT;
  1021. command.owner = this;
  1022. const index = opaqueAndTranslucent ? Math.floor(j / 2.0) : j;
  1023. command.boundingVolume = boundingVolume;
  1024. command.modelMatrix = modelMatrix;
  1025. command.shaderProgram = opaqueCommand ? this._sp : this._spTranslucent;
  1026. command.uniformMap = this._uniforms;
  1027. command.vertexArray = va[index].va;
  1028. command.renderState = opaqueCommand
  1029. ? this._rsOpaque
  1030. : this._rsTranslucent;
  1031. command.debugShowBoundingVolume = this.debugShowBoundingVolume;
  1032. command.pickId = "v_pickColor";
  1033. commandList.push(command);
  1034. }
  1035. }
  1036. };
  1037. /**
  1038. * Returns true if this object was destroyed; otherwise, false.
  1039. * <br /><br />
  1040. * If this object was destroyed, it should not be used; calling any function other than
  1041. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1042. *
  1043. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1044. *
  1045. * @see PointPrimitiveCollection#destroy
  1046. */
  1047. PointPrimitiveCollection.prototype.isDestroyed = function () {
  1048. return false;
  1049. };
  1050. /**
  1051. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1052. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1053. * <br /><br />
  1054. * Once an object is destroyed, it should not be used; calling any function other than
  1055. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1056. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1057. *
  1058. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1059. *
  1060. *
  1061. * @example
  1062. * pointPrimitives = pointPrimitives && pointPrimitives.destroy();
  1063. *
  1064. * @see PointPrimitiveCollection#isDestroyed
  1065. */
  1066. PointPrimitiveCollection.prototype.destroy = function () {
  1067. this._sp = this._sp && this._sp.destroy();
  1068. this._spTranslucent = this._spTranslucent && this._spTranslucent.destroy();
  1069. this._spPick = this._spPick && this._spPick.destroy();
  1070. this._vaf = this._vaf && this._vaf.destroy();
  1071. destroyPointPrimitives(this._pointPrimitives);
  1072. return destroyObject(this);
  1073. };
  1074. export default PointPrimitiveCollection;