123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- import defined from "../Core/defined.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import Intersect from "../Core/Intersect.js";
- import Cesium3DTileOptimizationHint from "./Cesium3DTileOptimizationHint.js";
- import Cesium3DTileRefine from "./Cesium3DTileRefine.js";
- /**
- * Traverses a {@link Cesium3DTileset} to determine which tiles to load and render.
- * This type describes an interface and is not intended to be instantiated directly.
- *
- * @alias Cesium3DTilesetTraversal
- * @constructor
- * @abstract
- *
- * @see Cesium3DTilesetBaseTraversal
- * @see Cesium3DTilesetSkipTraversal
- * @see Cesium3DTilesetMostDetailedTraversal
- *
- * @private
- */
- function Cesium3DTilesetTraversal() {}
- /**
- * Traverses a {@link Cesium3DTileset} to determine which tiles to load and render.
- *
- * @private
- * @param {Cesium3DTileset} tileset
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.selectTiles = function (tileset, frameState) {
- DeveloperError.throwInstantiationError();
- };
- /**
- * Sort by farthest child first since this is going on a stack
- *
- * @private
- * @param {Cesium3DTile} a
- * @param {Cesium3DTile} b
- * @returns {number}
- */
- Cesium3DTilesetTraversal.sortChildrenByDistanceToCamera = function (a, b) {
- if (b._distanceToCamera === 0 && a._distanceToCamera === 0) {
- return b._centerZDepth - a._centerZDepth;
- }
- return b._distanceToCamera - a._distanceToCamera;
- };
- /**
- * Determine if a tile can and should be traversed for children tiles that
- * would contribute to rendering the current view
- *
- * @private
- * @param {Cesium3DTile} tile
- * @returns {boolean}
- */
- Cesium3DTilesetTraversal.canTraverse = function (tile) {
- if (tile.children.length === 0) {
- return false;
- }
- if (tile.hasTilesetContent || tile.hasImplicitContent) {
- // Traverse external tileset to visit its root tile
- // Don't traverse if the subtree is expired because it will be destroyed
- return !tile.contentExpired;
- }
- return tile._screenSpaceError > tile.tileset._maximumScreenSpaceError;
- };
- /**
- * Mark a tile as selected, and add it to the tileset's list of selected tiles
- *
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.selectTile = function (tile, frameState) {
- if (tile.contentVisibility(frameState) === Intersect.OUTSIDE) {
- return;
- }
- const { content, tileset } = tile;
- if (content.featurePropertiesDirty) {
- // A feature's property in this tile changed, the tile needs to be re-styled.
- content.featurePropertiesDirty = false;
- tile.lastStyleTime = 0; // Force applying the style to this tile
- tileset._selectedTilesToStyle.push(tile);
- } else if (tile._selectedFrame < frameState.frameNumber - 1) {
- // Tile is newly selected; it is selected this frame, but was not selected last frame.
- tileset._selectedTilesToStyle.push(tile);
- }
- tile._selectedFrame = frameState.frameNumber;
- tileset._selectedTiles.push(tile);
- };
- /**
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.visitTile = function (tile, frameState) {
- ++tile.tileset._statistics.visited;
- tile._visitedFrame = frameState.frameNumber;
- };
- /**
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.touchTile = function (tile, frameState) {
- if (tile._touchedFrame === frameState.frameNumber) {
- // Prevents another pass from touching the frame again
- return;
- }
- tile.tileset._cache.touch(tile);
- tile._touchedFrame = frameState.frameNumber;
- };
- /**
- * Add a tile to the list of requested tiles, if appropriate
- *
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.loadTile = function (tile, frameState) {
- const { tileset } = tile;
- if (
- tile._requestedFrame === frameState.frameNumber ||
- (!tile.hasUnloadedRenderableContent && !tile.contentExpired)
- ) {
- return;
- }
- if (!isOnScreenLongEnough(tile, frameState)) {
- return;
- }
- const cameraHasNotStoppedMovingLongEnough =
- frameState.camera.timeSinceMoved < tileset.foveatedTimeDelay;
- if (tile.priorityDeferred && cameraHasNotStoppedMovingLongEnough) {
- return;
- }
- tile._requestedFrame = frameState.frameNumber;
- tileset._requestedTiles.push(tile);
- };
- /**
- * Prevent unnecessary loads while camera is moving by getting the ratio of travel distance to tile size.
- *
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- * @returns {boolean}
- */
- function isOnScreenLongEnough(tile, frameState) {
- const { tileset } = tile;
- if (!tileset._cullRequestsWhileMoving) {
- return true;
- }
- const {
- positionWCDeltaMagnitude,
- positionWCDeltaMagnitudeLastFrame,
- } = frameState.camera;
- const deltaMagnitude =
- positionWCDeltaMagnitude !== 0.0
- ? positionWCDeltaMagnitude
- : positionWCDeltaMagnitudeLastFrame;
- // How do n frames of this movement compare to the tile's physical size.
- const diameter = Math.max(tile.boundingSphere.radius * 2.0, 1.0);
- const movementRatio =
- (tileset.cullRequestsWhileMovingMultiplier * deltaMagnitude) / diameter;
- return movementRatio < 1.0;
- }
- /**
- * Reset some of the tile's flags and re-evaluate visibility and priority
- *
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- Cesium3DTilesetTraversal.updateTile = function (tile, frameState) {
- updateTileVisibility(tile, frameState);
- tile.updateExpiration();
- tile._wasMinPriorityChild = false;
- tile._priorityHolder = tile;
- updateMinimumMaximumPriority(tile);
- // SkipLOD
- tile._shouldSelect = false;
- tile._finalResolution = true;
- };
- /**
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- */
- function updateTileVisibility(tile, frameState) {
- tile.updateVisibility(frameState);
- if (!tile.isVisible) {
- return;
- }
- const hasChildren = tile.children.length > 0;
- if ((tile.hasTilesetContent || tile.hasImplicitContent) && hasChildren) {
- // Use the root tile's visibility instead of this tile's visibility.
- // The root tile may be culled by the children bounds optimization in which
- // case this tile should also be culled.
- const child = tile.children[0];
- updateTileVisibility(child, frameState);
- tile._visible = child._visible;
- return;
- }
- if (meetsScreenSpaceErrorEarly(tile, frameState)) {
- tile._visible = false;
- return;
- }
- // Optimization - if none of the tile's children are visible then this tile isn't visible
- const replace = tile.refine === Cesium3DTileRefine.REPLACE;
- const useOptimization =
- tile._optimChildrenWithinParent ===
- Cesium3DTileOptimizationHint.USE_OPTIMIZATION;
- if (replace && useOptimization && hasChildren) {
- if (!anyChildrenVisible(tile, frameState)) {
- ++tile.tileset._statistics.numberOfTilesCulledWithChildrenUnion;
- tile._visible = false;
- return;
- }
- }
- }
- /**
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- * @returns {boolean}
- */
- function meetsScreenSpaceErrorEarly(tile, frameState) {
- const { parent, tileset } = tile;
- if (
- !defined(parent) ||
- parent.hasTilesetContent ||
- parent.hasImplicitContent ||
- parent.refine !== Cesium3DTileRefine.ADD
- ) {
- return false;
- }
- // Use parent's geometric error with child's box to see if the tile already meet the SSE
- return (
- tile.getScreenSpaceError(frameState, true) <=
- tileset._maximumScreenSpaceError
- );
- }
- /**
- * @private
- * @param {Cesium3DTile} tile
- * @param {FrameState} frameState
- * @returns {boolean}
- */
- function anyChildrenVisible(tile, frameState) {
- let anyVisible = false;
- const children = tile.children;
- for (let i = 0; i < children.length; ++i) {
- const child = children[i];
- child.updateVisibility(frameState);
- anyVisible = anyVisible || child.isVisible;
- }
- return anyVisible;
- }
- /**
- * @private
- * @param {Cesium3DTile} tile
- */
- function updateMinimumMaximumPriority(tile) {
- const minimumPriority = tile.tileset._minimumPriority;
- const maximumPriority = tile.tileset._maximumPriority;
- const priorityHolder = tile._priorityHolder;
- maximumPriority.distance = Math.max(
- priorityHolder._distanceToCamera,
- maximumPriority.distance
- );
- minimumPriority.distance = Math.min(
- priorityHolder._distanceToCamera,
- minimumPriority.distance
- );
- maximumPriority.depth = Math.max(tile._depth, maximumPriority.depth);
- minimumPriority.depth = Math.min(tile._depth, minimumPriority.depth);
- maximumPriority.foveatedFactor = Math.max(
- priorityHolder._foveatedFactor,
- maximumPriority.foveatedFactor
- );
- minimumPriority.foveatedFactor = Math.min(
- priorityHolder._foveatedFactor,
- minimumPriority.foveatedFactor
- );
- maximumPriority.reverseScreenSpaceError = Math.max(
- tile._priorityReverseScreenSpaceError,
- maximumPriority.reverseScreenSpaceError
- );
- minimumPriority.reverseScreenSpaceError = Math.min(
- tile._priorityReverseScreenSpaceError,
- minimumPriority.reverseScreenSpaceError
- );
- }
- export default Cesium3DTilesetTraversal;
|