Vector3DTilePoints.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. import arraySlice from "../Core/arraySlice.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Color from "../Core/Color.js";
  5. import defined from "../Core/defined.js";
  6. import defer from "../Core/defer.js";
  7. import destroyObject from "../Core/destroyObject.js";
  8. import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";
  9. import Ellipsoid from "../Core/Ellipsoid.js";
  10. import NearFarScalar from "../Core/NearFarScalar.js";
  11. import Rectangle from "../Core/Rectangle.js";
  12. import TaskProcessor from "../Core/TaskProcessor.js";
  13. import BillboardCollection from "./BillboardCollection.js";
  14. import Cesium3DTilePointFeature from "./Cesium3DTilePointFeature.js";
  15. import HorizontalOrigin from "./HorizontalOrigin.js";
  16. import LabelCollection from "./LabelCollection.js";
  17. import LabelStyle from "./LabelStyle.js";
  18. import PolylineCollection from "./PolylineCollection.js";
  19. import VerticalOrigin from "./VerticalOrigin.js";
  20. /**
  21. * Creates a batch of points or billboards and labels.
  22. *
  23. * @alias Vector3DTilePoints
  24. * @constructor
  25. *
  26. * @param {Object} options An object with following properties:
  27. * @param {Uint16Array} options.positions The positions of the polygons.
  28. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile.
  29. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile.
  30. * @param {Rectangle} options.rectangle The rectangle containing the tile.
  31. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polygons.
  32. * @param {Uint16Array} options.batchIds The batch ids for each polygon.
  33. *
  34. * @private
  35. */
  36. function Vector3DTilePoints(options) {
  37. // released after the first update
  38. this._positions = options.positions;
  39. this._batchTable = options.batchTable;
  40. this._batchIds = options.batchIds;
  41. this._rectangle = options.rectangle;
  42. this._minHeight = options.minimumHeight;
  43. this._maxHeight = options.maximumHeight;
  44. this._billboardCollection = undefined;
  45. this._labelCollection = undefined;
  46. this._polylineCollection = undefined;
  47. this._verticesPromise = undefined;
  48. this._packedBuffer = undefined;
  49. this._ready = false;
  50. this._readyPromise = defer();
  51. this._resolvedPromise = false;
  52. }
  53. Object.defineProperties(Vector3DTilePoints.prototype, {
  54. /**
  55. * Gets the number of points.
  56. *
  57. * @memberof Vector3DTilePoints.prototype
  58. *
  59. * @type {Number}
  60. * @readonly
  61. */
  62. pointsLength: {
  63. get: function () {
  64. return this._billboardCollection.length;
  65. },
  66. },
  67. /**
  68. * Gets the texture atlas memory in bytes.
  69. *
  70. * @memberof Vector3DTilePoints.prototype
  71. *
  72. * @type {Number}
  73. * @readonly
  74. */
  75. texturesByteLength: {
  76. get: function () {
  77. const billboardSize = this._billboardCollection.textureAtlas.texture
  78. .sizeInBytes;
  79. const labelSize = this._labelCollection._textureAtlas.texture.sizeInBytes;
  80. return billboardSize + labelSize;
  81. },
  82. },
  83. /**
  84. * Gets a promise that resolves when the primitive is ready to render.
  85. * @memberof Vector3DTilePoints.prototype
  86. * @type {Promise<void>}
  87. * @readonly
  88. */
  89. readyPromise: {
  90. get: function () {
  91. return this._readyPromise.promise;
  92. },
  93. },
  94. });
  95. function packBuffer(points, ellipsoid) {
  96. const rectangle = points._rectangle;
  97. const minimumHeight = points._minHeight;
  98. const maximumHeight = points._maxHeight;
  99. const packedLength = 2 + Rectangle.packedLength + Ellipsoid.packedLength;
  100. const packedBuffer = new Float64Array(packedLength);
  101. let offset = 0;
  102. packedBuffer[offset++] = minimumHeight;
  103. packedBuffer[offset++] = maximumHeight;
  104. Rectangle.pack(rectangle, packedBuffer, offset);
  105. offset += Rectangle.packedLength;
  106. Ellipsoid.pack(ellipsoid, packedBuffer, offset);
  107. return packedBuffer;
  108. }
  109. const createVerticesTaskProcessor = new TaskProcessor(
  110. "createVectorTilePoints",
  111. 5
  112. );
  113. const scratchPosition = new Cartesian3();
  114. function createPoints(points, ellipsoid) {
  115. if (defined(points._billboardCollection)) {
  116. return;
  117. }
  118. let positions;
  119. if (!defined(points._verticesPromise)) {
  120. positions = points._positions;
  121. let packedBuffer = points._packedBuffer;
  122. if (!defined(packedBuffer)) {
  123. // Copy because they may be the views on the same buffer.
  124. positions = points._positions = arraySlice(positions);
  125. points._batchIds = arraySlice(points._batchIds);
  126. packedBuffer = points._packedBuffer = packBuffer(points, ellipsoid);
  127. }
  128. const transferrableObjects = [positions.buffer, packedBuffer.buffer];
  129. const parameters = {
  130. positions: positions.buffer,
  131. packedBuffer: packedBuffer.buffer,
  132. };
  133. const verticesPromise = (points._verticesPromise = createVerticesTaskProcessor.scheduleTask(
  134. parameters,
  135. transferrableObjects
  136. ));
  137. if (!defined(verticesPromise)) {
  138. // Postponed
  139. return;
  140. }
  141. verticesPromise.then(function (result) {
  142. points._positions = new Float64Array(result.positions);
  143. points._ready = true;
  144. });
  145. }
  146. if (points._ready && !defined(points._billboardCollection)) {
  147. positions = points._positions;
  148. const batchTable = points._batchTable;
  149. const batchIds = points._batchIds;
  150. const billboardCollection = (points._billboardCollection = new BillboardCollection(
  151. { batchTable: batchTable }
  152. ));
  153. const labelCollection = (points._labelCollection = new LabelCollection({
  154. batchTable: batchTable,
  155. }));
  156. const polylineCollection = (points._polylineCollection = new PolylineCollection());
  157. polylineCollection._useHighlightColor = true;
  158. const numberOfPoints = positions.length / 3;
  159. for (let i = 0; i < numberOfPoints; ++i) {
  160. const id = batchIds[i];
  161. const position = Cartesian3.unpack(positions, i * 3, scratchPosition);
  162. const b = billboardCollection.add();
  163. b.position = position;
  164. b._batchIndex = id;
  165. const l = labelCollection.add();
  166. l.text = " ";
  167. l.position = position;
  168. l._batchIndex = id;
  169. const p = polylineCollection.add();
  170. p.positions = [Cartesian3.clone(position), Cartesian3.clone(position)];
  171. }
  172. points._positions = undefined;
  173. points._packedBuffer = undefined;
  174. }
  175. }
  176. /**
  177. * Creates features for each point and places it at the batch id index of features.
  178. *
  179. * @param {Vector3DTileContent} content The vector tile content.
  180. * @param {Cesium3DTileFeature[]} features An array of features where the point features will be placed.
  181. */
  182. Vector3DTilePoints.prototype.createFeatures = function (content, features) {
  183. const billboardCollection = this._billboardCollection;
  184. const labelCollection = this._labelCollection;
  185. const polylineCollection = this._polylineCollection;
  186. const batchIds = this._batchIds;
  187. const length = batchIds.length;
  188. for (let i = 0; i < length; ++i) {
  189. const batchId = batchIds[i];
  190. const billboard = billboardCollection.get(i);
  191. const label = labelCollection.get(i);
  192. const polyline = polylineCollection.get(i);
  193. features[batchId] = new Cesium3DTilePointFeature(
  194. content,
  195. batchId,
  196. billboard,
  197. label,
  198. polyline
  199. );
  200. }
  201. };
  202. /**
  203. * Colors the entire tile when enabled is true. The resulting color will be (batch table color * color).
  204. *
  205. * @param {Boolean} enabled Whether to enable debug coloring.
  206. * @param {Color} color The debug color.
  207. */
  208. Vector3DTilePoints.prototype.applyDebugSettings = function (enabled, color) {
  209. if (enabled) {
  210. Color.clone(color, this._billboardCollection._highlightColor);
  211. Color.clone(color, this._labelCollection._highlightColor);
  212. Color.clone(color, this._polylineCollection._highlightColor);
  213. } else {
  214. Color.clone(Color.WHITE, this._billboardCollection._highlightColor);
  215. Color.clone(Color.WHITE, this._labelCollection._highlightColor);
  216. Color.clone(Color.WHITE, this._polylineCollection._highlightColor);
  217. }
  218. };
  219. function clearStyle(polygons, features) {
  220. const batchIds = polygons._batchIds;
  221. const length = batchIds.length;
  222. for (let i = 0; i < length; ++i) {
  223. const batchId = batchIds[i];
  224. const feature = features[batchId];
  225. feature.show = true;
  226. feature.pointSize = Cesium3DTilePointFeature.defaultPointSize;
  227. feature.color = Cesium3DTilePointFeature.defaultColor;
  228. feature.pointOutlineColor =
  229. Cesium3DTilePointFeature.defaultPointOutlineColor;
  230. feature.pointOutlineWidth =
  231. Cesium3DTilePointFeature.defaultPointOutlineWidth;
  232. feature.labelColor = Color.WHITE;
  233. feature.labelOutlineColor = Color.WHITE;
  234. feature.labelOutlineWidth = 1.0;
  235. feature.font = "30px sans-serif";
  236. feature.labelStyle = LabelStyle.FILL;
  237. feature.labelText = undefined;
  238. feature.backgroundColor = new Color(0.165, 0.165, 0.165, 0.8);
  239. feature.backgroundPadding = new Cartesian2(7, 5);
  240. feature.backgroundEnabled = false;
  241. feature.scaleByDistance = undefined;
  242. feature.translucencyByDistance = undefined;
  243. feature.distanceDisplayCondition = undefined;
  244. feature.heightOffset = 0.0;
  245. feature.anchorLineEnabled = false;
  246. feature.anchorLineColor = Color.WHITE;
  247. feature.image = undefined;
  248. feature.disableDepthTestDistance = 0.0;
  249. feature.horizontalOrigin = HorizontalOrigin.CENTER;
  250. feature.verticalOrigin = VerticalOrigin.CENTER;
  251. feature.labelHorizontalOrigin = HorizontalOrigin.RIGHT;
  252. feature.labelVerticalOrigin = VerticalOrigin.BASELINE;
  253. }
  254. }
  255. const scratchColor = new Color();
  256. const scratchColor2 = new Color();
  257. const scratchColor3 = new Color();
  258. const scratchColor4 = new Color();
  259. const scratchColor5 = new Color();
  260. const scratchColor6 = new Color();
  261. const scratchScaleByDistance = new NearFarScalar();
  262. const scratchTranslucencyByDistance = new NearFarScalar();
  263. const scratchDistanceDisplayCondition = new DistanceDisplayCondition();
  264. /**
  265. * Apply a style to the content.
  266. *
  267. * @param {Cesium3DTileStyle} style The style.
  268. * @param {Cesium3DTileFeature[]} features The array of features.
  269. */
  270. Vector3DTilePoints.prototype.applyStyle = function (style, features) {
  271. if (!defined(style)) {
  272. clearStyle(this, features);
  273. return;
  274. }
  275. const batchIds = this._batchIds;
  276. const length = batchIds.length;
  277. for (let i = 0; i < length; ++i) {
  278. const batchId = batchIds[i];
  279. const feature = features[batchId];
  280. if (defined(style.show)) {
  281. feature.show = style.show.evaluate(feature);
  282. }
  283. if (defined(style.pointSize)) {
  284. feature.pointSize = style.pointSize.evaluate(feature);
  285. }
  286. if (defined(style.color)) {
  287. feature.color = style.color.evaluateColor(feature, scratchColor);
  288. }
  289. if (defined(style.pointOutlineColor)) {
  290. feature.pointOutlineColor = style.pointOutlineColor.evaluateColor(
  291. feature,
  292. scratchColor2
  293. );
  294. }
  295. if (defined(style.pointOutlineWidth)) {
  296. feature.pointOutlineWidth = style.pointOutlineWidth.evaluate(feature);
  297. }
  298. if (defined(style.labelColor)) {
  299. feature.labelColor = style.labelColor.evaluateColor(
  300. feature,
  301. scratchColor3
  302. );
  303. }
  304. if (defined(style.labelOutlineColor)) {
  305. feature.labelOutlineColor = style.labelOutlineColor.evaluateColor(
  306. feature,
  307. scratchColor4
  308. );
  309. }
  310. if (defined(style.labelOutlineWidth)) {
  311. feature.labelOutlineWidth = style.labelOutlineWidth.evaluate(feature);
  312. }
  313. if (defined(style.font)) {
  314. feature.font = style.font.evaluate(feature);
  315. }
  316. if (defined(style.labelStyle)) {
  317. feature.labelStyle = style.labelStyle.evaluate(feature);
  318. }
  319. if (defined(style.labelText)) {
  320. feature.labelText = style.labelText.evaluate(feature);
  321. } else {
  322. feature.labelText = undefined;
  323. }
  324. if (defined(style.backgroundColor)) {
  325. feature.backgroundColor = style.backgroundColor.evaluateColor(
  326. feature,
  327. scratchColor5
  328. );
  329. }
  330. if (defined(style.backgroundPadding)) {
  331. feature.backgroundPadding = style.backgroundPadding.evaluate(feature);
  332. }
  333. if (defined(style.backgroundEnabled)) {
  334. feature.backgroundEnabled = style.backgroundEnabled.evaluate(feature);
  335. }
  336. if (defined(style.scaleByDistance)) {
  337. const scaleByDistanceCart4 = style.scaleByDistance.evaluate(feature);
  338. scratchScaleByDistance.near = scaleByDistanceCart4.x;
  339. scratchScaleByDistance.nearValue = scaleByDistanceCart4.y;
  340. scratchScaleByDistance.far = scaleByDistanceCart4.z;
  341. scratchScaleByDistance.farValue = scaleByDistanceCart4.w;
  342. feature.scaleByDistance = scratchScaleByDistance;
  343. } else {
  344. feature.scaleByDistance = undefined;
  345. }
  346. if (defined(style.translucencyByDistance)) {
  347. const translucencyByDistanceCart4 = style.translucencyByDistance.evaluate(
  348. feature
  349. );
  350. scratchTranslucencyByDistance.near = translucencyByDistanceCart4.x;
  351. scratchTranslucencyByDistance.nearValue = translucencyByDistanceCart4.y;
  352. scratchTranslucencyByDistance.far = translucencyByDistanceCart4.z;
  353. scratchTranslucencyByDistance.farValue = translucencyByDistanceCart4.w;
  354. feature.translucencyByDistance = scratchTranslucencyByDistance;
  355. } else {
  356. feature.translucencyByDistance = undefined;
  357. }
  358. if (defined(style.distanceDisplayCondition)) {
  359. const distanceDisplayConditionCart2 = style.distanceDisplayCondition.evaluate(
  360. feature
  361. );
  362. scratchDistanceDisplayCondition.near = distanceDisplayConditionCart2.x;
  363. scratchDistanceDisplayCondition.far = distanceDisplayConditionCart2.y;
  364. feature.distanceDisplayCondition = scratchDistanceDisplayCondition;
  365. } else {
  366. feature.distanceDisplayCondition = undefined;
  367. }
  368. if (defined(style.heightOffset)) {
  369. feature.heightOffset = style.heightOffset.evaluate(feature);
  370. }
  371. if (defined(style.anchorLineEnabled)) {
  372. feature.anchorLineEnabled = style.anchorLineEnabled.evaluate(feature);
  373. }
  374. if (defined(style.anchorLineColor)) {
  375. feature.anchorLineColor = style.anchorLineColor.evaluateColor(
  376. feature,
  377. scratchColor6
  378. );
  379. }
  380. if (defined(style.image)) {
  381. feature.image = style.image.evaluate(feature);
  382. } else {
  383. feature.image = undefined;
  384. }
  385. if (defined(style.disableDepthTestDistance)) {
  386. feature.disableDepthTestDistance = style.disableDepthTestDistance.evaluate(
  387. feature
  388. );
  389. }
  390. if (defined(style.horizontalOrigin)) {
  391. feature.horizontalOrigin = style.horizontalOrigin.evaluate(feature);
  392. }
  393. if (defined(style.verticalOrigin)) {
  394. feature.verticalOrigin = style.verticalOrigin.evaluate(feature);
  395. }
  396. if (defined(style.labelHorizontalOrigin)) {
  397. feature.labelHorizontalOrigin = style.labelHorizontalOrigin.evaluate(
  398. feature
  399. );
  400. }
  401. if (defined(style.labelVerticalOrigin)) {
  402. feature.labelVerticalOrigin = style.labelVerticalOrigin.evaluate(feature);
  403. }
  404. }
  405. };
  406. /**
  407. * @private
  408. */
  409. Vector3DTilePoints.prototype.update = function (frameState) {
  410. createPoints(this, frameState.mapProjection.ellipsoid);
  411. if (!this._ready) {
  412. return;
  413. }
  414. this._polylineCollection.update(frameState);
  415. this._billboardCollection.update(frameState);
  416. this._labelCollection.update(frameState);
  417. if (!this._resolvedPromise) {
  418. this._readyPromise.resolve();
  419. this._resolvedPromise = true;
  420. }
  421. };
  422. /**
  423. * Returns true if this object was destroyed; otherwise, false.
  424. * <p>
  425. * If this object was destroyed, it should not be used; calling any function other than
  426. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  427. * </p>
  428. *
  429. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  430. */
  431. Vector3DTilePoints.prototype.isDestroyed = function () {
  432. return false;
  433. };
  434. /**
  435. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  436. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  437. * <p>
  438. * Once an object is destroyed, it should not be used; calling any function other than
  439. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  440. * assign the return value (<code>undefined</code>) to the object as done in the example.
  441. * </p>
  442. *
  443. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  444. */
  445. Vector3DTilePoints.prototype.destroy = function () {
  446. this._billboardCollection =
  447. this._billboardCollection && this._billboardCollection.destroy();
  448. this._labelCollection =
  449. this._labelCollection && this._labelCollection.destroy();
  450. this._polylineCollection =
  451. this._polylineCollection && this._polylineCollection.destroy();
  452. return destroyObject(this);
  453. };
  454. export default Vector3DTilePoints;