VoxelTraversal.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import CesiumMath from "../Core/Math.js";
  3. import CullingVolume from "../Core/CullingVolume.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import DoubleEndedPriorityQueue from "../Core/DoubleEndedPriorityQueue.js";
  7. import getTimestamp from "../Core/getTimestamp.js";
  8. import KeyframeNode from "./KeyframeNode.js";
  9. import MetadataType from "./MetadataType.js";
  10. import Megatexture from "./Megatexture.js";
  11. import PixelFormat from "../Core/PixelFormat.js";
  12. import PixelDatatype from "../Renderer/PixelDatatype.js";
  13. import Sampler from "../Renderer/Sampler.js";
  14. import SpatialNode from "./SpatialNode.js";
  15. import Texture from "../Renderer/Texture.js";
  16. import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
  17. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  18. /**
  19. * Handles tileset traversal, tile requests, and GPU resources. Intended to be
  20. * private and paired with a {@link VoxelPrimitive}, which has a user-facing API.
  21. *
  22. * @alias VoxelTraversal
  23. * @constructor
  24. *
  25. * @param {VoxelPrimitive} primitive
  26. * @param {Context} context
  27. * @param {Cartesian3} dimensions
  28. * @param {MetadataType[]} types
  29. * @param {MetadataComponentType[]} componentTypes
  30. * @param {number} keyframeCount
  31. * @param {number} [maximumTextureMemoryByteLength]
  32. *
  33. * @private
  34. */
  35. function VoxelTraversal(
  36. primitive,
  37. context,
  38. dimensions,
  39. types,
  40. componentTypes,
  41. keyframeCount,
  42. maximumTextureMemoryByteLength
  43. ) {
  44. /**
  45. * TODO: maybe this shouldn't be stored or passed into update function?
  46. * @type {VoxelPrimitive}
  47. * @private
  48. */
  49. this._primitive = primitive;
  50. const length = types.length;
  51. /**
  52. * @type {Megatexture[]}
  53. * @readonly
  54. */
  55. this.megatextures = new Array(length);
  56. // TODO make sure to split the maximumTextureMemoryByteLength across all the megatextures
  57. for (let i = 0; i < length; i++) {
  58. const type = types[i];
  59. const componentCount = MetadataType.getComponentCount(type);
  60. const componentType = componentTypes[i];
  61. this.megatextures[i] = new Megatexture(
  62. context,
  63. dimensions,
  64. componentCount,
  65. componentType,
  66. maximumTextureMemoryByteLength
  67. );
  68. }
  69. const maximumTileCount = this.megatextures[0].maximumTileCount;
  70. /**
  71. * @type {number}
  72. * @private
  73. */
  74. this._simultaneousRequestCount = 0;
  75. /**
  76. * @type {boolean}
  77. * @private
  78. */
  79. this._debugPrint = false;
  80. /**
  81. * @type {number}
  82. * @private
  83. */
  84. this._frameNumber = 0;
  85. const shape = primitive._shape;
  86. /**
  87. * @type {SpatialNode}
  88. * @readonly
  89. */
  90. this.rootNode = new SpatialNode(0, 0, 0, 0, undefined, shape, dimensions);
  91. /**
  92. * @type {DoubleEndedPriorityQueue}
  93. * @private
  94. */
  95. this._priorityQueue = new DoubleEndedPriorityQueue({
  96. maximumLength: maximumTileCount,
  97. comparator: KeyframeNode.priorityComparator,
  98. });
  99. /**
  100. * @type {KeyframeNode[]}
  101. * @private
  102. */
  103. this._highPriorityKeyframeNodes = new Array(maximumTileCount);
  104. /**
  105. * @type {KeyframeNode[]}
  106. * @private
  107. */
  108. this._keyframeNodesInMegatexture = new Array(maximumTileCount);
  109. /**
  110. * @type {number}
  111. * @private
  112. */
  113. this._keyframeCount = keyframeCount;
  114. /**
  115. * @type {number}
  116. * @private
  117. */
  118. this._sampleCount = undefined;
  119. /**
  120. * @type {number}
  121. * @private
  122. */
  123. this._keyframeLocation = 0;
  124. /**
  125. * @type {number[]}
  126. * @private
  127. */
  128. this._binaryTreeKeyframeWeighting = new Array(keyframeCount);
  129. const binaryTreeKeyframeWeighting = this._binaryTreeKeyframeWeighting;
  130. binaryTreeKeyframeWeighting[0] = 0;
  131. binaryTreeKeyframeWeighting[keyframeCount - 1] = 0;
  132. binaryTreeWeightingRecursive(
  133. binaryTreeKeyframeWeighting,
  134. 1,
  135. keyframeCount - 2,
  136. 0
  137. );
  138. const internalNodeTexelCount = 9;
  139. const internalNodeTextureDimensionX = 2048;
  140. const internalNodeTilesPerRow = Math.floor(
  141. internalNodeTextureDimensionX / internalNodeTexelCount
  142. );
  143. const internalNodeTextureDimensionY = Math.ceil(
  144. maximumTileCount / internalNodeTilesPerRow
  145. );
  146. /**
  147. * @type {Texture}
  148. * @readonly
  149. */
  150. this.internalNodeTexture = new Texture({
  151. context: context,
  152. pixelFormat: PixelFormat.RGBA,
  153. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  154. flipY: false,
  155. width: internalNodeTextureDimensionX,
  156. height: internalNodeTextureDimensionY,
  157. sampler: new Sampler({
  158. minificationFilter: TextureMinificationFilter.NEAREST,
  159. magnificationFilter: TextureMagnificationFilter.NEAREST,
  160. }),
  161. });
  162. /**
  163. * @type {number}
  164. * @readonly
  165. */
  166. this.internalNodeTilesPerRow = internalNodeTilesPerRow;
  167. /**
  168. * @type {Cartesian2}
  169. * @readonly
  170. */
  171. this.internalNodeTexelSizeUv = new Cartesian2(
  172. 1.0 / internalNodeTextureDimensionX,
  173. 1.0 / internalNodeTextureDimensionY
  174. );
  175. /**
  176. * Only generated when there are two or more samples.
  177. * @type {Texture}
  178. * @readonly
  179. */
  180. this.leafNodeTexture = undefined;
  181. /**
  182. * Only generated when there are two or more samples.
  183. * @type {number}
  184. * @readonly
  185. */
  186. this.leafNodeTilesPerRow = undefined;
  187. /**
  188. * Only generated when there are two or more samples.
  189. * @type {Cartesian2}
  190. * @readonly
  191. */
  192. this.leafNodeTexelSizeUv = new Cartesian2();
  193. }
  194. function binaryTreeWeightingRecursive(arr, start, end, depth) {
  195. if (start > end) {
  196. return;
  197. }
  198. const mid = Math.floor((start + end) / 2);
  199. arr[mid] = depth;
  200. binaryTreeWeightingRecursive(arr, start, mid - 1, depth + 1);
  201. binaryTreeWeightingRecursive(arr, mid + 1, end, depth + 1);
  202. }
  203. VoxelTraversal.simultaneousRequestCountMaximum = 50;
  204. /**
  205. * @param {FrameState} frameState
  206. * @param {number} keyframeLocation
  207. * @param {boolean} recomputeBoundingVolumes
  208. * @param {boolean} pauseUpdate
  209. */
  210. VoxelTraversal.prototype.update = function (
  211. frameState,
  212. keyframeLocation,
  213. recomputeBoundingVolumes,
  214. pauseUpdate
  215. ) {
  216. const primitive = this._primitive;
  217. const context = frameState.context;
  218. const maximumTileCount = this.megatextures[0].maximumTileCount;
  219. const keyframeCount = this._keyframeCount;
  220. const levelBlendFactor = primitive._levelBlendFactor;
  221. const hasLevelBlendFactor = levelBlendFactor > 0.0;
  222. const hasKeyframes = keyframeCount > 1;
  223. const sampleCount = (hasLevelBlendFactor ? 2 : 1) * (hasKeyframes ? 2 : 1);
  224. this._sampleCount = sampleCount;
  225. const useLeafNodes = sampleCount >= 2;
  226. if (useLeafNodes && !defined(this.leafNodeTexture)) {
  227. const leafNodeTexelCount = 2;
  228. const leafNodeTextureDimensionX = 1024;
  229. const leafNodeTilesPerRow = Math.floor(
  230. leafNodeTextureDimensionX / leafNodeTexelCount
  231. );
  232. const leafNodeTextureDimensionY = Math.ceil(
  233. maximumTileCount / leafNodeTilesPerRow
  234. );
  235. this.leafNodeTexture = new Texture({
  236. context: context,
  237. pixelFormat: PixelFormat.RGBA,
  238. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  239. flipY: false,
  240. width: leafNodeTextureDimensionX,
  241. height: leafNodeTextureDimensionY,
  242. sampler: new Sampler({
  243. minificationFilter: TextureMinificationFilter.NEAREST,
  244. magnificationFilter: TextureMagnificationFilter.NEAREST,
  245. }),
  246. });
  247. this.leafNodeTexelSizeUv = Cartesian2.fromElements(
  248. 1.0 / leafNodeTextureDimensionX,
  249. 1.0 / leafNodeTextureDimensionY,
  250. this.leafNodeTexelSizeUv
  251. );
  252. this.leafNodeTilesPerRow = leafNodeTilesPerRow;
  253. } else if (!useLeafNodes && defined(this.leafNodeTexture)) {
  254. this.leafNodeTexture = this.leafNodeTexture.destroy();
  255. }
  256. this._keyframeLocation = CesiumMath.clamp(
  257. keyframeLocation,
  258. 0.0,
  259. keyframeCount - 1
  260. );
  261. if (recomputeBoundingVolumes) {
  262. recomputeBoundingVolumesRecursive(this, this.rootNode);
  263. }
  264. if (pauseUpdate) {
  265. return;
  266. }
  267. this._frameNumber = frameState.frameNumber;
  268. const timestamp0 = getTimestamp();
  269. loadAndUnload(this, frameState);
  270. const timestamp1 = getTimestamp();
  271. generateOctree(this, sampleCount, levelBlendFactor);
  272. const timestamp2 = getTimestamp();
  273. if (this._debugPrint) {
  274. const loadAndUnloadTimeMs = timestamp1 - timestamp0;
  275. const generateOctreeTimeMs = timestamp2 - timestamp1;
  276. const totalTimeMs = timestamp2 - timestamp0;
  277. printDebugInformation(
  278. this,
  279. loadAndUnloadTimeMs,
  280. generateOctreeTimeMs,
  281. totalTimeMs
  282. );
  283. }
  284. };
  285. /**
  286. * Check if a node is renderable.
  287. * @param {SpatialNode} tile
  288. * @returns {boolean}
  289. */
  290. VoxelTraversal.prototype.isRenderable = function (tile) {
  291. return tile.isRenderable(this._frameNumber);
  292. };
  293. /**
  294. * Returns true if this object was destroyed; otherwise, false.
  295. * <br /><br />
  296. * If this object was destroyed, it should not be used; calling any function other than
  297. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  298. *
  299. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  300. *
  301. * @see VoxelTraversal#destroy
  302. */
  303. VoxelTraversal.prototype.isDestroyed = function () {
  304. return false;
  305. };
  306. /**
  307. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  308. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  309. * <br /><br />
  310. * Once an object is destroyed, it should not be used; calling any function other than
  311. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  312. * assign the return value (<code>undefined</code>) to the object as done in the example.
  313. *
  314. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  315. *
  316. * @see VoxelTraversal#isDestroyed
  317. *
  318. * @example
  319. * voxelTraversal = voxelTraversal && voxelTraversal.destroy();
  320. */
  321. VoxelTraversal.prototype.destroy = function () {
  322. const megatextures = this.megatextures;
  323. const megatextureLength = megatextures.length;
  324. for (let i = 0; i < megatextureLength; i++) {
  325. megatextures[i] = megatextures[i] && megatextures[i].destroy();
  326. }
  327. this.internalNodeTexture =
  328. this.internalNodeTexture && this.internalNodeTexture.destroy();
  329. this.leafNodeTexture = this.leafNodeTexture && this.leafNodeTexture.destroy();
  330. return destroyObject(this);
  331. };
  332. /**
  333. * @function
  334. *
  335. * @param {VoxelTraversal} that
  336. * @param {SpatialNode} node
  337. *
  338. * @private
  339. */
  340. function recomputeBoundingVolumesRecursive(that, node) {
  341. const primitive = that._primitive;
  342. const shape = primitive._shape;
  343. const dimensions = primitive._provider.dimensions;
  344. node.computeBoundingVolumes(shape, dimensions);
  345. if (defined(node.children)) {
  346. for (let i = 0; i < 8; i++) {
  347. const child = node.children[i];
  348. recomputeBoundingVolumesRecursive(that, child);
  349. }
  350. }
  351. }
  352. /**
  353. * @function
  354. *
  355. * @param {VoxelTraversal} that
  356. * @param {KeyframeNode} keyframeNode
  357. *
  358. * @private
  359. */
  360. function requestData(that, keyframeNode) {
  361. if (
  362. that._simultaneousRequestCount >=
  363. VoxelTraversal.simultaneousRequestCountMaximum
  364. ) {
  365. return;
  366. }
  367. const primitive = that._primitive;
  368. const provider = primitive._provider;
  369. function postRequestSuccess(result) {
  370. that._simultaneousRequestCount--;
  371. const length = primitive._provider.types.length;
  372. if (!defined(result)) {
  373. keyframeNode.state = KeyframeNode.LoadState.UNAVAILABLE;
  374. } else if (result === KeyframeNode.LoadState.FAILED) {
  375. keyframeNode.state = KeyframeNode.LoadState.FAILED;
  376. } else if (!Array.isArray(result) || result.length !== length) {
  377. // TODO should this throw runtime error?
  378. keyframeNode.state = KeyframeNode.LoadState.FAILED;
  379. } else {
  380. const megatextures = that.megatextures;
  381. for (let i = 0; i < length; i++) {
  382. const { voxelCountPerTile, channelCount } = megatextures[i];
  383. const { x, y, z } = voxelCountPerTile;
  384. const tileVoxelCount = x * y * z;
  385. const data = result[i];
  386. const expectedLength = tileVoxelCount * channelCount;
  387. if (data.length === expectedLength) {
  388. keyframeNode.metadatas[i] = data;
  389. // State is received only when all metadata requests have been received
  390. keyframeNode.state = KeyframeNode.LoadState.RECEIVED;
  391. } else {
  392. keyframeNode.state = KeyframeNode.LoadState.FAILED;
  393. break;
  394. }
  395. }
  396. }
  397. }
  398. function postRequestFailure() {
  399. that._simultaneousRequestCount--;
  400. keyframeNode.state = KeyframeNode.LoadState.FAILED;
  401. }
  402. const { keyframe, spatialNode } = keyframeNode;
  403. const promise = provider.requestData({
  404. tileLevel: spatialNode.level,
  405. tileX: spatialNode.x,
  406. tileY: spatialNode.y,
  407. tileZ: spatialNode.z,
  408. keyframe: keyframe,
  409. });
  410. if (defined(promise)) {
  411. that._simultaneousRequestCount++;
  412. keyframeNode.state = KeyframeNode.LoadState.RECEIVING;
  413. promise.then(postRequestSuccess).catch(postRequestFailure);
  414. } else {
  415. keyframeNode.state = KeyframeNode.LoadState.FAILED;
  416. }
  417. }
  418. /**
  419. * @function
  420. *
  421. * @param {number} x
  422. * @returns {number}
  423. *
  424. * @private
  425. */
  426. function mapInfiniteRangeToZeroOne(x) {
  427. return x / (1.0 + x);
  428. }
  429. /**
  430. * @function
  431. *
  432. * @param {VoxelTraversal} that
  433. * @param {FrameState} frameState
  434. *
  435. * @private
  436. */
  437. function loadAndUnload(that, frameState) {
  438. const frameNumber = that._frameNumber;
  439. const primitive = that._primitive;
  440. const shape = primitive._shape;
  441. const { dimensions } = primitive;
  442. const targetScreenSpaceError = primitive.screenSpaceError;
  443. const priorityQueue = that._priorityQueue;
  444. const keyframeLocation = that._keyframeLocation;
  445. const keyframeCount = that._keyframeCount;
  446. const rootNode = that.rootNode;
  447. const { camera, context, pixelRatio } = frameState;
  448. const { positionWC, frustum } = camera;
  449. const screenHeight = context.drawingBufferHeight / pixelRatio;
  450. const screenSpaceErrorMultiplier = screenHeight / frustum.sseDenominator;
  451. function keyframePriority(previousKeyframe, keyframe, nextKeyframe) {
  452. const keyframeDifference = Math.min(
  453. Math.abs(keyframe - previousKeyframe),
  454. Math.abs(keyframe - nextKeyframe)
  455. );
  456. const maxKeyframeDifference = Math.max(
  457. previousKeyframe,
  458. keyframeCount - nextKeyframe - 1,
  459. 1
  460. );
  461. const keyframeFactor = Math.pow(
  462. 1.0 - keyframeDifference / maxKeyframeDifference,
  463. 4.0
  464. );
  465. const binaryTreeFactor = Math.exp(
  466. -that._binaryTreeKeyframeWeighting[keyframe]
  467. );
  468. return CesiumMath.lerp(
  469. binaryTreeFactor,
  470. keyframeFactor,
  471. 0.15 + 0.85 * keyframeFactor
  472. );
  473. }
  474. /**
  475. * @ignore
  476. * @param {SpatialNode} spatialNode
  477. * @param {number} visibilityPlaneMask
  478. */
  479. function addToQueueRecursive(spatialNode, visibilityPlaneMask) {
  480. spatialNode.computeScreenSpaceError(positionWC, screenSpaceErrorMultiplier);
  481. visibilityPlaneMask = spatialNode.visibility(
  482. frameState,
  483. visibilityPlaneMask
  484. );
  485. if (visibilityPlaneMask === CullingVolume.MASK_OUTSIDE) {
  486. return;
  487. }
  488. spatialNode.visitedFrameNumber = frameNumber;
  489. const previousKeyframe = CesiumMath.clamp(
  490. Math.floor(keyframeLocation),
  491. 0,
  492. keyframeCount - 2
  493. );
  494. const nextKeyframe = previousKeyframe + 1;
  495. // Create keyframe nodes at the playhead.
  496. // If they already exist, nothing will be created.
  497. if (keyframeCount === 1) {
  498. spatialNode.createKeyframeNode(0);
  499. } else if (spatialNode.keyframeNodes.length !== keyframeCount) {
  500. for (let k = 0; k < keyframeCount; k++) {
  501. spatialNode.createKeyframeNode(k);
  502. }
  503. }
  504. const ssePriority = mapInfiniteRangeToZeroOne(spatialNode.screenSpaceError);
  505. let hasLoadedKeyframe = false;
  506. const keyframeNodes = spatialNode.keyframeNodes;
  507. for (let i = 0; i < keyframeNodes.length; i++) {
  508. const keyframeNode = keyframeNodes[i];
  509. keyframeNode.priority =
  510. 10.0 * ssePriority +
  511. keyframePriority(previousKeyframe, keyframeNode.keyframe, nextKeyframe);
  512. if (
  513. keyframeNode.state !== KeyframeNode.LoadState.UNAVAILABLE &&
  514. keyframeNode.state !== KeyframeNode.LoadState.FAILED &&
  515. keyframeNode.priority !== -Number.MAX_VALUE
  516. ) {
  517. priorityQueue.insert(keyframeNode);
  518. }
  519. if (keyframeNode.state === KeyframeNode.LoadState.LOADED) {
  520. hasLoadedKeyframe = true;
  521. }
  522. }
  523. const meetsScreenSpaceError =
  524. spatialNode.screenSpaceError < targetScreenSpaceError;
  525. if (meetsScreenSpaceError || !hasLoadedKeyframe) {
  526. // Free up memory
  527. spatialNode.children = undefined;
  528. return;
  529. }
  530. if (!defined(spatialNode.children)) {
  531. spatialNode.constructChildNodes(shape, dimensions);
  532. }
  533. for (let childIndex = 0; childIndex < 8; childIndex++) {
  534. const child = spatialNode.children[childIndex];
  535. addToQueueRecursive(child, visibilityPlaneMask);
  536. }
  537. }
  538. priorityQueue.reset();
  539. addToQueueRecursive(rootNode, CullingVolume.MASK_INDETERMINATE);
  540. const highPriorityKeyframeNodes = that._highPriorityKeyframeNodes;
  541. let highPriorityKeyframeNodeCount = 0;
  542. let highPriorityKeyframeNode;
  543. while (priorityQueue.length > 0) {
  544. highPriorityKeyframeNode = priorityQueue.removeMaximum();
  545. highPriorityKeyframeNode.highPriorityFrameNumber = frameNumber;
  546. highPriorityKeyframeNodes[
  547. highPriorityKeyframeNodeCount
  548. ] = highPriorityKeyframeNode;
  549. highPriorityKeyframeNodeCount++;
  550. }
  551. const keyframeNodesInMegatexture = that._keyframeNodesInMegatexture;
  552. // TODO: some of the megatexture state should be stored once, not duplicate for each megatexture
  553. const megatexture = that.megatextures[0];
  554. const keyframeNodesInMegatextureCount = megatexture.occupiedCount;
  555. keyframeNodesInMegatexture.length = keyframeNodesInMegatextureCount;
  556. keyframeNodesInMegatexture.sort(function (a, b) {
  557. if (a.highPriorityFrameNumber === b.highPriorityFrameNumber) {
  558. return b.priority - a.priority;
  559. }
  560. return b.highPriorityFrameNumber - a.highPriorityFrameNumber;
  561. });
  562. let destroyedCount = 0;
  563. let addedCount = 0;
  564. for (
  565. let highPriorityKeyframeNodeIndex = 0;
  566. highPriorityKeyframeNodeIndex < highPriorityKeyframeNodeCount;
  567. highPriorityKeyframeNodeIndex++
  568. ) {
  569. highPriorityKeyframeNode =
  570. highPriorityKeyframeNodes[highPriorityKeyframeNodeIndex];
  571. if (
  572. highPriorityKeyframeNode.state === KeyframeNode.LoadState.LOADED ||
  573. highPriorityKeyframeNode.spatialNode === undefined
  574. ) {
  575. // Already loaded, so nothing to do.
  576. // Or destroyed when adding a higher priority node
  577. continue;
  578. }
  579. if (highPriorityKeyframeNode.state === KeyframeNode.LoadState.UNLOADED) {
  580. requestData(that, highPriorityKeyframeNode);
  581. }
  582. if (highPriorityKeyframeNode.state === KeyframeNode.LoadState.RECEIVED) {
  583. let addNodeIndex = 0;
  584. if (megatexture.isFull()) {
  585. // If the megatexture is full, try removing a discardable node with the lowest priority.
  586. addNodeIndex = keyframeNodesInMegatextureCount - 1 - destroyedCount;
  587. destroyedCount++;
  588. const discardNode = keyframeNodesInMegatexture[addNodeIndex];
  589. discardNode.spatialNode.destroyKeyframeNode(
  590. discardNode,
  591. that.megatextures
  592. );
  593. } else {
  594. addNodeIndex = keyframeNodesInMegatextureCount + addedCount;
  595. addedCount++;
  596. }
  597. highPriorityKeyframeNode.spatialNode.addKeyframeNodeToMegatextures(
  598. highPriorityKeyframeNode,
  599. that.megatextures
  600. );
  601. keyframeNodesInMegatexture[addNodeIndex] = highPriorityKeyframeNode;
  602. }
  603. }
  604. }
  605. /**
  606. * @function
  607. *
  608. * @param {VoxelTraversal} that
  609. *
  610. * @private
  611. */
  612. function printDebugInformation(
  613. that,
  614. loadAndUnloadTimeMs,
  615. generateOctreeTimeMs,
  616. totalTimeMs
  617. ) {
  618. const keyframeCount = that._keyframeCount;
  619. const rootNode = that.rootNode;
  620. const loadStateCount = Object.keys(KeyframeNode.LoadState).length;
  621. const loadStatesByKeyframe = new Array(loadStateCount);
  622. const loadStateByCount = new Array(loadStateCount);
  623. let nodeCountTotal = 0;
  624. for (
  625. let loadStateIndex = 0;
  626. loadStateIndex < loadStateCount;
  627. loadStateIndex++
  628. ) {
  629. const keyframeArray = new Array(keyframeCount);
  630. loadStatesByKeyframe[loadStateIndex] = keyframeArray;
  631. for (let i = 0; i < keyframeCount; i++) {
  632. keyframeArray[i] = 0;
  633. }
  634. loadStateByCount[loadStateIndex] = 0;
  635. }
  636. /**
  637. * @ignore
  638. * @param {SpatialNode} node
  639. */
  640. function traverseRecursive(node) {
  641. const keyframeNodes = node.keyframeNodes;
  642. for (
  643. let keyframeIndex = 0;
  644. keyframeIndex < keyframeNodes.length;
  645. keyframeIndex++
  646. ) {
  647. const keyframeNode = keyframeNodes[keyframeIndex];
  648. const keyframe = keyframeNode.keyframe;
  649. const state = keyframeNode.state;
  650. loadStatesByKeyframe[state][keyframe] += 1;
  651. loadStateByCount[state] += 1;
  652. nodeCountTotal++;
  653. }
  654. if (defined(node.children)) {
  655. for (let childIndex = 0; childIndex < 8; childIndex++) {
  656. const child = node.children[childIndex];
  657. traverseRecursive(child);
  658. }
  659. }
  660. }
  661. traverseRecursive(rootNode);
  662. const loadedKeyframeStatistics = `KEYFRAMES: ${
  663. loadStatesByKeyframe[KeyframeNode.LoadState.LOADED]
  664. }`;
  665. const loadStateStatistics =
  666. `UNLOADED: ${loadStateByCount[KeyframeNode.LoadState.UNLOADED]} | ` +
  667. `RECEIVING: ${loadStateByCount[KeyframeNode.LoadState.RECEIVING]} | ` +
  668. `RECEIVED: ${loadStateByCount[KeyframeNode.LoadState.RECEIVED]} | ` +
  669. `LOADED: ${loadStateByCount[KeyframeNode.LoadState.LOADED]} | ` +
  670. `FAILED: ${loadStateByCount[KeyframeNode.LoadState.FAILED]} | ` +
  671. `UNAVAILABLE: ${loadStateByCount[KeyframeNode.LoadState.UNAVAILABLE]} | ` +
  672. `TOTAL: ${nodeCountTotal}`;
  673. const loadAndUnloadTimeMsRounded =
  674. Math.round(loadAndUnloadTimeMs * 100) / 100;
  675. const generateOctreeTimeMsRounded =
  676. Math.round(generateOctreeTimeMs * 100) / 100;
  677. const totalTimeMsRounded = Math.round(totalTimeMs * 100) / 100;
  678. const timerStatistics =
  679. `LOAD: ${loadAndUnloadTimeMsRounded} | ` +
  680. `OCT: ${generateOctreeTimeMsRounded} | ` +
  681. `ALL: ${totalTimeMsRounded}`;
  682. console.log(
  683. `${loadedKeyframeStatistics} || ${loadStateStatistics} || ${timerStatistics}`
  684. );
  685. }
  686. // GPU Octree Layout
  687. // (shown as binary tree instead of octree for demonstration purposes)
  688. //
  689. // Tree representation:
  690. // 0
  691. // / \
  692. // / \
  693. // / \
  694. // 1 3
  695. // / \ / \
  696. // L0 2 L3 L4
  697. // / \
  698. // L1 L2
  699. //
  700. //
  701. // Array representation:
  702. // L = leaf index
  703. // * = index to parent node
  704. // index: 0_______ 1________ 2________ 3_________
  705. // array: [*0, 1, 3, *0, L0, 2, *1 L1, L2, *0, L3, L4]
  706. //
  707. // The array is generated from a depth-first traversal. The end result could be an unbalanced tree,
  708. // so the parent index is stored at each node to make it possible to traverse upwards.
  709. const GpuOctreeFlag = {
  710. // Data is an octree index.
  711. INTERNAL: 0,
  712. // Data is a leaf node.
  713. LEAF: 1,
  714. // When leaf data is packed in the octree and there's a node that is forced to
  715. // render but has no data of its own (such as when its siblings are renderable but it
  716. // is not), signal that it's using its parent's data.
  717. PACKED_LEAF_FROM_PARENT: 2,
  718. };
  719. /**
  720. * @function
  721. *
  722. * @param {VoxelTraversal} that
  723. * @param {FrameState} frameState
  724. * @param {number} sampleCount
  725. * @param {number} levelBlendFactor
  726. * @private
  727. */
  728. function generateOctree(that, sampleCount, levelBlendFactor) {
  729. const targetSse = that._primitive._screenSpaceError;
  730. const keyframeLocation = that._keyframeLocation;
  731. const frameNumber = that._frameNumber;
  732. const useLeafNodes = sampleCount >= 2;
  733. let internalNodeCount = 0;
  734. let leafNodeCount = 0;
  735. const internalNodeOctreeData = [];
  736. const leafNodeOctreeData = [];
  737. /**
  738. * @ignore
  739. * @param {SpatialNode} node
  740. * @param {number} childOctreeIndex
  741. * @param {number} childEntryIndex
  742. * @param {number} parentOctreeIndex
  743. * @param {number} parentEntryIndex
  744. */
  745. function buildOctree(
  746. node,
  747. childOctreeIndex,
  748. childEntryIndex,
  749. parentOctreeIndex,
  750. parentEntryIndex
  751. ) {
  752. let hasRenderableChildren = false;
  753. if (defined(node.children)) {
  754. for (let c = 0; c < 8; c++) {
  755. const childNode = node.children[c];
  756. childNode.computeSurroundingRenderableKeyframeNodes(keyframeLocation);
  757. if (childNode.isRenderable(frameNumber)) {
  758. hasRenderableChildren = true;
  759. }
  760. }
  761. }
  762. if (hasRenderableChildren) {
  763. // Point the parent and child octree indexes at each other
  764. internalNodeOctreeData[parentEntryIndex] =
  765. (GpuOctreeFlag.INTERNAL << 16) | childOctreeIndex;
  766. internalNodeOctreeData[childEntryIndex] = parentOctreeIndex;
  767. internalNodeCount++;
  768. // Recurse over children
  769. parentOctreeIndex = childOctreeIndex;
  770. parentEntryIndex = parentOctreeIndex * 9 + 1;
  771. for (let cc = 0; cc < 8; cc++) {
  772. const child = node.children[cc];
  773. childOctreeIndex = internalNodeCount;
  774. childEntryIndex = childOctreeIndex * 9 + 0;
  775. buildOctree(
  776. child,
  777. childOctreeIndex,
  778. childEntryIndex,
  779. parentOctreeIndex,
  780. parentEntryIndex + cc
  781. );
  782. }
  783. } else {
  784. // Store the leaf node information instead
  785. // Recursion stops here because there are no renderable children
  786. if (useLeafNodes) {
  787. const baseIdx = leafNodeCount * 5;
  788. const keyframeNode = node.renderableKeyframeNodePrevious;
  789. const levelDifference = node.level - keyframeNode.spatialNode.level;
  790. const parentNode = keyframeNode.spatialNode.parent;
  791. const parentKeyframeNode = defined(parentNode)
  792. ? parentNode.renderableKeyframeNodePrevious
  793. : keyframeNode;
  794. const lodLerp = getLodLerp(node, targetSse, levelBlendFactor);
  795. const levelDifferenceChild = levelDifference;
  796. const levelDifferenceParent = 1;
  797. const megatextureIndexChild = keyframeNode.megatextureIndex;
  798. const megatextureIndexParent = parentKeyframeNode.megatextureIndex;
  799. leafNodeOctreeData[baseIdx + 0] = lodLerp;
  800. leafNodeOctreeData[baseIdx + 1] = levelDifferenceChild;
  801. leafNodeOctreeData[baseIdx + 2] = levelDifferenceParent;
  802. leafNodeOctreeData[baseIdx + 3] = megatextureIndexChild;
  803. leafNodeOctreeData[baseIdx + 4] = megatextureIndexParent;
  804. internalNodeOctreeData[parentEntryIndex] =
  805. (GpuOctreeFlag.LEAF << 16) | leafNodeCount;
  806. } else {
  807. const keyframeNode = node.renderableKeyframeNodePrevious;
  808. const levelDifference = node.level - keyframeNode.spatialNode.level;
  809. const flag =
  810. levelDifference === 0
  811. ? GpuOctreeFlag.LEAF
  812. : GpuOctreeFlag.PACKED_LEAF_FROM_PARENT;
  813. internalNodeOctreeData[parentEntryIndex] =
  814. (flag << 16) | keyframeNode.megatextureIndex;
  815. }
  816. leafNodeCount++;
  817. }
  818. }
  819. const rootNode = that.rootNode;
  820. rootNode.computeSurroundingRenderableKeyframeNodes(keyframeLocation);
  821. if (rootNode.isRenderable(frameNumber)) {
  822. buildOctree(rootNode, 0, 0, 0, 0);
  823. }
  824. copyToInternalNodeTexture(
  825. internalNodeOctreeData,
  826. 9,
  827. that.internalNodeTilesPerRow,
  828. that.internalNodeTexture
  829. );
  830. if (useLeafNodes) {
  831. copyToLeafNodeTexture(
  832. leafNodeOctreeData,
  833. 2,
  834. that.leafNodeTilesPerRow,
  835. that.leafNodeTexture
  836. );
  837. }
  838. }
  839. /**
  840. * Compute an interpolation factor between a node and its parent
  841. * @param {SpatialNode} node
  842. * @param {number} targetSse
  843. * @param {number} levelBlendFactor
  844. * @returns {number}
  845. * @private
  846. */
  847. function getLodLerp(node, targetSse, levelBlendFactor) {
  848. if (node.parent === undefined) {
  849. return 0.0;
  850. }
  851. const sse = node.screenSpaceError;
  852. const parentSse = node.parent.screenSpaceError;
  853. const lodLerp = (targetSse - sse) / (parentSse - sse);
  854. const blended = (lodLerp + levelBlendFactor - 1.0) / levelBlendFactor;
  855. return CesiumMath.clamp(blended, 0.0, 1.0);
  856. }
  857. /**
  858. *
  859. * @param {number[]} data
  860. * @param {number} texelsPerTile
  861. * @param {number} tilesPerRow
  862. * @param {Texture} texture
  863. * @private
  864. */
  865. function copyToInternalNodeTexture(data, texelsPerTile, tilesPerRow, texture) {
  866. const channelCount = PixelFormat.componentsLength(texture.pixelFormat);
  867. const tileCount = Math.ceil(data.length / texelsPerTile);
  868. const copyWidth = Math.max(
  869. 1,
  870. texelsPerTile * Math.min(tileCount, tilesPerRow)
  871. );
  872. const copyHeight = Math.max(1, Math.ceil(tileCount / tilesPerRow));
  873. const textureData = new Uint8Array(copyWidth * copyHeight * channelCount);
  874. for (let i = 0; i < data.length; i++) {
  875. const val = data[i];
  876. const startIndex = i * channelCount;
  877. for (let j = 0; j < channelCount; j++) {
  878. textureData[startIndex + j] = (val >>> (j * 8)) & 0xff;
  879. }
  880. }
  881. const source = {
  882. arrayBufferView: textureData,
  883. width: copyWidth,
  884. height: copyHeight,
  885. };
  886. const copyOptions = {
  887. source: source,
  888. xOffset: 0,
  889. yOffset: 0,
  890. };
  891. texture.copyFrom(copyOptions);
  892. }
  893. /**
  894. *
  895. * @param {number[]} data
  896. * @param {number} texelsPerTile
  897. * @param {number} tilesPerRow
  898. * @param {Texture} texture
  899. * @private
  900. */
  901. function copyToLeafNodeTexture(data, texelsPerTile, tilesPerRow, texture) {
  902. const channelCount = PixelFormat.componentsLength(texture.pixelFormat);
  903. const datasPerTile = 5;
  904. const tileCount = Math.ceil(data.length / datasPerTile);
  905. const copyWidth = Math.max(
  906. 1,
  907. texelsPerTile * Math.min(tileCount, tilesPerRow)
  908. );
  909. const copyHeight = Math.max(1, Math.ceil(tileCount / tilesPerRow));
  910. const textureData = new Uint8Array(copyWidth * copyHeight * channelCount);
  911. for (let tileIndex = 0; tileIndex < tileCount; tileIndex++) {
  912. const timeLerp = data[tileIndex * datasPerTile + 0];
  913. const previousKeyframeLevelsAbove = data[tileIndex * datasPerTile + 1];
  914. const nextKeyframeLevelsAbove = data[tileIndex * datasPerTile + 2];
  915. const previousKeyframeMegatextureIndex = data[tileIndex * datasPerTile + 3];
  916. const nextKeyframeMegatextureIndex = data[tileIndex * datasPerTile + 4];
  917. const timeLerpCompressed = CesiumMath.clamp(
  918. Math.floor(65536 * timeLerp),
  919. 0,
  920. 65535
  921. );
  922. textureData[tileIndex * 8 + 0] = (timeLerpCompressed >>> 0) & 0xff;
  923. textureData[tileIndex * 8 + 1] = (timeLerpCompressed >>> 8) & 0xff;
  924. textureData[tileIndex * 8 + 2] = previousKeyframeLevelsAbove & 0xff;
  925. textureData[tileIndex * 8 + 3] = nextKeyframeLevelsAbove & 0xff;
  926. textureData[tileIndex * 8 + 4] =
  927. (previousKeyframeMegatextureIndex >>> 0) & 0xff;
  928. textureData[tileIndex * 8 + 5] =
  929. (previousKeyframeMegatextureIndex >>> 8) & 0xff;
  930. textureData[tileIndex * 8 + 6] =
  931. (nextKeyframeMegatextureIndex >>> 0) & 0xff;
  932. textureData[tileIndex * 8 + 7] =
  933. (nextKeyframeMegatextureIndex >>> 8) & 0xff;
  934. }
  935. const source = {
  936. arrayBufferView: textureData,
  937. width: copyWidth,
  938. height: copyHeight,
  939. };
  940. const copyOptions = {
  941. source: source,
  942. xOffset: 0,
  943. yOffset: 0,
  944. };
  945. texture.copyFrom(copyOptions);
  946. }
  947. /**
  948. * @param {number} tileCount
  949. * @param {Cartesian3} dimensions
  950. * @param {MetadataType[]} types
  951. * @param {MetadataComponentType[]} componentTypes
  952. */
  953. VoxelTraversal.getApproximateTextureMemoryByteLength = function (
  954. tileCount,
  955. dimensions,
  956. types,
  957. componentTypes
  958. ) {
  959. let textureMemoryByteLength = 0;
  960. const length = types.length;
  961. for (let i = 0; i < length; i++) {
  962. const type = types[i];
  963. const componentType = componentTypes[i];
  964. const componentCount = MetadataType.getComponentCount(type);
  965. textureMemoryByteLength += Megatexture.getApproximateTextureMemoryByteLength(
  966. tileCount,
  967. dimensions,
  968. componentCount,
  969. componentType
  970. );
  971. }
  972. return textureMemoryByteLength;
  973. };
  974. export default VoxelTraversal;