Vector3DTilePrimitive.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  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 IndexDatatype from "../Core/IndexDatatype.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import PrimitiveType from "../Core/PrimitiveType.js";
  10. import Buffer from "../Renderer/Buffer.js";
  11. import BufferUsage from "../Renderer/BufferUsage.js";
  12. import DrawCommand from "../Renderer/DrawCommand.js";
  13. import Pass from "../Renderer/Pass.js";
  14. import RenderState from "../Renderer/RenderState.js";
  15. import ShaderProgram from "../Renderer/ShaderProgram.js";
  16. import ShaderSource from "../Renderer/ShaderSource.js";
  17. import VertexArray from "../Renderer/VertexArray.js";
  18. import ShadowVolumeFS from "../Shaders/ShadowVolumeFS.js";
  19. import VectorTileVS from "../Shaders/VectorTileVS.js";
  20. import BlendingState from "./BlendingState.js";
  21. import Cesium3DTileFeature from "./Cesium3DTileFeature.js";
  22. import ClassificationType from "./ClassificationType.js";
  23. import DepthFunction from "./DepthFunction.js";
  24. import Expression from "./Expression.js";
  25. import StencilConstants from "./StencilConstants.js";
  26. import StencilFunction from "./StencilFunction.js";
  27. import StencilOperation from "./StencilOperation.js";
  28. import Vector3DTileBatch from "./Vector3DTileBatch.js";
  29. /**
  30. * Creates a batch of classification meshes.
  31. *
  32. * @alias Vector3DTilePrimitive
  33. * @constructor
  34. *
  35. * @param {object} options An object with following properties:
  36. * @param {Float32Array} options.positions The positions of the meshes.
  37. * @param {Uint16Array|Uint32Array} options.indices The indices of the triangulated meshes. The indices must be contiguous so that
  38. * the indices for mesh n are in [i, i + indexCounts[n]] where i = sum{indexCounts[0], indexCounts[n - 1]}.
  39. * @param {Uint32Array} options.indexCounts The number of indices for each mesh.
  40. * @param {Uint32Array} options.indexOffsets The offset into the index buffer for each mesh.
  41. * @param {Vector3DTileBatch[]} options.batchedIndices The index offset and count for each batch with the same color.
  42. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center.
  43. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched meshes.
  44. * @param {Uint16Array} options.batchIds The batch ids for each mesh.
  45. * @param {Uint16Array} options.vertexBatchIds The batch id for each vertex.
  46. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of meshes.
  47. * @param {BoundingSphere[]} options.boundingVolumes The bounding volume for each mesh.
  48. * @param {ClassificationType} [options.classificationType] What this tile will classify.
  49. *
  50. * @private
  51. */
  52. function Vector3DTilePrimitive(options) {
  53. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  54. this._batchTable = options.batchTable;
  55. this._batchIds = options.batchIds;
  56. // These arrays are released after VAO creation.
  57. this._positions = options.positions;
  58. this._vertexBatchIds = options.vertexBatchIds;
  59. // These arrays are kept for re-batching indices based on colors.
  60. // If WebGL 2 is supported, indices will be released and re-batching uses buffer-to-buffer copies.
  61. this._indices = options.indices;
  62. this._indexCounts = options.indexCounts;
  63. this._indexOffsets = options.indexOffsets;
  64. this._batchedIndices = options.batchedIndices;
  65. this._boundingVolume = options.boundingVolume;
  66. this._boundingVolumes = options.boundingVolumes;
  67. this._center = defaultValue(options.center, Cartesian3.ZERO);
  68. this._va = undefined;
  69. this._sp = undefined;
  70. this._spStencil = undefined;
  71. this._spPick = undefined;
  72. this._uniformMap = undefined;
  73. // Only used with WebGL 2 to ping-pong ibos after copy.
  74. this._vaSwap = undefined;
  75. this._rsStencilDepthPass = undefined;
  76. this._rsStencilDepthPass3DTiles = undefined;
  77. this._rsColorPass = undefined;
  78. this._rsPickPass = undefined;
  79. this._rsWireframe = undefined;
  80. this._commands = [];
  81. this._commandsIgnoreShow = [];
  82. this._pickCommands = [];
  83. this._constantColor = Color.clone(Color.WHITE);
  84. this._highlightColor = this._constantColor;
  85. this._batchDirty = true;
  86. this._pickCommandsDirty = true;
  87. this._framesSinceLastRebatch = 0;
  88. this._updatingAllCommands = false;
  89. this._trianglesLength = this._indices.length / 3;
  90. this._geometryByteLength =
  91. this._indices.byteLength +
  92. this._positions.byteLength +
  93. this._vertexBatchIds.byteLength;
  94. /**
  95. * Draw the wireframe of the classification meshes.
  96. * @type {boolean}
  97. * @default false
  98. */
  99. this.debugWireframe = false;
  100. this._debugWireframe = this.debugWireframe;
  101. this._wireframeDirty = false;
  102. /**
  103. * Forces a re-batch instead of waiting after a number of frames have been rendered. For testing only.
  104. * @type {boolean}
  105. * @default false
  106. */
  107. this.forceRebatch = false;
  108. /**
  109. * What this tile will classify.
  110. * @type {ClassificationType}
  111. * @default ClassificationType.BOTH
  112. */
  113. this.classificationType = defaultValue(
  114. options.classificationType,
  115. ClassificationType.BOTH
  116. );
  117. // Hidden options
  118. this._vertexShaderSource = options._vertexShaderSource;
  119. this._fragmentShaderSource = options._fragmentShaderSource;
  120. this._attributeLocations = options._attributeLocations;
  121. this._uniformMap = options._uniformMap;
  122. this._pickId = options._pickId;
  123. this._modelMatrix = options._modelMatrix;
  124. this._boundingSphere = options._boundingSphere;
  125. this._batchIdLookUp = {};
  126. const length = this._batchIds.length;
  127. for (let i = 0; i < length; ++i) {
  128. const batchId = this._batchIds[i];
  129. this._batchIdLookUp[batchId] = i;
  130. }
  131. }
  132. Object.defineProperties(Vector3DTilePrimitive.prototype, {
  133. /**
  134. * Gets the number of triangles.
  135. *
  136. * @memberof Vector3DTilePrimitive.prototype
  137. *
  138. * @type {number}
  139. * @readonly
  140. */
  141. trianglesLength: {
  142. get: function () {
  143. return this._trianglesLength;
  144. },
  145. },
  146. /**
  147. * Gets the geometry memory in bytes.
  148. *
  149. * @memberof Vector3DTilePrimitive.prototype
  150. *
  151. * @type {number}
  152. * @readonly
  153. */
  154. geometryByteLength: {
  155. get: function () {
  156. return this._geometryByteLength;
  157. },
  158. },
  159. });
  160. const defaultAttributeLocations = {
  161. position: 0,
  162. a_batchId: 1,
  163. };
  164. function createVertexArray(primitive, context) {
  165. if (defined(primitive._va)) {
  166. return;
  167. }
  168. const positionBuffer = Buffer.createVertexBuffer({
  169. context: context,
  170. typedArray: primitive._positions,
  171. usage: BufferUsage.STATIC_DRAW,
  172. });
  173. const idBuffer = Buffer.createVertexBuffer({
  174. context: context,
  175. typedArray: primitive._vertexBatchIds,
  176. usage: BufferUsage.STATIC_DRAW,
  177. });
  178. const indexBuffer = Buffer.createIndexBuffer({
  179. context: context,
  180. typedArray: primitive._indices,
  181. usage: BufferUsage.DYNAMIC_DRAW,
  182. indexDatatype:
  183. primitive._indices.BYTES_PER_ELEMENT === 2
  184. ? IndexDatatype.UNSIGNED_SHORT
  185. : IndexDatatype.UNSIGNED_INT,
  186. });
  187. const vertexAttributes = [
  188. {
  189. index: 0,
  190. vertexBuffer: positionBuffer,
  191. componentDatatype: ComponentDatatype.fromTypedArray(primitive._positions),
  192. componentsPerAttribute: 3,
  193. },
  194. {
  195. index: 1,
  196. vertexBuffer: idBuffer,
  197. componentDatatype: ComponentDatatype.fromTypedArray(
  198. primitive._vertexBatchIds
  199. ),
  200. componentsPerAttribute: 1,
  201. },
  202. ];
  203. primitive._va = new VertexArray({
  204. context: context,
  205. attributes: vertexAttributes,
  206. indexBuffer: indexBuffer,
  207. });
  208. if (context.webgl2) {
  209. primitive._vaSwap = new VertexArray({
  210. context: context,
  211. attributes: vertexAttributes,
  212. indexBuffer: Buffer.createIndexBuffer({
  213. context: context,
  214. sizeInBytes: indexBuffer.sizeInBytes,
  215. usage: BufferUsage.DYNAMIC_DRAW,
  216. indexDatatype: indexBuffer.indexDatatype,
  217. }),
  218. });
  219. }
  220. primitive._batchedPositions = undefined;
  221. primitive._transferrableBatchIds = undefined;
  222. primitive._vertexBatchIds = undefined;
  223. }
  224. function createShaders(primitive, context) {
  225. if (defined(primitive._sp)) {
  226. return;
  227. }
  228. const batchTable = primitive._batchTable;
  229. const attributeLocations = defaultValue(
  230. primitive._attributeLocations,
  231. defaultAttributeLocations
  232. );
  233. let pickId = primitive._pickId;
  234. const vertexShaderSource = primitive._vertexShaderSource;
  235. let fragmentShaderSource = primitive._fragmentShaderSource;
  236. if (defined(vertexShaderSource)) {
  237. primitive._sp = ShaderProgram.fromCache({
  238. context: context,
  239. vertexShaderSource: vertexShaderSource,
  240. fragmentShaderSource: fragmentShaderSource,
  241. attributeLocations: attributeLocations,
  242. });
  243. primitive._spStencil = primitive._sp;
  244. fragmentShaderSource = ShaderSource.replaceMain(
  245. fragmentShaderSource,
  246. "czm_non_pick_main"
  247. );
  248. fragmentShaderSource =
  249. `${fragmentShaderSource}void main() \n` +
  250. `{ \n` +
  251. ` czm_non_pick_main(); \n` +
  252. ` out_FragColor = ${pickId}; \n` +
  253. `} \n`;
  254. primitive._spPick = ShaderProgram.fromCache({
  255. context: context,
  256. vertexShaderSource: vertexShaderSource,
  257. fragmentShaderSource: fragmentShaderSource,
  258. attributeLocations: attributeLocations,
  259. });
  260. return;
  261. }
  262. const vsSource = batchTable.getVertexShaderCallback(
  263. false,
  264. "a_batchId",
  265. undefined
  266. )(VectorTileVS);
  267. let fsSource = batchTable.getFragmentShaderCallback(
  268. false,
  269. undefined,
  270. true
  271. )(ShadowVolumeFS);
  272. pickId = batchTable.getPickId();
  273. let vs = new ShaderSource({
  274. sources: [vsSource],
  275. });
  276. let fs = new ShaderSource({
  277. defines: ["VECTOR_TILE"],
  278. sources: [fsSource],
  279. });
  280. primitive._sp = ShaderProgram.fromCache({
  281. context: context,
  282. vertexShaderSource: vs,
  283. fragmentShaderSource: fs,
  284. attributeLocations: attributeLocations,
  285. });
  286. vs = new ShaderSource({
  287. sources: [VectorTileVS],
  288. });
  289. fs = new ShaderSource({
  290. defines: ["VECTOR_TILE"],
  291. sources: [ShadowVolumeFS],
  292. });
  293. primitive._spStencil = ShaderProgram.fromCache({
  294. context: context,
  295. vertexShaderSource: vs,
  296. fragmentShaderSource: fs,
  297. attributeLocations: attributeLocations,
  298. });
  299. fsSource = ShaderSource.replaceMain(fsSource, "czm_non_pick_main");
  300. fsSource =
  301. `${fsSource}\n` +
  302. `void main() \n` +
  303. `{ \n` +
  304. ` czm_non_pick_main(); \n` +
  305. ` out_FragColor = ${pickId}; \n` +
  306. `} \n`;
  307. const pickVS = new ShaderSource({
  308. sources: [vsSource],
  309. });
  310. const pickFS = new ShaderSource({
  311. defines: ["VECTOR_TILE"],
  312. sources: [fsSource],
  313. });
  314. primitive._spPick = ShaderProgram.fromCache({
  315. context: context,
  316. vertexShaderSource: pickVS,
  317. fragmentShaderSource: pickFS,
  318. attributeLocations: attributeLocations,
  319. });
  320. }
  321. function getStencilDepthRenderState(mask3DTiles) {
  322. const stencilFunction = mask3DTiles
  323. ? StencilFunction.EQUAL
  324. : StencilFunction.ALWAYS;
  325. return {
  326. colorMask: {
  327. red: false,
  328. green: false,
  329. blue: false,
  330. alpha: false,
  331. },
  332. stencilTest: {
  333. enabled: true,
  334. frontFunction: stencilFunction,
  335. frontOperation: {
  336. fail: StencilOperation.KEEP,
  337. zFail: StencilOperation.DECREMENT_WRAP,
  338. zPass: StencilOperation.KEEP,
  339. },
  340. backFunction: stencilFunction,
  341. backOperation: {
  342. fail: StencilOperation.KEEP,
  343. zFail: StencilOperation.INCREMENT_WRAP,
  344. zPass: StencilOperation.KEEP,
  345. },
  346. reference: StencilConstants.CESIUM_3D_TILE_MASK,
  347. mask: StencilConstants.CESIUM_3D_TILE_MASK,
  348. },
  349. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  350. depthTest: {
  351. enabled: true,
  352. func: DepthFunction.LESS_OR_EQUAL,
  353. },
  354. depthMask: false,
  355. };
  356. }
  357. const colorRenderState = {
  358. stencilTest: {
  359. enabled: true,
  360. frontFunction: StencilFunction.NOT_EQUAL,
  361. frontOperation: {
  362. fail: StencilOperation.ZERO,
  363. zFail: StencilOperation.ZERO,
  364. zPass: StencilOperation.ZERO,
  365. },
  366. backFunction: StencilFunction.NOT_EQUAL,
  367. backOperation: {
  368. fail: StencilOperation.ZERO,
  369. zFail: StencilOperation.ZERO,
  370. zPass: StencilOperation.ZERO,
  371. },
  372. reference: 0,
  373. mask: StencilConstants.CLASSIFICATION_MASK,
  374. },
  375. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  376. depthTest: {
  377. enabled: false,
  378. },
  379. depthMask: false,
  380. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  381. };
  382. const pickRenderState = {
  383. stencilTest: {
  384. enabled: true,
  385. frontFunction: StencilFunction.NOT_EQUAL,
  386. frontOperation: {
  387. fail: StencilOperation.ZERO,
  388. zFail: StencilOperation.ZERO,
  389. zPass: StencilOperation.ZERO,
  390. },
  391. backFunction: StencilFunction.NOT_EQUAL,
  392. backOperation: {
  393. fail: StencilOperation.ZERO,
  394. zFail: StencilOperation.ZERO,
  395. zPass: StencilOperation.ZERO,
  396. },
  397. reference: 0,
  398. mask: StencilConstants.CLASSIFICATION_MASK,
  399. },
  400. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  401. depthTest: {
  402. enabled: false,
  403. },
  404. depthMask: false,
  405. };
  406. function createRenderStates(primitive) {
  407. if (defined(primitive._rsStencilDepthPass)) {
  408. return;
  409. }
  410. primitive._rsStencilDepthPass = RenderState.fromCache(
  411. getStencilDepthRenderState(false)
  412. );
  413. primitive._rsStencilDepthPass3DTiles = RenderState.fromCache(
  414. getStencilDepthRenderState(true)
  415. );
  416. primitive._rsColorPass = RenderState.fromCache(colorRenderState);
  417. primitive._rsPickPass = RenderState.fromCache(pickRenderState);
  418. }
  419. const modifiedModelViewScratch = new Matrix4();
  420. const rtcScratch = new Cartesian3();
  421. function createUniformMap(primitive, context) {
  422. if (defined(primitive._uniformMap)) {
  423. return;
  424. }
  425. const uniformMap = {
  426. u_modifiedModelViewProjection: function () {
  427. const viewMatrix = context.uniformState.view;
  428. const projectionMatrix = context.uniformState.projection;
  429. Matrix4.clone(viewMatrix, modifiedModelViewScratch);
  430. Matrix4.multiplyByPoint(
  431. modifiedModelViewScratch,
  432. primitive._center,
  433. rtcScratch
  434. );
  435. Matrix4.setTranslation(
  436. modifiedModelViewScratch,
  437. rtcScratch,
  438. modifiedModelViewScratch
  439. );
  440. Matrix4.multiply(
  441. projectionMatrix,
  442. modifiedModelViewScratch,
  443. modifiedModelViewScratch
  444. );
  445. return modifiedModelViewScratch;
  446. },
  447. u_highlightColor: function () {
  448. return primitive._highlightColor;
  449. },
  450. };
  451. primitive._uniformMap = primitive._batchTable.getUniformMapCallback()(
  452. uniformMap
  453. );
  454. }
  455. function copyIndicesCPU(
  456. indices,
  457. newIndices,
  458. currentOffset,
  459. offsets,
  460. counts,
  461. batchIds,
  462. batchIdLookUp
  463. ) {
  464. const sizeInBytes = indices.constructor.BYTES_PER_ELEMENT;
  465. const batchedIdsLength = batchIds.length;
  466. for (let j = 0; j < batchedIdsLength; ++j) {
  467. const batchedId = batchIds[j];
  468. const index = batchIdLookUp[batchedId];
  469. const offset = offsets[index];
  470. const count = counts[index];
  471. const subarray = new indices.constructor(
  472. indices.buffer,
  473. sizeInBytes * offset,
  474. count
  475. );
  476. newIndices.set(subarray, currentOffset);
  477. offsets[index] = currentOffset;
  478. currentOffset += count;
  479. }
  480. return currentOffset;
  481. }
  482. function rebatchCPU(primitive, batchedIndices) {
  483. const indices = primitive._indices;
  484. const indexOffsets = primitive._indexOffsets;
  485. const indexCounts = primitive._indexCounts;
  486. const batchIdLookUp = primitive._batchIdLookUp;
  487. const newIndices = new indices.constructor(indices.length);
  488. let current = batchedIndices.pop();
  489. const newBatchedIndices = [current];
  490. let currentOffset = copyIndicesCPU(
  491. indices,
  492. newIndices,
  493. 0,
  494. indexOffsets,
  495. indexCounts,
  496. current.batchIds,
  497. batchIdLookUp
  498. );
  499. current.offset = 0;
  500. current.count = currentOffset;
  501. while (batchedIndices.length > 0) {
  502. const next = batchedIndices.pop();
  503. if (Color.equals(next.color, current.color)) {
  504. currentOffset = copyIndicesCPU(
  505. indices,
  506. newIndices,
  507. currentOffset,
  508. indexOffsets,
  509. indexCounts,
  510. next.batchIds,
  511. batchIdLookUp
  512. );
  513. current.batchIds = current.batchIds.concat(next.batchIds);
  514. current.count = currentOffset - current.offset;
  515. } else {
  516. const offset = currentOffset;
  517. currentOffset = copyIndicesCPU(
  518. indices,
  519. newIndices,
  520. currentOffset,
  521. indexOffsets,
  522. indexCounts,
  523. next.batchIds,
  524. batchIdLookUp
  525. );
  526. next.offset = offset;
  527. next.count = currentOffset - offset;
  528. newBatchedIndices.push(next);
  529. current = next;
  530. }
  531. }
  532. primitive._va.indexBuffer.copyFromArrayView(newIndices);
  533. primitive._indices = newIndices;
  534. primitive._batchedIndices = newBatchedIndices;
  535. }
  536. function copyIndicesGPU(
  537. readBuffer,
  538. writeBuffer,
  539. currentOffset,
  540. offsets,
  541. counts,
  542. batchIds,
  543. batchIdLookUp
  544. ) {
  545. const sizeInBytes = readBuffer.bytesPerIndex;
  546. const batchedIdsLength = batchIds.length;
  547. for (let j = 0; j < batchedIdsLength; ++j) {
  548. const batchedId = batchIds[j];
  549. const index = batchIdLookUp[batchedId];
  550. const offset = offsets[index];
  551. const count = counts[index];
  552. writeBuffer.copyFromBuffer(
  553. readBuffer,
  554. offset * sizeInBytes,
  555. currentOffset * sizeInBytes,
  556. count * sizeInBytes
  557. );
  558. offsets[index] = currentOffset;
  559. currentOffset += count;
  560. }
  561. return currentOffset;
  562. }
  563. function rebatchGPU(primitive, batchedIndices) {
  564. const indexOffsets = primitive._indexOffsets;
  565. const indexCounts = primitive._indexCounts;
  566. const batchIdLookUp = primitive._batchIdLookUp;
  567. let current = batchedIndices.pop();
  568. const newBatchedIndices = [current];
  569. const readBuffer = primitive._va.indexBuffer;
  570. const writeBuffer = primitive._vaSwap.indexBuffer;
  571. let currentOffset = copyIndicesGPU(
  572. readBuffer,
  573. writeBuffer,
  574. 0,
  575. indexOffsets,
  576. indexCounts,
  577. current.batchIds,
  578. batchIdLookUp
  579. );
  580. current.offset = 0;
  581. current.count = currentOffset;
  582. while (batchedIndices.length > 0) {
  583. const next = batchedIndices.pop();
  584. if (Color.equals(next.color, current.color)) {
  585. currentOffset = copyIndicesGPU(
  586. readBuffer,
  587. writeBuffer,
  588. currentOffset,
  589. indexOffsets,
  590. indexCounts,
  591. next.batchIds,
  592. batchIdLookUp
  593. );
  594. current.batchIds = current.batchIds.concat(next.batchIds);
  595. current.count = currentOffset - current.offset;
  596. } else {
  597. const offset = currentOffset;
  598. currentOffset = copyIndicesGPU(
  599. readBuffer,
  600. writeBuffer,
  601. currentOffset,
  602. indexOffsets,
  603. indexCounts,
  604. next.batchIds,
  605. batchIdLookUp
  606. );
  607. next.offset = offset;
  608. next.count = currentOffset - offset;
  609. newBatchedIndices.push(next);
  610. current = next;
  611. }
  612. }
  613. const temp = primitive._va;
  614. primitive._va = primitive._vaSwap;
  615. primitive._vaSwap = temp;
  616. primitive._batchedIndices = newBatchedIndices;
  617. }
  618. function compareColors(a, b) {
  619. return b.color.toRgba() - a.color.toRgba();
  620. }
  621. // PERFORMANCE_IDEA: For WebGL 2, we can use copyBufferSubData for buffer-to-buffer copies.
  622. // PERFORMANCE_IDEA: Not supported, but we could use glMultiDrawElements here.
  623. function rebatchCommands(primitive, context) {
  624. if (!primitive._batchDirty) {
  625. return false;
  626. }
  627. const batchedIndices = primitive._batchedIndices;
  628. const length = batchedIndices.length;
  629. let needToRebatch = false;
  630. const colorCounts = {};
  631. for (let i = 0; i < length; ++i) {
  632. const color = batchedIndices[i].color;
  633. const rgba = color.toRgba();
  634. if (defined(colorCounts[rgba])) {
  635. needToRebatch = true;
  636. break;
  637. } else {
  638. colorCounts[rgba] = true;
  639. }
  640. }
  641. if (!needToRebatch) {
  642. primitive._batchDirty = false;
  643. return false;
  644. }
  645. if (
  646. needToRebatch &&
  647. !primitive.forceRebatch &&
  648. primitive._framesSinceLastRebatch < 120
  649. ) {
  650. ++primitive._framesSinceLastRebatch;
  651. return;
  652. }
  653. batchedIndices.sort(compareColors);
  654. if (context.webgl2) {
  655. rebatchGPU(primitive, batchedIndices);
  656. } else {
  657. rebatchCPU(primitive, batchedIndices);
  658. }
  659. primitive._framesSinceLastRebatch = 0;
  660. primitive._batchDirty = false;
  661. primitive._pickCommandsDirty = true;
  662. primitive._wireframeDirty = true;
  663. return true;
  664. }
  665. function createColorCommands(primitive, context) {
  666. const needsRebatch = rebatchCommands(primitive, context);
  667. const commands = primitive._commands;
  668. const batchedIndices = primitive._batchedIndices;
  669. const length = batchedIndices.length;
  670. const commandsLength = length * 2;
  671. if (
  672. defined(commands) &&
  673. !needsRebatch &&
  674. commands.length === commandsLength
  675. ) {
  676. return;
  677. }
  678. commands.length = commandsLength;
  679. const vertexArray = primitive._va;
  680. const sp = primitive._sp;
  681. const modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY);
  682. const uniformMap = primitive._uniformMap;
  683. const bv = primitive._boundingVolume;
  684. for (let j = 0; j < length; ++j) {
  685. const offset = batchedIndices[j].offset;
  686. const count = batchedIndices[j].count;
  687. let stencilDepthCommand = commands[j * 2];
  688. if (!defined(stencilDepthCommand)) {
  689. stencilDepthCommand = commands[j * 2] = new DrawCommand({
  690. owner: primitive,
  691. });
  692. }
  693. stencilDepthCommand.vertexArray = vertexArray;
  694. stencilDepthCommand.modelMatrix = modelMatrix;
  695. stencilDepthCommand.offset = offset;
  696. stencilDepthCommand.count = count;
  697. stencilDepthCommand.renderState = primitive._rsStencilDepthPass;
  698. stencilDepthCommand.shaderProgram = sp;
  699. stencilDepthCommand.uniformMap = uniformMap;
  700. stencilDepthCommand.boundingVolume = bv;
  701. stencilDepthCommand.cull = false;
  702. stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION;
  703. const stencilDepthDerivedCommand = DrawCommand.shallowClone(
  704. stencilDepthCommand,
  705. stencilDepthCommand.derivedCommands.tileset
  706. );
  707. stencilDepthDerivedCommand.renderState =
  708. primitive._rsStencilDepthPass3DTiles;
  709. stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  710. stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand;
  711. let colorCommand = commands[j * 2 + 1];
  712. if (!defined(colorCommand)) {
  713. colorCommand = commands[j * 2 + 1] = new DrawCommand({
  714. owner: primitive,
  715. });
  716. }
  717. colorCommand.vertexArray = vertexArray;
  718. colorCommand.modelMatrix = modelMatrix;
  719. colorCommand.offset = offset;
  720. colorCommand.count = count;
  721. colorCommand.renderState = primitive._rsColorPass;
  722. colorCommand.shaderProgram = sp;
  723. colorCommand.uniformMap = uniformMap;
  724. colorCommand.boundingVolume = bv;
  725. colorCommand.cull = false;
  726. colorCommand.pass = Pass.TERRAIN_CLASSIFICATION;
  727. const colorDerivedCommand = DrawCommand.shallowClone(
  728. colorCommand,
  729. colorCommand.derivedCommands.tileset
  730. );
  731. colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  732. colorCommand.derivedCommands.tileset = colorDerivedCommand;
  733. }
  734. primitive._commandsDirty = true;
  735. }
  736. function createColorCommandsIgnoreShow(primitive, frameState) {
  737. if (
  738. primitive.classificationType === ClassificationType.TERRAIN ||
  739. !frameState.invertClassification ||
  740. (defined(primitive._commandsIgnoreShow) && !primitive._commandsDirty)
  741. ) {
  742. return;
  743. }
  744. const commands = primitive._commands;
  745. const commandsIgnoreShow = primitive._commandsIgnoreShow;
  746. const spStencil = primitive._spStencil;
  747. const commandsLength = commands.length;
  748. const length = (commandsIgnoreShow.length = commandsLength / 2);
  749. let commandIndex = 0;
  750. for (let j = 0; j < length; ++j) {
  751. const commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone(
  752. commands[commandIndex],
  753. commandsIgnoreShow[j]
  754. ));
  755. commandIgnoreShow.shaderProgram = spStencil;
  756. commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
  757. commandIndex += 2;
  758. }
  759. primitive._commandsDirty = false;
  760. }
  761. function createPickCommands(primitive) {
  762. if (!primitive._pickCommandsDirty) {
  763. return;
  764. }
  765. const length = primitive._indexOffsets.length;
  766. const pickCommands = primitive._pickCommands;
  767. pickCommands.length = length * 2;
  768. const vertexArray = primitive._va;
  769. const spStencil = primitive._spStencil;
  770. const spPick = primitive._spPick;
  771. const modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY);
  772. const uniformMap = primitive._uniformMap;
  773. for (let j = 0; j < length; ++j) {
  774. const offset = primitive._indexOffsets[j];
  775. const count = primitive._indexCounts[j];
  776. const bv = defined(primitive._boundingVolumes)
  777. ? primitive._boundingVolumes[j]
  778. : primitive.boundingVolume;
  779. let stencilDepthCommand = pickCommands[j * 2];
  780. if (!defined(stencilDepthCommand)) {
  781. stencilDepthCommand = pickCommands[j * 2] = new DrawCommand({
  782. owner: primitive,
  783. pickOnly: true,
  784. });
  785. }
  786. stencilDepthCommand.vertexArray = vertexArray;
  787. stencilDepthCommand.modelMatrix = modelMatrix;
  788. stencilDepthCommand.offset = offset;
  789. stencilDepthCommand.count = count;
  790. stencilDepthCommand.renderState = primitive._rsStencilDepthPass;
  791. stencilDepthCommand.shaderProgram = spStencil;
  792. stencilDepthCommand.uniformMap = uniformMap;
  793. stencilDepthCommand.boundingVolume = bv;
  794. stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION;
  795. const stencilDepthDerivedCommand = DrawCommand.shallowClone(
  796. stencilDepthCommand,
  797. stencilDepthCommand.derivedCommands.tileset
  798. );
  799. stencilDepthDerivedCommand.renderState =
  800. primitive._rsStencilDepthPass3DTiles;
  801. stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  802. stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand;
  803. let colorCommand = pickCommands[j * 2 + 1];
  804. if (!defined(colorCommand)) {
  805. colorCommand = pickCommands[j * 2 + 1] = new DrawCommand({
  806. owner: primitive,
  807. pickOnly: true,
  808. });
  809. }
  810. colorCommand.vertexArray = vertexArray;
  811. colorCommand.modelMatrix = modelMatrix;
  812. colorCommand.offset = offset;
  813. colorCommand.count = count;
  814. colorCommand.renderState = primitive._rsPickPass;
  815. colorCommand.shaderProgram = spPick;
  816. colorCommand.uniformMap = uniformMap;
  817. colorCommand.boundingVolume = bv;
  818. colorCommand.pass = Pass.TERRAIN_CLASSIFICATION;
  819. const colorDerivedCommand = DrawCommand.shallowClone(
  820. colorCommand,
  821. colorCommand.derivedCommands.tileset
  822. );
  823. colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  824. colorCommand.derivedCommands.tileset = colorDerivedCommand;
  825. }
  826. primitive._pickCommandsDirty = false;
  827. }
  828. /**
  829. * Creates features for each mesh and places it at the batch id index of features.
  830. *
  831. * @param {Vector3DTileContent} content The vector tile content.
  832. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed.
  833. */
  834. Vector3DTilePrimitive.prototype.createFeatures = function (content, features) {
  835. const batchIds = this._batchIds;
  836. const length = batchIds.length;
  837. for (let i = 0; i < length; ++i) {
  838. const batchId = batchIds[i];
  839. features[batchId] = new Cesium3DTileFeature(content, batchId);
  840. }
  841. };
  842. /**
  843. * Colors the entire tile when enabled is true. The resulting color will be (mesh batch table color * color).
  844. *
  845. * @param {boolean} enabled Whether to enable debug coloring.
  846. * @param {Color} color The debug color.
  847. */
  848. Vector3DTilePrimitive.prototype.applyDebugSettings = function (enabled, color) {
  849. this._highlightColor = enabled ? color : this._constantColor;
  850. };
  851. function clearStyle(polygons, features) {
  852. polygons._updatingAllCommands = true;
  853. const batchIds = polygons._batchIds;
  854. let length = batchIds.length;
  855. let i;
  856. for (i = 0; i < length; ++i) {
  857. const batchId = batchIds[i];
  858. const feature = features[batchId];
  859. feature.show = true;
  860. feature.color = Color.WHITE;
  861. }
  862. const batchedIndices = polygons._batchedIndices;
  863. length = batchedIndices.length;
  864. for (i = 0; i < length; ++i) {
  865. batchedIndices[i].color = Color.clone(Color.WHITE);
  866. }
  867. polygons._updatingAllCommands = false;
  868. polygons._batchDirty = true;
  869. }
  870. const scratchColor = new Color();
  871. const DEFAULT_COLOR_VALUE = Color.WHITE;
  872. const DEFAULT_SHOW_VALUE = true;
  873. const complexExpressionReg = /\$/;
  874. /**
  875. * Apply a style to the content.
  876. *
  877. * @param {Cesium3DTileStyle} style The style.
  878. * @param {Cesium3DTileFeature[]} features The array of features.
  879. */
  880. Vector3DTilePrimitive.prototype.applyStyle = function (style, features) {
  881. if (!defined(style)) {
  882. clearStyle(this, features);
  883. return;
  884. }
  885. const colorExpression = style.color;
  886. const isSimpleStyle =
  887. colorExpression instanceof Expression &&
  888. !complexExpressionReg.test(colorExpression.expression);
  889. this._updatingAllCommands = isSimpleStyle;
  890. const batchIds = this._batchIds;
  891. let length = batchIds.length;
  892. let i;
  893. for (i = 0; i < length; ++i) {
  894. const batchId = batchIds[i];
  895. const feature = features[batchId];
  896. feature.color = defined(style.color)
  897. ? style.color.evaluateColor(feature, scratchColor)
  898. : DEFAULT_COLOR_VALUE;
  899. feature.show = defined(style.show)
  900. ? style.show.evaluate(feature)
  901. : DEFAULT_SHOW_VALUE;
  902. }
  903. if (isSimpleStyle) {
  904. const batchedIndices = this._batchedIndices;
  905. length = batchedIndices.length;
  906. for (i = 0; i < length; ++i) {
  907. batchedIndices[i].color = Color.clone(Color.WHITE);
  908. }
  909. this._updatingAllCommands = false;
  910. this._batchDirty = true;
  911. }
  912. };
  913. /**
  914. * Call when updating the color of a mesh with batchId changes color. The meshes will need to be re-batched
  915. * on the next update.
  916. *
  917. * @param {number} batchId The batch id of the meshes whose color has changed.
  918. * @param {Color} color The new polygon color.
  919. */
  920. Vector3DTilePrimitive.prototype.updateCommands = function (batchId, color) {
  921. if (this._updatingAllCommands) {
  922. return;
  923. }
  924. const batchIdLookUp = this._batchIdLookUp;
  925. const index = batchIdLookUp[batchId];
  926. if (!defined(index)) {
  927. return;
  928. }
  929. const indexOffsets = this._indexOffsets;
  930. const indexCounts = this._indexCounts;
  931. const offset = indexOffsets[index];
  932. const count = indexCounts[index];
  933. const batchedIndices = this._batchedIndices;
  934. const length = batchedIndices.length;
  935. let i;
  936. for (i = 0; i < length; ++i) {
  937. const batchedOffset = batchedIndices[i].offset;
  938. const batchedCount = batchedIndices[i].count;
  939. if (offset >= batchedOffset && offset < batchedOffset + batchedCount) {
  940. break;
  941. }
  942. }
  943. batchedIndices.push(
  944. new Vector3DTileBatch({
  945. color: Color.clone(color),
  946. offset: offset,
  947. count: count,
  948. batchIds: [batchId],
  949. })
  950. );
  951. const startIds = [];
  952. const endIds = [];
  953. const batchIds = batchedIndices[i].batchIds;
  954. const batchIdsLength = batchIds.length;
  955. for (let j = 0; j < batchIdsLength; ++j) {
  956. const id = batchIds[j];
  957. if (id === batchId) {
  958. continue;
  959. }
  960. const offsetIndex = batchIdLookUp[id];
  961. if (indexOffsets[offsetIndex] < offset) {
  962. startIds.push(id);
  963. } else {
  964. endIds.push(id);
  965. }
  966. }
  967. if (endIds.length !== 0) {
  968. batchedIndices.push(
  969. new Vector3DTileBatch({
  970. color: Color.clone(batchedIndices[i].color),
  971. offset: offset + count,
  972. count:
  973. batchedIndices[i].offset + batchedIndices[i].count - (offset + count),
  974. batchIds: endIds,
  975. })
  976. );
  977. }
  978. if (startIds.length !== 0) {
  979. batchedIndices[i].count = offset - batchedIndices[i].offset;
  980. batchedIndices[i].batchIds = startIds;
  981. } else {
  982. batchedIndices.splice(i, 1);
  983. }
  984. this._batchDirty = true;
  985. };
  986. function queueCommands(primitive, frameState, commands, commandsIgnoreShow) {
  987. const classificationType = primitive.classificationType;
  988. const queueTerrainCommands =
  989. classificationType !== ClassificationType.CESIUM_3D_TILE;
  990. const queue3DTilesCommands =
  991. classificationType !== ClassificationType.TERRAIN;
  992. const commandList = frameState.commandList;
  993. let commandLength = commands.length;
  994. let command;
  995. let i;
  996. for (i = 0; i < commandLength; ++i) {
  997. if (queueTerrainCommands) {
  998. command = commands[i];
  999. command.pass = Pass.TERRAIN_CLASSIFICATION;
  1000. commandList.push(command);
  1001. }
  1002. if (queue3DTilesCommands) {
  1003. command = commands[i].derivedCommands.tileset;
  1004. command.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  1005. commandList.push(command);
  1006. }
  1007. }
  1008. if (!frameState.invertClassification || !defined(commandsIgnoreShow)) {
  1009. return;
  1010. }
  1011. commandLength = commandsIgnoreShow.length;
  1012. for (i = 0; i < commandLength; ++i) {
  1013. commandList.push(commandsIgnoreShow[i]);
  1014. }
  1015. }
  1016. function queueWireframeCommands(frameState, commands) {
  1017. const commandList = frameState.commandList;
  1018. const commandLength = commands.length;
  1019. for (let i = 0; i < commandLength; i += 2) {
  1020. const command = commands[i + 1];
  1021. command.pass = Pass.OPAQUE;
  1022. commandList.push(command);
  1023. }
  1024. }
  1025. function updateWireframe(primitive) {
  1026. let earlyExit = primitive.debugWireframe === primitive._debugWireframe;
  1027. earlyExit =
  1028. earlyExit && !(primitive.debugWireframe && primitive._wireframeDirty);
  1029. if (earlyExit) {
  1030. return;
  1031. }
  1032. if (!defined(primitive._rsWireframe)) {
  1033. primitive._rsWireframe = RenderState.fromCache({});
  1034. }
  1035. let rs;
  1036. let type;
  1037. if (primitive.debugWireframe) {
  1038. rs = primitive._rsWireframe;
  1039. type = PrimitiveType.LINES;
  1040. } else {
  1041. rs = primitive._rsColorPass;
  1042. type = PrimitiveType.TRIANGLES;
  1043. }
  1044. const commands = primitive._commands;
  1045. const commandLength = commands.length;
  1046. for (let i = 0; i < commandLength; i += 2) {
  1047. const command = commands[i + 1];
  1048. command.renderState = rs;
  1049. command.primitiveType = type;
  1050. }
  1051. primitive._debugWireframe = primitive.debugWireframe;
  1052. primitive._wireframeDirty = false;
  1053. }
  1054. /**
  1055. * Updates the batches and queues the commands for rendering.
  1056. *
  1057. * @param {FrameState} frameState The current frame state.
  1058. */
  1059. Vector3DTilePrimitive.prototype.update = function (frameState) {
  1060. const context = frameState.context;
  1061. createVertexArray(this, context);
  1062. createShaders(this, context);
  1063. createRenderStates(this);
  1064. createUniformMap(this, context);
  1065. const passes = frameState.passes;
  1066. if (passes.render) {
  1067. createColorCommands(this, context);
  1068. createColorCommandsIgnoreShow(this, frameState);
  1069. updateWireframe(this);
  1070. if (this._debugWireframe) {
  1071. queueWireframeCommands(frameState, this._commands);
  1072. } else {
  1073. queueCommands(this, frameState, this._commands, this._commandsIgnoreShow);
  1074. }
  1075. }
  1076. if (passes.pick) {
  1077. createPickCommands(this);
  1078. queueCommands(this, frameState, this._pickCommands);
  1079. }
  1080. };
  1081. /**
  1082. * Returns true if this object was destroyed; otherwise, false.
  1083. * <p>
  1084. * If this object was destroyed, it should not be used; calling any function other than
  1085. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1086. * </p>
  1087. *
  1088. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1089. */
  1090. Vector3DTilePrimitive.prototype.isDestroyed = function () {
  1091. return false;
  1092. };
  1093. /**
  1094. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1095. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1096. * <p>
  1097. * Once an object is destroyed, it should not be used; calling any function other than
  1098. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1099. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1100. * </p>
  1101. *
  1102. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1103. */
  1104. Vector3DTilePrimitive.prototype.destroy = function () {
  1105. this._va = this._va && this._va.destroy();
  1106. this._sp = this._sp && this._sp.destroy();
  1107. this._spPick = this._spPick && this._spPick.destroy();
  1108. this._vaSwap = this._vaSwap && this._vaSwap.destroy();
  1109. return destroyObject(this);
  1110. };
  1111. export default Vector3DTilePrimitive;