Vector3DTilePoints.js 17 KB

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