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

QuadtreePrimitive.js 51KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import Cartographic from "../Core/Cartographic.js";
  3. import defined from "../Core/defined.js";
  4. import DeveloperError from "../Core/DeveloperError.js";
  5. import Event from "../Core/Event.js";
  6. import getTimestamp from "../Core/getTimestamp.js";
  7. import CesiumMath from "../Core/Math.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  10. import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";
  11. import Ray from "../Core/Ray.js";
  12. import Rectangle from "../Core/Rectangle.js";
  13. import Visibility from "../Core/Visibility.js";
  14. import QuadtreeOccluders from "./QuadtreeOccluders.js";
  15. import QuadtreeTile from "./QuadtreeTile.js";
  16. import QuadtreeTileLoadState from "./QuadtreeTileLoadState.js";
  17. import SceneMode from "./SceneMode.js";
  18. import TileReplacementQueue from "./TileReplacementQueue.js";
  19. import TileSelectionResult from "./TileSelectionResult.js";
  20. /**
  21. * Renders massive sets of data by utilizing level-of-detail and culling. The globe surface is divided into
  22. * a quadtree of tiles with large, low-detail tiles at the root and small, high-detail tiles at the leaves.
  23. * The set of tiles to render is selected by projecting an estimate of the geometric error in a tile onto
  24. * the screen to estimate screen-space error, in pixels, which must be below a user-specified threshold.
  25. * The actual content of the tiles is arbitrary and is specified using a {@link QuadtreeTileProvider}.
  26. *
  27. * @alias QuadtreePrimitive
  28. * @constructor
  29. * @private
  30. *
  31. * @param {QuadtreeTileProvider} options.tileProvider The tile provider that loads, renders, and estimates
  32. * the distance to individual tiles.
  33. * @param {number} [options.maximumScreenSpaceError=2] The maximum screen-space error, in pixels, that is allowed.
  34. * A higher maximum error will render fewer tiles and improve performance, while a lower
  35. * value will improve visual quality.
  36. * @param {number} [options.tileCacheSize=100] The maximum number of tiles that will be retained in the tile cache.
  37. * Note that tiles will never be unloaded if they were used for rendering the last
  38. * frame, so the actual number of resident tiles may be higher. The value of
  39. * this property will not affect visual quality.
  40. */
  41. function QuadtreePrimitive(options) {
  42. //>>includeStart('debug', pragmas.debug);
  43. if (!defined(options) || !defined(options.tileProvider)) {
  44. throw new DeveloperError("options.tileProvider is required.");
  45. }
  46. if (defined(options.tileProvider.quadtree)) {
  47. throw new DeveloperError(
  48. "A QuadtreeTileProvider can only be used with a single QuadtreePrimitive",
  49. );
  50. }
  51. //>>includeEnd('debug');
  52. this._tileProvider = options.tileProvider;
  53. this._tileProvider.quadtree = this;
  54. this._debug = {
  55. enableDebugOutput: false,
  56. maxDepth: 0,
  57. maxDepthVisited: 0,
  58. tilesVisited: 0,
  59. tilesCulled: 0,
  60. tilesRendered: 0,
  61. tilesWaitingForChildren: 0,
  62. lastMaxDepth: -1,
  63. lastMaxDepthVisited: -1,
  64. lastTilesVisited: -1,
  65. lastTilesCulled: -1,
  66. lastTilesRendered: -1,
  67. lastTilesWaitingForChildren: -1,
  68. suspendLodUpdate: false,
  69. };
  70. const tilingScheme = this._tileProvider.tilingScheme;
  71. const ellipsoid = tilingScheme.ellipsoid;
  72. this._tilesRenderedThisFrame = new Set(); // collect all tiles selected to render (useful when multiple render calls are made in a single frame (as in 2D mode))
  73. this._tilesToRender = [];
  74. this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement
  75. this._tileLoadQueueMedium = []; // medium priority tiles are being rendered
  76. this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads.
  77. this._tileReplacementQueue = new TileReplacementQueue();
  78. this._levelZeroTiles = undefined;
  79. this._loadQueueTimeSlice = 5.0;
  80. this._tilesInvalidated = false;
  81. this._addHeightCallbacks = [];
  82. this._removeHeightCallbacks = [];
  83. this._tileToUpdateHeights = [];
  84. this._updateHeightsTimeSlice = 2.0;
  85. // If a culled tile contains _cameraPositionCartographic or _cameraReferenceFrameOriginCartographic, it will be marked
  86. // TileSelectionResult.CULLED_BUT_NEEDED and added to the list of tiles to update heights,
  87. // even though it is not rendered.
  88. // These are updated each frame in `selectTilesForRendering`.
  89. this._cameraPositionCartographic = undefined;
  90. this._cameraReferenceFrameOriginCartographic = undefined;
  91. /**
  92. * Gets or sets the maximum screen-space error, in pixels, that is allowed.
  93. * A higher maximum error will render fewer tiles and improve performance, while a lower
  94. * value will improve visual quality.
  95. * @type {number}
  96. * @default 2
  97. */
  98. this.maximumScreenSpaceError = options.maximumScreenSpaceError ?? 2;
  99. /**
  100. * Gets or sets the maximum number of tiles that will be retained in the tile cache.
  101. * Note that tiles will never be unloaded if they were used for rendering the last
  102. * frame, so the actual number of resident tiles may be higher. The value of
  103. * this property will not affect visual quality.
  104. * @type {number}
  105. * @default 100
  106. */
  107. this.tileCacheSize = options.tileCacheSize ?? 100;
  108. /**
  109. * Gets or sets the number of loading descendant tiles that is considered "too many".
  110. * If a tile has too many loading descendants, that tile will be loaded and rendered before any of
  111. * its descendants are loaded and rendered. This means more feedback for the user that something
  112. * is happening at the cost of a longer overall load time. Setting this to 0 will cause each
  113. * tile level to be loaded successively, significantly increasing load time. Setting it to a large
  114. * number (e.g. 1000) will minimize the number of tiles that are loaded but tend to make
  115. * detail appear all at once after a long wait.
  116. * @type {number}
  117. * @default 20
  118. */
  119. this.loadingDescendantLimit = 20;
  120. /**
  121. * Gets or sets a value indicating whether the ancestors of rendered tiles should be preloaded.
  122. * Setting this to true optimizes the zoom-out experience and provides more detail in
  123. * newly-exposed areas when panning. The down side is that it requires loading more tiles.
  124. * @type {boolean}
  125. * @default true
  126. */
  127. this.preloadAncestors = true;
  128. /**
  129. * Gets or sets a value indicating whether the siblings of rendered tiles should be preloaded.
  130. * Setting this to true causes tiles with the same parent as a rendered tile to be loaded, even
  131. * if they are culled. Setting this to true may provide a better panning experience at the
  132. * cost of loading more tiles.
  133. * @type {boolean}
  134. * @default false
  135. */
  136. this.preloadSiblings = false;
  137. this._occluders = new QuadtreeOccluders({
  138. ellipsoid: ellipsoid,
  139. });
  140. this._tileLoadProgressEvent = new Event();
  141. this._lastTileLoadQueueLength = 0;
  142. this._lastSelectionFrameNumber = undefined;
  143. }
  144. Object.defineProperties(QuadtreePrimitive.prototype, {
  145. /**
  146. * Gets the provider of {@link QuadtreeTile} instances for this quadtree.
  147. * @type {QuadtreeTile}
  148. * @memberof QuadtreePrimitive.prototype
  149. */
  150. tileProvider: {
  151. get: function () {
  152. return this._tileProvider;
  153. },
  154. },
  155. /**
  156. * Gets an event that's raised when the length of the tile load queue has changed since the last render frame. When the load queue is empty,
  157. * all terrain and imagery for the current view have been loaded. The event passes the new length of the tile load queue.
  158. *
  159. * @memberof QuadtreePrimitive.prototype
  160. * @type {Event}
  161. */
  162. tileLoadProgressEvent: {
  163. get: function () {
  164. return this._tileLoadProgressEvent;
  165. },
  166. },
  167. occluders: {
  168. get: function () {
  169. return this._occluders;
  170. },
  171. },
  172. });
  173. /**
  174. * Invalidates and frees all the tiles in the quadtree. The tiles must be reloaded
  175. * before they can be displayed.
  176. *
  177. * @memberof QuadtreePrimitive
  178. */
  179. QuadtreePrimitive.prototype.invalidateAllTiles = function () {
  180. this._tilesInvalidated = true;
  181. };
  182. function invalidateAllTiles(primitive) {
  183. // Clear the replacement queue
  184. const replacementQueue = primitive._tileReplacementQueue;
  185. replacementQueue.head = undefined;
  186. replacementQueue.tail = undefined;
  187. replacementQueue.count = 0;
  188. clearTileLoadQueue(primitive);
  189. // Free and recreate the level zero tiles.
  190. const levelZeroTiles = primitive._levelZeroTiles;
  191. if (defined(levelZeroTiles)) {
  192. for (let i = 0; i < levelZeroTiles.length; ++i) {
  193. const tile = levelZeroTiles[i];
  194. const customData = tile.customData;
  195. for (const data of customData) {
  196. data.level = 0;
  197. primitive._addHeightCallbacks.push(data);
  198. }
  199. levelZeroTiles[i].freeResources();
  200. }
  201. }
  202. primitive._levelZeroTiles = undefined;
  203. primitive._tileProvider.cancelReprojections();
  204. }
  205. /**
  206. * Invokes a specified function for each {@link QuadtreeTile} that is partially
  207. * or completely loaded.
  208. *
  209. * @param {Function} tileFunction The function to invoke for each loaded tile. The
  210. * function is passed a reference to the tile as its only parameter.
  211. */
  212. QuadtreePrimitive.prototype.forEachLoadedTile = function (tileFunction) {
  213. let tile = this._tileReplacementQueue.head;
  214. while (defined(tile)) {
  215. if (tile.state !== QuadtreeTileLoadState.START) {
  216. tileFunction(tile);
  217. }
  218. tile = tile.replacementNext;
  219. }
  220. };
  221. /**
  222. * Invokes a specified function for each {@link QuadtreeTile} that was rendered
  223. * in the most recent frame.
  224. *
  225. * @param {Function} tileFunction The function to invoke for each rendered tile. The
  226. * function is passed a reference to the tile as its only parameter.
  227. */
  228. QuadtreePrimitive.prototype.forEachRenderedTile = function (tileFunction) {
  229. const tilesRendered = this._tilesRenderedThisFrame;
  230. for (const tile of tilesRendered) {
  231. tileFunction(tile);
  232. }
  233. };
  234. /**
  235. * Calls the callback when a new tile is rendered that contains the given cartographic. The only parameter
  236. * is the cartesian position on the tile.
  237. *
  238. * @param {Cartographic} cartographic The cartographic position.
  239. * @param {Function} callback The function to be called when a new tile is loaded containing the updated cartographic.
  240. * @returns {Function} The function to remove this callback from the quadtree.
  241. */
  242. QuadtreePrimitive.prototype.updateHeight = function (cartographic, callback) {
  243. const primitive = this;
  244. const object = {
  245. positionOnEllipsoidSurface: undefined,
  246. positionCartographic: cartographic,
  247. level: -1,
  248. callback: callback,
  249. };
  250. object.removeFunc = function () {
  251. const addedCallbacks = primitive._addHeightCallbacks;
  252. const length = addedCallbacks.length;
  253. for (let i = 0; i < length; ++i) {
  254. if (addedCallbacks[i] === object) {
  255. addedCallbacks.splice(i, 1);
  256. break;
  257. }
  258. }
  259. primitive._removeHeightCallbacks.push(object);
  260. if (object.callback) {
  261. object.callback = undefined;
  262. }
  263. };
  264. primitive._addHeightCallbacks.push(object);
  265. return object.removeFunc;
  266. };
  267. /**
  268. * Updates the tile provider imagery and continues to process the tile load queue.
  269. * @private
  270. */
  271. QuadtreePrimitive.prototype.update = function (frameState) {
  272. if (defined(this._tileProvider.update)) {
  273. this._tileProvider.update(frameState);
  274. }
  275. };
  276. function clearTileLoadQueue(primitive) {
  277. const debug = primitive._debug;
  278. debug.maxDepth = 0;
  279. debug.maxDepthVisited = 0;
  280. debug.tilesVisited = 0;
  281. debug.tilesCulled = 0;
  282. debug.tilesRendered = 0;
  283. debug.tilesWaitingForChildren = 0;
  284. primitive._tileLoadQueueHigh.length = 0;
  285. primitive._tileLoadQueueMedium.length = 0;
  286. primitive._tileLoadQueueLow.length = 0;
  287. }
  288. /**
  289. * Initializes values for a new render frame and prepare the tile load queue.
  290. * @private
  291. */
  292. QuadtreePrimitive.prototype.beginFrame = function (frameState) {
  293. const passes = frameState.passes;
  294. if (!passes.render) {
  295. return;
  296. }
  297. if (this._tilesInvalidated) {
  298. invalidateAllTiles(this);
  299. this._tilesInvalidated = false;
  300. }
  301. // Gets commands for any texture re-projections
  302. this._tileProvider.initialize(frameState);
  303. clearTileLoadQueue(this);
  304. if (this._debug.suspendLodUpdate) {
  305. return;
  306. }
  307. this._tileReplacementQueue.markStartOfRenderFrame();
  308. this._tilesRenderedThisFrame.clear();
  309. };
  310. /**
  311. * Selects new tiles to load based on the frame state and creates render commands.
  312. * @private
  313. */
  314. QuadtreePrimitive.prototype.render = function (frameState) {
  315. const passes = frameState.passes;
  316. const tileProvider = this._tileProvider;
  317. if (passes.render) {
  318. tileProvider.beginUpdate(frameState);
  319. selectTilesForRendering(this, frameState);
  320. createRenderCommandsForSelectedTiles(this, frameState);
  321. tileProvider.endUpdate(frameState);
  322. }
  323. if (passes.pick && this._tilesToRender.length > 0) {
  324. tileProvider.updateForPick(frameState);
  325. }
  326. };
  327. /**
  328. * Checks if the load queue length has changed since the last time we raised a queue change event - if so, raises
  329. * a new change event at the end of the render cycle.
  330. * @private
  331. */
  332. function updateTileLoadProgress(primitive, frameState) {
  333. const currentLoadQueueLength =
  334. primitive._tileLoadQueueHigh.length +
  335. primitive._tileLoadQueueMedium.length +
  336. primitive._tileLoadQueueLow.length;
  337. if (
  338. currentLoadQueueLength !== primitive._lastTileLoadQueueLength ||
  339. primitive._tilesInvalidated
  340. ) {
  341. const raiseEvent = Event.prototype.raiseEvent.bind(
  342. primitive._tileLoadProgressEvent,
  343. currentLoadQueueLength,
  344. );
  345. frameState.afterRender.push(() => {
  346. raiseEvent();
  347. return true;
  348. });
  349. primitive._lastTileLoadQueueLength = currentLoadQueueLength;
  350. }
  351. const debug = primitive._debug;
  352. if (debug.enableDebugOutput && !debug.suspendLodUpdate) {
  353. debug.maxDepth = primitive._tilesToRender.reduce(function (max, tile) {
  354. return Math.max(max, tile.level);
  355. }, -1);
  356. debug.tilesRendered = primitive._tilesToRender.length;
  357. if (
  358. debug.tilesVisited !== debug.lastTilesVisited ||
  359. debug.tilesRendered !== debug.lastTilesRendered ||
  360. debug.tilesCulled !== debug.lastTilesCulled ||
  361. debug.maxDepth !== debug.lastMaxDepth ||
  362. debug.tilesWaitingForChildren !== debug.lastTilesWaitingForChildren ||
  363. debug.maxDepthVisited !== debug.lastMaxDepthVisited
  364. ) {
  365. console.log(
  366. `Visited ${debug.tilesVisited}, Rendered: ${debug.tilesRendered}, Culled: ${debug.tilesCulled}, Max Depth Rendered: ${debug.maxDepth}, Max Depth Visited: ${debug.maxDepthVisited}, Waiting for children: ${debug.tilesWaitingForChildren}`,
  367. );
  368. debug.lastTilesVisited = debug.tilesVisited;
  369. debug.lastTilesRendered = debug.tilesRendered;
  370. debug.lastTilesCulled = debug.tilesCulled;
  371. debug.lastMaxDepth = debug.maxDepth;
  372. debug.lastTilesWaitingForChildren = debug.tilesWaitingForChildren;
  373. debug.lastMaxDepthVisited = debug.maxDepthVisited;
  374. }
  375. }
  376. }
  377. /**
  378. * Updates terrain heights.
  379. * @private
  380. */
  381. QuadtreePrimitive.prototype.endFrame = function (frameState) {
  382. const passes = frameState.passes;
  383. if (!passes.render || frameState.mode === SceneMode.MORPHING) {
  384. // Only process the load queue for a single pass.
  385. // Don't process the load queue or update heights during the morph flights.
  386. return;
  387. }
  388. // Load/create resources for terrain and imagery. Prepare texture re-projections for the next frame.
  389. processTileLoadQueue(this, frameState);
  390. updateHeights(this, frameState);
  391. updateTileLoadProgress(this, frameState);
  392. };
  393. /**
  394. * Returns true if this object was destroyed; otherwise, false.
  395. * <br /><br />
  396. * If this object was destroyed, it should not be used; calling any function other than
  397. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  398. *
  399. * @memberof QuadtreePrimitive
  400. *
  401. * @returns {boolean} True if this object was destroyed; otherwise, false.
  402. *
  403. * @see QuadtreePrimitive#destroy
  404. */
  405. QuadtreePrimitive.prototype.isDestroyed = function () {
  406. return false;
  407. };
  408. /**
  409. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  410. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  411. * <br /><br />
  412. * Once an object is destroyed, it should not be used; calling any function other than
  413. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  414. * assign the return value (<code>undefined</code>) to the object as done in the example.
  415. *
  416. * @memberof QuadtreePrimitive
  417. *
  418. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  419. *
  420. *
  421. * @example
  422. * primitive = primitive && primitive.destroy();
  423. *
  424. * @see QuadtreePrimitive#isDestroyed
  425. */
  426. QuadtreePrimitive.prototype.destroy = function () {
  427. this._tileProvider = this._tileProvider && this._tileProvider.destroy();
  428. };
  429. let comparisonPoint;
  430. const centerScratch = new Cartographic();
  431. function compareDistanceToPoint(a, b) {
  432. let center = Rectangle.center(a.rectangle, centerScratch);
  433. const alon = center.longitude - comparisonPoint.longitude;
  434. const alat = center.latitude - comparisonPoint.latitude;
  435. center = Rectangle.center(b.rectangle, centerScratch);
  436. const blon = center.longitude - comparisonPoint.longitude;
  437. const blat = center.latitude - comparisonPoint.latitude;
  438. return alon * alon + alat * alat - (blon * blon + blat * blat);
  439. }
  440. const cameraOriginScratch = new Cartesian3();
  441. let rootTraversalDetails = [];
  442. function selectTilesForRendering(primitive, frameState) {
  443. const debug = primitive._debug;
  444. if (debug.suspendLodUpdate) {
  445. return;
  446. }
  447. // Clear the render list.
  448. const tilesToRender = primitive._tilesToRender;
  449. tilesToRender.length = 0;
  450. // We can't render anything before the level zero tiles exist.
  451. const tileProvider = primitive._tileProvider;
  452. if (!defined(primitive._levelZeroTiles)) {
  453. const tilingScheme = tileProvider.tilingScheme;
  454. if (defined(tilingScheme)) {
  455. const tilingScheme = tileProvider.tilingScheme;
  456. primitive._levelZeroTiles =
  457. QuadtreeTile.createLevelZeroTiles(tilingScheme);
  458. const numberOfRootTiles = primitive._levelZeroTiles.length;
  459. if (rootTraversalDetails.length < numberOfRootTiles) {
  460. rootTraversalDetails = new Array(numberOfRootTiles);
  461. for (let i = 0; i < numberOfRootTiles; ++i) {
  462. if (rootTraversalDetails[i] === undefined) {
  463. rootTraversalDetails[i] = new TraversalDetails();
  464. }
  465. }
  466. }
  467. } else {
  468. return;
  469. }
  470. }
  471. primitive._occluders.ellipsoid.cameraPosition = frameState.camera.positionWC;
  472. const levelZeroTiles = primitive._levelZeroTiles;
  473. const occluders =
  474. levelZeroTiles.length > 1 ? primitive._occluders : undefined;
  475. // Sort the level zero tiles by the distance from the center to the camera.
  476. // The level zero tiles aren't necessarily a nice neat quad, so we can't use the
  477. // quadtree ordering we use elsewhere in the tree
  478. comparisonPoint = frameState.camera.positionCartographic;
  479. levelZeroTiles.sort(compareDistanceToPoint);
  480. const customDataAdded = primitive._addHeightCallbacks;
  481. const customDataRemoved = primitive._removeHeightCallbacks;
  482. customDataAdded.forEach((data) => {
  483. const tile = levelZeroTiles.find((tile) =>
  484. Rectangle.contains(tile.rectangle, data.positionCartographic),
  485. );
  486. if (tile) {
  487. tile._addedCustomData.push(data);
  488. }
  489. });
  490. customDataRemoved.forEach((data) => {
  491. const tile = levelZeroTiles.find((tile) =>
  492. Rectangle.contains(tile.rectangle, data.positionCartographic),
  493. );
  494. if (tile) {
  495. tile._removedCustomData.push(data);
  496. }
  497. });
  498. levelZeroTiles.forEach((tile) => tile.updateCustomData());
  499. customDataAdded.length = 0;
  500. customDataRemoved.length = 0;
  501. const camera = frameState.camera;
  502. primitive._cameraPositionCartographic = camera.positionCartographic;
  503. const cameraFrameOrigin = Matrix4.getTranslation(
  504. camera.transform,
  505. cameraOriginScratch,
  506. );
  507. primitive._cameraReferenceFrameOriginCartographic =
  508. primitive.tileProvider.tilingScheme.ellipsoid.cartesianToCartographic(
  509. cameraFrameOrigin,
  510. primitive._cameraReferenceFrameOriginCartographic,
  511. );
  512. // Traverse in depth-first, near-to-far order.
  513. for (let i = 0; i < levelZeroTiles.length; ++i) {
  514. const tile = levelZeroTiles[i];
  515. primitive._tileReplacementQueue.markTileRendered(tile);
  516. if (!tile.renderable) {
  517. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  518. ++debug.tilesWaitingForChildren;
  519. } else {
  520. visitIfVisible(
  521. primitive,
  522. tile,
  523. tileProvider,
  524. frameState,
  525. occluders,
  526. false,
  527. rootTraversalDetails[i],
  528. );
  529. }
  530. }
  531. primitive._lastSelectionFrameNumber = frameState.frameNumber;
  532. }
  533. function queueTileLoad(primitive, queue, tile, frameState) {
  534. if (!tile.needsLoading) {
  535. return;
  536. }
  537. if (primitive.tileProvider.computeTileLoadPriority !== undefined) {
  538. tile._loadPriority = primitive.tileProvider.computeTileLoadPriority(
  539. tile,
  540. frameState,
  541. );
  542. }
  543. queue.push(tile);
  544. }
  545. /**
  546. * Tracks details of traversing a tile while selecting tiles for rendering.
  547. * @alias TraversalDetails
  548. * @constructor
  549. * @private
  550. */
  551. function TraversalDetails() {
  552. /**
  553. * True if all selected (i.e. not culled or refined) tiles in this tile's subtree
  554. * are renderable. If the subtree is renderable, we'll render it; no drama.
  555. */
  556. this.allAreRenderable = true;
  557. /**
  558. * True if any tiles in this tile's subtree were rendered last frame. If any
  559. * were, we must render the subtree rather than this tile, because rendering
  560. * this tile would cause detail to vanish that was visible last frame, and
  561. * that's no good.
  562. */
  563. this.anyWereRenderedLastFrame = false;
  564. /**
  565. * Counts the number of selected tiles in this tile's subtree that are
  566. * not yet ready to be rendered because they need more loading. Note that
  567. * this value will _not_ necessarily be zero when
  568. * {@link TraversalDetails#allAreRenderable} is true, for subtle reasons.
  569. * When {@link TraversalDetails#allAreRenderable} and
  570. * {@link TraversalDetails#anyWereRenderedLastFrame} are both false, we
  571. * will render this tile instead of any tiles in its subtree and
  572. * the `allAreRenderable` value for this tile will reflect only whether _this_
  573. * tile is renderable. The `notYetRenderableCount` value, however, will still
  574. * reflect the total number of tiles that we are waiting on, including the
  575. * ones that we're not rendering. `notYetRenderableCount` is only reset
  576. * when a subtree is removed from the render queue because the
  577. * `notYetRenderableCount` exceeds the
  578. * {@link QuadtreePrimitive#loadingDescendantLimit}.
  579. */
  580. this.notYetRenderableCount = 0;
  581. }
  582. function TraversalQuadDetails() {
  583. this.southwest = new TraversalDetails();
  584. this.southeast = new TraversalDetails();
  585. this.northwest = new TraversalDetails();
  586. this.northeast = new TraversalDetails();
  587. }
  588. TraversalQuadDetails.prototype.combine = function (result) {
  589. const southwest = this.southwest;
  590. const southeast = this.southeast;
  591. const northwest = this.northwest;
  592. const northeast = this.northeast;
  593. result.allAreRenderable =
  594. southwest.allAreRenderable &&
  595. southeast.allAreRenderable &&
  596. northwest.allAreRenderable &&
  597. northeast.allAreRenderable;
  598. result.anyWereRenderedLastFrame =
  599. southwest.anyWereRenderedLastFrame ||
  600. southeast.anyWereRenderedLastFrame ||
  601. northwest.anyWereRenderedLastFrame ||
  602. northeast.anyWereRenderedLastFrame;
  603. result.notYetRenderableCount =
  604. southwest.notYetRenderableCount +
  605. southeast.notYetRenderableCount +
  606. northwest.notYetRenderableCount +
  607. northeast.notYetRenderableCount;
  608. };
  609. const traversalQuadsByLevel = new Array(31); // level 30 tiles are ~2cm wide at the equator, should be good enough.
  610. for (let i = 0; i < traversalQuadsByLevel.length; ++i) {
  611. traversalQuadsByLevel[i] = new TraversalQuadDetails();
  612. }
  613. /**
  614. * Visits a tile for possible rendering. When we call this function with a tile:
  615. *
  616. * * the tile has been determined to be visible (possibly based on a bounding volume that is not very tight-fitting)
  617. * * its parent tile does _not_ meet the SSE (unless ancestorMeetsSse=true, see comments below)
  618. * * the tile may or may not be renderable
  619. *
  620. * @private
  621. *
  622. * @param {Primitive} primitive The QuadtreePrimitive.
  623. * @param {FrameState} frameState The frame state.
  624. * @param {QuadtreeTile} tile The tile to visit
  625. * @param {boolean} ancestorMeetsSse True if a tile higher in the tile tree already met the SSE and we're refining further only
  626. * to maintain detail while that higher tile loads.
  627. * @param {TraversalDetails} traveralDetails On return, populated with details of how the traversal of this tile went.
  628. */
  629. function visitTile(
  630. primitive,
  631. frameState,
  632. tile,
  633. ancestorMeetsSse,
  634. traversalDetails,
  635. ) {
  636. const debug = primitive._debug;
  637. ++debug.tilesVisited;
  638. primitive._tileReplacementQueue.markTileRendered(tile);
  639. tile.updateCustomData();
  640. if (tile.level > debug.maxDepthVisited) {
  641. debug.maxDepthVisited = tile.level;
  642. }
  643. const meetsSse =
  644. screenSpaceError(primitive, frameState, tile) <
  645. primitive.maximumScreenSpaceError;
  646. const southwestChild = tile.southwestChild;
  647. const southeastChild = tile.southeastChild;
  648. const northwestChild = tile.northwestChild;
  649. const northeastChild = tile.northeastChild;
  650. const lastFrame = primitive._lastSelectionFrameNumber;
  651. const lastFrameSelectionResult =
  652. tile._lastSelectionResultFrame === lastFrame
  653. ? tile._lastSelectionResult
  654. : TileSelectionResult.NONE;
  655. const tileProvider = primitive.tileProvider;
  656. if (meetsSse || ancestorMeetsSse) {
  657. // This tile (or an ancestor) is the one we want to render this frame, but we'll do different things depending
  658. // on the state of this tile and on what we did _last_ frame.
  659. // We can render it if _any_ of the following are true:
  660. // 1. We rendered it (or kicked it) last frame.
  661. // 2. This tile was culled last frame, or it wasn't even visited because an ancestor was culled.
  662. // 3. The tile is completely done loading.
  663. // 4. a) Terrain is ready, and
  664. // b) All necessary imagery is ready. Necessary imagery is imagery that was rendered with this tile
  665. // or any descendants last frame. Such imagery is required because rendering this tile without
  666. // it would cause detail to disappear.
  667. //
  668. // Determining condition 4 is more expensive, so we check the others first.
  669. //
  670. // Note that even if we decide to render a tile here, it may later get "kicked" in favor of an ancestor.
  671. const oneRenderedLastFrame =
  672. TileSelectionResult.originalResult(lastFrameSelectionResult) ===
  673. TileSelectionResult.RENDERED;
  674. const twoCulledOrNotVisited =
  675. TileSelectionResult.originalResult(lastFrameSelectionResult) ===
  676. TileSelectionResult.CULLED ||
  677. lastFrameSelectionResult === TileSelectionResult.NONE;
  678. const threeCompletelyLoaded = tile.state === QuadtreeTileLoadState.DONE;
  679. let renderable =
  680. oneRenderedLastFrame || twoCulledOrNotVisited || threeCompletelyLoaded;
  681. if (!renderable) {
  682. // Check the more expensive condition 4 above. This requires details of the thing
  683. // we're rendering (e.g. the globe surface), so delegate it to the tile provider.
  684. if (defined(tileProvider.canRenderWithoutLosingDetail)) {
  685. renderable = tileProvider.canRenderWithoutLosingDetail(tile);
  686. }
  687. }
  688. if (renderable) {
  689. // Only load this tile if it (not just an ancestor) meets the SSE.
  690. if (meetsSse) {
  691. queueTileLoad(
  692. primitive,
  693. primitive._tileLoadQueueMedium,
  694. tile,
  695. frameState,
  696. );
  697. }
  698. addTileToRenderList(primitive, tile);
  699. traversalDetails.allAreRenderable = tile.renderable;
  700. traversalDetails.anyWereRenderedLastFrame =
  701. lastFrameSelectionResult === TileSelectionResult.RENDERED;
  702. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  703. tile._lastSelectionResultFrame = frameState.frameNumber;
  704. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  705. if (!traversalDetails.anyWereRenderedLastFrame) {
  706. // Tile is newly-rendered this frame, so update its heights.
  707. primitive._tileToUpdateHeights.push(tile);
  708. }
  709. return;
  710. }
  711. // Otherwise, we can't render this tile (or its fill) because doing so would cause detail to disappear
  712. // that was visible last frame. Instead, keep rendering any still-visible descendants that were rendered
  713. // last frame and render fills for newly-visible descendants. E.g. if we were rendering level 15 last
  714. // frame but this frame we want level 14 and the closest renderable level <= 14 is 0, rendering level
  715. // zero would be pretty jarring so instead we keep rendering level 15 even though its SSE is better
  716. // than required. So fall through to continue traversal...
  717. ancestorMeetsSse = true;
  718. // Load this blocker tile with high priority, but only if this tile (not just an ancestor) meets the SSE.
  719. if (meetsSse) {
  720. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  721. }
  722. }
  723. if (tileProvider.canRefine(tile)) {
  724. const allAreUpsampled =
  725. southwestChild.upsampledFromParent &&
  726. southeastChild.upsampledFromParent &&
  727. northwestChild.upsampledFromParent &&
  728. northeastChild.upsampledFromParent;
  729. if (allAreUpsampled) {
  730. // No point in rendering the children because they're all upsampled. Render this tile instead.
  731. addTileToRenderList(primitive, tile);
  732. // Rendered tile that's not waiting on children loads with medium priority.
  733. queueTileLoad(
  734. primitive,
  735. primitive._tileLoadQueueMedium,
  736. tile,
  737. frameState,
  738. );
  739. // Make sure we don't unload the children and forget they're upsampled.
  740. primitive._tileReplacementQueue.markTileRendered(southwestChild);
  741. primitive._tileReplacementQueue.markTileRendered(southeastChild);
  742. primitive._tileReplacementQueue.markTileRendered(northwestChild);
  743. primitive._tileReplacementQueue.markTileRendered(northeastChild);
  744. traversalDetails.allAreRenderable = tile.renderable;
  745. traversalDetails.anyWereRenderedLastFrame =
  746. lastFrameSelectionResult === TileSelectionResult.RENDERED;
  747. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  748. tile._lastSelectionResultFrame = frameState.frameNumber;
  749. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  750. if (!traversalDetails.anyWereRenderedLastFrame) {
  751. // Tile is newly-rendered this frame, so update its heights.
  752. primitive._tileToUpdateHeights.push(tile);
  753. }
  754. return;
  755. }
  756. // SSE is not good enough, so refine.
  757. tile._lastSelectionResultFrame = frameState.frameNumber;
  758. tile._lastSelectionResult = TileSelectionResult.REFINED;
  759. const firstRenderedDescendantIndex = primitive._tilesToRender.length;
  760. const loadIndexLow = primitive._tileLoadQueueLow.length;
  761. const loadIndexMedium = primitive._tileLoadQueueMedium.length;
  762. const loadIndexHigh = primitive._tileLoadQueueHigh.length;
  763. const tilesToUpdateHeightsIndex = primitive._tileToUpdateHeights.length;
  764. // No need to add the children to the load queue because they'll be added (if necessary) when they're visited.
  765. visitVisibleChildrenNearToFar(
  766. primitive,
  767. southwestChild,
  768. southeastChild,
  769. northwestChild,
  770. northeastChild,
  771. frameState,
  772. ancestorMeetsSse,
  773. traversalDetails,
  774. );
  775. // If no descendant tiles were added to the render list by the function above, it means they were all
  776. // culled even though this tile was deemed visible. That's pretty common.
  777. if (firstRenderedDescendantIndex !== primitive._tilesToRender.length) {
  778. // At least one descendant tile was added to the render list.
  779. // The traversalDetails tell us what happened while visiting the children.
  780. const allAreRenderable = traversalDetails.allAreRenderable;
  781. const anyWereRenderedLastFrame =
  782. traversalDetails.anyWereRenderedLastFrame;
  783. const notYetRenderableCount = traversalDetails.notYetRenderableCount;
  784. let queuedForLoad = false;
  785. if (!allAreRenderable && !anyWereRenderedLastFrame) {
  786. // Some of our descendants aren't ready to render yet, and none were rendered last frame,
  787. // so kick them all out of the render list and render this tile instead. Continue to load them though!
  788. // Mark the rendered descendants and their ancestors - up to this tile - as kicked.
  789. const renderList = primitive._tilesToRender;
  790. for (let i = firstRenderedDescendantIndex; i < renderList.length; ++i) {
  791. let workTile = renderList[i];
  792. while (
  793. workTile !== undefined &&
  794. workTile._lastSelectionResult !== TileSelectionResult.KICKED &&
  795. workTile !== tile
  796. ) {
  797. workTile._lastSelectionResult = TileSelectionResult.kick(
  798. workTile._lastSelectionResult,
  799. );
  800. workTile = workTile.parent;
  801. }
  802. }
  803. // Remove all descendants from the render list and add this tile.
  804. primitive._tilesToRender.length = firstRenderedDescendantIndex;
  805. primitive._tileToUpdateHeights.length = tilesToUpdateHeightsIndex;
  806. addTileToRenderList(primitive, tile);
  807. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  808. // If we're waiting on heaps of descendants, the above will take too long. So in that case,
  809. // load this tile INSTEAD of loading any of the descendants, and tell the up-level we're only waiting
  810. // on this tile. Keep doing this until we actually manage to render this tile.
  811. const wasRenderedLastFrame =
  812. lastFrameSelectionResult === TileSelectionResult.RENDERED;
  813. if (
  814. !wasRenderedLastFrame &&
  815. notYetRenderableCount > primitive.loadingDescendantLimit
  816. ) {
  817. // Remove all descendants from the load queues.
  818. primitive._tileLoadQueueLow.length = loadIndexLow;
  819. primitive._tileLoadQueueMedium.length = loadIndexMedium;
  820. primitive._tileLoadQueueHigh.length = loadIndexHigh;
  821. queueTileLoad(
  822. primitive,
  823. primitive._tileLoadQueueMedium,
  824. tile,
  825. frameState,
  826. );
  827. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  828. queuedForLoad = true;
  829. }
  830. traversalDetails.allAreRenderable = tile.renderable;
  831. traversalDetails.anyWereRenderedLastFrame = wasRenderedLastFrame;
  832. if (!wasRenderedLastFrame) {
  833. // Tile is newly-rendered this frame, so update its heights.
  834. primitive._tileToUpdateHeights.push(tile);
  835. }
  836. ++debug.tilesWaitingForChildren;
  837. }
  838. if (primitive.preloadAncestors && !queuedForLoad) {
  839. queueTileLoad(primitive, primitive._tileLoadQueueLow, tile, frameState);
  840. }
  841. }
  842. return;
  843. }
  844. tile._lastSelectionResultFrame = frameState.frameNumber;
  845. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  846. // We'd like to refine but can't because we have no availability data for this tile's children,
  847. // so we have no idea if refinining would involve a load or an upsample. We'll have to finish
  848. // loading this tile first in order to find that out, so load this refinement blocker with
  849. // high priority.
  850. addTileToRenderList(primitive, tile);
  851. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  852. traversalDetails.allAreRenderable = tile.renderable;
  853. traversalDetails.anyWereRenderedLastFrame =
  854. lastFrameSelectionResult === TileSelectionResult.RENDERED;
  855. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  856. }
  857. function visitVisibleChildrenNearToFar(
  858. primitive,
  859. southwest,
  860. southeast,
  861. northwest,
  862. northeast,
  863. frameState,
  864. ancestorMeetsSse,
  865. traversalDetails,
  866. ) {
  867. const cameraPosition = frameState.camera.positionCartographic;
  868. const tileProvider = primitive._tileProvider;
  869. const occluders = primitive._occluders;
  870. const quadDetails = traversalQuadsByLevel[southwest.level];
  871. const southwestDetails = quadDetails.southwest;
  872. const southeastDetails = quadDetails.southeast;
  873. const northwestDetails = quadDetails.northwest;
  874. const northeastDetails = quadDetails.northeast;
  875. if (cameraPosition.longitude < southwest.rectangle.east) {
  876. if (cameraPosition.latitude < southwest.rectangle.north) {
  877. // Camera in southwest quadrant
  878. visitIfVisible(
  879. primitive,
  880. southwest,
  881. tileProvider,
  882. frameState,
  883. occluders,
  884. ancestorMeetsSse,
  885. southwestDetails,
  886. );
  887. visitIfVisible(
  888. primitive,
  889. southeast,
  890. tileProvider,
  891. frameState,
  892. occluders,
  893. ancestorMeetsSse,
  894. southeastDetails,
  895. );
  896. visitIfVisible(
  897. primitive,
  898. northwest,
  899. tileProvider,
  900. frameState,
  901. occluders,
  902. ancestorMeetsSse,
  903. northwestDetails,
  904. );
  905. visitIfVisible(
  906. primitive,
  907. northeast,
  908. tileProvider,
  909. frameState,
  910. occluders,
  911. ancestorMeetsSse,
  912. northeastDetails,
  913. );
  914. } else {
  915. // Camera in northwest quadrant
  916. visitIfVisible(
  917. primitive,
  918. northwest,
  919. tileProvider,
  920. frameState,
  921. occluders,
  922. ancestorMeetsSse,
  923. northwestDetails,
  924. );
  925. visitIfVisible(
  926. primitive,
  927. southwest,
  928. tileProvider,
  929. frameState,
  930. occluders,
  931. ancestorMeetsSse,
  932. southwestDetails,
  933. );
  934. visitIfVisible(
  935. primitive,
  936. northeast,
  937. tileProvider,
  938. frameState,
  939. occluders,
  940. ancestorMeetsSse,
  941. northeastDetails,
  942. );
  943. visitIfVisible(
  944. primitive,
  945. southeast,
  946. tileProvider,
  947. frameState,
  948. occluders,
  949. ancestorMeetsSse,
  950. southeastDetails,
  951. );
  952. }
  953. } else if (cameraPosition.latitude < southwest.rectangle.north) {
  954. // Camera southeast quadrant
  955. visitIfVisible(
  956. primitive,
  957. southeast,
  958. tileProvider,
  959. frameState,
  960. occluders,
  961. ancestorMeetsSse,
  962. southeastDetails,
  963. );
  964. visitIfVisible(
  965. primitive,
  966. southwest,
  967. tileProvider,
  968. frameState,
  969. occluders,
  970. ancestorMeetsSse,
  971. southwestDetails,
  972. );
  973. visitIfVisible(
  974. primitive,
  975. northeast,
  976. tileProvider,
  977. frameState,
  978. occluders,
  979. ancestorMeetsSse,
  980. northeastDetails,
  981. );
  982. visitIfVisible(
  983. primitive,
  984. northwest,
  985. tileProvider,
  986. frameState,
  987. occluders,
  988. ancestorMeetsSse,
  989. northwestDetails,
  990. );
  991. } else {
  992. // Camera in northeast quadrant
  993. visitIfVisible(
  994. primitive,
  995. northeast,
  996. tileProvider,
  997. frameState,
  998. occluders,
  999. ancestorMeetsSse,
  1000. northeastDetails,
  1001. );
  1002. visitIfVisible(
  1003. primitive,
  1004. northwest,
  1005. tileProvider,
  1006. frameState,
  1007. occluders,
  1008. ancestorMeetsSse,
  1009. northwestDetails,
  1010. );
  1011. visitIfVisible(
  1012. primitive,
  1013. southeast,
  1014. tileProvider,
  1015. frameState,
  1016. occluders,
  1017. ancestorMeetsSse,
  1018. southeastDetails,
  1019. );
  1020. visitIfVisible(
  1021. primitive,
  1022. southwest,
  1023. tileProvider,
  1024. frameState,
  1025. occluders,
  1026. ancestorMeetsSse,
  1027. southwestDetails,
  1028. );
  1029. }
  1030. quadDetails.combine(traversalDetails);
  1031. }
  1032. function containsNeededPosition(primitive, tile) {
  1033. const rectangle = tile.rectangle;
  1034. return (
  1035. (defined(primitive._cameraPositionCartographic) &&
  1036. Rectangle.contains(rectangle, primitive._cameraPositionCartographic)) ||
  1037. (defined(primitive._cameraReferenceFrameOriginCartographic) &&
  1038. Rectangle.contains(
  1039. rectangle,
  1040. primitive._cameraReferenceFrameOriginCartographic,
  1041. ))
  1042. );
  1043. }
  1044. function visitIfVisible(
  1045. primitive,
  1046. tile,
  1047. tileProvider,
  1048. frameState,
  1049. occluders,
  1050. ancestorMeetsSse,
  1051. traversalDetails,
  1052. ) {
  1053. if (
  1054. tileProvider.computeTileVisibility(tile, frameState, occluders) !==
  1055. Visibility.NONE
  1056. ) {
  1057. return visitTile(
  1058. primitive,
  1059. frameState,
  1060. tile,
  1061. ancestorMeetsSse,
  1062. traversalDetails,
  1063. );
  1064. }
  1065. ++primitive._debug.tilesCulled;
  1066. primitive._tileReplacementQueue.markTileRendered(tile);
  1067. traversalDetails.allAreRenderable = true;
  1068. traversalDetails.anyWereRenderedLastFrame = false;
  1069. traversalDetails.notYetRenderableCount = 0;
  1070. if (containsNeededPosition(primitive, tile)) {
  1071. // Load the tile(s) that contains the camera's position and
  1072. // the origin of its reference frame with medium priority.
  1073. // But we only need to load until the terrain is available, no need to load imagery.
  1074. if (!defined(tile.data) || !defined(tile.data.vertexArray)) {
  1075. queueTileLoad(
  1076. primitive,
  1077. primitive._tileLoadQueueMedium,
  1078. tile,
  1079. frameState,
  1080. );
  1081. }
  1082. const lastFrame = primitive._lastSelectionFrameNumber;
  1083. const lastFrameSelectionResult =
  1084. tile._lastSelectionResultFrame === lastFrame
  1085. ? tile._lastSelectionResult
  1086. : TileSelectionResult.NONE;
  1087. if (
  1088. lastFrameSelectionResult !== TileSelectionResult.CULLED_BUT_NEEDED &&
  1089. lastFrameSelectionResult !== TileSelectionResult.RENDERED
  1090. ) {
  1091. primitive._tileToUpdateHeights.push(tile);
  1092. }
  1093. tile._lastSelectionResult = TileSelectionResult.CULLED_BUT_NEEDED;
  1094. } else if (primitive.preloadSiblings || tile.level === 0) {
  1095. // Load culled level zero tiles with low priority.
  1096. // For all other levels, only load culled tiles if preloadSiblings is enabled.
  1097. queueTileLoad(primitive, primitive._tileLoadQueueLow, tile, frameState);
  1098. tile._lastSelectionResult = TileSelectionResult.CULLED;
  1099. } else {
  1100. tile._lastSelectionResult = TileSelectionResult.CULLED;
  1101. }
  1102. tile._lastSelectionResultFrame = frameState.frameNumber;
  1103. }
  1104. function screenSpaceError(primitive, frameState, tile) {
  1105. if (
  1106. frameState.mode === SceneMode.SCENE2D ||
  1107. frameState.camera.frustum instanceof OrthographicFrustum ||
  1108. frameState.camera.frustum instanceof OrthographicOffCenterFrustum
  1109. ) {
  1110. return screenSpaceError2D(primitive, frameState, tile);
  1111. }
  1112. const maxGeometricError =
  1113. primitive._tileProvider.getLevelMaximumGeometricError(tile.level);
  1114. const distance = tile._distance;
  1115. const height = frameState.context.drawingBufferHeight;
  1116. const sseDenominator = frameState.camera.frustum.sseDenominator;
  1117. let error = (maxGeometricError * height) / (distance * sseDenominator);
  1118. if (frameState.fog.enabled) {
  1119. error -=
  1120. CesiumMath.fog(distance, frameState.fog.density) * frameState.fog.sse;
  1121. }
  1122. error /= frameState.pixelRatio;
  1123. return error;
  1124. }
  1125. function screenSpaceError2D(primitive, frameState, tile) {
  1126. const camera = frameState.camera;
  1127. let frustum = camera.frustum;
  1128. const offCenterFrustum = frustum.offCenterFrustum;
  1129. if (defined(offCenterFrustum)) {
  1130. frustum = offCenterFrustum;
  1131. }
  1132. const context = frameState.context;
  1133. const width = context.drawingBufferWidth;
  1134. const height = context.drawingBufferHeight;
  1135. const maxGeometricError =
  1136. primitive._tileProvider.getLevelMaximumGeometricError(tile.level);
  1137. const pixelSize =
  1138. Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) /
  1139. Math.max(width, height);
  1140. let error = maxGeometricError / pixelSize;
  1141. if (frameState.fog.enabled && frameState.mode !== SceneMode.SCENE2D) {
  1142. error -=
  1143. CesiumMath.fog(tile._distance, frameState.fog.density) *
  1144. frameState.fog.sse;
  1145. }
  1146. error /= frameState.pixelRatio;
  1147. return error;
  1148. }
  1149. function addTileToRenderList(primitive, tile) {
  1150. primitive._tilesToRender.push(tile);
  1151. primitive._tilesRenderedThisFrame.add(tile);
  1152. }
  1153. function processTileLoadQueue(primitive, frameState) {
  1154. const tileLoadQueueHigh = primitive._tileLoadQueueHigh;
  1155. const tileLoadQueueMedium = primitive._tileLoadQueueMedium;
  1156. const tileLoadQueueLow = primitive._tileLoadQueueLow;
  1157. if (
  1158. tileLoadQueueHigh.length === 0 &&
  1159. tileLoadQueueMedium.length === 0 &&
  1160. tileLoadQueueLow.length === 0
  1161. ) {
  1162. return;
  1163. }
  1164. // Remove any tiles that were not used this frame beyond the number
  1165. // we're allowed to keep.
  1166. primitive._tileReplacementQueue.trimTiles(primitive.tileCacheSize);
  1167. const endTime = getTimestamp() + primitive._loadQueueTimeSlice;
  1168. const tileProvider = primitive._tileProvider;
  1169. let didSomeLoading = processSinglePriorityLoadQueue(
  1170. primitive,
  1171. frameState,
  1172. tileProvider,
  1173. endTime,
  1174. tileLoadQueueHigh,
  1175. false,
  1176. );
  1177. didSomeLoading = processSinglePriorityLoadQueue(
  1178. primitive,
  1179. frameState,
  1180. tileProvider,
  1181. endTime,
  1182. tileLoadQueueMedium,
  1183. didSomeLoading,
  1184. );
  1185. processSinglePriorityLoadQueue(
  1186. primitive,
  1187. frameState,
  1188. tileProvider,
  1189. endTime,
  1190. tileLoadQueueLow,
  1191. didSomeLoading,
  1192. );
  1193. }
  1194. function sortByLoadPriority(a, b) {
  1195. return a._loadPriority - b._loadPriority;
  1196. }
  1197. function processSinglePriorityLoadQueue(
  1198. primitive,
  1199. frameState,
  1200. tileProvider,
  1201. endTime,
  1202. loadQueue,
  1203. didSomeLoading,
  1204. ) {
  1205. if (tileProvider.computeTileLoadPriority !== undefined) {
  1206. loadQueue.sort(sortByLoadPriority);
  1207. }
  1208. for (
  1209. let i = 0, len = loadQueue.length;
  1210. i < len && (getTimestamp() < endTime || !didSomeLoading);
  1211. ++i
  1212. ) {
  1213. const tile = loadQueue[i];
  1214. primitive._tileReplacementQueue.markTileRendered(tile);
  1215. tileProvider.loadTile(frameState, tile);
  1216. didSomeLoading = true;
  1217. }
  1218. return didSomeLoading;
  1219. }
  1220. const scratchRay = new Ray();
  1221. const scratchCartographic = new Cartographic();
  1222. const scratchPosition = new Cartesian3();
  1223. const scratchArray = [];
  1224. function updateHeights(primitive, frameState) {
  1225. if (!defined(primitive.tileProvider.tilingScheme)) {
  1226. return;
  1227. }
  1228. const tryNextFrame = scratchArray;
  1229. tryNextFrame.length = 0;
  1230. const tilesToUpdateHeights = primitive._tileToUpdateHeights;
  1231. const startTime = getTimestamp();
  1232. const timeSlice = primitive._updateHeightsTimeSlice;
  1233. const endTime = startTime + timeSlice;
  1234. const mode = frameState.mode;
  1235. const projection = frameState.mapProjection;
  1236. const ellipsoid = primitive.tileProvider.tilingScheme.ellipsoid;
  1237. let i;
  1238. while (tilesToUpdateHeights.length > 0) {
  1239. const tile = tilesToUpdateHeights[0];
  1240. if (!defined(tile.data) || !defined(tile.data.mesh)) {
  1241. // Tile isn't loaded enough yet, so try again next frame if this tile is still
  1242. // being rendered.
  1243. const selectionResult =
  1244. tile._lastSelectionResultFrame === primitive._lastSelectionFrameNumber
  1245. ? tile._lastSelectionResult
  1246. : TileSelectionResult.NONE;
  1247. if (
  1248. selectionResult === TileSelectionResult.RENDERED ||
  1249. selectionResult === TileSelectionResult.CULLED_BUT_NEEDED
  1250. ) {
  1251. tryNextFrame.push(tile);
  1252. }
  1253. // Ensure stale position cache is cleared
  1254. tile.clearPositionCache();
  1255. tilesToUpdateHeights.shift();
  1256. continue;
  1257. }
  1258. const customData = tile.customData;
  1259. if (!defined(tile._customDataIterator)) {
  1260. tile._customDataIterator = customData.values();
  1261. }
  1262. const customDataIterator = tile._customDataIterator;
  1263. let timeSliceMax = false;
  1264. let nextData;
  1265. while (!(nextData = customDataIterator.next()).done) {
  1266. const data = nextData.value;
  1267. // No need to run this code when the tile is upsampled, because the height will be the same as its parent.
  1268. const terrainData = tile.data.terrainData;
  1269. const upsampledGeometryFromParent =
  1270. defined(terrainData) && terrainData.wasCreatedByUpsampling();
  1271. if (tile.level > data.level && !upsampledGeometryFromParent) {
  1272. let position;
  1273. // find cached entry
  1274. const cachedData = tile.getPositionCacheEntry(
  1275. data.positionCartographic,
  1276. primitive.maximumScreenSpaceError,
  1277. );
  1278. if (defined(cachedData)) {
  1279. // cache hit
  1280. position = cachedData;
  1281. } else {
  1282. if (!defined(data.positionOnEllipsoidSurface)) {
  1283. // cartesian has to be on the ellipsoid surface for `ellipsoid.geodeticSurfaceNormal`
  1284. data.positionOnEllipsoidSurface = Cartesian3.fromRadians(
  1285. data.positionCartographic.longitude,
  1286. data.positionCartographic.latitude,
  1287. 0.0,
  1288. ellipsoid,
  1289. );
  1290. }
  1291. if (mode === SceneMode.SCENE3D) {
  1292. const surfaceNormal = ellipsoid.geodeticSurfaceNormal(
  1293. data.positionOnEllipsoidSurface,
  1294. scratchRay.direction,
  1295. );
  1296. // compute origin point
  1297. // Try to find the intersection point between the surface normal and z-axis.
  1298. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  1299. const rayOrigin = ellipsoid.getSurfaceNormalIntersectionWithZAxis(
  1300. data.positionOnEllipsoidSurface,
  1301. 11500.0,
  1302. scratchRay.origin,
  1303. );
  1304. // Theoretically, not with Earth datums, the intersection point can be outside the ellipsoid
  1305. if (!defined(rayOrigin)) {
  1306. // intersection point is outside the ellipsoid, try other value
  1307. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  1308. let minimumHeight = 0.0;
  1309. if (defined(tile.data.tileBoundingRegion)) {
  1310. minimumHeight = tile.data.tileBoundingRegion.minimumHeight;
  1311. }
  1312. const magnitude = Math.min(minimumHeight, -11500.0);
  1313. // multiply by the *positive* value of the magnitude
  1314. const vectorToMinimumPoint = Cartesian3.multiplyByScalar(
  1315. surfaceNormal,
  1316. Math.abs(magnitude) + 1,
  1317. scratchPosition,
  1318. );
  1319. Cartesian3.subtract(
  1320. data.positionOnEllipsoidSurface,
  1321. vectorToMinimumPoint,
  1322. scratchRay.origin,
  1323. );
  1324. }
  1325. } else {
  1326. Cartographic.clone(data.positionCartographic, scratchCartographic);
  1327. // minimum height for the terrain set, need to get this information from the terrain provider
  1328. scratchCartographic.height = -11500.0;
  1329. projection.project(scratchCartographic, scratchPosition);
  1330. Cartesian3.fromElements(
  1331. scratchPosition.z,
  1332. scratchPosition.x,
  1333. scratchPosition.y,
  1334. scratchPosition,
  1335. );
  1336. Cartesian3.clone(scratchPosition, scratchRay.origin);
  1337. Cartesian3.clone(Cartesian3.UNIT_X, scratchRay.direction);
  1338. }
  1339. position = tile.data.pick(
  1340. scratchRay,
  1341. mode,
  1342. projection,
  1343. false,
  1344. scratchPosition,
  1345. );
  1346. if (defined(position)) {
  1347. // Store the computed position in the cache for future reuse
  1348. tile.setPositionCacheEntry(
  1349. data.positionCartographic,
  1350. primitive.maximumScreenSpaceError,
  1351. position,
  1352. );
  1353. }
  1354. }
  1355. if (defined(position)) {
  1356. if (defined(data.callback)) {
  1357. const positionCarto = ellipsoid.cartesianToCartographic(
  1358. position,
  1359. scratchCartographic,
  1360. );
  1361. data.callback(positionCarto);
  1362. }
  1363. data.level = tile.level;
  1364. }
  1365. }
  1366. if (getTimestamp() >= endTime) {
  1367. timeSliceMax = true;
  1368. break;
  1369. }
  1370. }
  1371. if (timeSliceMax) {
  1372. tile._customDataIterator = customDataIterator;
  1373. break;
  1374. } else {
  1375. tile._customDataIterator = undefined;
  1376. tilesToUpdateHeights.shift();
  1377. }
  1378. }
  1379. for (i = 0; i < tryNextFrame.length; i++) {
  1380. tilesToUpdateHeights.push(tryNextFrame[i]);
  1381. }
  1382. }
  1383. function createRenderCommandsForSelectedTiles(primitive, frameState) {
  1384. const tileProvider = primitive._tileProvider;
  1385. const tilesToRender = primitive._tilesToRender;
  1386. for (let i = 0, len = tilesToRender.length; i < len; ++i) {
  1387. const tile = tilesToRender[i];
  1388. tileProvider.showTileThisFrame(tile, frameState);
  1389. }
  1390. }
  1391. export default QuadtreePrimitive;