QuadtreePrimitive.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  1. import Cartesian3 from '../Core/Cartesian3.js';
  2. import Cartographic from '../Core/Cartographic.js';
  3. import defaultValue from '../Core/defaultValue.js';
  4. import defined from '../Core/defined.js';
  5. import defineProperties from '../Core/defineProperties.js';
  6. import DeveloperError from '../Core/DeveloperError.js';
  7. import Event from '../Core/Event.js';
  8. import getTimestamp from '../Core/getTimestamp.js';
  9. import CesiumMath from '../Core/Math.js';
  10. import Matrix4 from '../Core/Matrix4.js';
  11. import OrthographicFrustum from '../Core/OrthographicFrustum.js';
  12. import OrthographicOffCenterFrustum from '../Core/OrthographicOffCenterFrustum.js';
  13. import Ray from '../Core/Ray.js';
  14. import Rectangle from '../Core/Rectangle.js';
  15. import Visibility from '../Core/Visibility.js';
  16. import QuadtreeOccluders from './QuadtreeOccluders.js';
  17. import QuadtreeTile from './QuadtreeTile.js';
  18. import QuadtreeTileLoadState from './QuadtreeTileLoadState.js';
  19. import SceneMode from './SceneMode.js';
  20. import TileReplacementQueue from './TileReplacementQueue.js';
  21. import TileSelectionResult from './TileSelectionResult.js';
  22. /**
  23. * Renders massive sets of data by utilizing level-of-detail and culling. The globe surface is divided into
  24. * a quadtree of tiles with large, low-detail tiles at the root and small, high-detail tiles at the leaves.
  25. * The set of tiles to render is selected by projecting an estimate of the geometric error in a tile onto
  26. * the screen to estimate screen-space error, in pixels, which must be below a user-specified threshold.
  27. * The actual content of the tiles is arbitrary and is specified using a {@link QuadtreeTileProvider}.
  28. *
  29. * @alias QuadtreePrimitive
  30. * @constructor
  31. * @private
  32. *
  33. * @param {QuadtreeTileProvider} options.tileProvider The tile provider that loads, renders, and estimates
  34. * the distance to individual tiles.
  35. * @param {Number} [options.maximumScreenSpaceError=2] The maximum screen-space error, in pixels, that is allowed.
  36. * A higher maximum error will render fewer tiles and improve performance, while a lower
  37. * value will improve visual quality.
  38. * @param {Number} [options.tileCacheSize=100] The maximum number of tiles that will be retained in the tile cache.
  39. * Note that tiles will never be unloaded if they were used for rendering the last
  40. * frame, so the actual number of resident tiles may be higher. The value of
  41. * this property will not affect visual quality.
  42. */
  43. function QuadtreePrimitive(options) {
  44. //>>includeStart('debug', pragmas.debug);
  45. if (!defined(options) || !defined(options.tileProvider)) {
  46. throw new DeveloperError('options.tileProvider is required.');
  47. }
  48. if (defined(options.tileProvider.quadtree)) {
  49. throw new DeveloperError('A QuadtreeTileProvider can only be used with a single QuadtreePrimitive');
  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. var tilingScheme = this._tileProvider.tilingScheme;
  71. var ellipsoid = tilingScheme.ellipsoid;
  72. this._tilesToRender = [];
  73. this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement
  74. this._tileLoadQueueMedium = []; // medium priority tiles are being rendered
  75. this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads.
  76. this._tileReplacementQueue = new TileReplacementQueue();
  77. this._levelZeroTiles = undefined;
  78. this._loadQueueTimeSlice = 5.0;
  79. this._tilesInvalidated = false;
  80. this._addHeightCallbacks = [];
  81. this._removeHeightCallbacks = [];
  82. this._tileToUpdateHeights = [];
  83. this._lastTileIndex = 0;
  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 = defaultValue(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 = defaultValue(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. 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. var 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. var levelZeroTiles = primitive._levelZeroTiles;
  191. if (defined(levelZeroTiles)) {
  192. for (var i = 0; i < levelZeroTiles.length; ++i) {
  193. var tile = levelZeroTiles[i];
  194. var customData = tile.customData;
  195. var customDataLength = customData.length;
  196. for (var j = 0; j < customDataLength; ++j) {
  197. var data = customData[j];
  198. data.level = 0;
  199. primitive._addHeightCallbacks.push(data);
  200. }
  201. levelZeroTiles[i].freeResources();
  202. }
  203. }
  204. primitive._levelZeroTiles = undefined;
  205. primitive._tileProvider.cancelReprojections();
  206. }
  207. /**
  208. * Invokes a specified function for each {@link QuadtreeTile} that is partially
  209. * or completely loaded.
  210. *
  211. * @param {Function} tileFunction The function to invoke for each loaded tile. The
  212. * function is passed a reference to the tile as its only parameter.
  213. */
  214. QuadtreePrimitive.prototype.forEachLoadedTile = function(tileFunction) {
  215. var tile = this._tileReplacementQueue.head;
  216. while (defined(tile)) {
  217. if (tile.state !== QuadtreeTileLoadState.START) {
  218. tileFunction(tile);
  219. }
  220. tile = tile.replacementNext;
  221. }
  222. };
  223. /**
  224. * Invokes a specified function for each {@link QuadtreeTile} that was rendered
  225. * in the most recent frame.
  226. *
  227. * @param {Function} tileFunction The function to invoke for each rendered tile. The
  228. * function is passed a reference to the tile as its only parameter.
  229. */
  230. QuadtreePrimitive.prototype.forEachRenderedTile = function(tileFunction) {
  231. var tilesRendered = this._tilesToRender;
  232. for (var i = 0, len = tilesRendered.length; i < len; ++i) {
  233. tileFunction(tilesRendered[i]);
  234. }
  235. };
  236. /**
  237. * Calls the callback when a new tile is rendered that contains the given cartographic. The only parameter
  238. * is the cartesian position on the tile.
  239. *
  240. * @param {Cartographic} cartographic The cartographic position.
  241. * @param {Function} callback The function to be called when a new tile is loaded containing cartographic.
  242. * @returns {Function} The function to remove this callback from the quadtree.
  243. */
  244. QuadtreePrimitive.prototype.updateHeight = function(cartographic, callback) {
  245. var primitive = this;
  246. var object = {
  247. positionOnEllipsoidSurface : undefined,
  248. positionCartographic : cartographic,
  249. level : -1,
  250. callback : callback
  251. };
  252. object.removeFunc = function() {
  253. var addedCallbacks = primitive._addHeightCallbacks;
  254. var length = addedCallbacks.length;
  255. for (var i = 0; i < length; ++i) {
  256. if (addedCallbacks[i] === object) {
  257. addedCallbacks.splice(i, 1);
  258. break;
  259. }
  260. }
  261. primitive._removeHeightCallbacks.push(object);
  262. };
  263. primitive._addHeightCallbacks.push(object);
  264. return object.removeFunc;
  265. };
  266. /**
  267. * Updates the tile provider imagery and continues to process the tile load queue.
  268. * @private
  269. */
  270. QuadtreePrimitive.prototype.update = function(frameState) {
  271. if (defined(this._tileProvider.update)) {
  272. this._tileProvider.update(frameState);
  273. }
  274. };
  275. function clearTileLoadQueue(primitive) {
  276. var debug = primitive._debug;
  277. debug.maxDepth = 0;
  278. debug.maxDepthVisited = 0;
  279. debug.tilesVisited = 0;
  280. debug.tilesCulled = 0;
  281. debug.tilesRendered = 0;
  282. debug.tilesWaitingForChildren = 0;
  283. primitive._tileLoadQueueHigh.length = 0;
  284. primitive._tileLoadQueueMedium.length = 0;
  285. primitive._tileLoadQueueLow.length = 0;
  286. }
  287. /**
  288. * Initializes values for a new render frame and prepare the tile load queue.
  289. * @private
  290. */
  291. QuadtreePrimitive.prototype.beginFrame = function(frameState) {
  292. var passes = frameState.passes;
  293. if (!passes.render) {
  294. return;
  295. }
  296. if (this._tilesInvalidated) {
  297. invalidateAllTiles(this);
  298. this._tilesInvalidated = false;
  299. }
  300. // Gets commands for any texture re-projections
  301. this._tileProvider.initialize(frameState);
  302. clearTileLoadQueue(this);
  303. if (this._debug.suspendLodUpdate) {
  304. return;
  305. }
  306. this._tileReplacementQueue.markStartOfRenderFrame();
  307. };
  308. /**
  309. * Selects new tiles to load based on the frame state and creates render commands.
  310. * @private
  311. */
  312. QuadtreePrimitive.prototype.render = function(frameState) {
  313. var passes = frameState.passes;
  314. var tileProvider = this._tileProvider;
  315. if (passes.render) {
  316. tileProvider.beginUpdate(frameState);
  317. selectTilesForRendering(this, frameState);
  318. createRenderCommandsForSelectedTiles(this, frameState);
  319. tileProvider.endUpdate(frameState);
  320. }
  321. if (passes.pick && this._tilesToRender.length > 0) {
  322. tileProvider.updateForPick(frameState);
  323. }
  324. };
  325. /**
  326. * Checks if the load queue length has changed since the last time we raised a queue change event - if so, raises
  327. * a new change event at the end of the render cycle.
  328. */
  329. function updateTileLoadProgress(primitive, frameState) {
  330. var currentLoadQueueLength = primitive._tileLoadQueueHigh.length + primitive._tileLoadQueueMedium.length + primitive._tileLoadQueueLow.length;
  331. if (currentLoadQueueLength !== primitive._lastTileLoadQueueLength || primitive._tilesInvalidated) {
  332. frameState.afterRender.push(Event.prototype.raiseEvent.bind(primitive._tileLoadProgressEvent, currentLoadQueueLength));
  333. primitive._lastTileLoadQueueLength = currentLoadQueueLength;
  334. }
  335. var debug = primitive._debug;
  336. if (debug.enableDebugOutput && !debug.suspendLodUpdate) {
  337. debug.maxDepth = primitive._tilesToRender.reduce(function(max, tile) {
  338. return Math.max(max, tile.level);
  339. }, -1);
  340. debug.tilesRendered = primitive._tilesToRender.length;
  341. if (debug.tilesVisited !== debug.lastTilesVisited ||
  342. debug.tilesRendered !== debug.lastTilesRendered ||
  343. debug.tilesCulled !== debug.lastTilesCulled ||
  344. debug.maxDepth !== debug.lastMaxDepth ||
  345. debug.tilesWaitingForChildren !== debug.lastTilesWaitingForChildren ||
  346. debug.maxDepthVisited !== debug.lastMaxDepthVisited) {
  347. console.log('Visited ' + debug.tilesVisited + ', Rendered: ' + debug.tilesRendered + ', Culled: ' + debug.tilesCulled + ', Max Depth Rendered: ' + debug.maxDepth + ', Max Depth Visited: ' + debug.maxDepthVisited + ', Waiting for children: ' + debug.tilesWaitingForChildren);
  348. debug.lastTilesVisited = debug.tilesVisited;
  349. debug.lastTilesRendered = debug.tilesRendered;
  350. debug.lastTilesCulled = debug.tilesCulled;
  351. debug.lastMaxDepth = debug.maxDepth;
  352. debug.lastTilesWaitingForChildren = debug.tilesWaitingForChildren;
  353. debug.lastMaxDepthVisited = debug.maxDepthVisited;
  354. }
  355. }
  356. }
  357. /**
  358. * Updates terrain heights.
  359. * @private
  360. */
  361. QuadtreePrimitive.prototype.endFrame = function(frameState) {
  362. var passes = frameState.passes;
  363. if (!passes.render || frameState.mode === SceneMode.MORPHING) {
  364. // Only process the load queue for a single pass.
  365. // Don't process the load queue or update heights during the morph flights.
  366. return;
  367. }
  368. // Load/create resources for terrain and imagery. Prepare texture re-projections for the next frame.
  369. processTileLoadQueue(this, frameState);
  370. updateHeights(this, frameState);
  371. updateTileLoadProgress(this, frameState);
  372. };
  373. /**
  374. * Returns true if this object was destroyed; otherwise, false.
  375. * <br /><br />
  376. * If this object was destroyed, it should not be used; calling any function other than
  377. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  378. *
  379. * @memberof QuadtreePrimitive
  380. *
  381. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  382. *
  383. * @see QuadtreePrimitive#destroy
  384. */
  385. QuadtreePrimitive.prototype.isDestroyed = function() {
  386. return false;
  387. };
  388. /**
  389. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  390. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  391. * <br /><br />
  392. * Once an object is destroyed, it should not be used; calling any function other than
  393. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  394. * assign the return value (<code>undefined</code>) to the object as done in the example.
  395. *
  396. * @memberof QuadtreePrimitive
  397. *
  398. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  399. *
  400. *
  401. * @example
  402. * primitive = primitive && primitive.destroy();
  403. *
  404. * @see QuadtreePrimitive#isDestroyed
  405. */
  406. QuadtreePrimitive.prototype.destroy = function() {
  407. this._tileProvider = this._tileProvider && this._tileProvider.destroy();
  408. };
  409. var comparisonPoint;
  410. var centerScratch = new Cartographic();
  411. function compareDistanceToPoint(a, b) {
  412. var center = Rectangle.center(a.rectangle, centerScratch);
  413. var alon = center.longitude - comparisonPoint.longitude;
  414. var alat = center.latitude - comparisonPoint.latitude;
  415. center = Rectangle.center(b.rectangle, centerScratch);
  416. var blon = center.longitude - comparisonPoint.longitude;
  417. var blat = center.latitude - comparisonPoint.latitude;
  418. return (alon * alon + alat * alat) - (blon * blon + blat * blat);
  419. }
  420. var cameraOriginScratch = new Cartesian3();
  421. var rootTraversalDetails = [];
  422. function selectTilesForRendering(primitive, frameState) {
  423. var debug = primitive._debug;
  424. if (debug.suspendLodUpdate) {
  425. return;
  426. }
  427. // Clear the render list.
  428. var tilesToRender = primitive._tilesToRender;
  429. tilesToRender.length = 0;
  430. // We can't render anything before the level zero tiles exist.
  431. var i;
  432. var tileProvider = primitive._tileProvider;
  433. if (!defined(primitive._levelZeroTiles)) {
  434. if (tileProvider.ready) {
  435. var tilingScheme = tileProvider.tilingScheme;
  436. primitive._levelZeroTiles = QuadtreeTile.createLevelZeroTiles(tilingScheme);
  437. var numberOfRootTiles = primitive._levelZeroTiles.length;
  438. if (rootTraversalDetails.length < numberOfRootTiles) {
  439. rootTraversalDetails = new Array(numberOfRootTiles);
  440. for (i = 0; i < numberOfRootTiles; ++i) {
  441. if (rootTraversalDetails[i] === undefined) {
  442. rootTraversalDetails[i] = new TraversalDetails();
  443. }
  444. }
  445. }
  446. } else {
  447. // Nothing to do until the provider is ready.
  448. return;
  449. }
  450. }
  451. primitive._occluders.ellipsoid.cameraPosition = frameState.camera.positionWC;
  452. var tile;
  453. var levelZeroTiles = primitive._levelZeroTiles;
  454. var occluders = levelZeroTiles.length > 1 ? primitive._occluders : undefined;
  455. // Sort the level zero tiles by the distance from the center to the camera.
  456. // The level zero tiles aren't necessarily a nice neat quad, so we can't use the
  457. // quadtree ordering we use elsewhere in the tree
  458. comparisonPoint = frameState.camera.positionCartographic;
  459. levelZeroTiles.sort(compareDistanceToPoint);
  460. var customDataAdded = primitive._addHeightCallbacks;
  461. var customDataRemoved = primitive._removeHeightCallbacks;
  462. var frameNumber = frameState.frameNumber;
  463. var len;
  464. if (customDataAdded.length > 0 || customDataRemoved.length > 0) {
  465. for (i = 0, len = levelZeroTiles.length; i < len; ++i) {
  466. tile = levelZeroTiles[i];
  467. tile._updateCustomData(frameNumber, customDataAdded, customDataRemoved);
  468. }
  469. customDataAdded.length = 0;
  470. customDataRemoved.length = 0;
  471. }
  472. var camera = frameState.camera;
  473. primitive._cameraPositionCartographic = camera.positionCartographic;
  474. var cameraFrameOrigin = Matrix4.getTranslation(camera.transform, cameraOriginScratch);
  475. primitive._cameraReferenceFrameOriginCartographic = primitive.tileProvider.tilingScheme.ellipsoid.cartesianToCartographic(cameraFrameOrigin, primitive._cameraReferenceFrameOriginCartographic);
  476. // Traverse in depth-first, near-to-far order.
  477. for (i = 0, len = levelZeroTiles.length; i < len; ++i) {
  478. tile = levelZeroTiles[i];
  479. primitive._tileReplacementQueue.markTileRendered(tile);
  480. if (!tile.renderable) {
  481. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  482. ++debug.tilesWaitingForChildren;
  483. } else {
  484. visitIfVisible(primitive, tile, tileProvider, frameState, occluders, false, rootTraversalDetails[i]);
  485. }
  486. }
  487. primitive._lastSelectionFrameNumber = frameNumber;
  488. }
  489. function queueTileLoad(primitive, queue, tile, frameState) {
  490. if (!tile.needsLoading) {
  491. return;
  492. }
  493. if (primitive.tileProvider.computeTileLoadPriority !== undefined) {
  494. tile._loadPriority = primitive.tileProvider.computeTileLoadPriority(tile, frameState);
  495. }
  496. queue.push(tile);
  497. }
  498. /**
  499. * Tracks details of traversing a tile while selecting tiles for rendering.
  500. * @alias TraversalDetails
  501. * @constructor
  502. * @private
  503. */
  504. function TraversalDetails() {
  505. /**
  506. * True if all selected (i.e. not culled or refined) tiles in this tile's subtree
  507. * are renderable. If the subtree is renderable, we'll render it; no drama.
  508. */
  509. this.allAreRenderable = true;
  510. /**
  511. * True if any tiles in this tile's subtree were rendered last frame. If any
  512. * were, we must render the subtree rather than this tile, because rendering
  513. * this tile would cause detail to vanish that was visible last frame, and
  514. * that's no good.
  515. */
  516. this.anyWereRenderedLastFrame = false;
  517. /**
  518. * Counts the number of selected tiles in this tile's subtree that are
  519. * not yet ready to be rendered because they need more loading. Note that
  520. * this value will _not_ necessarily be zero when
  521. * {@link TraversalDetails#allAreRenderable} is true, for subtle reasons.
  522. * When {@link TraversalDetails#allAreRenderable} and
  523. * {@link TraversalDetails#anyWereRenderedLastFrame} are both false, we
  524. * will render this tile instead of any tiles in its subtree and
  525. * the `allAreRenderable` value for this tile will reflect only whether _this_
  526. * tile is renderable. The `notYetRenderableCount` value, however, will still
  527. * reflect the total number of tiles that we are waiting on, including the
  528. * ones that we're not rendering. `notYetRenderableCount` is only reset
  529. * when a subtree is removed from the render queue because the
  530. * `notYetRenderableCount` exceeds the
  531. * {@link QuadtreePrimitive#loadingDescendantLimit}.
  532. */
  533. this.notYetRenderableCount = 0;
  534. }
  535. function TraversalQuadDetails() {
  536. this.southwest = new TraversalDetails();
  537. this.southeast = new TraversalDetails();
  538. this.northwest = new TraversalDetails();
  539. this.northeast = new TraversalDetails();
  540. }
  541. TraversalQuadDetails.prototype.combine = function(result) {
  542. var southwest = this.southwest;
  543. var southeast = this.southeast;
  544. var northwest = this.northwest;
  545. var northeast = this.northeast;
  546. result.allAreRenderable = southwest.allAreRenderable && southeast.allAreRenderable && northwest.allAreRenderable && northeast.allAreRenderable;
  547. result.anyWereRenderedLastFrame = southwest.anyWereRenderedLastFrame || southeast.anyWereRenderedLastFrame || northwest.anyWereRenderedLastFrame || northeast.anyWereRenderedLastFrame;
  548. result.notYetRenderableCount = southwest.notYetRenderableCount + southeast.notYetRenderableCount + northwest.notYetRenderableCount + northeast.notYetRenderableCount;
  549. };
  550. var traversalQuadsByLevel = new Array(31); // level 30 tiles are ~2cm wide at the equator, should be good enough.
  551. for (var i = 0; i < traversalQuadsByLevel.length; ++i) {
  552. traversalQuadsByLevel[i] = new TraversalQuadDetails();
  553. }
  554. /**
  555. * Visits a tile for possible rendering. When we call this function with a tile:
  556. *
  557. * * the tile has been determined to be visible (possibly based on a bounding volume that is not very tight-fitting)
  558. * * its parent tile does _not_ meet the SSE (unless ancestorMeetsSse=true, see comments below)
  559. * * the tile may or may not be renderable
  560. *
  561. * @private
  562. *
  563. * @param {Primitive} primitive The QuadtreePrimitive.
  564. * @param {FrameState} frameState The frame state.
  565. * @param {QuadtreeTile} tile The tile to visit
  566. * @param {Boolean} ancestorMeetsSse True if a tile higher in the tile tree already met the SSE and we're refining further only
  567. * to maintain detail while that higher tile loads.
  568. * @param {TraversalDetails} traveralDetails On return, populated with details of how the traversal of this tile went.
  569. */
  570. function visitTile(primitive, frameState, tile, ancestorMeetsSse, traversalDetails) {
  571. var debug = primitive._debug;
  572. ++debug.tilesVisited;
  573. primitive._tileReplacementQueue.markTileRendered(tile);
  574. tile._updateCustomData(frameState.frameNumber);
  575. if (tile.level > debug.maxDepthVisited) {
  576. debug.maxDepthVisited = tile.level;
  577. }
  578. var meetsSse = screenSpaceError(primitive, frameState, tile) < primitive.maximumScreenSpaceError;
  579. var southwestChild = tile.southwestChild;
  580. var southeastChild = tile.southeastChild;
  581. var northwestChild = tile.northwestChild;
  582. var northeastChild = tile.northeastChild;
  583. var lastFrame = primitive._lastSelectionFrameNumber;
  584. var lastFrameSelectionResult = tile._lastSelectionResultFrame === lastFrame ? tile._lastSelectionResult : TileSelectionResult.NONE;
  585. var tileProvider = primitive.tileProvider;
  586. if (meetsSse || ancestorMeetsSse) {
  587. // This tile (or an ancestor) is the one we want to render this frame, but we'll do different things depending
  588. // on the state of this tile and on what we did _last_ frame.
  589. // We can render it if _any_ of the following are true:
  590. // 1. We rendered it (or kicked it) last frame.
  591. // 2. This tile was culled last frame, or it wasn't even visited because an ancestor was culled.
  592. // 3. The tile is completely done loading.
  593. // 4. a) Terrain is ready, and
  594. // b) All necessary imagery is ready. Necessary imagery is imagery that was rendered with this tile
  595. // or any descendants last frame. Such imagery is required because rendering this tile without
  596. // it would cause detail to disappear.
  597. //
  598. // Determining condition 4 is more expensive, so we check the others first.
  599. //
  600. // Note that even if we decide to render a tile here, it may later get "kicked" in favor of an ancestor.
  601. var oneRenderedLastFrame = TileSelectionResult.originalResult(lastFrameSelectionResult) === TileSelectionResult.RENDERED;
  602. var twoCulledOrNotVisited = TileSelectionResult.originalResult(lastFrameSelectionResult) === TileSelectionResult.CULLED || lastFrameSelectionResult === TileSelectionResult.NONE;
  603. var threeCompletelyLoaded = tile.state === QuadtreeTileLoadState.DONE;
  604. var renderable = oneRenderedLastFrame || twoCulledOrNotVisited || threeCompletelyLoaded;
  605. if (!renderable) {
  606. // Check the more expensive condition 4 above. This requires details of the thing
  607. // we're rendering (e.g. the globe surface), so delegate it to the tile provider.
  608. if (defined(tileProvider.canRenderWithoutLosingDetail)) {
  609. renderable = tileProvider.canRenderWithoutLosingDetail(tile);
  610. }
  611. }
  612. if (renderable) {
  613. // Only load this tile if it (not just an ancestor) meets the SSE.
  614. if (meetsSse) {
  615. queueTileLoad(primitive, primitive._tileLoadQueueMedium, tile, frameState);
  616. }
  617. addTileToRenderList(primitive, tile);
  618. traversalDetails.allAreRenderable = tile.renderable;
  619. traversalDetails.anyWereRenderedLastFrame = lastFrameSelectionResult === TileSelectionResult.RENDERED;
  620. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  621. tile._lastSelectionResultFrame = frameState.frameNumber;
  622. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  623. if (!traversalDetails.anyWereRenderedLastFrame) {
  624. // Tile is newly-rendered this frame, so update its heights.
  625. primitive._tileToUpdateHeights.push(tile);
  626. }
  627. return;
  628. }
  629. // Otherwise, we can't render this tile (or its fill) because doing so would cause detail to disappear
  630. // that was visible last frame. Instead, keep rendering any still-visible descendants that were rendered
  631. // last frame and render fills for newly-visible descendants. E.g. if we were rendering level 15 last
  632. // frame but this frame we want level 14 and the closest renderable level <= 14 is 0, rendering level
  633. // zero would be pretty jarring so instead we keep rendering level 15 even though its SSE is better
  634. // than required. So fall through to continue traversal...
  635. ancestorMeetsSse = true;
  636. // Load this blocker tile with high priority, but only if this tile (not just an ancestor) meets the SSE.
  637. if (meetsSse) {
  638. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  639. }
  640. }
  641. if (tileProvider.canRefine(tile)) {
  642. var allAreUpsampled = southwestChild.upsampledFromParent && southeastChild.upsampledFromParent &&
  643. northwestChild.upsampledFromParent && northeastChild.upsampledFromParent;
  644. if (allAreUpsampled) {
  645. // No point in rendering the children because they're all upsampled. Render this tile instead.
  646. addTileToRenderList(primitive, tile);
  647. // Rendered tile that's not waiting on children loads with medium priority.
  648. queueTileLoad(primitive, primitive._tileLoadQueueMedium, tile, frameState);
  649. // Make sure we don't unload the children and forget they're upsampled.
  650. primitive._tileReplacementQueue.markTileRendered(southwestChild);
  651. primitive._tileReplacementQueue.markTileRendered(southeastChild);
  652. primitive._tileReplacementQueue.markTileRendered(northwestChild);
  653. primitive._tileReplacementQueue.markTileRendered(northeastChild);
  654. traversalDetails.allAreRenderable = tile.renderable;
  655. traversalDetails.anyWereRenderedLastFrame = lastFrameSelectionResult === TileSelectionResult.RENDERED;
  656. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  657. tile._lastSelectionResultFrame = frameState.frameNumber;
  658. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  659. if (!traversalDetails.anyWereRenderedLastFrame) {
  660. // Tile is newly-rendered this frame, so update its heights.
  661. primitive._tileToUpdateHeights.push(tile);
  662. }
  663. return;
  664. }
  665. // SSE is not good enough, so refine.
  666. tile._lastSelectionResultFrame = frameState.frameNumber;
  667. tile._lastSelectionResult = TileSelectionResult.REFINED;
  668. var firstRenderedDescendantIndex = primitive._tilesToRender.length;
  669. var loadIndexLow = primitive._tileLoadQueueLow.length;
  670. var loadIndexMedium = primitive._tileLoadQueueMedium.length;
  671. var loadIndexHigh = primitive._tileLoadQueueHigh.length;
  672. var tilesToUpdateHeightsIndex = primitive._tileToUpdateHeights.length;
  673. // No need to add the children to the load queue because they'll be added (if necessary) when they're visited.
  674. visitVisibleChildrenNearToFar(primitive, southwestChild, southeastChild, northwestChild, northeastChild, frameState, ancestorMeetsSse, traversalDetails);
  675. // If no descendant tiles were added to the render list by the function above, it means they were all
  676. // culled even though this tile was deemed visible. That's pretty common.
  677. if (firstRenderedDescendantIndex !== primitive._tilesToRender.length) {
  678. // At least one descendant tile was added to the render list.
  679. // The traversalDetails tell us what happened while visiting the children.
  680. var allAreRenderable = traversalDetails.allAreRenderable;
  681. var anyWereRenderedLastFrame = traversalDetails.anyWereRenderedLastFrame;
  682. var notYetRenderableCount = traversalDetails.notYetRenderableCount;
  683. var queuedForLoad = false;
  684. if (!allAreRenderable && !anyWereRenderedLastFrame) {
  685. // Some of our descendants aren't ready to render yet, and none were rendered last frame,
  686. // so kick them all out of the render list and render this tile instead. Continue to load them though!
  687. // Mark the rendered descendants and their ancestors - up to this tile - as kicked.
  688. var renderList = primitive._tilesToRender;
  689. for (var i = firstRenderedDescendantIndex; i < renderList.length; ++i) {
  690. var workTile = renderList[i];
  691. while (workTile !== undefined && workTile._lastSelectionResult !== TileSelectionResult.KICKED && workTile !== tile) {
  692. workTile._lastSelectionResult = TileSelectionResult.kick(workTile._lastSelectionResult);
  693. workTile = workTile.parent;
  694. }
  695. }
  696. // Remove all descendants from the render list and add this tile.
  697. primitive._tilesToRender.length = firstRenderedDescendantIndex;
  698. primitive._tileToUpdateHeights.length = tilesToUpdateHeightsIndex;
  699. addTileToRenderList(primitive, tile);
  700. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  701. // If we're waiting on heaps of descendants, the above will take too long. So in that case,
  702. // load this tile INSTEAD of loading any of the descendants, and tell the up-level we're only waiting
  703. // on this tile. Keep doing this until we actually manage to render this tile.
  704. var wasRenderedLastFrame = lastFrameSelectionResult === TileSelectionResult.RENDERED;
  705. if (!wasRenderedLastFrame && notYetRenderableCount > primitive.loadingDescendantLimit) {
  706. // Remove all descendants from the load queues.
  707. primitive._tileLoadQueueLow.length = loadIndexLow;
  708. primitive._tileLoadQueueMedium.length = loadIndexMedium;
  709. primitive._tileLoadQueueHigh.length = loadIndexHigh;
  710. queueTileLoad(primitive, primitive._tileLoadQueueMedium, tile, frameState);
  711. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  712. queuedForLoad = true;
  713. }
  714. traversalDetails.allAreRenderable = tile.renderable;
  715. traversalDetails.anyWereRenderedLastFrame = wasRenderedLastFrame;
  716. if (!wasRenderedLastFrame) {
  717. // Tile is newly-rendered this frame, so update its heights.
  718. primitive._tileToUpdateHeights.push(tile);
  719. }
  720. ++debug.tilesWaitingForChildren;
  721. }
  722. if (primitive.preloadAncestors && !queuedForLoad) {
  723. queueTileLoad(primitive, primitive._tileLoadQueueLow, tile, frameState);
  724. }
  725. }
  726. return;
  727. }
  728. tile._lastSelectionResultFrame = frameState.frameNumber;
  729. tile._lastSelectionResult = TileSelectionResult.RENDERED;
  730. // We'd like to refine but can't because we have no availability data for this tile's children,
  731. // so we have no idea if refinining would involve a load or an upsample. We'll have to finish
  732. // loading this tile first in order to find that out, so load this refinement blocker with
  733. // high priority.
  734. addTileToRenderList(primitive, tile);
  735. queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
  736. traversalDetails.allAreRenderable = tile.renderable;
  737. traversalDetails.anyWereRenderedLastFrame = lastFrameSelectionResult === TileSelectionResult.RENDERED;
  738. traversalDetails.notYetRenderableCount = tile.renderable ? 0 : 1;
  739. }
  740. function visitVisibleChildrenNearToFar(primitive, southwest, southeast, northwest, northeast, frameState, ancestorMeetsSse, traversalDetails) {
  741. var cameraPosition = frameState.camera.positionCartographic;
  742. var tileProvider = primitive._tileProvider;
  743. var occluders = primitive._occluders;
  744. var quadDetails = traversalQuadsByLevel[southwest.level];
  745. var southwestDetails = quadDetails.southwest;
  746. var southeastDetails = quadDetails.southeast;
  747. var northwestDetails = quadDetails.northwest;
  748. var northeastDetails = quadDetails.northeast;
  749. if (cameraPosition.longitude < southwest.rectangle.east) {
  750. if (cameraPosition.latitude < southwest.rectangle.north) {
  751. // Camera in southwest quadrant
  752. visitIfVisible(primitive, southwest, tileProvider, frameState, occluders, ancestorMeetsSse, southwestDetails);
  753. visitIfVisible(primitive, southeast, tileProvider, frameState, occluders, ancestorMeetsSse, southeastDetails);
  754. visitIfVisible(primitive, northwest, tileProvider, frameState, occluders, ancestorMeetsSse, northwestDetails);
  755. visitIfVisible(primitive, northeast, tileProvider, frameState, occluders, ancestorMeetsSse, northeastDetails);
  756. } else {
  757. // Camera in northwest quadrant
  758. visitIfVisible(primitive, northwest, tileProvider, frameState, occluders, ancestorMeetsSse, northwestDetails);
  759. visitIfVisible(primitive, southwest, tileProvider, frameState, occluders, ancestorMeetsSse, southwestDetails);
  760. visitIfVisible(primitive, northeast, tileProvider, frameState, occluders, ancestorMeetsSse, northeastDetails);
  761. visitIfVisible(primitive, southeast, tileProvider, frameState, occluders, ancestorMeetsSse, southeastDetails);
  762. }
  763. } else if (cameraPosition.latitude < southwest.rectangle.north) {
  764. // Camera southeast quadrant
  765. visitIfVisible(primitive, southeast, tileProvider, frameState, occluders, ancestorMeetsSse, southeastDetails);
  766. visitIfVisible(primitive, southwest, tileProvider, frameState, occluders, ancestorMeetsSse, southwestDetails);
  767. visitIfVisible(primitive, northeast, tileProvider, frameState, occluders, ancestorMeetsSse, northeastDetails);
  768. visitIfVisible(primitive, northwest, tileProvider, frameState, occluders, ancestorMeetsSse, northwestDetails);
  769. } else {
  770. // Camera in northeast quadrant
  771. visitIfVisible(primitive, northeast, tileProvider, frameState, occluders, ancestorMeetsSse, northeastDetails);
  772. visitIfVisible(primitive, northwest, tileProvider, frameState, occluders, ancestorMeetsSse, northwestDetails);
  773. visitIfVisible(primitive, southeast, tileProvider, frameState, occluders, ancestorMeetsSse, southeastDetails);
  774. visitIfVisible(primitive, southwest, tileProvider, frameState, occluders, ancestorMeetsSse, southwestDetails);
  775. }
  776. quadDetails.combine(traversalDetails);
  777. }
  778. function containsNeededPosition(primitive, tile) {
  779. var rectangle = tile.rectangle;
  780. return (defined(primitive._cameraPositionCartographic) && Rectangle.contains(rectangle, primitive._cameraPositionCartographic)) ||
  781. (defined(primitive._cameraReferenceFrameOriginCartographic) && Rectangle.contains(rectangle, primitive._cameraReferenceFrameOriginCartographic));
  782. }
  783. function visitIfVisible(primitive, tile, tileProvider, frameState, occluders, ancestorMeetsSse, traversalDetails) {
  784. if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) {
  785. return visitTile(primitive, frameState, tile, ancestorMeetsSse, traversalDetails);
  786. }
  787. ++primitive._debug.tilesCulled;
  788. primitive._tileReplacementQueue.markTileRendered(tile);
  789. traversalDetails.allAreRenderable = true;
  790. traversalDetails.anyWereRenderedLastFrame = false;
  791. traversalDetails.notYetRenderableCount = 0;
  792. if (containsNeededPosition(primitive, tile)) {
  793. // Load the tile(s) that contains the camera's position and
  794. // the origin of its reference frame with medium priority.
  795. // But we only need to load until the terrain is available, no need to load imagery.
  796. if (!defined(tile.data) || !defined(tile.data.vertexArray)) {
  797. queueTileLoad(primitive, primitive._tileLoadQueueMedium, tile, frameState);
  798. }
  799. var lastFrame = primitive._lastSelectionFrameNumber;
  800. var lastFrameSelectionResult = tile._lastSelectionResultFrame === lastFrame ? tile._lastSelectionResult : TileSelectionResult.NONE;
  801. if (lastFrameSelectionResult !== TileSelectionResult.CULLED_BUT_NEEDED && lastFrameSelectionResult !== TileSelectionResult.RENDERED) {
  802. primitive._tileToUpdateHeights.push(tile);
  803. }
  804. tile._lastSelectionResult = TileSelectionResult.CULLED_BUT_NEEDED;
  805. } else if (primitive.preloadSiblings || tile.level === 0) {
  806. // Load culled level zero tiles with low priority.
  807. // For all other levels, only load culled tiles if preloadSiblings is enabled.
  808. queueTileLoad(primitive, primitive._tileLoadQueueLow, tile, frameState);
  809. tile._lastSelectionResult = TileSelectionResult.CULLED;
  810. } else {
  811. tile._lastSelectionResult = TileSelectionResult.CULLED;
  812. }
  813. tile._lastSelectionResultFrame = frameState.frameNumber;
  814. }
  815. function screenSpaceError(primitive, frameState, tile) {
  816. if (frameState.mode === SceneMode.SCENE2D || frameState.camera.frustum instanceof OrthographicFrustum || frameState.camera.frustum instanceof OrthographicOffCenterFrustum) {
  817. return screenSpaceError2D(primitive, frameState, tile);
  818. }
  819. var maxGeometricError = primitive._tileProvider.getLevelMaximumGeometricError(tile.level);
  820. var distance = tile._distance;
  821. var height = frameState.context.drawingBufferHeight;
  822. var sseDenominator = frameState.camera.frustum.sseDenominator;
  823. var error = (maxGeometricError * height) / (distance * sseDenominator);
  824. if (frameState.fog.enabled) {
  825. error -= CesiumMath.fog(distance, frameState.fog.density) * frameState.fog.sse;
  826. }
  827. error /= frameState.pixelRatio;
  828. return error;
  829. }
  830. function screenSpaceError2D(primitive, frameState, tile) {
  831. var camera = frameState.camera;
  832. var frustum = camera.frustum;
  833. if (defined(frustum._offCenterFrustum)) {
  834. frustum = frustum._offCenterFrustum;
  835. }
  836. var context = frameState.context;
  837. var width = context.drawingBufferWidth;
  838. var height = context.drawingBufferHeight;
  839. var maxGeometricError = primitive._tileProvider.getLevelMaximumGeometricError(tile.level);
  840. var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height);
  841. var error = maxGeometricError / pixelSize;
  842. if (frameState.fog.enabled && frameState.mode !== SceneMode.SCENE2D) {
  843. error -= CesiumMath.fog(tile._distance, frameState.fog.density) * frameState.fog.sse;
  844. }
  845. error /= frameState.pixelRatio;
  846. return error;
  847. }
  848. function addTileToRenderList(primitive, tile) {
  849. primitive._tilesToRender.push(tile);
  850. }
  851. function processTileLoadQueue(primitive, frameState) {
  852. var tileLoadQueueHigh = primitive._tileLoadQueueHigh;
  853. var tileLoadQueueMedium = primitive._tileLoadQueueMedium;
  854. var tileLoadQueueLow = primitive._tileLoadQueueLow;
  855. if (tileLoadQueueHigh.length === 0 && tileLoadQueueMedium.length === 0 && tileLoadQueueLow.length === 0) {
  856. return;
  857. }
  858. // Remove any tiles that were not used this frame beyond the number
  859. // we're allowed to keep.
  860. primitive._tileReplacementQueue.trimTiles(primitive.tileCacheSize);
  861. var endTime = getTimestamp() + primitive._loadQueueTimeSlice;
  862. var tileProvider = primitive._tileProvider;
  863. var didSomeLoading = processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueHigh, false);
  864. didSomeLoading = processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueMedium, didSomeLoading);
  865. processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow, didSomeLoading);
  866. }
  867. function sortByLoadPriority(a, b) {
  868. return a._loadPriority - b._loadPriority;
  869. }
  870. function processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, loadQueue, didSomeLoading) {
  871. if (tileProvider.computeTileLoadPriority !== undefined) {
  872. loadQueue.sort(sortByLoadPriority);
  873. }
  874. for (var i = 0, len = loadQueue.length; i < len && (getTimestamp() < endTime || !didSomeLoading); ++i) {
  875. var tile = loadQueue[i];
  876. primitive._tileReplacementQueue.markTileRendered(tile);
  877. tileProvider.loadTile(frameState, tile);
  878. didSomeLoading = true;
  879. }
  880. return didSomeLoading;
  881. }
  882. var scratchRay = new Ray();
  883. var scratchCartographic = new Cartographic();
  884. var scratchPosition = new Cartesian3();
  885. var scratchArray = [];
  886. function updateHeights(primitive, frameState) {
  887. if (!primitive.tileProvider.ready) {
  888. return;
  889. }
  890. var tryNextFrame = scratchArray;
  891. tryNextFrame.length = 0;
  892. var tilesToUpdateHeights = primitive._tileToUpdateHeights;
  893. var terrainProvider = primitive._tileProvider.terrainProvider;
  894. var startTime = getTimestamp();
  895. var timeSlice = primitive._updateHeightsTimeSlice;
  896. var endTime = startTime + timeSlice;
  897. var mode = frameState.mode;
  898. var projection = frameState.mapProjection;
  899. var ellipsoid = primitive.tileProvider.tilingScheme.ellipsoid;
  900. var i;
  901. while (tilesToUpdateHeights.length > 0) {
  902. var tile = tilesToUpdateHeights[0];
  903. if (!defined(tile.data) || !defined(tile.data.mesh)) {
  904. // Tile isn't loaded enough yet, so try again next frame if this tile is still
  905. // being rendered.
  906. var selectionResult = tile._lastSelectionResultFrame === primitive._lastSelectionFrameNumber
  907. ? tile._lastSelectionResult
  908. : TileSelectionResult.NONE;
  909. if (selectionResult === TileSelectionResult.RENDERED || selectionResult === TileSelectionResult.CULLED_BUT_NEEDED) {
  910. tryNextFrame.push(tile);
  911. }
  912. tilesToUpdateHeights.shift();
  913. primitive._lastTileIndex = 0;
  914. continue;
  915. }
  916. var customData = tile.customData;
  917. var customDataLength = customData.length;
  918. var timeSliceMax = false;
  919. for (i = primitive._lastTileIndex; i < customDataLength; ++i) {
  920. var data = customData[i];
  921. if (tile.level > data.level) {
  922. if (!defined(data.positionOnEllipsoidSurface)) {
  923. // cartesian has to be on the ellipsoid surface for `ellipsoid.geodeticSurfaceNormal`
  924. data.positionOnEllipsoidSurface = Cartesian3.fromRadians(data.positionCartographic.longitude, data.positionCartographic.latitude, 0.0, ellipsoid);
  925. }
  926. if (mode === SceneMode.SCENE3D) {
  927. var surfaceNormal = ellipsoid.geodeticSurfaceNormal(data.positionOnEllipsoidSurface, scratchRay.direction);
  928. // compute origin point
  929. // Try to find the intersection point between the surface normal and z-axis.
  930. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  931. var rayOrigin = ellipsoid.getSurfaceNormalIntersectionWithZAxis(data.positionOnEllipsoidSurface, 11500.0, scratchRay.origin);
  932. // Theoretically, not with Earth datums, the intersection point can be outside the ellipsoid
  933. if (!defined(rayOrigin)) {
  934. // intersection point is outside the ellipsoid, try other value
  935. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  936. var minimumHeight;
  937. if (defined(tile.data.tileBoundingRegion)) {
  938. minimumHeight = tile.data.tileBoundingRegion.minimumHeight;
  939. }
  940. var magnitude = Math.min(defaultValue(minimumHeight, 0.0), -11500.0);
  941. // multiply by the *positive* value of the magnitude
  942. var vectorToMinimumPoint = Cartesian3.multiplyByScalar(surfaceNormal, Math.abs(magnitude) + 1, scratchPosition);
  943. Cartesian3.subtract(data.positionOnEllipsoidSurface, vectorToMinimumPoint, scratchRay.origin);
  944. }
  945. } else {
  946. Cartographic.clone(data.positionCartographic, scratchCartographic);
  947. // minimum height for the terrain set, need to get this information from the terrain provider
  948. scratchCartographic.height = -11500.0;
  949. projection.project(scratchCartographic, scratchPosition);
  950. Cartesian3.fromElements(scratchPosition.z, scratchPosition.x, scratchPosition.y, scratchPosition);
  951. Cartesian3.clone(scratchPosition, scratchRay.origin);
  952. Cartesian3.clone(Cartesian3.UNIT_X, scratchRay.direction);
  953. }
  954. var position = tile.data.pick(scratchRay, mode, projection, false, scratchPosition);
  955. if (defined(position)) {
  956. data.callback(position);
  957. data.level = tile.level;
  958. }
  959. } else if (tile.level === data.level) {
  960. var children = tile.children;
  961. var childrenLength = children.length;
  962. var child;
  963. for (var j = 0; j < childrenLength; ++j) {
  964. child = children[j];
  965. if (Rectangle.contains(child.rectangle, data.positionCartographic)) {
  966. break;
  967. }
  968. }
  969. var tileDataAvailable = terrainProvider.getTileDataAvailable(child.x, child.y, child.level);
  970. var parentTile = tile.parent;
  971. if ((defined(tileDataAvailable) && !tileDataAvailable) ||
  972. (defined(parentTile) && defined(parentTile.data) && defined(parentTile.data.terrainData) &&
  973. !parentTile.data.terrainData.isChildAvailable(parentTile.x, parentTile.y, child.x, child.y))) {
  974. data.removeFunc();
  975. }
  976. }
  977. if (getTimestamp() >= endTime) {
  978. timeSliceMax = true;
  979. break;
  980. }
  981. }
  982. if (timeSliceMax) {
  983. primitive._lastTileIndex = i;
  984. break;
  985. } else {
  986. primitive._lastTileIndex = 0;
  987. tilesToUpdateHeights.shift();
  988. }
  989. }
  990. for (i = 0; i < tryNextFrame.length; i++) {
  991. tilesToUpdateHeights.push(tryNextFrame[i]);
  992. }
  993. }
  994. function createRenderCommandsForSelectedTiles(primitive, frameState) {
  995. var tileProvider = primitive._tileProvider;
  996. var tilesToRender = primitive._tilesToRender;
  997. for (var i = 0, len = tilesToRender.length; i < len; ++i) {
  998. var tile = tilesToRender[i];
  999. tileProvider.showTileThisFrame(tile, frameState);
  1000. }
  1001. }
  1002. export default QuadtreePrimitive;