Vector3DTilePrimitive.js 35 KB

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