智慧水务管理系统 - 精河县供水工程综合管理平台

VoxelTraversal.js 35KB

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