Vector3DTilePolylines.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. import Cartesian3 from "../Core/Cartesian3.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 Ellipsoid from "../Core/Ellipsoid.js";
  8. import FeatureDetection from "../Core/FeatureDetection.js";
  9. import IndexDatatype from "../Core/IndexDatatype.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import Rectangle from "../Core/Rectangle.js";
  12. import TaskProcessor from "../Core/TaskProcessor.js";
  13. import Buffer from "../Renderer/Buffer.js";
  14. import BufferUsage from "../Renderer/BufferUsage.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 VertexArray from "../Renderer/VertexArray.js";
  21. import PolylineCommon from "../Shaders/PolylineCommon.js";
  22. import Vector3DTilePolylinesVS from "../Shaders/Vector3DTilePolylinesVS.js";
  23. import BlendingState from "./BlendingState.js";
  24. import Cesium3DTileFeature from "./Cesium3DTileFeature.js";
  25. /**
  26. * Creates a batch of polylines that have been subdivided to be draped on terrain.
  27. *
  28. * @alias Vector3DTilePolylines
  29. * @constructor
  30. *
  31. * @param {object} options An object with following properties:
  32. * @param {Uint16Array} options.positions The positions of the polylines
  33. * @param {Uint32Array} options.counts The number or positions in the each polyline.
  34. * @param {Uint16Array} options.widths The width of each polyline.
  35. * @param {number} options.minimumHeight The minimum height of the terrain covered by the tile.
  36. * @param {number} options.maximumHeight The maximum height of the terrain covered by the tile.
  37. * @param {Rectangle} options.rectangle The rectangle containing the tile.
  38. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center.
  39. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polylines.
  40. * @param {Uint16Array} options.batchIds The batch ids for each polyline.
  41. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of polylines.
  42. * @param {boolean} options.keepDecodedPositions Whether to keep decoded positions in memory.
  43. *
  44. * @private
  45. */
  46. function Vector3DTilePolylines(options) {
  47. // these arrays are all released after the first update.
  48. this._positions = options.positions;
  49. this._widths = options.widths;
  50. this._counts = options.counts;
  51. this._batchIds = options.batchIds;
  52. this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  53. this._minimumHeight = options.minimumHeight;
  54. this._maximumHeight = options.maximumHeight;
  55. this._center = options.center;
  56. this._rectangle = options.rectangle;
  57. this._boundingVolume = options.boundingVolume;
  58. this._batchTable = options.batchTable;
  59. this._va = undefined;
  60. this._sp = undefined;
  61. this._rs = undefined;
  62. this._uniformMap = undefined;
  63. this._command = undefined;
  64. this._transferrableBatchIds = undefined;
  65. this._packedBuffer = undefined;
  66. this._keepDecodedPositions = options.keepDecodedPositions;
  67. this._decodedPositions = undefined;
  68. this._decodedPositionOffsets = undefined;
  69. this._currentPositions = undefined;
  70. this._previousPositions = undefined;
  71. this._nextPositions = undefined;
  72. this._expandAndWidth = undefined;
  73. this._vertexBatchIds = undefined;
  74. this._indices = undefined;
  75. this._constantColor = Color.clone(Color.WHITE);
  76. this._highlightColor = this._constantColor;
  77. this._trianglesLength = 0;
  78. this._geometryByteLength = 0;
  79. this._ready = false;
  80. this._promise = undefined;
  81. this._error = undefined;
  82. }
  83. Object.defineProperties(Vector3DTilePolylines.prototype, {
  84. /**
  85. * Gets the number of triangles.
  86. *
  87. * @memberof Vector3DTilePolylines.prototype
  88. *
  89. * @type {number}
  90. * @readonly
  91. * @private
  92. */
  93. trianglesLength: {
  94. get: function () {
  95. return this._trianglesLength;
  96. },
  97. },
  98. /**
  99. * Gets the geometry memory in bytes.
  100. *
  101. * @memberof Vector3DTilePolylines.prototype
  102. *
  103. * @type {number}
  104. * @readonly
  105. * @private
  106. */
  107. geometryByteLength: {
  108. get: function () {
  109. return this._geometryByteLength;
  110. },
  111. },
  112. /**
  113. * Returns true when the primitive is ready to render.
  114. * @memberof Vector3DTilePolylines.prototype
  115. * @type {boolean}
  116. * @readonly
  117. * @private
  118. */
  119. ready: {
  120. get: function () {
  121. return this._ready;
  122. },
  123. },
  124. });
  125. function packBuffer(polylines) {
  126. const rectangle = polylines._rectangle;
  127. const minimumHeight = polylines._minimumHeight;
  128. const maximumHeight = polylines._maximumHeight;
  129. const ellipsoid = polylines._ellipsoid;
  130. const center = polylines._center;
  131. const packedLength =
  132. 2 +
  133. Rectangle.packedLength +
  134. Ellipsoid.packedLength +
  135. Cartesian3.packedLength;
  136. const packedBuffer = new Float64Array(packedLength);
  137. let offset = 0;
  138. packedBuffer[offset++] = minimumHeight;
  139. packedBuffer[offset++] = maximumHeight;
  140. Rectangle.pack(rectangle, packedBuffer, offset);
  141. offset += Rectangle.packedLength;
  142. Ellipsoid.pack(ellipsoid, packedBuffer, offset);
  143. offset += Ellipsoid.packedLength;
  144. Cartesian3.pack(center, packedBuffer, offset);
  145. return packedBuffer;
  146. }
  147. const createVerticesTaskProcessor = new TaskProcessor(
  148. "createVectorTilePolylines",
  149. 5
  150. );
  151. const attributeLocations = {
  152. previousPosition: 0,
  153. currentPosition: 1,
  154. nextPosition: 2,
  155. expandAndWidth: 3,
  156. a_batchId: 4,
  157. };
  158. function createVertexArray(polylines, context) {
  159. if (defined(polylines._va)) {
  160. return;
  161. }
  162. let positions = polylines._positions;
  163. let widths = polylines._widths;
  164. let counts = polylines._counts;
  165. let batchIds = polylines._transferrableBatchIds;
  166. let packedBuffer = polylines._packedBuffer;
  167. if (!defined(packedBuffer)) {
  168. // Copy because they may be the views on the same buffer.
  169. positions = polylines._positions = positions.slice();
  170. widths = polylines._widths = widths.slice();
  171. counts = polylines._counts = counts.slice();
  172. batchIds = polylines._transferrableBatchIds = polylines._batchIds.slice();
  173. packedBuffer = polylines._packedBuffer = packBuffer(polylines);
  174. }
  175. const transferrableObjects = [
  176. positions.buffer,
  177. widths.buffer,
  178. counts.buffer,
  179. batchIds.buffer,
  180. packedBuffer.buffer,
  181. ];
  182. const parameters = {
  183. positions: positions.buffer,
  184. widths: widths.buffer,
  185. counts: counts.buffer,
  186. batchIds: batchIds.buffer,
  187. packedBuffer: packedBuffer.buffer,
  188. keepDecodedPositions: polylines._keepDecodedPositions,
  189. };
  190. const verticesPromise = createVerticesTaskProcessor.scheduleTask(
  191. parameters,
  192. transferrableObjects
  193. );
  194. if (!defined(verticesPromise)) {
  195. // Postponed
  196. return;
  197. }
  198. return verticesPromise
  199. .then(function (result) {
  200. if (polylines.isDestroyed()) {
  201. return;
  202. }
  203. if (polylines._keepDecodedPositions) {
  204. polylines._decodedPositions = new Float64Array(result.decodedPositions);
  205. polylines._decodedPositionOffsets = new Uint32Array(
  206. result.decodedPositionOffsets
  207. );
  208. }
  209. polylines._currentPositions = new Float32Array(result.currentPositions);
  210. polylines._previousPositions = new Float32Array(result.previousPositions);
  211. polylines._nextPositions = new Float32Array(result.nextPositions);
  212. polylines._expandAndWidth = new Float32Array(result.expandAndWidth);
  213. polylines._vertexBatchIds = new Uint16Array(result.batchIds);
  214. const indexDatatype = result.indexDatatype;
  215. polylines._indices =
  216. indexDatatype === IndexDatatype.UNSIGNED_SHORT
  217. ? new Uint16Array(result.indices)
  218. : new Uint32Array(result.indices);
  219. finishVertexArray(polylines, context);
  220. polylines._ready = true;
  221. })
  222. .catch((error) => {
  223. if (polylines.isDestroyed()) {
  224. return;
  225. }
  226. // Throw the error next frame
  227. polylines._error = error;
  228. });
  229. }
  230. function finishVertexArray(polylines, context) {
  231. if (!defined(polylines._va)) {
  232. const curPositions = polylines._currentPositions;
  233. const prevPositions = polylines._previousPositions;
  234. const nextPositions = polylines._nextPositions;
  235. const expandAndWidth = polylines._expandAndWidth;
  236. const vertexBatchIds = polylines._vertexBatchIds;
  237. const indices = polylines._indices;
  238. let byteLength =
  239. prevPositions.byteLength +
  240. curPositions.byteLength +
  241. nextPositions.byteLength;
  242. byteLength +=
  243. expandAndWidth.byteLength +
  244. vertexBatchIds.byteLength +
  245. indices.byteLength;
  246. polylines._trianglesLength = indices.length / 3;
  247. polylines._geometryByteLength = byteLength;
  248. const prevPositionBuffer = Buffer.createVertexBuffer({
  249. context: context,
  250. typedArray: prevPositions,
  251. usage: BufferUsage.STATIC_DRAW,
  252. });
  253. const curPositionBuffer = Buffer.createVertexBuffer({
  254. context: context,
  255. typedArray: curPositions,
  256. usage: BufferUsage.STATIC_DRAW,
  257. });
  258. const nextPositionBuffer = Buffer.createVertexBuffer({
  259. context: context,
  260. typedArray: nextPositions,
  261. usage: BufferUsage.STATIC_DRAW,
  262. });
  263. const expandAndWidthBuffer = Buffer.createVertexBuffer({
  264. context: context,
  265. typedArray: expandAndWidth,
  266. usage: BufferUsage.STATIC_DRAW,
  267. });
  268. const idBuffer = Buffer.createVertexBuffer({
  269. context: context,
  270. typedArray: vertexBatchIds,
  271. usage: BufferUsage.STATIC_DRAW,
  272. });
  273. const indexBuffer = Buffer.createIndexBuffer({
  274. context: context,
  275. typedArray: indices,
  276. usage: BufferUsage.STATIC_DRAW,
  277. indexDatatype:
  278. indices.BYTES_PER_ELEMENT === 2
  279. ? IndexDatatype.UNSIGNED_SHORT
  280. : IndexDatatype.UNSIGNED_INT,
  281. });
  282. const vertexAttributes = [
  283. {
  284. index: attributeLocations.previousPosition,
  285. vertexBuffer: prevPositionBuffer,
  286. componentDatatype: ComponentDatatype.FLOAT,
  287. componentsPerAttribute: 3,
  288. },
  289. {
  290. index: attributeLocations.currentPosition,
  291. vertexBuffer: curPositionBuffer,
  292. componentDatatype: ComponentDatatype.FLOAT,
  293. componentsPerAttribute: 3,
  294. },
  295. {
  296. index: attributeLocations.nextPosition,
  297. vertexBuffer: nextPositionBuffer,
  298. componentDatatype: ComponentDatatype.FLOAT,
  299. componentsPerAttribute: 3,
  300. },
  301. {
  302. index: attributeLocations.expandAndWidth,
  303. vertexBuffer: expandAndWidthBuffer,
  304. componentDatatype: ComponentDatatype.FLOAT,
  305. componentsPerAttribute: 2,
  306. },
  307. {
  308. index: attributeLocations.a_batchId,
  309. vertexBuffer: idBuffer,
  310. componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
  311. componentsPerAttribute: 1,
  312. },
  313. ];
  314. polylines._va = new VertexArray({
  315. context: context,
  316. attributes: vertexAttributes,
  317. indexBuffer: indexBuffer,
  318. });
  319. polylines._positions = undefined;
  320. polylines._widths = undefined;
  321. polylines._counts = undefined;
  322. polylines._ellipsoid = undefined;
  323. polylines._minimumHeight = undefined;
  324. polylines._maximumHeight = undefined;
  325. polylines._rectangle = undefined;
  326. polylines._transferrableBatchIds = undefined;
  327. polylines._packedBuffer = undefined;
  328. polylines._currentPositions = undefined;
  329. polylines._previousPositions = undefined;
  330. polylines._nextPositions = undefined;
  331. polylines._expandAndWidth = undefined;
  332. polylines._vertexBatchIds = undefined;
  333. polylines._indices = undefined;
  334. }
  335. }
  336. const modifiedModelViewScratch = new Matrix4();
  337. const rtcScratch = new Cartesian3();
  338. function createUniformMap(primitive, context) {
  339. if (defined(primitive._uniformMap)) {
  340. return;
  341. }
  342. primitive._uniformMap = {
  343. u_modifiedModelView: function () {
  344. const viewMatrix = context.uniformState.view;
  345. Matrix4.clone(viewMatrix, modifiedModelViewScratch);
  346. Matrix4.multiplyByPoint(
  347. modifiedModelViewScratch,
  348. primitive._center,
  349. rtcScratch
  350. );
  351. Matrix4.setTranslation(
  352. modifiedModelViewScratch,
  353. rtcScratch,
  354. modifiedModelViewScratch
  355. );
  356. return modifiedModelViewScratch;
  357. },
  358. u_highlightColor: function () {
  359. return primitive._highlightColor;
  360. },
  361. };
  362. }
  363. function createRenderStates(primitive) {
  364. if (defined(primitive._rs)) {
  365. return;
  366. }
  367. const polygonOffset = {
  368. enabled: true,
  369. factor: -5.0,
  370. units: -5.0,
  371. };
  372. primitive._rs = RenderState.fromCache({
  373. blending: BlendingState.ALPHA_BLEND,
  374. depthMask: false,
  375. depthTest: {
  376. enabled: true,
  377. },
  378. polygonOffset: polygonOffset,
  379. });
  380. }
  381. const PolylineFS =
  382. "uniform vec4 u_highlightColor; \n" +
  383. "void main()\n" +
  384. "{\n" +
  385. " out_FragColor = u_highlightColor;\n" +
  386. "}\n";
  387. function createShaders(primitive, context) {
  388. if (defined(primitive._sp)) {
  389. return;
  390. }
  391. const batchTable = primitive._batchTable;
  392. const vsSource = batchTable.getVertexShaderCallback(
  393. false,
  394. "a_batchId",
  395. undefined
  396. )(Vector3DTilePolylinesVS);
  397. const fsSource = batchTable.getFragmentShaderCallback(
  398. false,
  399. undefined,
  400. false
  401. )(PolylineFS);
  402. const vs = new ShaderSource({
  403. defines: [
  404. "VECTOR_TILE",
  405. !FeatureDetection.isInternetExplorer() ? "CLIP_POLYLINE" : "",
  406. ],
  407. sources: [PolylineCommon, vsSource],
  408. });
  409. const fs = new ShaderSource({
  410. defines: ["VECTOR_TILE"],
  411. sources: [fsSource],
  412. });
  413. primitive._sp = ShaderProgram.fromCache({
  414. context: context,
  415. vertexShaderSource: vs,
  416. fragmentShaderSource: fs,
  417. attributeLocations: attributeLocations,
  418. });
  419. }
  420. function queueCommands(primitive, frameState) {
  421. if (!defined(primitive._command)) {
  422. const uniformMap = primitive._batchTable.getUniformMapCallback()(
  423. primitive._uniformMap
  424. );
  425. primitive._command = new DrawCommand({
  426. owner: primitive,
  427. vertexArray: primitive._va,
  428. renderState: primitive._rs,
  429. shaderProgram: primitive._sp,
  430. uniformMap: uniformMap,
  431. boundingVolume: primitive._boundingVolume,
  432. pass: Pass.TRANSLUCENT,
  433. pickId: primitive._batchTable.getPickId(),
  434. });
  435. }
  436. frameState.commandList.push(primitive._command);
  437. }
  438. Vector3DTilePolylines.getPolylinePositions = function (polylines, batchId) {
  439. const batchIds = polylines._batchIds;
  440. const positions = polylines._decodedPositions;
  441. const offsets = polylines._decodedPositionOffsets;
  442. if (!defined(batchIds) || !defined(positions)) {
  443. return undefined;
  444. }
  445. let i;
  446. let j;
  447. const polylinesLength = batchIds.length;
  448. let positionsLength = 0;
  449. let resultCounter = 0;
  450. for (i = 0; i < polylinesLength; ++i) {
  451. if (batchIds[i] === batchId) {
  452. positionsLength += offsets[i + 1] - offsets[i];
  453. }
  454. }
  455. if (positionsLength === 0) {
  456. return undefined;
  457. }
  458. const results = new Float64Array(positionsLength * 3);
  459. for (i = 0; i < polylinesLength; ++i) {
  460. if (batchIds[i] === batchId) {
  461. const offset = offsets[i];
  462. const count = offsets[i + 1] - offset;
  463. for (j = 0; j < count; ++j) {
  464. const decodedOffset = (offset + j) * 3;
  465. results[resultCounter++] = positions[decodedOffset];
  466. results[resultCounter++] = positions[decodedOffset + 1];
  467. results[resultCounter++] = positions[decodedOffset + 2];
  468. }
  469. }
  470. }
  471. return results;
  472. };
  473. /**
  474. * Get the polyline positions for the given feature.
  475. *
  476. * @param {number} batchId The batch ID of the feature.
  477. */
  478. Vector3DTilePolylines.prototype.getPositions = function (batchId) {
  479. return Vector3DTilePolylines.getPolylinePositions(this, batchId);
  480. };
  481. /**
  482. * Creates features for each polyline and places it at the batch id index of features.
  483. *
  484. * @param {Vector3DTileContent} content The vector tile content.
  485. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed.
  486. */
  487. Vector3DTilePolylines.prototype.createFeatures = function (content, features) {
  488. const batchIds = this._batchIds;
  489. const length = batchIds.length;
  490. for (let i = 0; i < length; ++i) {
  491. const batchId = batchIds[i];
  492. features[batchId] = new Cesium3DTileFeature(content, batchId);
  493. }
  494. };
  495. /**
  496. * Colors the entire tile when enabled is true. The resulting color will be (polyline batch table color * color).
  497. *
  498. * @param {boolean} enabled Whether to enable debug coloring.
  499. * @param {Color} color The debug color.
  500. */
  501. Vector3DTilePolylines.prototype.applyDebugSettings = function (enabled, color) {
  502. this._highlightColor = enabled ? color : this._constantColor;
  503. };
  504. function clearStyle(polygons, features) {
  505. const batchIds = polygons._batchIds;
  506. const length = batchIds.length;
  507. for (let i = 0; i < length; ++i) {
  508. const batchId = batchIds[i];
  509. const feature = features[batchId];
  510. feature.show = true;
  511. feature.color = Color.WHITE;
  512. }
  513. }
  514. const scratchColor = new Color();
  515. const DEFAULT_COLOR_VALUE = Color.WHITE;
  516. const DEFAULT_SHOW_VALUE = true;
  517. /**
  518. * Apply a style to the content.
  519. *
  520. * @param {Cesium3DTileStyle} style The style.
  521. * @param {Cesium3DTileFeature[]} features The array of features.
  522. */
  523. Vector3DTilePolylines.prototype.applyStyle = function (style, features) {
  524. if (!defined(style)) {
  525. clearStyle(this, features);
  526. return;
  527. }
  528. const batchIds = this._batchIds;
  529. const length = batchIds.length;
  530. for (let i = 0; i < length; ++i) {
  531. const batchId = batchIds[i];
  532. const feature = features[batchId];
  533. feature.color = defined(style.color)
  534. ? style.color.evaluateColor(feature, scratchColor)
  535. : DEFAULT_COLOR_VALUE;
  536. feature.show = defined(style.show)
  537. ? style.show.evaluate(feature)
  538. : DEFAULT_SHOW_VALUE;
  539. }
  540. };
  541. /**
  542. * Updates the batches and queues the commands for rendering.
  543. *
  544. * @param {FrameState} frameState The current frame state.
  545. */
  546. Vector3DTilePolylines.prototype.update = function (frameState) {
  547. const context = frameState.context;
  548. if (!this._ready) {
  549. if (!defined(this._promise)) {
  550. this._promise = createVertexArray(this, context);
  551. }
  552. if (defined(this._error)) {
  553. const error = this._error;
  554. this._error = undefined;
  555. throw error;
  556. }
  557. return;
  558. }
  559. createUniformMap(this, context);
  560. createShaders(this, context);
  561. createRenderStates(this);
  562. const passes = frameState.passes;
  563. if (passes.render || passes.pick) {
  564. queueCommands(this, frameState);
  565. }
  566. };
  567. /**
  568. * Returns true if this object was destroyed; otherwise, false.
  569. * <p>
  570. * If this object was destroyed, it should not be used; calling any function other than
  571. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  572. * </p>
  573. *
  574. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  575. */
  576. Vector3DTilePolylines.prototype.isDestroyed = function () {
  577. return false;
  578. };
  579. /**
  580. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  581. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  582. * <p>
  583. * Once an object is destroyed, it should not be used; calling any function other than
  584. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  585. * assign the return value (<code>undefined</code>) to the object as done in the example.
  586. * </p>
  587. *
  588. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  589. */
  590. Vector3DTilePolylines.prototype.destroy = function () {
  591. this._va = this._va && this._va.destroy();
  592. this._sp = this._sp && this._sp.destroy();
  593. return destroyObject(this);
  594. };
  595. export default Vector3DTilePolylines;