Vector3DTilePrimitive.js 35 KB

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