Vector3DTilePolylines.js 19 KB

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