PointPrimitiveCollection.js 43 KB

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