From d53e065e13f1f7deb31022994d8e83b4f1b0b72f Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Wed, 26 Apr 2017 10:39:44 -0400 Subject: [PATCH 01/20] initial work to load base sse first --- Source/Scene/Cesium3DTile.js | 5 + Source/Scene/Cesium3DTileset.js | 82 ++++ Source/Scene/Cesium3DTilesetTraversal.js | 476 +++++++++++++++++++++++ 3 files changed, 563 insertions(+) create mode 100644 Source/Scene/Cesium3DTilesetTraversal.js diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index ae1a1c0787e6..aafb27b1ca82 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -334,6 +334,7 @@ define([ this._optimChildrenWithinParent = Cesium3DTileOptimizationHint.NOT_COMPUTED; this._sse = 0; + this._sseComputedFrame = -1; this._finalResolution = true; this._requestHeap = undefined; this._depth = 0; @@ -343,6 +344,9 @@ define([ this._selectionDepth = 0; this._lastFinalResolution = undefined; this._lastSelectionDepth = undefined; + + this._ancestorWithContent = undefined; + this._ancestorWithLoadedContent = undefined; } defineProperties(Cesium3DTile.prototype, { @@ -624,6 +628,7 @@ define([ var box = boundingVolumeHeader.box; center = Cartesian3.fromElements(box[0], box[1], box[2], scratchCenter); var halfAxes = Matrix3.fromArray(box, 3, scratchHalfAxes); + Matrix3.multiplyByScalar(halfAxes, 0.5, halfAxes); // Find the transformed center and halfAxes center = Matrix4.multiplyByPoint(transform, center, center); diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 801e202191df..baf34e588990 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -37,6 +37,7 @@ define([ './Cesium3DTileOptimizations', './Cesium3DTileOptimizationHint', './Cesium3DTileRefine', + './Cesium3DTilesetTraversal', './Cesium3DTileStyleEngine', './CullingVolume', './DebugCameraPrimitive', @@ -85,6 +86,7 @@ define([ Cesium3DTileOptimizations, Cesium3DTileOptimizationHint, Cesium3DTileRefine, + Cesium3DTilesetTraversal, Cesium3DTileStyleEngine, CullingVolume, DebugCameraPrimitive, @@ -174,6 +176,7 @@ define([ this._selectedTilesToStyle = []; this._loadTimestamp = undefined; this._timeSinceLoad = 0.0; + this._desiredTiles = []; var replacementList = new DoublyLinkedList(); @@ -263,6 +266,7 @@ define([ this.show = defaultValue(options.show, true); this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16); + this._baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 50000000000); this._maximumNumberOfLoadedTiles = defaultValue(options.maximumNumberOfLoadedTiles, 256); this._styleEngine = new Cesium3DTileStyleEngine(); @@ -403,6 +407,8 @@ define([ this.debugShowGeometricError = defaultValue(options.debugShowGeometricError, false); this._geometricErrorLabels = undefined; + this._labels = undefined; + /** * The event fired to indicate progress of loading new tiles. This event is fired when a new tile * is requested, when a requested tile is finished downloading, and when a downloaded tile has been @@ -551,6 +557,12 @@ define([ this._requestHeaps = {}; this._hasMixedContent = false; + this._baseTraversal = new Cesium3DTilesetTraversal.BaseTraversal(); + this._skipTraversal = new Cesium3DTilesetTraversal.SkipTraversal({ + selectionHeuristic: selectionHeuristic, + selectedTiles: this._selectedTiles + }); + this._backfaceCommands = new ManagedArray(); this._selectionState = { @@ -1466,6 +1478,37 @@ define([ processingQueue.push(root); selectionState.done = false; + var baseLeaves = tileset._baseTraversal.execute(root, frameState, outOfCore); + for (var i = 0; i < baseLeaves.length; ++i) { + var tile = baseLeaves.get(i); + + if (tile._sse <= tileset._baseScreenSpaceError) { + // tile.selected = true; + // tile._selectedFrame = frameState.frameNumber; + tileset._skipTraversal.execute(tile, frameState, outOfCore); + } else { + tile.selected = true; + tile._selectedFrame = frameState.frameNumber; + // selectTile(tileset, tile, frameState); + } + + // if (defined(tile._ancestorWithLoadedContent)) { + // tileset._skipTraversal.execute(tile, frameState, outOfCore); + // } else { + // if (tile.hasContent) { + // loadTile(tile); + // } else if (defined(tile._ancestorWithContent)) { + // loadTile(tile._ancestorWithContent); + // } + // } + } + + traverseAndSelect(tileset, root, frameState); + + requestTiles(tileset, tileset._requestHeaps, outOfCore); + + return; + var processLength = 0; while (!selectionState.done) { selectionState.nextQueue.length = 0; @@ -1876,6 +1919,36 @@ define([ tileset._geometricErrorLabels.update(frameState); } + function updateLabels(tileset, frameState) { + return; + var selectedTiles = tileset._selectedTiles; + var length = selectedTiles.length; + tileset._labels.removeAll(); + for (var i = 0; i < length; ++i) { + var tile = selectedTiles[i]; + var boundingVolume = tile._boundingVolume.boundingVolume; + var halfAxes = boundingVolume.halfAxes; + var radius = boundingVolume.radius; + + var position = Cartesian3.clone(boundingVolume.center, scratchCartesian2); + if (defined(halfAxes)) { + position.x += 0.75 * (halfAxes[0] + halfAxes[3] + halfAxes[6]); + position.y += 0.75 * (halfAxes[1] + halfAxes[4] + halfAxes[7]); + position.z += 0.75 * (halfAxes[2] + halfAxes[5] + halfAxes[8]); + } else if (defined(radius)) { + var normal = Cartesian3.normalize(boundingVolume.center, scratchCartesian2); + normal = Cartesian3.multiplyByScalar(normal, 0.75 * radius, scratchCartesian2); + position = Cartesian3.add(normal, boundingVolume.center, scratchCartesian2); + } + tileset._labels.add({ + text: tile._sse.toString(), + position: position, + scale: 0.5 + }); + } + tileset._labels.update(frameState); + } + var stencilClearCommand = new ClearCommand({ stencil : 0, pass : Pass.CESIUM_3D_TILE @@ -1966,6 +2039,15 @@ define([ } else { tileset._geometricErrorLabels = tileset._geometricErrorLabels && tileset._geometricErrorLabels.destroy(); } + + if (true) { + if (!defined(tileset._labels)) { + tileset._labels = new LabelCollection(); + } + updateLabels(tileset, frameState); + } else { + tileset._labels = tileset._labels && tileset._labels.destroy(); + } } function unloadTiles(tileset, frameState) { diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js new file mode 100644 index 000000000000..b93167cdf002 --- /dev/null +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -0,0 +1,476 @@ +/*global define*/ +define([ + '../Core/defined', + '../Core/defineProperties', + '../Core/freezeObject', + '../Core/Intersect', + '../Core/ManagedArray', + '../Core/Math', + './Cesium3DTileChildrenVisibility', + './Cesium3DTileRefine', + './CullingVolume', + './OrthographicFrustum', + './SceneMode' + ], function( + defined, + defineProperties, + freezeObject, + Intersect, + ManagedArray, + CesiumMath, + Cesium3DTileChildrenVisibility, + Cesium3DTileRefine, + CullingVolume, + OrthographicFrustum, + SceneMode) { + 'use strict'; + + var emptyArray = freezeObject([]); + + function BaseTraversal() { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.stack = new ManagedArray(); + this.leaves = new ManagedArray(); + } + + BaseTraversal.prototype.execute = function(root, frameState, outOfCore) { + this.tileset = root._tileset; + this.frameState = frameState; + this.outOfCore = outOfCore; + this.leaves.length = 0; + DFS(root, this); + return this.leaves; + }; + + BaseTraversal.prototype.visit = function(tile) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + }; + + BaseTraversal.prototype.getChildren = function(tile) { + var tileset = this.tileset; + var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + + if (tile.hasTilesetContent) { + // load any tilesets of tilesets now because at this point we still have not achieved a base level of content + if (!defined(tile._ancestorWithContent)) { + loadTile(tile, this.frameState); + } + if (!tile.contentReady) { + return emptyArray; + } + } + + if (tile.refine === Cesium3DTileRefine.ADD) { + // add to final + } else { + if (tile.hasContent && // continue traversal until some content exists at all branches of the tree + (tile._sse <= maximumScreenSpaceError || // stop traversal when we have met screen space error + tile._sse <= tileset._baseScreenSpaceError)) { // stop traversal when we've attained the desired base level of error + return emptyArray; + } + } + + var childrenVisibility = updateChildren(tileset, tile, this.frameState); + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; + var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + + if (showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent)) { + var children = tile.children; + var childrenLength = children.length; + var allVisibleReady = true; + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + if (isVisible(child.visibilityPlaneMask)) { + loadTile(child, this.frameState); + touch(tileset, tile, this.outOfCore); + allVisibleReady = allVisibleReady && child.contentReady; + } + } + + if (allVisibleReady) { + return children; + } + } + + return emptyArray; + }; + + BaseTraversal.prototype.leafHandler = function(tile) { + var tileWithContent = tile; + if (!tileWithContent.hasContent || !tileWithContent.contentReady) { + if (defined(tile._ancestorWithLoadedContent)) { + tileWithContent = tile._ancestorWithLoadedContent; + } + } + this.leaves.push(tileWithContent); + }; + + function SkipTraversal(options) { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.queue1 = new ManagedArray(); + this.queue2 = new ManagedArray(); + this.internalDFS = new InternalSkipTraversal(options.selectionHeuristic); + this.selectedTiles = options.selectedTiles; + } + + SkipTraversal.prototype.execute = function(root, frameState, outOfCore) { + this.tileset = root._tileset; + this.frameState = frameState; + this.outOfCore = outOfCore; + this.internalDFS.frameState = frameState; + this.internalDFS.outOfCore = outOfCore; + this.queue1.length = 0; + this.queue2.length = 0; + BFS(root, this); + }; + + SkipTraversal.prototype.visit = function(tile) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + }; + + SkipTraversal.prototype.getChildren = function(tile) { + this.internalDFS.execute(tile, this.queue2); + return this.queue2; + }; + + SkipTraversal.prototype.addChildren = function(children, queue) { + // the internal DFS already adds to queue2 + }; + + SkipTraversal.prototype.leafHandler = function(tile) { + loadTile(tile, this.frameState); + var tileWithContent = tile; + if (!tileWithContent.hasContent || !tileWithContent.contentReady) { + if (defined(tile._ancestorWithLoadedContent)) { + tileWithContent = tile._ancestorWithLoadedContent; + } + } + + tileWithContent.selected = true; + tileWithContent._selectedFrame = this.frameState.frameNumber; + }; + + function InternalSkipTraversal(selectionHeuristic) { + this.selectionHeuristic = selectionHeuristic; + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.root = undefined; + this.queue = undefined; + this.stack = new ManagedArray(); + } + + InternalSkipTraversal.prototype.execute = function(root, queue) { + this.tileset = root._tileset; + this.root = root; + this.queue = queue; + DFS(root, this); + }; + + InternalSkipTraversal.prototype.visit = function(tile) { + var tileset = tile._tileset; + visitTile(tileset, tile, this.frameState, this.outOfCore); + }; + + InternalSkipTraversal.prototype.getChildren = function(tile) { + var tileset = tile._tileset; + var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + + if (tile.hasTilesetContent) { + if (!tile.contentReady) { + return emptyArray; + } + } else { + if (tile.refine === Cesium3DTileRefine.ADD) { + // add to final + } else { + if (tile._sse <= maximumScreenSpaceError) { + return emptyArray; + } + + // if we have reached the skipping threshold without any loaded ancestors, return empty so this tile is loaded + if (defined(tile._ancestorWithLoadedContent) && this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { + return emptyArray; + } + } + } + + var childrenVisibility = updateChildren(tileset, tile, this.frameState); + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; + var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + + if (showAdditive || showReplacement || tile.hasTilesetContent) { + var children = tile.children; + var childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + touch(tileset, children[i], this.outOfCore); + } + return children; + } else { + return emptyArray; + } + }; + + InternalSkipTraversal.prototype.shouldVisit = function(tile) { + var maximumScreenSpaceError = this.tileset._maximumScreenSpaceError; + var parent = tile.parent; + var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._sse > maximumScreenSpaceError; + var showReplacement = parent.refine === Cesium3DTileRefine.REPLACE && (parent.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + + return isVisible(tile.visibilityPlaneMask) && (showReplacement || (showAdditive && getScreenSpaceError(this.tileset, parent.geometricError, tile, frameState) > maximumScreenSpaceError)); + }; + + InternalSkipTraversal.prototype.leafHandler = function(tile) { + if (tile !== this.root) { + this.queue.push(tile); + } + }; + + function selectTile(tileset, tile, frameState) { + // There may also be a tight box around just the tile's contents, e.g., for a city, we may be + // zoomed into a neighborhood and can cull the skyscrapers in the root node. + if (tile.contentReady && ( + (tile.visibilityPlaneMask === CullingVolume.MASK_INSIDE) || + (tile.contentsVisibility(frameState) !== Intersect.OUTSIDE) + )) { + tileset._selectedTiles.push(tile); + + var tileContent = tile.content; + if (tileContent.featurePropertiesDirty) { + // A feature's property in this tile changed, the tile needs to be re-styled. + tileContent.featurePropertiesDirty = false; + tile.lastStyleTime = 0; // Force applying the style to this tile + tileset._selectedTilesToStyle.push(tile); + } else if ((tile.lastSelectedFrameNumber !== frameState.frameNumber - 1)) { + // Tile is newly selected; it is selected this frame, but was not selected last frame. + tileset._selectedTilesToStyle.push(tile); + } + tile.lastSelectedFrameNumber = frameState.frameNumber; + } + } + + function updateChildren(tileset, tile, frameState) { + var children = tile.children; + var childrenLength = children.length; + + updateTransforms(children, tile.computedTransform); + computeDistanceToCamera(children, frameState); + + return computeChildrenVisibility(tile, frameState, true); + } + + function visitTile(tileset, tile, frameState, outOfCore) { + ++tileset._statistics.visited; + tile.selected = false; + tile._finalResolution = false; + touch(tileset, tile, outOfCore); + tile._ancestorWithContent = undefined; + tile._ancestorWithLoadedContent = undefined; + var parent = tile.parent; + if (defined(parent)) { + var replace = parent.refine === Cesium3DTileRefine.REPLACE; + tile._ancestorWithContent = (replace && parent.hasContent) ? parent : parent._ancestorWithContent; + tile._ancestorWithLoadedContent = (replace && parent.hasContent && parent.contentReady) ? parent : parent._ancestorWithLoadedContent; + } + } + + function touch(tileset, tile, outOfCore) { + if (!outOfCore) { + return; + } + var node = tile.replacementNode; + if (defined(node)) { + tileset._replacementList.splice(tileset._replacementSentinel, node); + } + } + + function loadTile(tile, frameState) { + if (tile.contentUnloaded) { + if (tile._sseComputedFrame !== frameState.frameNumber) { + tile._sseComputedFrame = frameState.frameNumber; + tile._sse = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); + } + tile._requestHeap.insert(tile); + } + } + + function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { + var flag = Cesium3DTileChildrenVisibility.NONE; + var children = tile.children; + var childrenLength = children.length; + var visibilityPlaneMask = tile.visibilityPlaneMask; + for (var k = 0; k < childrenLength; ++k) { + var child = children[k]; + + var visibilityMask = child.visibility(frameState, visibilityPlaneMask); + + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE; + } + + if (checkViewerRequestVolume) { + if (!child.insideViewerRequestVolume(frameState)) { + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; + } + visibilityMask = CullingVolume.MASK_OUTSIDE; + } else { + flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; + } + } + } + + child.visibilityPlaneMask = visibilityMask; + } + + tile.childrenVisibility = flag; + + return flag; + } + + function getScreenSpaceError(tileset, geometricError, tile, frameState) { + if (geometricError === 0.0) { + // Leaf nodes do not have any error so save the computation + return 0.0; + } + + // Avoid divide by zero when viewer is inside the tile + var camera = frameState.camera; + var frustum = camera.frustum; + var context = frameState.context; + var height = context.drawingBufferHeight; + + var error; + if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { + if (defined(frustum._offCenterFrustum)) { + frustum = frustum._offCenterFrustum; + } + var width = context.drawingBufferWidth; + var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); + error = geometricError / pixelSize; + } else { + var distance = Math.max(tile.distanceToCamera, CesiumMath.EPSILON7); + var sseDenominator = camera.frustum.sseDenominator; + error = (geometricError * height) / (distance * sseDenominator); + + if (tileset.dynamicScreenSpaceError) { + var density = tileset._dynamicScreenSpaceErrorComputedDensity; + var factor = tileset.dynamicScreenSpaceErrorFactor; + var dynamicError = CesiumMath.fog(distance, density) * factor; + error -= dynamicError; + } + } + + return error; + } + + function computeDistanceToCamera(children, frameState) { + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = children[i]; + child.distanceToCamera = child.distanceToTile(frameState); + child._centerZDepth = child.distanceToTileCenter(frameState); + } + } + + function updateTransforms(children, parentTransform) { + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = children[i]; + child.updateTransform(parentTransform); + } + } + + function isVisible(visibilityPlaneMask) { + return visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; + } + + function DFS(root, options) { + var stack = options.stack; + + if (!defined(options.shouldVisit) || options.shouldVisit(root)) { + stack.push(root); + } + + var maxLength = 0; + while (stack.length > 0) { + maxLength = Math.max(maxLength, stack.length); + + var node = stack.pop(); + options.visit(node); + var children = options.getChildren(node); + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = children[i]; + + if (!defined(options.shouldVisit) || options.shouldVisit(child)) { + stack.push(child); + } + } + + if (length === 0 && defined(options.leafHandler)) { + options.leafHandler(node); + } + } + + if (defined(stack.trim)) { + stack.trim(maxLength); + } + } + + function BFS(root, options) { + var queue1 = options.queue1; + var queue2 = options.queue2; + + if (!defined(options.shouldVisit) || options.shouldVisit(root)) { + queue1.push(root); + } + + var maxLength = 0; + while (queue1.length > 0) { + var length = queue1.length; + maxLength = Math.max(maxLength, length); + + for (var i = 0; i < length; ++i) { + var node = queue1.get(i); + options.visit(node); + var children = options.getChildren(node); + var childrenLength = children.length; + maxLength = Math.max(maxLength, childrenLength); + options.addChildren(children, queue2); + + if (childrenLength === 0 && defined(options.leafHandler)) { + options.leafHandler(node); + } + } + + queue1.length = 0; + var temp = queue1; + queue1 = queue2; + queue2 = temp; + options.queue1 = queue1; + options.queue2 = queue2; + } + + queue1.length = 0; + queue2.length = 0; + + if (defined(queue1.trim)) { + queue1.trim(maxLength); + } + if (defined(queue2.trim)) { + queue2.trim(maxLength); + } + } + + return { + BaseTraversal: BaseTraversal, + SkipTraversal: SkipTraversal + }; +}); \ No newline at end of file From 53da6974b14b90031a84b51f13dbf6f2eed18994 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Wed, 26 Apr 2017 16:38:09 -0400 Subject: [PATCH 02/20] refactor traversal --- Source/Scene/Cesium3DTile.js | 1 - Source/Scene/Cesium3DTileset.js | 158 +++++++++-------------- Source/Scene/Cesium3DTilesetTraversal.js | 122 +++++++++-------- 3 files changed, 123 insertions(+), 158 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index aafb27b1ca82..b28cce3aebd8 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -628,7 +628,6 @@ define([ var box = boundingVolumeHeader.box; center = Cartesian3.fromElements(box[0], box[1], box[2], scratchCenter); var halfAxes = Matrix3.fromArray(box, 3, scratchHalfAxes); - Matrix3.multiplyByScalar(halfAxes, 0.5, halfAxes); // Find the transformed center and halfAxes center = Matrix4.multiplyByPoint(transform, center, center); diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index baf34e588990..803fa6c3af62 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -173,10 +173,10 @@ define([ this._gltfUpAxis = undefined; this._processingQueue = []; this._selectedTiles = []; + this._desiredTiles = new ManagedArray(); this._selectedTilesToStyle = []; this._loadTimestamp = undefined; this._timeSinceLoad = 0.0; - this._desiredTiles = []; var replacementList = new DoublyLinkedList(); @@ -266,7 +266,7 @@ define([ this.show = defaultValue(options.show, true); this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16); - this._baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 50000000000); + this._baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 1024); this._maximumNumberOfLoadedTiles = defaultValue(options.maximumNumberOfLoadedTiles, 256); this._styleEngine = new Cesium3DTileStyleEngine(); @@ -1339,6 +1339,26 @@ define([ var descendantStack = []; + function markLoadedTilesForSelection(tileset, frameState) { + var tiles = tileset._desiredTiles; + var length = tiles.length; + for (var i = 0; i < length; ++i) { + var tile = tiles.get(i); + + var loadedTile = tile._ancestorWithLoadedContent; + if (tile.hasContent && tile.contentReady) { + loadedTile = tile; + } + + if (defined(loadedTile)) { + if (!loadedTile.selected) { + loadedTile.selected = true; + loadedTile._selectedFrame = frameState.frameNumber; + } + } + } + } + function markNearestLoadedTilesForSelection(tileset, frameState, selectionState, outOfCore) { var finalQueue = selectionState.finalQueue; var selectionQueue = selectionState.selectionQueue; @@ -1416,11 +1436,23 @@ define([ } function sortForLoad(a, b) { - var diff = b._sse - a._sse; - if (diff === 0 || a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { - return a.distanceToCamera - b.distanceToCamera; + var diff = a.distanceToCamera - b.distanceToCamera; + if (a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { + return diff; } - return diff; + + var ancestorA = a._ancestorWithLoadedContent; + var ancestorB = b._ancestorWithLoadedContent; + + if (ancestorA === ancestorB) { + return diff; + } + + var sseA = defined(ancestorA) ? ancestorA._sse - a._sse : a._sse; + var sseB = defined(ancestorB) ? ancestorB._sse - b._sse : b._sse; + + var sseDiff = sseB - sseA; + return sseDiff === 0 ? diff : sseDiff; } function selectTiles(tileset, frameState, outOfCore) { @@ -1430,6 +1462,7 @@ define([ var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + tileset._desiredTiles.length = 0; tileset._selectedTiles.length = 0; tileset._selectedTilesToStyle.length = 0; tileset._hasMixedContent = false; @@ -1478,63 +1511,27 @@ define([ processingQueue.push(root); selectionState.done = false; - var baseLeaves = tileset._baseTraversal.execute(root, frameState, outOfCore); - for (var i = 0; i < baseLeaves.length; ++i) { - var tile = baseLeaves.get(i); + // tileset._baseScreenSpaceError = Number(tileset._maximumScreenSpaceError); - if (tile._sse <= tileset._baseScreenSpaceError) { - // tile.selected = true; - // tile._selectedFrame = frameState.frameNumber; - tileset._skipTraversal.execute(tile, frameState, outOfCore); - } else { - tile.selected = true; - tile._selectedFrame = frameState.frameNumber; - // selectTile(tileset, tile, frameState); - } - - // if (defined(tile._ancestorWithLoadedContent)) { - // tileset._skipTraversal.execute(tile, frameState, outOfCore); - // } else { - // if (tile.hasContent) { - // loadTile(tile); - // } else if (defined(tile._ancestorWithContent)) { - // loadTile(tile._ancestorWithContent); - // } - // } - } - - traverseAndSelect(tileset, root, frameState); - - requestTiles(tileset, tileset._requestHeaps, outOfCore); + // leaves of the base traversal is where we start the skip traversal + tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; - return; + // load and select tiles without skipping up to tileset._baseScreenSpaceError + tileset._baseTraversal.execute(tileset, root, frameState, outOfCore); - var processLength = 0; - while (!selectionState.done) { - selectionState.nextQueue.length = 0; - processSelectionQueue(tileset, frameState, selectionState, outOfCore); + // skip traversal starts from a prepopulated queue from the base traversal + tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); - processLength = Math.max(processLength, selectionState.nextQueue.length); - - processingQueue = selectionState.processingQueue; - selectionState.processingQueue = selectionState.nextQueue; - selectionState.nextQueue = processingQueue; - } - - // on the next frame, we will likely need an array about the same size - processingQueue.trim(processLength); - nextQueue.trim(processLength); - finalQueue.trim(); - - markNearestLoadedTilesForSelection(tileset, frameState, selectionState, outOfCore); - - if (tileset._hasMixedContent) { - markTilesAsFinal(selectionQueue); - } + // mark tiles for selection or their nearest loaded ancestor + markLoadedTilesForSelection(tileset, frameState); + // sort selected tiles by distance to camera and call selectTile on each + // set tile._selectionDepth on all tiles traverseAndSelect(tileset, root, frameState); requestTiles(tileset, tileset._requestHeaps, outOfCore); + + tileset._desiredTiles.trim(); } function requestTiles(tileset, requestHeaps, outOfCore) { @@ -1713,12 +1710,16 @@ define([ var stack = scratchStack; var ancestorStack = scratchStack2; + var lastAncestor; stack.push(root); while (stack.length > 0 || ancestorStack.length > 0) { if (ancestorStack.length > 0) { var waitingTile = ancestorStack[ancestorStack.length - 1]; if (waitingTile._stackLength === stack.length) { ancestorStack.pop(); + if (waitingTile === lastAncestor) { + waitingTile._finalResolution = true; + } selectTile(tileset, waitingTile, frameState); continue; } @@ -1741,7 +1742,12 @@ define([ if (shouldSelect) { tile._selectionDepth = ancestorStack.length; - if (tile._finalResolution || childrenLength === 0) { + if (tile._selectionDepth > 0) { + tileset._hasMixedContent = true; + } + + if (childrenLength === 0) { + tile._finalResolution = true; selectTile(tileset, tile, frameState); if (!additive) { continue; @@ -1750,6 +1756,7 @@ define([ if (!additive) { ancestorStack.push(tile); + lastAncestor = tile; tile._stackLength = stack.length; } } @@ -1919,36 +1926,6 @@ define([ tileset._geometricErrorLabels.update(frameState); } - function updateLabels(tileset, frameState) { - return; - var selectedTiles = tileset._selectedTiles; - var length = selectedTiles.length; - tileset._labels.removeAll(); - for (var i = 0; i < length; ++i) { - var tile = selectedTiles[i]; - var boundingVolume = tile._boundingVolume.boundingVolume; - var halfAxes = boundingVolume.halfAxes; - var radius = boundingVolume.radius; - - var position = Cartesian3.clone(boundingVolume.center, scratchCartesian2); - if (defined(halfAxes)) { - position.x += 0.75 * (halfAxes[0] + halfAxes[3] + halfAxes[6]); - position.y += 0.75 * (halfAxes[1] + halfAxes[4] + halfAxes[7]); - position.z += 0.75 * (halfAxes[2] + halfAxes[5] + halfAxes[8]); - } else if (defined(radius)) { - var normal = Cartesian3.normalize(boundingVolume.center, scratchCartesian2); - normal = Cartesian3.multiplyByScalar(normal, 0.75 * radius, scratchCartesian2); - position = Cartesian3.add(normal, boundingVolume.center, scratchCartesian2); - } - tileset._labels.add({ - text: tile._sse.toString(), - position: position, - scale: 0.5 - }); - } - tileset._labels.update(frameState); - } - var stencilClearCommand = new ClearCommand({ stencil : 0, pass : Pass.CESIUM_3D_TILE @@ -2039,15 +2016,6 @@ define([ } else { tileset._geometricErrorLabels = tileset._geometricErrorLabels && tileset._geometricErrorLabels.destroy(); } - - if (true) { - if (!defined(tileset._labels)) { - tileset._labels = new LabelCollection(); - } - updateLabels(tileset, frameState); - } else { - tileset._labels = tileset._labels && tileset._labels.destroy(); - } } function unloadTiles(tileset, frameState) { diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index b93167cdf002..030dd54575cd 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../Core/Check', '../Core/defined', '../Core/defineProperties', '../Core/freezeObject', @@ -12,6 +13,7 @@ define([ './OrthographicFrustum', './SceneMode' ], function( + Check, defined, defineProperties, freezeObject, @@ -35,13 +37,18 @@ define([ this.leaves = new ManagedArray(); } - BaseTraversal.prototype.execute = function(root, frameState, outOfCore) { - this.tileset = root._tileset; + BaseTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { + this.tileset = tileset; this.frameState = frameState; this.outOfCore = outOfCore; this.leaves.length = 0; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('baseScreenSpaceError', this.tileset._baseScreenSpaceError, this.tileset._maximumScreenSpaceError); + //>>includeEnd('debug'); + DFS(root, this); - return this.leaves; + // return this.leaves; }; BaseTraversal.prototype.visit = function(tile) { @@ -50,7 +57,7 @@ define([ BaseTraversal.prototype.getChildren = function(tile) { var tileset = this.tileset; - var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + var baseScreenSpaceError = tileset._baseScreenSpaceError; if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content @@ -63,17 +70,18 @@ define([ } if (tile.refine === Cesium3DTileRefine.ADD) { - // add to final + if (tile.hasContent) { + tileset._desiredTiles.push(tile); + } } else { if (tile.hasContent && // continue traversal until some content exists at all branches of the tree - (tile._sse <= maximumScreenSpaceError || // stop traversal when we have met screen space error - tile._sse <= tileset._baseScreenSpaceError)) { // stop traversal when we've attained the desired base level of error + tile._sse <= baseScreenSpaceError) { // stop traversal when we've attained the desired level of error return emptyArray; } } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > baseScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; if (showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent)) { @@ -84,9 +92,9 @@ define([ var child = children[i]; if (isVisible(child.visibilityPlaneMask)) { loadTile(child, this.frameState); - touch(tileset, tile, this.outOfCore); allVisibleReady = allVisibleReady && child.contentReady; } + touch(tileset, child, this.outOfCore); } if (allVisibleReady) { @@ -97,14 +105,21 @@ define([ return emptyArray; }; + BaseTraversal.prototype.shouldVisit = function(tile) { + return isVisible(tile.visibilityPlaneMask); + } + BaseTraversal.prototype.leafHandler = function(tile) { - var tileWithContent = tile; - if (!tileWithContent.hasContent || !tileWithContent.contentReady) { - if (defined(tile._ancestorWithLoadedContent)) { - tileWithContent = tile._ancestorWithLoadedContent; - } + var contentTile = tile._ancestorWithLoadedContent; + if (tile.hasContent && tile.contentReady) { + contentTile = tile; + } + + if (defined(contentTile)) { + this.leaves.push(contentTile); + } else { + this.tileset._desiredTiles.push(tile); } - this.leaves.push(tileWithContent); }; function SkipTraversal(options) { @@ -117,15 +132,16 @@ define([ this.selectedTiles = options.selectedTiles; } - SkipTraversal.prototype.execute = function(root, frameState, outOfCore) { - this.tileset = root._tileset; + SkipTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { + this.tileset = tileset; this.frameState = frameState; this.outOfCore = outOfCore; this.internalDFS.frameState = frameState; this.internalDFS.outOfCore = outOfCore; + + BFS(root, this); this.queue1.length = 0; this.queue2.length = 0; - BFS(root, this); }; SkipTraversal.prototype.visit = function(tile) { @@ -142,16 +158,7 @@ define([ }; SkipTraversal.prototype.leafHandler = function(tile) { - loadTile(tile, this.frameState); - var tileWithContent = tile; - if (!tileWithContent.hasContent || !tileWithContent.contentReady) { - if (defined(tile._ancestorWithLoadedContent)) { - tileWithContent = tile._ancestorWithLoadedContent; - } - } - - tileWithContent.selected = true; - tileWithContent._selectedFrame = this.frameState.frameNumber; + this.tileset._desiredTiles.push(tile); }; function InternalSkipTraversal(selectionHeuristic) { @@ -172,8 +179,7 @@ define([ }; InternalSkipTraversal.prototype.visit = function(tile) { - var tileset = tile._tileset; - visitTile(tileset, tile, this.frameState, this.outOfCore); + visitTile(this.tileset, tile, this.frameState, this.outOfCore); }; InternalSkipTraversal.prototype.getChildren = function(tile) { @@ -186,8 +192,10 @@ define([ } } else { if (tile.refine === Cesium3DTileRefine.ADD) { - // add to final - } else { + if (tile.hasContent) { + tileset._desiredTiles.push(tile); + } + } else if (tile.hasContent) { // require the tile to have content before refining it if (tile._sse <= maximumScreenSpaceError) { return emptyArray; } @@ -221,37 +229,22 @@ define([ var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._sse > maximumScreenSpaceError; var showReplacement = parent.refine === Cesium3DTileRefine.REPLACE && (parent.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; - return isVisible(tile.visibilityPlaneMask) && (showReplacement || (showAdditive && getScreenSpaceError(this.tileset, parent.geometricError, tile, frameState) > maximumScreenSpaceError)); + return isVisible(tile.visibilityPlaneMask) && (!showAdditive || getScreenSpaceError(this.tileset, parent.geometricError, tile, frameState) > maximumScreenSpaceError); }; InternalSkipTraversal.prototype.leafHandler = function(tile) { if (tile !== this.root) { - this.queue.push(tile); - } - }; - function selectTile(tileset, tile, frameState) { - // There may also be a tight box around just the tile's contents, e.g., for a city, we may be - // zoomed into a neighborhood and can cull the skyscrapers in the root node. - if (tile.contentReady && ( - (tile.visibilityPlaneMask === CullingVolume.MASK_INSIDE) || - (tile.contentsVisibility(frameState) !== Intersect.OUTSIDE) - )) { - tileset._selectedTiles.push(tile); - - var tileContent = tile.content; - if (tileContent.featurePropertiesDirty) { - // A feature's property in this tile changed, the tile needs to be re-styled. - tileContent.featurePropertiesDirty = false; - tile.lastStyleTime = 0; // Force applying the style to this tile - tileset._selectedTilesToStyle.push(tile); - } else if ((tile.lastSelectedFrameNumber !== frameState.frameNumber - 1)) { - // Tile is newly selected; it is selected this frame, but was not selected last frame. - tileset._selectedTilesToStyle.push(tile); + if (tile.hasContent || tile.hasTilesetContent) { + loadTile(tile, this.frameState); } - tile.lastSelectedFrameNumber = frameState.frameNumber; + + this.queue.push(tile); + + } else { + this.tileset._desiredTiles.push(tile); } - } + }; function updateChildren(tileset, tile, frameState) { var children = tile.children; @@ -267,6 +260,7 @@ define([ ++tileset._statistics.visited; tile.selected = false; tile._finalResolution = false; + computeSSE(tile, frameState); touch(tileset, tile, outOfCore); tile._ancestorWithContent = undefined; tile._ancestorWithLoadedContent = undefined; @@ -288,12 +282,16 @@ define([ } } + function computeSSE(tile, frameState) { + if (tile._sseComputedFrame !== frameState.frameNumber) { + tile._sseComputedFrame = frameState.frameNumber; + tile._sse = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); + } + } + function loadTile(tile, frameState) { if (tile.contentUnloaded) { - if (tile._sseComputedFrame !== frameState.frameNumber) { - tile._sseComputedFrame = frameState.frameNumber; - tile._sse = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); - } + computeSSE(tile, frameState); tile._requestHeap.insert(tile); } } @@ -394,7 +392,7 @@ define([ function DFS(root, options) { var stack = options.stack; - if (!defined(options.shouldVisit) || options.shouldVisit(root)) { + if (defined(root) && (!defined(options.shouldVisit) || options.shouldVisit(root))) { stack.push(root); } @@ -428,7 +426,7 @@ define([ var queue1 = options.queue1; var queue2 = options.queue2; - if (!defined(options.shouldVisit) || options.shouldVisit(root)) { + if (defined(root) && (!defined(options.shouldVisit) || options.shouldVisit(root))) { queue1.push(root); } From ecb1d70154dd982b23c0023729ff36dc67a3986c Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 27 Apr 2017 10:59:59 -0400 Subject: [PATCH 03/20] skip after base traversal completed --- Source/Scene/Cesium3DTileset.js | 24 +++++++---- Source/Scene/Cesium3DTilesetTraversal.js | 52 ++++++++++++------------ 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 803fa6c3af62..26171033b114 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1519,8 +1519,15 @@ define([ // load and select tiles without skipping up to tileset._baseScreenSpaceError tileset._baseTraversal.execute(tileset, root, frameState, outOfCore); - // skip traversal starts from a prepopulated queue from the base traversal - tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); + if (tileset.skipLODs) { + // skip traversal starts from a prepopulated queue from the base traversal + tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); + } else { + for (var i = 0; i < tileset._baseTraversal.leaves.length; ++i) { + var tile = tileset._baseTraversal.leaves.get(i); + tileset._desiredTiles.push(tile); + } + } // mark tiles for selection or their nearest loaded ancestor markLoadedTilesForSelection(tileset, frameState); @@ -1953,11 +1960,14 @@ define([ var lengthBeforeUpdate = commandList.length; for (i = 0; i < length; ++i) { tile = selectedTiles[i]; - // Raise visible event before update in case the visible event - // makes changes that update needs to apply to WebGL resources - tileVisible.raiseEvent(tile); - tile.update(tileset, frameState); - incrementPointAndFeatureSelectionCounts(tileset, tile.content); + // tiles may get unloaded and destroyed between selection and update + if (tile.selected) { + // Raise visible event before update in case the visible event + // makes changes that update needs to apply to WebGL resources + tileVisible.raiseEvent(tile); + tile.update(tileset, frameState); + incrementPointAndFeatureSelectionCounts(tileset, tile.content); + } } var lengthAfterUpdate = commandList.length; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 030dd54575cd..c5e37494404a 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -48,7 +48,6 @@ define([ //>>includeEnd('debug'); DFS(root, this); - // return this.leaves; }; BaseTraversal.prototype.visit = function(tile) { @@ -87,17 +86,15 @@ define([ if (showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent)) { var children = tile.children; var childrenLength = children.length; - var allVisibleReady = true; + var allReady = true; for (var i = 0; i < childrenLength; ++i) { var child = children[i]; - if (isVisible(child.visibilityPlaneMask)) { - loadTile(child, this.frameState); - allVisibleReady = allVisibleReady && child.contentReady; - } + loadTile(child, this.frameState); + allReady = allReady && child.contentReady; touch(tileset, child, this.outOfCore); } - if (allVisibleReady) { + if (allReady) { return children; } } @@ -110,16 +107,7 @@ define([ } BaseTraversal.prototype.leafHandler = function(tile) { - var contentTile = tile._ancestorWithLoadedContent; - if (tile.hasContent && tile.contentReady) { - contentTile = tile; - } - - if (defined(contentTile)) { - this.leaves.push(contentTile); - } else { - this.tileset._desiredTiles.push(tile); - } + this.leaves.push(tile); }; function SkipTraversal(options) { @@ -148,13 +136,12 @@ define([ visitTile(this.tileset, tile, this.frameState, this.outOfCore); }; - SkipTraversal.prototype.getChildren = function(tile) { - this.internalDFS.execute(tile, this.queue2); - return this.queue2; - }; + var scratchQueue = []; - SkipTraversal.prototype.addChildren = function(children, queue) { - // the internal DFS already adds to queue2 + SkipTraversal.prototype.getChildren = function(tile) { + scratchQueue.length = 0; + this.internalDFS.execute(tile, scratchQueue); + return scratchQueue; }; SkipTraversal.prototype.leafHandler = function(tile) { @@ -201,7 +188,10 @@ define([ } // if we have reached the skipping threshold without any loaded ancestors, return empty so this tile is loaded - if (defined(tile._ancestorWithLoadedContent) && this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { + if ( + (tile.hasContent && tile.contentUnloaded) && + defined(tile._ancestorWithLoadedContent) && + this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { return emptyArray; } } @@ -226,10 +216,13 @@ define([ InternalSkipTraversal.prototype.shouldVisit = function(tile) { var maximumScreenSpaceError = this.tileset._maximumScreenSpaceError; var parent = tile.parent; + if (!defined(parent)) { + return true; + } var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._sse > maximumScreenSpaceError; var showReplacement = parent.refine === Cesium3DTileRefine.REPLACE && (parent.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; - return isVisible(tile.visibilityPlaneMask) && (!showAdditive || getScreenSpaceError(this.tileset, parent.geometricError, tile, frameState) > maximumScreenSpaceError); + return isVisible(tile.visibilityPlaneMask) && (!showAdditive || getScreenSpaceError(this.tileset, parent.geometricError, tile, this.frameState) > maximumScreenSpaceError); }; InternalSkipTraversal.prototype.leafHandler = function(tile) { @@ -440,8 +433,13 @@ define([ options.visit(node); var children = options.getChildren(node); var childrenLength = children.length; - maxLength = Math.max(maxLength, childrenLength); - options.addChildren(children, queue2); + for (var j = 0; j < childrenLength; ++j) { + var child = children[j]; + + if (!defined(options.shouldVisit) || options.shouldVisit(child)) { + queue2.push(child); + } + } if (childrenLength === 0 && defined(options.leafHandler)) { options.leafHandler(node); From 4d640ae442d3ed016360f6472c74fd3cc33b9049 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 27 Apr 2017 13:33:55 -0400 Subject: [PATCH 04/20] set base error to max error when skip LODs is disabled; add flags to avoid double loading --- Source/Scene/Cesium3DTile.js | 2 +- Source/Scene/Cesium3DTileset.js | 3 ++- Source/Scene/Cesium3DTilesetTraversal.js | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index b28cce3aebd8..5eecbd44d01b 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -344,7 +344,7 @@ define([ this._selectionDepth = 0; this._lastFinalResolution = undefined; this._lastSelectionDepth = undefined; - + this._requestedFrame = undefined; this._ancestorWithContent = undefined; this._ancestorWithLoadedContent = undefined; } diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 26171033b114..423f02821052 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1517,7 +1517,8 @@ define([ tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; // load and select tiles without skipping up to tileset._baseScreenSpaceError - tileset._baseTraversal.execute(tileset, root, frameState, outOfCore); + var baseScreenSpaceError = tileset.skipLODs ? tileset._baseScreenSpaceError : tileset._maximumScreenSpaceError; + tileset._baseTraversal.execute(tileset, root, baseScreenSpaceError, frameState, outOfCore); if (tileset.skipLODs) { // skip traversal starts from a prepopulated queue from the base traversal diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index c5e37494404a..917fe961cfa9 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -35,16 +35,18 @@ define([ this.outOfCore = undefined; this.stack = new ManagedArray(); this.leaves = new ManagedArray(); + this.baseScreenSpaceError = undefined; } - BaseTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { + BaseTraversal.prototype.execute = function(tileset, root, baseScreenSpaceError, frameState, outOfCore) { this.tileset = tileset; this.frameState = frameState; this.outOfCore = outOfCore; this.leaves.length = 0; + this.baseScreenSpaceError = baseScreenSpaceError; //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals('baseScreenSpaceError', this.tileset._baseScreenSpaceError, this.tileset._maximumScreenSpaceError); + Check.typeOf.number.greaterThanOrEquals('baseScreenSpaceError', baseScreenSpaceError, this.tileset._maximumScreenSpaceError); //>>includeEnd('debug'); DFS(root, this); @@ -56,7 +58,7 @@ define([ BaseTraversal.prototype.getChildren = function(tile) { var tileset = this.tileset; - var baseScreenSpaceError = tileset._baseScreenSpaceError; + var baseScreenSpaceError = this.baseScreenSpaceError; if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content @@ -283,7 +285,8 @@ define([ } function loadTile(tile, frameState) { - if (tile.contentUnloaded) { + if (tile.contentUnloaded && tile._requestedFrame !== frameState.frameNumber) { + tile._requestedFrame = frameState.frameNumber; computeSSE(tile, frameState); tile._requestHeap.insert(tile); } From 815628c1f43dbc0564f1116a6c7dba86e690e29e Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 27 Apr 2017 13:50:03 -0400 Subject: [PATCH 05/20] remove double visits --- Source/Scene/Cesium3DTile.js | 1 + Source/Scene/Cesium3DTileset.js | 16 ------------ Source/Scene/Cesium3DTilesetTraversal.js | 32 +++++++++++++----------- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 5eecbd44d01b..21f8d023e4ae 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -345,6 +345,7 @@ define([ this._lastFinalResolution = undefined; this._lastSelectionDepth = undefined; this._requestedFrame = undefined; + this._lastVisitedFrame = undefined; this._ancestorWithContent = undefined; this._ancestorWithLoadedContent = undefined; } diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 423f02821052..a154b38d22e8 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1497,22 +1497,6 @@ define([ return; } - var selectionState = tileset._selectionState; - var processingQueue = selectionState.processingQueue; - var nextQueue = selectionState.nextQueue; - var finalQueue = selectionState.finalQueue; - var selectionQueue = selectionState.selectionQueue; - - processingQueue.length = 0; - nextQueue.length = 0; - finalQueue.length = 0; - - visitTile(tileset, root, frameState, outOfCore); - processingQueue.push(root); - selectionState.done = false; - - // tileset._baseScreenSpaceError = Number(tileset._maximumScreenSpaceError); - // leaves of the base traversal is where we start the skip traversal tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 917fe961cfa9..e62e41cf0f40 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -106,7 +106,7 @@ define([ BaseTraversal.prototype.shouldVisit = function(tile) { return isVisible(tile.visibilityPlaneMask); - } + }; BaseTraversal.prototype.leafHandler = function(tile) { this.leaves.push(tile); @@ -243,7 +243,6 @@ define([ function updateChildren(tileset, tile, frameState) { var children = tile.children; - var childrenLength = children.length; updateTransforms(children, tile.computedTransform); computeDistanceToCamera(children, frameState); @@ -252,18 +251,23 @@ define([ } function visitTile(tileset, tile, frameState, outOfCore) { - ++tileset._statistics.visited; - tile.selected = false; - tile._finalResolution = false; - computeSSE(tile, frameState); - touch(tileset, tile, outOfCore); - tile._ancestorWithContent = undefined; - tile._ancestorWithLoadedContent = undefined; - var parent = tile.parent; - if (defined(parent)) { - var replace = parent.refine === Cesium3DTileRefine.REPLACE; - tile._ancestorWithContent = (replace && parent.hasContent) ? parent : parent._ancestorWithContent; - tile._ancestorWithLoadedContent = (replace && parent.hasContent && parent.contentReady) ? parent : parent._ancestorWithLoadedContent; + // because the leaves of one tree traversal are the root of the subsequent traversal, avoid double visitation + if (tile._lastSeenFrame !== frameState.frameNumber) { + tile._lastSeenFrame = frameState.frameNumber; + + ++tileset._statistics.visited; + tile.selected = false; + tile._finalResolution = false; + computeSSE(tile, frameState); + touch(tileset, tile, outOfCore); + tile._ancestorWithContent = undefined; + tile._ancestorWithLoadedContent = undefined; + var parent = tile.parent; + if (defined(parent)) { + var replace = parent.refine === Cesium3DTileRefine.REPLACE; + tile._ancestorWithContent = (replace && parent.hasContent) ? parent : parent._ancestorWithContent; + tile._ancestorWithLoadedContent = (replace && parent.hasContent && parent.contentReady) ? parent : parent._ancestorWithLoadedContent; + } } } From c714501acca73ff2344240d414238c789edde1cc Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 27 Apr 2017 14:19:02 -0400 Subject: [PATCH 06/20] fix selection for additive refinement --- Source/Scene/Cesium3DTileset.js | 31 ++++++++++++++---------- Source/Scene/Cesium3DTilesetTraversal.js | 4 +-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index a154b38d22e8..000854ece708 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1345,6 +1345,12 @@ define([ for (var i = 0; i < length; ++i) { var tile = tiles.get(i); + if (tile.refine === Cesium3DTileRefine.ADD) { + tile.selected = true; + tile._selectedFrame = frameState.frameNumber; + continue; + } + var loadedTile = tile._ancestorWithLoadedContent; if (tile.hasContent && tile.contentReady) { loadedTile = tile; @@ -1718,7 +1724,7 @@ define([ } var tile = stack.pop(); - if (!defined(tile)) { + if (!defined(tile) || tile._lastVisitedFrame !== frameState.frameNumber) { continue; } @@ -1729,24 +1735,23 @@ define([ children.sort(sortChildrenByDistanceToCamera); - var additive = tile.refine === Cesium3DTileRefine.ADD; - if (shouldSelect) { - tile._selectionDepth = ancestorStack.length; - - if (tile._selectionDepth > 0) { - tileset._hasMixedContent = true; - } - - if (childrenLength === 0) { + if (tile.refine === Cesium3DTileRefine.ADD) { tile._finalResolution = true; selectTile(tileset, tile, frameState); - if (!additive) { + } else { + tile._selectionDepth = ancestorStack.length; + + if (tile._selectionDepth > 0) { + tileset._hasMixedContent = true; + } + + if (childrenLength === 0) { + tile._finalResolution = true; + selectTile(tileset, tile, frameState); continue; } - } - if (!additive) { ancestorStack.push(tile); lastAncestor = tile; tile._stackLength = stack.length; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index e62e41cf0f40..dec38bfbc531 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -252,8 +252,8 @@ define([ function visitTile(tileset, tile, frameState, outOfCore) { // because the leaves of one tree traversal are the root of the subsequent traversal, avoid double visitation - if (tile._lastSeenFrame !== frameState.frameNumber) { - tile._lastSeenFrame = frameState.frameNumber; + if (tile._lastVisitedFrame !== frameState.frameNumber) { + tile._lastVisitedFrame = frameState.frameNumber; ++tileset._statistics.visited; tile.selected = false; From 85ff19cee79742c91db5fd80c2a3ba256a224113 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 28 Apr 2017 09:57:57 -0400 Subject: [PATCH 07/20] bugfixes and update tests --- Source/Scene/Cesium3DTileset.js | 76 ++++++--- Source/Scene/Cesium3DTilesetTraversal.js | 68 ++++---- Specs/Scene/Cesium3DTilesetSpec.js | 189 +++++++++++------------ 3 files changed, 185 insertions(+), 148 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 000854ece708..fbc1b2e477e2 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -126,6 +126,7 @@ define([ * @param {Boolean} [options.debugShowGeometricError=false] For debugging only. When true, draws labels to indicate the geometric error of each tile * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source. * @param {Boolean} [options.skipLODs=true] Determines if level-of-detail skipping optimization should be used. + * @param {Number} [options.baseScreenSpaceError=1024] The screen-space error that must be reached before skipping LODs * @param {Number} [options.skipSSEFactor=10] Multiplier defining the minimum screen space error to skip when loading tiles. Used in conjuction with skipLevels to determine which tiles to load. * @param {Number} [options.skipLevels=1] Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjuction with skipSSEFactor to determine which tiles to load. * @param {Boolean} [options.immediatelyLoadDesiredLOD=false] When true, do not progressively refine. Immediately load the desired LOD. @@ -1339,21 +1340,21 @@ define([ var descendantStack = []; - function markLoadedTilesForSelection(tileset, frameState) { + function markLoadedTilesForSelection(tileset, frameState, outOfCore) { var tiles = tileset._desiredTiles; var length = tiles.length; for (var i = 0; i < length; ++i) { - var tile = tiles.get(i); + var original = tiles.get(i); - if (tile.refine === Cesium3DTileRefine.ADD) { - tile.selected = true; - tile._selectedFrame = frameState.frameNumber; + if (original.refine === Cesium3DTileRefine.ADD && original.contentReady) { + original.selected = true; + original._selectedFrame = frameState.frameNumber; continue; } - var loadedTile = tile._ancestorWithLoadedContent; - if (tile.hasContent && tile.contentReady) { - loadedTile = tile; + var loadedTile = original._ancestorWithLoadedContent; + if (original.hasContent && original.contentReady) { + loadedTile = original; } if (defined(loadedTile)) { @@ -1361,6 +1362,30 @@ define([ loadedTile.selected = true; loadedTile._selectedFrame = frameState.frameNumber; } + } else { + // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions + descendantStack.push(original); + while (descendantStack.length > 0) { + var tile = descendantStack.pop(); + var children = tile.children; + var childrenLength = children.length; + for (var j = 0; j < childrenLength; ++j) { + var child = children[j]; + touch(tileset, child, outOfCore); + if (child.contentReady) { + if (!child.selected) { + child.selected = true; + child._finalResolution = true; + child._selectedFrame = frameState.frameNumber; + } + } + if (child._depth - original._depth < 2) { // prevent traversing too far + if (!child.contentReady || child.refine === Cesium3DTileRefine.ADD) { + descendantStack.push(child); + } + } + } + } } } } @@ -1503,25 +1528,31 @@ define([ return; } - // leaves of the base traversal is where we start the skip traversal - tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; + if (!tileset.skipLODs) { + // just execute base traversal and add tiles to _desiredTiles + tileset._baseTraversal.execute(tileset, root, tileset._maximumScreenSpaceError, frameState, outOfCore); + var leaves = tileset._baseTraversal.leaves; + var length = leaves.length; + for (var i = 0; i < length; ++i) { + tileset._desiredTiles.push(leaves.get(i)); + } + } else { + if (tileset.immediatelyLoadDesiredLOD) { + tileset._skipTraversal.execute(tileset, root, frameState, outOfCore); + } else { + // leaves of the base traversal is where we start the skip traversal + tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; - // load and select tiles without skipping up to tileset._baseScreenSpaceError - var baseScreenSpaceError = tileset.skipLODs ? tileset._baseScreenSpaceError : tileset._maximumScreenSpaceError; - tileset._baseTraversal.execute(tileset, root, baseScreenSpaceError, frameState, outOfCore); + // load and select tiles without skipping up to tileset._baseScreenSpaceError + tileset._baseTraversal.execute(tileset, root, tileset._baseScreenSpaceError, frameState, outOfCore); - if (tileset.skipLODs) { - // skip traversal starts from a prepopulated queue from the base traversal - tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); - } else { - for (var i = 0; i < tileset._baseTraversal.leaves.length; ++i) { - var tile = tileset._baseTraversal.leaves.get(i); - tileset._desiredTiles.push(tile); + // skip traversal starts from a prepopulated queue from the base traversal + tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); } } // mark tiles for selection or their nearest loaded ancestor - markLoadedTilesForSelection(tileset, frameState); + markLoadedTilesForSelection(tileset, frameState, outOfCore); // sort selected tiles by distance to camera and call selectTile on each // set tile._selectionDepth on all tiles @@ -1728,7 +1759,7 @@ define([ continue; } - var shouldSelect = tile.selected && tile._selectedFrame === frameState.frameNumber; + var shouldSelect = tile.selected && tile._selectedFrame === frameState.frameNumber && tile.hasContent; var children = tile.children; var childrenLength = children.length; @@ -1748,6 +1779,7 @@ define([ if (childrenLength === 0) { tile._finalResolution = true; + lastAncestor = tile; selectTile(tileset, tile, frameState); continue; } diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index dec38bfbc531..d6ffab10869e 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -71,14 +71,12 @@ define([ } if (tile.refine === Cesium3DTileRefine.ADD) { - if (tile.hasContent) { - tileset._desiredTiles.push(tile); - } - } else { - if (tile.hasContent && // continue traversal until some content exists at all branches of the tree - tile._sse <= baseScreenSpaceError) { // stop traversal when we've attained the desired level of error - return emptyArray; - } + tileset._desiredTiles.push(tile); + } + + // stop traversal when we've attained the desired level of error + if (tile._sse <= baseScreenSpaceError) { + return emptyArray; } var childrenVisibility = updateChildren(tileset, tile, this.frameState); @@ -147,7 +145,10 @@ define([ }; SkipTraversal.prototype.leafHandler = function(tile) { - this.tileset._desiredTiles.push(tile); + // additive tiles have already been pushed + if (tile.refine === Cesium3DTileRefine.REPLACE) { + this.tileset._desiredTiles.push(tile); + } }; function InternalSkipTraversal(selectionHeuristic) { @@ -181,21 +182,20 @@ define([ } } else { if (tile.refine === Cesium3DTileRefine.ADD) { - if (tile.hasContent) { - tileset._desiredTiles.push(tile); - } - } else if (tile.hasContent) { // require the tile to have content before refining it - if (tile._sse <= maximumScreenSpaceError) { - return emptyArray; - } + tileset._desiredTiles.push(tile); + } - // if we have reached the skipping threshold without any loaded ancestors, return empty so this tile is loaded - if ( - (tile.hasContent && tile.contentUnloaded) && - defined(tile._ancestorWithLoadedContent) && - this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { - return emptyArray; - } + // stop traversal when we've attained the desired level of error + if (tile._sse <= maximumScreenSpaceError) { + return emptyArray; + } + + // if we have reached the skipping threshold without any loaded ancestors, return empty so this tile is loaded + if ( + (tile.hasContent && tile.contentUnloaded) && + defined(tile._ancestorWithLoadedContent) && + this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { + return emptyArray; } } @@ -203,6 +203,11 @@ define([ var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + // at least one child is visible, but is not in request volume. the parent must be selected + if (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME && tile.refine === Cesium3DTileRefine.REPLACE) { + this.tileset._desiredTiles.push(tile); + } + if (showAdditive || showReplacement || tile.hasTilesetContent) { var children = tile.children; var childrenLength = children.length; @@ -229,14 +234,21 @@ define([ InternalSkipTraversal.prototype.leafHandler = function(tile) { if (tile !== this.root) { - if (tile.hasContent || tile.hasTilesetContent) { - loadTile(tile, this.frameState); + if (this.tileset.loadSiblings) { + var parent = tile.parent; + var tiles = parent.children; + var length = tiles.length; + for (var i = 0; i < length; ++i) { + loadTile(tiles[i], this.frameState); + } + } else { + loadTile(tile, this.frameState); + } } - this.queue.push(tile); - - } else { + } else if (tile.refine === Cesium3DTileRefine.REPLACE) { + // additive tiles have already been pushed this.tileset._desiredTiles.push(tile); } }; diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index e29a22aaad73..0a6443446d91 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -872,8 +872,8 @@ defineSuite([ var stats = tileset._statistics; // LOD skipping visits all visible expect(stats.visited).toEqual(5); - // tileset is refining and adds a stencil clear command - expect(stats.numberOfCommands).toEqual(2); + // no stencil clear command because only the root tile + expect(stats.numberOfCommands).toEqual(1); expect(stats.numberOfPendingRequests).toEqual(4); expect(numberOfChildrenWithoutContent(root)).toEqual(4); }); @@ -896,8 +896,9 @@ defineSuite([ // No longer renders the tile with a request volume setZoom(1500.0); + root.hasContent = true; // mock content scene.renderForSpecs(); - expect(stats.numberOfCommands).toEqual(5); + expect(stats.numberOfCommands).toEqual(4); expect(root.selected).toBe(true); // one child is no longer selected. root is chosen instead }); }); @@ -922,7 +923,7 @@ defineSuite([ // Even though root's children are loaded, the grandchildren need to be loaded before it becomes refinable scene.renderForSpecs(); expect(numberOfChildrenWithoutContent(root)).toEqual(0); // Children are loaded - expect(stats.numberOfCommands).toEqual(3); // Stencil, root backfaces, root + expect(stats.numberOfCommands).toEqual(1); // No stencil or backface commands; no mixed content expect(stats.numberOfPendingRequests).toEqual(4); // Loading grandchildren return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { @@ -983,7 +984,7 @@ defineSuite([ return root.children[0].content.readyPromise.then(function() { // The external tileset json is loaded, but the external tileset isn't. scene.renderForSpecs(); - expect(stats.numberOfCommands).toEqual(2); // Stencil + Render root + expect(stats.numberOfCommands).toEqual(1); // root expect(stats.numberOfPendingRequests).toEqual(4); // Loading child content tiles return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { @@ -1378,13 +1379,12 @@ defineSuite([ tileset.debugShowGeometricError = true; scene.renderForSpecs(); expect(tileset._geometricErrorLabels).toBeDefined(); - expect(tileset._geometricErrorLabels.length).toEqual(6); - expect(tileset._geometricErrorLabels._labels[0].text).toEqual('70'); + expect(tileset._geometricErrorLabels.length).toEqual(5); + expect(tileset._geometricErrorLabels._labels[0].text).toEqual('0'); expect(tileset._geometricErrorLabels._labels[1].text).toEqual('0'); expect(tileset._geometricErrorLabels._labels[2].text).toEqual('0'); expect(tileset._geometricErrorLabels._labels[3].text).toEqual('0'); expect(tileset._geometricErrorLabels._labels[4].text).toEqual('0'); - expect(tileset._geometricErrorLabels._labels[5].text).toEqual('0'); tileset.debugShowGeometricError = false; scene.renderForSpecs(); @@ -2270,14 +2270,15 @@ defineSuite([ }); }); - it('marks tileset as refining when tiles not resolved', function() { + it('does not mark tileset as refining when tiles have selection depth 0', function() { viewRootOnly(); return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { viewAllTiles(); scene.renderForSpecs(); var stats = tileset._statistics; expect(stats.numberContentReady).toEqual(1); - expect(tileset._hasMixedContent).toBe(true); + expect(tileset._selectedTiles[0]._selectionDepth).toEqual(0); + expect(tileset._hasMixedContent).toBe(false); return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { expect(stats.numberContentReady).toEqual(5); @@ -2286,78 +2287,91 @@ defineSuite([ }); }); - it('adds stencil clear command first when unresolved', function() { - viewRootOnly(); + it('marks tileset as mixed when tiles have nonzero selection depth', function() { return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { scene.renderForSpecs(); var stats = tileset._statistics; var root = tileset._root; - viewAllTiles(); + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + stats.numberContentReady -= 3; + scene.renderForSpecs(); - return root.children[0].content.readyPromise.then(function() { - scene.renderForSpecs(); - // 1 for root tiles, 1 for stencil clear - expect(stats.numberOfCommands).toEqual(2); - expect(tileset._hasMixedContent).toBe(true); + expect(tileset._hasMixedContent).toBe(true); + expect(stats.numberContentReady).toEqual(2); + expect(tileset._root.children[0].children[0].children[3]._selectionDepth).toEqual(1); + expect(tileset._root._selectionDepth).toEqual(0); - var commandList = scene.frameState.commandList; - expect(commandList[0] instanceof ClearCommand).toBe(true); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + expect(stats.numberContentReady).toEqual(5); + expect(tileset._hasMixedContent).toBe(false); }); }); }); - it('creates duplicate backface commands', function() { - // Look at bottom-left corner of tileset so child is loaded first - scene.camera.moveLeft(200.0); - scene.camera.moveDown(200.0); - + it('adds stencil clear command first when unresolved', function() { return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + scene.renderForSpecs(); + var commandList = scene.frameState.commandList; + expect(commandList[0] instanceof ClearCommand).toBe(true); + expect(commandList[0].stencil).toBe(0); + }); + }); + + it('creates duplicate backface commands', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { var stats = tileset._statistics; var root = tileset._root; - viewAllTiles(); - scene.renderForSpecs(); - return root.children[0].content.readyPromise.then(function() { - scene.renderForSpecs(); - // 2 for root tile, 1 for child, 1 for stencil clear - expect(stats.numberOfCommands).toEqual(4); - expect(root.selected).toBe(true); - expect(root._finalResolution).toBe(false); - expect(root.children[0].children[0].children[3].selected).toBe(true); - expect(root.children[0].children[0].children[3]._finalResolution).toBe(true); - expect(tileset._hasMixedContent).toBe(true); - - var commandList = scene.frameState.commandList; - expect(commandList[0] instanceof ClearCommand).toBe(true); - expect(commandList[0].stencil).toBe(0); - var rs = commandList[1].renderState; - expect(rs.cull.enabled).toBe(true); - expect(rs.cull.face).toBe(CullFace.FRONT); - }); + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + + scene.renderForSpecs();; + + // 2 for root tile, 1 for child, 1 for stencil clear + expect(stats.numberOfCommands).toEqual(4); + expect(root.selected).toBe(true); + expect(root._finalResolution).toBe(false); + expect(root.children[0].children[0].children[3].selected).toBe(true); + expect(root.children[0].children[0].children[3]._finalResolution).toBe(true); + expect(tileset._hasMixedContent).toBe(true); + + var commandList = scene.frameState.commandList; + var rs = commandList[1].renderState; + expect(rs.cull.enabled).toBe(true); + expect(rs.cull.face).toBe(CullFace.FRONT); }); }); it('does not create duplicate backface commands if no selected descendants', function() { - viewRootOnly(); return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { - scene.renderForSpecs(); var stats = tileset._statistics; var root = tileset._root; - viewAllTiles(); - scene.renderForSpecs(); - return root.children[0].content.readyPromise.then(function() { - scene.renderForSpecs(); - // 1 for root tile, 1 for stencil clear - expect(stats.numberOfCommands).toEqual(2); - expect(tileset._hasMixedContent).toBe(true); + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + tileset._root.children[0].children[0].children[3].unloadContent(); - var commandList = scene.frameState.commandList; - expect(commandList[0] instanceof ClearCommand).toBe(true); - expect(commandList[0].stencil).toBe(0); - }); + scene.renderForSpecs();; + + // 2 for root tile, 1 for child, 1 for stencil clear + expect(stats.numberOfCommands).toEqual(1); + expect(root.selected).toBe(true); + expect(root._finalResolution).toBe(true); + expect(root.children[0].children[0].children[0].selected).toBe(false); + expect(root.children[0].children[0].children[1].selected).toBe(false); + expect(root.children[0].children[0].children[2].selected).toBe(false); + expect(root.children[0].children[0].children[3].selected).toBe(false); + expect(tileset._hasMixedContent).toBe(false); }); }); @@ -2393,7 +2407,10 @@ defineSuite([ scene.camera.moveLeft(200.0); scene.camera.moveDown(200.0); - return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url, {loadSiblings : false}).then(function(tileset) { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url, { + loadSiblings : false, + baseScreenSpaceError: 1000000000 + }).then(function(tileset) { var stats = tileset._statistics; scene.renderForSpecs(); expect(stats.numberContentReady).toBe(2); @@ -2413,64 +2430,40 @@ defineSuite([ var tileset = scene.primitives.add(new Cesium3DTileset({ url : tilesetOfTilesetsUrl, - immediatelyLoadDesiredLOD : true, - skipSSEFactor : 0.1, // tiny skip factor so we would normally load all tiles - skipLevels : 0 + immediatelyLoadDesiredLOD : true })); return Cesium3DTilesTester.waitForReady(scene, tileset).then(function(tileset) { scene.renderForSpecs(); return tileset._root.content.readyPromise.then(function() { + tileset._root.refine = Cesium3DTileRefine.REPLACE; tileset._root.children[0].refine = Cesium3DTileRefine.REPLACE; return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { var stats = tileset._statistics; scene.renderForSpecs(); expect(stats.numberContentReady).toBe(1); - - tileset.immediatelyLoadDesiredLOD = false; - scene.renderForSpecs(); - return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { - scene.renderForSpecs(); - expect(stats.numberContentReady).toBe(2); - }); }); }); }); }); it('selects children if no ancestors available', function() { - // Look at bottom-left corner of tileset - scene.camera.moveLeft(200.0); - scene.camera.moveDown(200.0); - - var tileset = scene.primitives.add(new Cesium3DTileset({ - url : tilesetOfTilesetsUrl - })); + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var stats = tileset._statistics; + var root = tileset._root; + var parent = tileset._root.children[0]; + var child = parent.children[3].children[0]; + parent.refine = Cesium3DTileRefine.REPLACE; + parent.unloadContent(); - return Cesium3DTilesTester.waitForReady(scene, tileset).then(function(tileset) { + scene.camera.moveLeft(200.0); + scene.camera.moveDown(200.0); scene.renderForSpecs(); - return tileset._root.content.readyPromise.then(function() { - tileset._root.children[0].refine = Cesium3DTileRefine.REPLACE; - return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { - var stats = tileset._statistics; - scene.renderForSpecs(); - - var parent = tileset._root.children[0]; - var child = parent.children[3].children[0]; - expect(child.contentReady).toBe(true); - expect(parent.contentReady).toBe(false); - expect(child.selected).toBe(true); - expect(parent.selected).toBe(false); - expect(stats.numberOfCommands).toEqual(1); - viewAllTiles(); - scene.renderForSpecs(); - expect(child.contentReady).toBe(true); - expect(parent.contentReady).toBe(false); - expect(child.selected).toBe(true); - expect(parent.selected).toBe(false); - expect(stats.numberOfCommands).toEqual(2); - }); - }); + expect(child.contentReady).toBe(true); + expect(parent.contentReady).toBe(false); + expect(child.selected).toBe(true); + expect(parent.selected).toBe(false); + expect(stats.numberOfCommands).toEqual(1); }); }); From eedd70f97ec84ebd2aea7e81771d8c92d79d01e3 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 28 Apr 2017 10:44:35 -0400 Subject: [PATCH 08/20] cleanup --- Source/Scene/Cesium3DTile.js | 4 +- Source/Scene/Cesium3DTileset.js | 596 +---------------------- Source/Scene/Cesium3DTilesetTraversal.js | 277 ++++++++++- Specs/Scene/Cesium3DTilesetSpec.js | 6 +- 4 files changed, 283 insertions(+), 600 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 21f8d023e4ae..594057f7bb91 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -333,8 +333,8 @@ define([ */ this._optimChildrenWithinParent = Cesium3DTileOptimizationHint.NOT_COMPUTED; - this._sse = 0; - this._sseComputedFrame = -1; + this._screenSpaceError = 0; + this._screenSpaceErrorComputedFrame = -1; this._finalResolution = true; this._requestHeap = undefined; this._depth = 0; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index fbc1b2e477e2..7eea049a086e 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -565,14 +565,6 @@ define([ }); this._backfaceCommands = new ManagedArray(); - - this._selectionState = { - processingQueue : new ManagedArray(), - nextQueue : new ManagedArray(), - finalQueue : new ManagedArray(), - selectionQueue : new ManagedArray(), - done : false - }; } function Cesium3DTilesetStatistics() { @@ -1176,76 +1168,25 @@ define([ tileset._dynamicScreenSpaceErrorComputedDensity = density; } - function getScreenSpaceError(tileset, geometricError, tile, frameState) { - if (geometricError === 0.0) { - // Leaf nodes do not have any error so save the computation - return 0.0; - } - - // Avoid divide by zero when viewer is inside the tile - var camera = frameState.camera; - var frustum = camera.frustum; - var context = frameState.context; - var height = context.drawingBufferHeight; - - var error; - if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { - if (defined(frustum._offCenterFrustum)) { - frustum = frustum._offCenterFrustum; - } - var width = context.drawingBufferWidth; - var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); - error = geometricError / pixelSize; - } else { - var distance = Math.max(tile.distanceToCamera, CesiumMath.EPSILON7); - var sseDenominator = camera.frustum.sseDenominator; - error = (geometricError * height) / (distance * sseDenominator); - - if (tileset.dynamicScreenSpaceError) { - var density = tileset._dynamicScreenSpaceErrorComputedDensity; - var factor = tileset.dynamicScreenSpaceErrorFactor; - var dynamicError = CesiumMath.fog(distance, density) * factor; - error -= dynamicError; - } - } - - return error; - } - - function computeDistanceToCamera(children, frameState) { - var length = children.length; - for (var i = 0; i < length; ++i) { - var child = children[i]; - child.distanceToCamera = child.distanceToTile(frameState); - child._centerZDepth = child.distanceToTileCenter(frameState); + function sortForLoad(a, b) { + var diff = a.distanceToCamera - b.distanceToCamera; + if (a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { + return diff; } - } - function updateTransforms(children, parentTransform) { - var length = children.length; - for (var i = 0; i < length; ++i) { - var child = children[i]; - child.updateTransform(parentTransform); - } - } + var ancestorA = a._ancestorWithLoadedContent; + var ancestorB = b._ancestorWithLoadedContent; - // PERFORMANCE_IDEA: is it worth exploiting frame-to-frame coherence in the sort, i.e., the - // list of children are probably fully or mostly sorted unless the camera moved significantly? - function sortChildrenByDistanceToCamera(a, b) { - // Sort by farthest child first since this is going on a stack - if (b.distanceToCamera === 0 && a.distanceToCamera === 0) { - return b._centerZDepth - a._centerZDepth; + if (ancestorA === ancestorB) { + return diff; } - return b.distanceToCamera - a.distanceToCamera; + var screenSpaceErrorDiff = b._screenSpaceError - a._screenSpaceError; + return screenSpaceErrorDiff === 0 ? diff : screenSpaceErrorDiff; } /////////////////////////////////////////////////////////////////////////// - function isVisible(visibilityPlaneMask) { - return visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; - } - function requestContent(tileset, tile, outOfCore) { if (!outOfCore) { return; @@ -1269,300 +1210,6 @@ define([ } } - function selectTile(tileset, tile, frameState) { - // There may also be a tight box around just the tile's contents, e.g., for a city, we may be - // zoomed into a neighborhood and can cull the skyscrapers in the root node. - if (tile.contentReady && ( - (tile.visibilityPlaneMask === CullingVolume.MASK_INSIDE) || - (tile.contentsVisibility(frameState) !== Intersect.OUTSIDE) - )) { - tileset._selectedTiles.push(tile); - - var tileContent = tile.content; - if (tileContent.featurePropertiesDirty) { - // A feature's property in this tile changed, the tile needs to be re-styled. - tileContent.featurePropertiesDirty = false; - tile.lastStyleTime = 0; // Force applying the style to this tile - tileset._selectedTilesToStyle.push(tile); - } else if ((tile.lastSelectedFrameNumber !== frameState.frameNumber - 1)) { - // Tile is newly selected; it is selected this frame, but was not selected last frame. - tileset._selectedTilesToStyle.push(tile); - } - tile.lastSelectedFrameNumber = frameState.frameNumber; - } - } - - function touch(tileset, tile, outOfCore) { - if (!outOfCore) { - return; - } - var node = tile.replacementNode; - if (defined(node)) { - tileset._replacementList.splice(tileset._replacementSentinel, node); - } - } - - function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { - var flag = Cesium3DTileChildrenVisibility.NONE; - var children = tile.children; - var childrenLength = children.length; - var visibilityPlaneMask = tile.visibilityPlaneMask; - for (var k = 0; k < childrenLength; ++k) { - var child = children[k]; - - var visibilityMask = child.visibility(frameState, visibilityPlaneMask); - - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE; - } - - if (checkViewerRequestVolume) { - if (!child.insideViewerRequestVolume(frameState)) { - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; - } - visibilityMask = CullingVolume.MASK_OUTSIDE; - } else { - flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; - } - } - } - - child.visibilityPlaneMask = visibilityMask; - } - - tile.childrenVisibility = flag; - - return flag; - } - - var descendantStack = []; - - function markLoadedTilesForSelection(tileset, frameState, outOfCore) { - var tiles = tileset._desiredTiles; - var length = tiles.length; - for (var i = 0; i < length; ++i) { - var original = tiles.get(i); - - if (original.refine === Cesium3DTileRefine.ADD && original.contentReady) { - original.selected = true; - original._selectedFrame = frameState.frameNumber; - continue; - } - - var loadedTile = original._ancestorWithLoadedContent; - if (original.hasContent && original.contentReady) { - loadedTile = original; - } - - if (defined(loadedTile)) { - if (!loadedTile.selected) { - loadedTile.selected = true; - loadedTile._selectedFrame = frameState.frameNumber; - } - } else { - // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions - descendantStack.push(original); - while (descendantStack.length > 0) { - var tile = descendantStack.pop(); - var children = tile.children; - var childrenLength = children.length; - for (var j = 0; j < childrenLength; ++j) { - var child = children[j]; - touch(tileset, child, outOfCore); - if (child.contentReady) { - if (!child.selected) { - child.selected = true; - child._finalResolution = true; - child._selectedFrame = frameState.frameNumber; - } - } - if (child._depth - original._depth < 2) { // prevent traversing too far - if (!child.contentReady || child.refine === Cesium3DTileRefine.ADD) { - descendantStack.push(child); - } - } - } - } - } - } - } - - function markNearestLoadedTilesForSelection(tileset, frameState, selectionState, outOfCore) { - var finalQueue = selectionState.finalQueue; - var selectionQueue = selectionState.selectionQueue; - selectionQueue.length = 0; - var frameNumber = frameState.frameNumber; - - var length = finalQueue.length; - for (var i = 0; i < length; ++i) { - var original = finalQueue.get(i); - var tile = original; - // traverse up the tree to find a ready ancestor - if (tile.hasContent || tile.hasTilesetContent) { // could be Empty3DTileContent - while (defined(tile) && !(tile.hasContent && tile.contentReady)) { - if (!tile.contentReady) { - tileset._hasMixedContent = true; - } - tile = tile.parent; - } - } - - if (defined(tile)) { - if (!tile.selected) { - tile._finalResolution = (tile === original || tile.refine === Cesium3DTileRefine.ADD); - tile.selected = true; - tile._selectedFrame = frameNumber; - selectionQueue.push(tile); - } - } else { - // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions - descendantStack.push(original); - while (descendantStack.length > 0) { - tile = descendantStack.pop(); - var children = tile.children; - var childrenLength = children.length; - for (var j = 0; j < childrenLength; ++j) { - var child = children[j]; - if (child.contentReady) { - if (!child.selected) { - child._finalResolution = true; - child.selected = true; - child._selectedFrame = frameNumber; - touch(tileset, child, outOfCore); - selectionQueue.push(child); - } - } - if (child._depth - original._depth < 2) { // prevent traversing too far - if (!child.contentReady || child.refine === Cesium3DTileRefine.ADD) { - descendantStack.push(child); - } - } - } - } - } - } - - selectionQueue.trim(); - } - - function markTilesAsFinal(tiles) { - var length = tiles.length; - var tilesArray = tiles.internalArray; - var i; - for (i = 0; i < length; ++i) { - tilesArray[i]._finalResolution = true; - } - - for (i = 0; i < length; ++i) { - var parent = tilesArray[i].parent; - while (defined(parent)) { - // tiles using additive refinement are always final - parent._finalResolution = (parent.refine === Cesium3DTileRefine.ADD); - parent = parent.parent; - } - } - } - - function sortForLoad(a, b) { - var diff = a.distanceToCamera - b.distanceToCamera; - if (a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { - return diff; - } - - var ancestorA = a._ancestorWithLoadedContent; - var ancestorB = b._ancestorWithLoadedContent; - - if (ancestorA === ancestorB) { - return diff; - } - - var sseA = defined(ancestorA) ? ancestorA._sse - a._sse : a._sse; - var sseB = defined(ancestorB) ? ancestorB._sse - b._sse : b._sse; - - var sseDiff = sseB - sseA; - return sseDiff === 0 ? diff : sseDiff; - } - - function selectTiles(tileset, frameState, outOfCore) { - if (tileset.debugFreezeFrame) { - return; - } - - var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - - tileset._desiredTiles.length = 0; - tileset._selectedTiles.length = 0; - tileset._selectedTilesToStyle.length = 0; - tileset._hasMixedContent = false; - - // Move sentinel node to the tail so, at the start of the frame, all tiles - // may be potentially replaced. Tiles are moved to the right of the sentinel - // when they are selected so they will not be replaced. - var replacementList = tileset._replacementList; - replacementList.splice(replacementList.tail, tileset._replacementSentinel); - - var root = tileset._root; - root.updateTransform(tileset._modelMatrix); - - if (!root.insideViewerRequestVolume(frameState)) { - return; - } - - root.distanceToCamera = root.distanceToTile(frameState); - - if (getScreenSpaceError(tileset, tileset._geometricError, root, frameState) <= maximumScreenSpaceError) { - // The SSE of not rendering the tree is small enough that the tree does not need to be rendered - return; - } - - root.visibilityPlaneMask = root.visibility(frameState, CullingVolume.MASK_INDETERMINATE); - if (root.visibilityPlaneMask === CullingVolume.MASK_OUTSIDE) { - return; - } - - if (root.contentUnloaded) { - requestContent(tileset, root, outOfCore); - return; - } - - if (!tileset.skipLODs) { - // just execute base traversal and add tiles to _desiredTiles - tileset._baseTraversal.execute(tileset, root, tileset._maximumScreenSpaceError, frameState, outOfCore); - var leaves = tileset._baseTraversal.leaves; - var length = leaves.length; - for (var i = 0; i < length; ++i) { - tileset._desiredTiles.push(leaves.get(i)); - } - } else { - if (tileset.immediatelyLoadDesiredLOD) { - tileset._skipTraversal.execute(tileset, root, frameState, outOfCore); - } else { - // leaves of the base traversal is where we start the skip traversal - tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; - - // load and select tiles without skipping up to tileset._baseScreenSpaceError - tileset._baseTraversal.execute(tileset, root, tileset._baseScreenSpaceError, frameState, outOfCore); - - // skip traversal starts from a prepopulated queue from the base traversal - tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); - } - } - - // mark tiles for selection or their nearest loaded ancestor - markLoadedTilesForSelection(tileset, frameState, outOfCore); - - // sort selected tiles by distance to camera and call selectTile on each - // set tile._selectionDepth on all tiles - traverseAndSelect(tileset, root, frameState); - - requestTiles(tileset, tileset._requestHeaps, outOfCore); - - tileset._desiredTiles.trim(); - } - function requestTiles(tileset, requestHeaps, outOfCore) { for (var name in requestHeaps) { if (requestHeaps.hasOwnProperty(name)) { @@ -1575,230 +1222,14 @@ define([ } } - function processSelectionQueue(tileset, frameState, selectionState, outOfCore) { - var processingQueue = selectionState.processingQueue; - var nextQueue = selectionState.nextQueue; - var length = processingQueue.length; - - for (var i = 0; i < length; ++i) { - queueDescendants(tileset, processingQueue.get(i), frameState, selectionState, outOfCore); - } - selectionState.done = (nextQueue.length === 0); - } - function selectionHeuristic(tileset, ancestor, tile) { var skipLevels = tileset.skipLODs ? tileset._skipLevels : 0; var skipSSEFactor = tileset.skipLODs ? tileset.skipSSEFactor : 0.1; return (ancestor !== tile && tile.hasContent && !tileset.immediatelyLoadDesiredLOD) && - (tile._sse < ancestor._sse / skipSSEFactor) && + (tile._screenSpaceError < ancestor._screenSpaceError / skipSSEFactor) && (tile._depth > ancestor._depth + skipLevels); } - function updateAndPushChildren(tileset, tile, frameState, stack, loadSiblings, outOfCore) { - var children = tile.children; - var childrenLength = children.length; - - updateTransforms(children, tile.computedTransform); - computeDistanceToCamera(children, frameState); - - var childrenVisibility = computeChildrenVisibility(tile, frameState, true); - - var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; - var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; - - if (showAdditive || showReplacement || tile.hasTilesetContent) { - for (var i = 0; i < childrenLength; ++i) { - var child = children[i]; - if (isVisible(child.visibilityPlaneMask)) { - if (!showAdditive || getScreenSpaceError(tileset, tile.geometricError, child, frameState) > maximumScreenSpaceError || tile.hasTilesetContent) { - stack.push(child); - } - } else { - touch(tileset, child, outOfCore); - if (loadSiblings) { - loadTile(child); - } - } - } - } - } - - function loadTile(tile) { - if (tile.contentUnloaded) { - tile._requestHeap.insert(tile); - } - } - - function loadAndAddToQueue(tileset, tile, queue) { - loadTile(tile); - if (isVisible(tile.visibilityPlaneMask)) { - queue.push(tile); - } - } - - function visitTile(tileset, tile, frameState, outOfCore) { - ++tileset._statistics.visited; - tile._sse = getScreenSpaceError(tileset, tile.geometricError, tile, frameState); - tile.selected = false; - tile._finalResolution = false; - touch(tileset, tile, outOfCore); - } - - var scratchStack = []; - - /** - * Add all descendants of the starting tile that meet the selection heuristic to selectionState.nextQueue. - * Any tiles that have no children, are additive, or meet the tileset's maximum screen space error are added - * to selectionState.finalQueue instead - */ - function queueDescendants(tileset, start, frameState, selectionState, outOfCore) { - var stack = scratchStack; - - queueTile(tileset, start, start, frameState, selectionState, stack, outOfCore); - - while (stack.length > 0) { - var tile = stack.pop(); - visitTile(tileset, tile, frameState, outOfCore); - queueTile(tileset, start, tile, frameState, selectionState, stack, outOfCore); - } - } - - /** - * Load and add a tile to the proper queue and load and push children to the processing stack - */ - function queueTile(tileset, start, tile, frameState, selectionState, stack, outOfCore) { - var nextQueue = selectionState.nextQueue; - var finalQueue = selectionState.finalQueue; - var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - var loadSiblings = tileset.loadSiblings; - - if (tile.hasTilesetContent) { - loadTile(tile); - if (!tile.contentReady) { - finalQueue.push(tile); - } else { - updateAndPushChildren(tileset, tile, frameState, stack, loadSiblings, outOfCore); - } - } else { - if (tile.refine === Cesium3DTileRefine.ADD) { - loadAndAddToQueue(tileset, tile, finalQueue); - updateAndPushChildren(tileset, tile, frameState, stack, loadSiblings, outOfCore); - } else { - if (tile.children.length === 0) { - loadAndAddToQueue(tileset, tile, finalQueue); - } else if (tile._sse <= maximumScreenSpaceError) { - if (tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint.USE_OPTIMIZATION) { - updateTransforms(tile.children, tile.computedTransform); - if (computeChildrenVisibility(tile, frameState, false) & Cesium3DTileChildrenVisibility.VISIBLE) { - loadAndAddToQueue(tileset, tile, finalQueue); - } else { - ++tileset._statistics.numberOfTilesCulledWithChildrenUnion; - } - } else { - loadAndAddToQueue(tileset, tile, finalQueue); - } - } else if (selectionHeuristic(tileset, start, tile)) { - loadAndAddToQueue(tileset, tile, nextQueue); - } else { - updateAndPushChildren(tileset, tile, frameState, stack, loadSiblings, outOfCore); - // at least one child was visible but not in request volume. Add the parent. - if (tile.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME) { - loadAndAddToQueue(tileset, tile, finalQueue); - if (tile.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE) { - tileset._hasMixedContent = true; - } - } - } - } - } - } - - var scratchStack2 = []; - - /** - * Traverse the tree while tiles are visible and check if their selected frame is the current frame. - * If so, add it to a selection queue. - * Tiles are sorted near to far so we can take advantage of early Z. - * Furthermore, this is a preorder traversal so children tiles are selected before ancestor tiles. - * - * The reason for the preorder traversal is so that tiles can easily be marked with their - * selection depth. A tile's _selectionDepth is its depth in the tree where all non-selected tiles are removed. - * This property is important for use in the stencil test because we want to render deeper tiles on top of their - * ancestors. If a tileset is very deep, the depth is unlikely to fit into the stencil buffer. - * - * We want to select children before their ancestors because there is no guarantee on the relationship between - * the children's z-depth and the ancestor's z-depth. We cannot rely on Z because we want the child to appear on top - * of ancestor regardless of true depth. The stencil tests used require children to be drawn first. @see {@link updateTiles} - * - * NOTE: this will no longer work when there is a chain of selected tiles that is longer than the size of the - * stencil buffer (usually 8 bits). In other words, the subset of the tree containing only selected tiles must be - * no deeper than 255. It is very, very unlikely this will cause a problem. - */ - function traverseAndSelect(tileset, root, frameState) { - var stack = scratchStack; - var ancestorStack = scratchStack2; - - var lastAncestor; - stack.push(root); - while (stack.length > 0 || ancestorStack.length > 0) { - if (ancestorStack.length > 0) { - var waitingTile = ancestorStack[ancestorStack.length - 1]; - if (waitingTile._stackLength === stack.length) { - ancestorStack.pop(); - if (waitingTile === lastAncestor) { - waitingTile._finalResolution = true; - } - selectTile(tileset, waitingTile, frameState); - continue; - } - } - - var tile = stack.pop(); - if (!defined(tile) || tile._lastVisitedFrame !== frameState.frameNumber) { - continue; - } - - var shouldSelect = tile.selected && tile._selectedFrame === frameState.frameNumber && tile.hasContent; - - var children = tile.children; - var childrenLength = children.length; - - children.sort(sortChildrenByDistanceToCamera); - - if (shouldSelect) { - if (tile.refine === Cesium3DTileRefine.ADD) { - tile._finalResolution = true; - selectTile(tileset, tile, frameState); - } else { - tile._selectionDepth = ancestorStack.length; - - if (tile._selectionDepth > 0) { - tileset._hasMixedContent = true; - } - - if (childrenLength === 0) { - tile._finalResolution = true; - lastAncestor = tile; - selectTile(tileset, tile, frameState); - continue; - } - - ancestorStack.push(tile); - lastAncestor = tile; - tile._stackLength = stack.length; - } - } - - for (var i = 0; i < childrenLength; ++i) { - var child = children[i]; - if (isVisible(child.visibilityPlaneMask)) { - stack.push(child); - } - } - } - } - /////////////////////////////////////////////////////////////////////////// function addToProcessingQueue(tileset, tile) { @@ -2159,7 +1590,8 @@ define([ updateDynamicScreenSpaceError(this, frameState); } - selectTiles(this, frameState, outOfCore); + Cesium3DTilesetTraversal.selectTiles(this, frameState, outOfCore); + requestTiles(this, this._requestHeaps, outOfCore); updateTiles(this, frameState); if (outOfCore) { @@ -2188,6 +1620,8 @@ define([ return false; }; + var scratchStack = []; + /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index d6ffab10869e..93911911e8d2 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -27,6 +27,255 @@ define([ SceneMode) { 'use strict'; + var Cesium3DTilesetTraversal = {}; + + function selectTiles(tileset, frameState, outOfCore) { + if (tileset.debugFreezeFrame) { + return; + } + + var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + + tileset._desiredTiles.length = 0; + tileset._selectedTiles.length = 0; + tileset._selectedTilesToStyle.length = 0; + tileset._hasMixedContent = false; + + // Move sentinel node to the tail so, at the start of the frame, all tiles + // may be potentially replaced. Tiles are moved to the right of the sentinel + // when they are selected so they will not be replaced. + var replacementList = tileset._replacementList; + replacementList.splice(replacementList.tail, tileset._replacementSentinel); + + var root = tileset._root; + root.updateTransform(tileset._modelMatrix); + + if (!root.insideViewerRequestVolume(frameState)) { + return; + } + + root.distanceToCamera = root.distanceToTile(frameState); + + if (getScreenSpaceError(tileset, tileset._geometricError, root, frameState) <= maximumScreenSpaceError) { + // The SSE of not rendering the tree is small enough that the tree does not need to be rendered + return; + } + + root.visibilityPlaneMask = root.visibility(frameState, CullingVolume.MASK_INDETERMINATE); + if (root.visibilityPlaneMask === CullingVolume.MASK_OUTSIDE) { + return; + } + + if (root.contentUnloaded) { + root._requestHeap.insert(root); + return; + } + + if (!tileset.skipLODs) { + // just execute base traversal and add tiles to _desiredTiles + tileset._baseTraversal.execute(tileset, root, maximumScreenSpaceError, frameState, outOfCore); + var leaves = tileset._baseTraversal.leaves; + var length = leaves.length; + for (var i = 0; i < length; ++i) { + tileset._desiredTiles.push(leaves.get(i)); + } + } else { + if (tileset.immediatelyLoadDesiredLOD) { + tileset._skipTraversal.execute(tileset, root, frameState, outOfCore); + } else { + // leaves of the base traversal is where we start the skip traversal + tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; + + // load and select tiles without skipping up to tileset._baseScreenSpaceError + tileset._baseTraversal.execute(tileset, root, tileset._baseScreenSpaceError, frameState, outOfCore); + + // skip traversal starts from a prepopulated queue from the base traversal + tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); + } + } + + // mark tiles for selection or their nearest loaded ancestor + markLoadedTilesForSelection(tileset, frameState, outOfCore); + + // sort selected tiles by distance to camera and call selectTile on each + // set tile._selectionDepth on all tiles + traverseAndSelect(tileset, root, frameState); + + tileset._desiredTiles.trim(); + } + + var descendantStack = []; + + function markLoadedTilesForSelection(tileset, frameState, outOfCore) { + var tiles = tileset._desiredTiles; + var length = tiles.length; + for (var i = 0; i < length; ++i) { + var original = tiles.get(i); + + if (original.refine === Cesium3DTileRefine.ADD && original.contentReady) { + original.selected = true; + original._selectedFrame = frameState.frameNumber; + continue; + } + + var loadedTile = original._ancestorWithLoadedContent; + if (original.hasContent && original.contentReady) { + loadedTile = original; + } + + if (defined(loadedTile)) { + if (!loadedTile.selected) { + loadedTile.selected = true; + loadedTile._selectedFrame = frameState.frameNumber; + } + } else { + // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions + descendantStack.push(original); + while (descendantStack.length > 0) { + var tile = descendantStack.pop(); + var children = tile.children; + var childrenLength = children.length; + for (var j = 0; j < childrenLength; ++j) { + var child = children[j]; + touch(tileset, child, outOfCore); + if (child.contentReady) { + if (!child.selected) { + child.selected = true; + child._finalResolution = true; + child._selectedFrame = frameState.frameNumber; + } + } + if (child._depth - original._depth < 2) { // prevent traversing too far + if (!child.contentReady || child.refine === Cesium3DTileRefine.ADD) { + descendantStack.push(child); + } + } + } + } + } + } + } + + var scratchStack = []; + var scratchStack2 = []; + + /** + * Traverse the tree while tiles are visible and check if their selected frame is the current frame. + * If so, add it to a selection queue. + * Tiles are sorted near to far so we can take advantage of early Z. + * Furthermore, this is a preorder traversal so children tiles are selected before ancestor tiles. + * + * The reason for the preorder traversal is so that tiles can easily be marked with their + * selection depth. A tile's _selectionDepth is its depth in the tree where all non-selected tiles are removed. + * This property is important for use in the stencil test because we want to render deeper tiles on top of their + * ancestors. If a tileset is very deep, the depth is unlikely to fit into the stencil buffer. + * + * We want to select children before their ancestors because there is no guarantee on the relationship between + * the children's z-depth and the ancestor's z-depth. We cannot rely on Z because we want the child to appear on top + * of ancestor regardless of true depth. The stencil tests used require children to be drawn first. @see {@link updateTiles} + * + * NOTE: this will no longer work when there is a chain of selected tiles that is longer than the size of the + * stencil buffer (usually 8 bits). In other words, the subset of the tree containing only selected tiles must be + * no deeper than 255. It is very, very unlikely this will cause a problem. + */ + function traverseAndSelect(tileset, root, frameState) { + var stack = scratchStack; + var ancestorStack = scratchStack2; + + var lastAncestor; + stack.push(root); + while (stack.length > 0 || ancestorStack.length > 0) { + if (ancestorStack.length > 0) { + var waitingTile = ancestorStack[ancestorStack.length - 1]; + if (waitingTile._stackLength === stack.length) { + ancestorStack.pop(); + if (waitingTile === lastAncestor) { + waitingTile._finalResolution = true; + } + selectTile(tileset, waitingTile, frameState); + continue; + } + } + + var tile = stack.pop(); + if (!defined(tile) || tile._lastVisitedFrame !== frameState.frameNumber) { + continue; + } + + var shouldSelect = tile.selected && tile._selectedFrame === frameState.frameNumber && tile.hasContent; + + var children = tile.children; + var childrenLength = children.length; + + children.sort(sortChildrenByDistanceToCamera); + + if (shouldSelect) { + if (tile.refine === Cesium3DTileRefine.ADD) { + tile._finalResolution = true; + selectTile(tileset, tile, frameState); + } else { + tile._selectionDepth = ancestorStack.length; + + if (tile._selectionDepth > 0) { + tileset._hasMixedContent = true; + } + + if (childrenLength === 0) { + tile._finalResolution = true; + lastAncestor = tile; + selectTile(tileset, tile, frameState); + continue; + } + + ancestorStack.push(tile); + lastAncestor = tile; + tile._stackLength = stack.length; + } + } + + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + if (isVisible(child.visibilityPlaneMask)) { + stack.push(child); + } + } + } + } + + function selectTile(tileset, tile, frameState) { + // There may also be a tight box around just the tile's contents, e.g., for a city, we may be + // zoomed into a neighborhood and can cull the skyscrapers in the root node. + if (tile.contentReady && ( + (tile.visibilityPlaneMask === CullingVolume.MASK_INSIDE) || + (tile.contentsVisibility(frameState) !== Intersect.OUTSIDE) + )) { + tileset._selectedTiles.push(tile); + + var tileContent = tile.content; + if (tileContent.featurePropertiesDirty) { + // A feature's property in this tile changed, the tile needs to be re-styled. + tileContent.featurePropertiesDirty = false; + tile.lastStyleTime = 0; // Force applying the style to this tile + tileset._selectedTilesToStyle.push(tile); + } else if ((tile.lastSelectedFrameNumber !== frameState.frameNumber - 1)) { + // Tile is newly selected; it is selected this frame, but was not selected last frame. + tileset._selectedTilesToStyle.push(tile); + } + tile.lastSelectedFrameNumber = frameState.frameNumber; + } + } + + // PERFORMANCE_IDEA: is it worth exploiting frame-to-frame coherence in the sort, i.e., the + // list of children are probably fully or mostly sorted unless the camera moved significantly? + function sortChildrenByDistanceToCamera(a, b) { + // Sort by farthest child first since this is going on a stack + if (b.distanceToCamera === 0 && a.distanceToCamera === 0) { + return b._centerZDepth - a._centerZDepth; + } + + return b.distanceToCamera - a.distanceToCamera; + } + var emptyArray = freezeObject([]); function BaseTraversal() { @@ -75,12 +324,12 @@ define([ } // stop traversal when we've attained the desired level of error - if (tile._sse <= baseScreenSpaceError) { + if (tile._screenSpaceError <= baseScreenSpaceError) { return emptyArray; } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > baseScreenSpaceError; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > baseScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; if (showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent)) { @@ -186,7 +435,7 @@ define([ } // stop traversal when we've attained the desired level of error - if (tile._sse <= maximumScreenSpaceError) { + if (tile._screenSpaceError <= maximumScreenSpaceError) { return emptyArray; } @@ -200,7 +449,7 @@ define([ } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._sse > maximumScreenSpaceError; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > maximumScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; // at least one child is visible, but is not in request volume. the parent must be selected @@ -226,8 +475,7 @@ define([ if (!defined(parent)) { return true; } - var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._sse > maximumScreenSpaceError; - var showReplacement = parent.refine === Cesium3DTileRefine.REPLACE && (parent.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._screenSpaceError > maximumScreenSpaceError; return isVisible(tile.visibilityPlaneMask) && (!showAdditive || getScreenSpaceError(this.tileset, parent.geometricError, tile, this.frameState) > maximumScreenSpaceError); }; @@ -294,9 +542,9 @@ define([ } function computeSSE(tile, frameState) { - if (tile._sseComputedFrame !== frameState.frameNumber) { - tile._sseComputedFrame = frameState.frameNumber; - tile._sse = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); + if (tile._screenSpaceErrorComputedFrame !== frameState.frameNumber) { + tile._screenSpaceErrorComputedFrame = frameState.frameNumber; + tile._screenSpaceError = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); } } @@ -484,8 +732,11 @@ define([ } } - return { - BaseTraversal: BaseTraversal, - SkipTraversal: SkipTraversal - }; + Cesium3DTilesetTraversal.selectTiles = selectTiles; + + Cesium3DTilesetTraversal.BaseTraversal = BaseTraversal; + + Cesium3DTilesetTraversal.SkipTraversal = SkipTraversal; + + return Cesium3DTilesetTraversal; }); \ No newline at end of file diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index 0a6443446d91..d8dffbecee20 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2291,7 +2291,6 @@ defineSuite([ return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { scene.renderForSpecs(); var stats = tileset._statistics; - var root = tileset._root; tileset._root.children[0].children[0].children[0].unloadContent(); tileset._root.children[0].children[0].children[1].unloadContent(); @@ -2334,7 +2333,7 @@ defineSuite([ tileset._root.children[0].children[0].children[1].unloadContent(); tileset._root.children[0].children[0].children[2].unloadContent(); - scene.renderForSpecs();; + scene.renderForSpecs(); // 2 for root tile, 1 for child, 1 for stencil clear expect(stats.numberOfCommands).toEqual(4); @@ -2361,7 +2360,7 @@ defineSuite([ tileset._root.children[0].children[0].children[2].unloadContent(); tileset._root.children[0].children[0].children[3].unloadContent(); - scene.renderForSpecs();; + scene.renderForSpecs(); // 2 for root tile, 1 for child, 1 for stencil clear expect(stats.numberOfCommands).toEqual(1); @@ -2449,7 +2448,6 @@ defineSuite([ it('selects children if no ancestors available', function() { return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { var stats = tileset._statistics; - var root = tileset._root; var parent = tileset._root.children[0]; var child = parent.children[3].children[0]; parent.refine = Cesium3DTileRefine.REPLACE; From 4d976f9c3eb227e9a8852ed9cc4c45910c0e6928 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 28 Apr 2017 11:30:57 -0400 Subject: [PATCH 09/20] unused flags --- Source/Scene/Cesium3DTileset.js | 5 +---- Source/Scene/Cesium3DTilesetTraversal.js | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 2ce31171a1dd..efa97dbd9c33 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -408,8 +408,6 @@ define([ this.debugShowGeometricError = defaultValue(options.debugShowGeometricError, false); this._geometricErrorLabels = undefined; - this._labels = undefined; - /** * The event fired to indicate progress of loading new tiles. This event is fired when a new tile * is requested, when a requested tile is finished downloading, and when a downloaded tile has been @@ -565,8 +563,7 @@ define([ this._baseTraversal = new Cesium3DTilesetTraversal.BaseTraversal(); this._skipTraversal = new Cesium3DTilesetTraversal.SkipTraversal({ - selectionHeuristic: selectionHeuristic, - selectedTiles: this._selectedTiles + selectionHeuristic: selectionHeuristic }); this._backfaceCommands = new ManagedArray(); diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index ba6d0019c2e6..3d8666774b91 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -366,7 +366,6 @@ define([ this.queue1 = new ManagedArray(); this.queue2 = new ManagedArray(); this.internalDFS = new InternalSkipTraversal(options.selectionHeuristic); - this.selectedTiles = options.selectedTiles; } SkipTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { From a9786fb8c5e7050c92ad3c1effffd2ca8710251d Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 28 Apr 2017 11:38:12 -0400 Subject: [PATCH 10/20] update sort for load --- Source/Scene/Cesium3DTileset.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index efa97dbd9c33..7ce043129fc5 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1164,13 +1164,6 @@ define([ return diff; } - var ancestorA = a._ancestorWithLoadedContent; - var ancestorB = b._ancestorWithLoadedContent; - - if (ancestorA === ancestorB) { - return diff; - } - var screenSpaceErrorDiff = b._screenSpaceError - a._screenSpaceError; return screenSpaceErrorDiff === 0 ? diff : screenSpaceErrorDiff; } From 0e6b512c385c3c639ce95bd50421be28d2c06f16 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Tue, 2 May 2017 12:01:13 -0400 Subject: [PATCH 11/20] updates; wait for nearest descendents to load before refining for base traversal --- Source/Scene/Cesium3DTile.js | 1 - Source/Scene/Cesium3DTileset.js | 26 ++-- Source/Scene/Cesium3DTilesetTraversal.js | 146 +++++++++++++++-------- 3 files changed, 106 insertions(+), 67 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 9efdcc503eb7..f0d23daee0f8 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -337,7 +337,6 @@ define([ this._stackLength = 0; this._selectedFrame = -1; this._selectionDepth = 0; - this._lastFinalResolution = undefined; this._lastSelectionDepth = undefined; this._requestedFrame = undefined; this._lastVisitedFrame = undefined; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 7ce043129fc5..851e42e5f80f 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -127,8 +127,8 @@ define([ * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source. * @param {Boolean} [options.skipLODs=true] Determines if level-of-detail skipping optimization should be used. * @param {Number} [options.baseScreenSpaceError=1024] The screen-space error that must be reached before skipping LODs - * @param {Number} [options.skipSSEFactor=10] Multiplier defining the minimum screen space error to skip when loading tiles. Used in conjuction with skipLevels to determine which tiles to load. - * @param {Number} [options.skipLevels=1] Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjuction with skipSSEFactor to determine which tiles to load. + * @param {Number} [options.skipScreenSpaceErrorFactor=10] Multiplier defining the minimum screen space error to skip when loading tiles. Used in conjuction with skipLevels to determine which tiles to load. + * @param {Number} [options.skipLevels=1] Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjuction with skipScreenSpaceErrorFactor to determine which tiles to load. * @param {Boolean} [options.immediatelyLoadDesiredLOD=false] When true, do not progressively refine. Immediately load the desired LOD. * @param {Boolean} [options.loadSiblings=false] Determines whether sibling tiles should be loaded when skipping levels-of-detail. When true, the siblings of any visible and downloaded tile are downloaded as well. * @@ -531,7 +531,7 @@ define([ */ this.skipLODs = defaultValue(options.skipLODs, true); - this._skipSSEFactor = defaultValue(options.skipSSEFactor, 10); + this.skipScreenSpaceErrorFactor = defaultValue(options.skipScreenSpaceErrorFactor, 10); this._skipLevels = defaultValue(options.skipLevels, 1); @@ -958,13 +958,13 @@ define([ * @type {Number} * @default 10 */ - skipSSEFactor : { + skipScreenSpaceErrorFactor : { get : function() { - return this._skipSSEFactor; + return this._skipScreenSpaceErrorFactor; }, set : function(value) { - this._skipSSEFactor = value; + this._skipScreenSpaceErrorFactor = value; } }, @@ -1159,13 +1159,13 @@ define([ } function sortForLoad(a, b) { - var diff = a.distanceToCamera - b.distanceToCamera; + var distanceDifference = a.distanceToCamera - b.distanceToCamera; if (a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { - return diff; + return distanceDifference; } - var screenSpaceErrorDiff = b._screenSpaceError - a._screenSpaceError; - return screenSpaceErrorDiff === 0 ? diff : screenSpaceErrorDiff; + var screenSpaceErrorDifference = b._screenSpaceError - a._screenSpaceError; + return screenSpaceErrorDifference === 0 ? distanceDifference : screenSpaceErrorDifference; } /////////////////////////////////////////////////////////////////////////// @@ -1209,10 +1209,10 @@ define([ function selectionHeuristic(tileset, ancestor, tile) { var skipLevels = tileset.skipLODs ? tileset._skipLevels : 0; - var skipSSEFactor = tileset.skipLODs ? tileset.skipSSEFactor : 0.1; + var skipScreenSpaceErrorFactor = tileset.skipLODs ? tileset.skipScreenSpaceErrorFactor : 0.1; return (ancestor !== tile && !tile.hasEmptyContent && !tileset.immediatelyLoadDesiredLOD) && - (tile._screenSpaceError < ancestor._screenSpaceError / skipSSEFactor) && + (tile._screenSpaceError < ancestor._screenSpaceError / skipScreenSpaceErrorFactor) && (tile._depth > ancestor._depth + skipLevels); } @@ -1388,7 +1388,7 @@ define([ var tile, i; - var bivariateVisibilityTest = tileset._hasMixedContent && frameState.context.stencilBuffer && length > 0; + var bivariateVisibilityTest = tileset.skipLODs && tileset._hasMixedContent && frameState.context.stencilBuffer && length > 0; tileset._backfaceCommands.length = 0; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 3d8666774b91..33f5b08c1546 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -1,6 +1,5 @@ /*global define*/ define([ - '../Core/Check', '../Core/defined', '../Core/defineProperties', '../Core/freezeObject', @@ -13,7 +12,6 @@ define([ './OrthographicFrustum', './SceneMode' ], function( - Check, defined, defineProperties, freezeObject, @@ -124,10 +122,8 @@ define([ } if (defined(loadedTile)) { - if (!loadedTile.selected) { - loadedTile.selected = true; - loadedTile._selectedFrame = frameState.frameNumber; - } + loadedTile.selected = true; + loadedTile._selectedFrame = frameState.frameNumber; } else { // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions descendantStack.push(original); @@ -139,11 +135,9 @@ define([ var child = children[j]; touch(tileset, child, outOfCore); if (child.contentReady) { - if (!child.selected) { - child.selected = true; - child._finalResolution = true; - child._selectedFrame = frameState.frameNumber; - } + child.selected = true; + child._finalResolution = true; + child._selectedFrame = frameState.frameNumber; } if (child._depth - original._depth < 2) { // prevent traversing too far if (!child.contentReady || child.refine === Cesium3DTileRefine.ADD) { @@ -285,6 +279,7 @@ define([ this.stack = new ManagedArray(); this.leaves = new ManagedArray(); this.baseScreenSpaceError = undefined; + this.internalDFS = new InternalBaseTraversal(); } BaseTraversal.prototype.execute = function(tileset, root, baseScreenSpaceError, frameState, outOfCore) { @@ -292,12 +287,11 @@ define([ this.frameState = frameState; this.outOfCore = outOfCore; this.leaves.length = 0; - this.baseScreenSpaceError = baseScreenSpaceError; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals('baseScreenSpaceError', baseScreenSpaceError, this.tileset._maximumScreenSpaceError); - //>>includeEnd('debug'); - + this.baseScreenSpaceError = Math.max(baseScreenSpaceError, this.tileset._maximumScreenSpaceError); + this.internalDFS.tileset = this.tileset; + this.internalDFS.frameState = this.frameState; + this.internalDFS.outOfCore = this.outOfCore; + this.internalDFS.baseScreenSpaceError = this.baseScreenSpaceError; DFS(root, this); }; @@ -306,59 +300,109 @@ define([ }; BaseTraversal.prototype.getChildren = function(tile) { - var tileset = this.tileset; - var baseScreenSpaceError = this.baseScreenSpaceError; + if (hasVisibleChildren(this, tile, this.baseScreenSpaceError)) { + var children = tile.children; + var childrenLength = children.length; + var allReady = true; + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + loadTile(child, this.frameState); + touch(this.tileset, child, this.outOfCore); + + // this content cannot be replaced until all of the nearest descendants with content are all loaded + if (tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent) { + if (!child.hasEmptyContent) { + allReady = allReady && child.contentReady; + } else { + allReady = allReady && this.internalDFS.execute(child); + } + } + } + + if (allReady) { + return children; + } + } + return emptyArray; + }; + + function hasVisibleChildren(traversal, tile, screenSpaceError) { + var tileset = traversal.tileset; if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content if (!defined(tile._ancestorWithContent)) { - loadTile(tile, this.frameState); - } - if (!tile.contentReady) { - return emptyArray; + loadTile(tile, traversal.frameState); } } - if (tile.refine === Cesium3DTileRefine.ADD) { + if (tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent) { tileset._desiredTiles.push(tile); } // stop traversal when we've attained the desired level of error - if (tile._screenSpaceError <= baseScreenSpaceError) { - return emptyArray; + if (tile._screenSpaceError <= screenSpaceError && tile.hasRenderableContent) { + return false; } - var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > baseScreenSpaceError; + var childrenVisibility = updateChildren(tileset, tile, traversal.frameState); + var showAdditive = tile.refine === Cesium3DTileRefine.ADD; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; - if (showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent)) { + return showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent); + }; + + BaseTraversal.prototype.shouldVisit = function(tile) { + return isVisible(tile.visibilityPlaneMask); + }; + + BaseTraversal.prototype.leafHandler = function(tile) { + // additive tiles have already been pushed to tileset._desiredTiles + if (tile.refine === Cesium3DTileRefine.REPLACE) { + this.leaves.push(tile); + } + }; + + function InternalBaseTraversal(options) { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.baseScreenSpaceError = undefined; + this.stack = new ManagedArray(); + } + + InternalBaseTraversal.prototype.execute = function(root) { + this.allLoaded = true; + DFS(root, this); + return this.allLoaded; + }; + + InternalBaseTraversal.prototype.visit = function(tile) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + if (!tile.contentReady) { + this.allLoaded = false; + } + }; + + // Continue traversing until we have renderable content. We want the first descendants with content of the root to load + InternalBaseTraversal.prototype.shouldVisit = function(tile) { + return !tile.hasRenderableContent && isVisible(tile.visibilityPlaneMask); + }; + + InternalBaseTraversal.prototype.getChildren = function(tile) { + if (hasVisibleChildren(this, tile, this.baseScreenSpaceError)) { var children = tile.children; var childrenLength = children.length; - var allReady = true; for (var i = 0; i < childrenLength; ++i) { var child = children[i]; loadTile(child, this.frameState); - allReady = allReady && child.contentReady; - touch(tileset, child, this.outOfCore); - } - - if (allReady) { - return children; + touch(this.tileset, child, this.outOfCore); } + return children; } - return emptyArray; }; - BaseTraversal.prototype.shouldVisit = function(tile) { - return isVisible(tile.visibilityPlaneMask); - }; - - BaseTraversal.prototype.leafHandler = function(tile) { - this.leaves.push(tile); - }; - function SkipTraversal(options) { this.tileset = undefined; this.frameState = undefined; @@ -421,15 +465,11 @@ define([ }; InternalSkipTraversal.prototype.getChildren = function(tile) { - var tileset = tile._tileset; + var tileset = this.tileset; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - if (tile.hasTilesetContent) { - if (!tile.contentReady) { - return emptyArray; - } - } else { - if (tile.refine === Cesium3DTileRefine.ADD) { + if (!tile.hasTilesetContent) { + if (tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent) { tileset._desiredTiles.push(tile); } @@ -448,7 +488,7 @@ define([ } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > maximumScreenSpaceError; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; // at least one child is visible, but is not in request volume. the parent must be selected From 67964c0559c61caf6348769efccbfa7cee9455c8 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Tue, 2 May 2017 12:31:10 -0400 Subject: [PATCH 12/20] add children union culling optimization --- Source/Scene/Cesium3DTilesetTraversal.js | 62 ++++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 33f5b08c1546..f6bdbc050f46 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -300,7 +300,12 @@ define([ }; BaseTraversal.prototype.getChildren = function(tile) { - if (hasVisibleChildren(this, tile, this.baseScreenSpaceError)) { + if (this.updateAndCheckChildren(tile)) { + if (!childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return emptyArray; + } + var children = tile.children; var childrenLength = children.length; var allReady = true; @@ -326,13 +331,13 @@ define([ return emptyArray; }; - function hasVisibleChildren(traversal, tile, screenSpaceError) { - var tileset = traversal.tileset; + BaseTraversal.prototype.updateAndCheckChildren = function(tile) { + var tileset = this.tileset; if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content if (!defined(tile._ancestorWithContent)) { - loadTile(tile, traversal.frameState); + loadTile(tile, this.frameState); } } @@ -341,11 +346,11 @@ define([ } // stop traversal when we've attained the desired level of error - if (tile._screenSpaceError <= screenSpaceError && tile.hasRenderableContent) { + if (tile._screenSpaceError <= this.baseScreenSpaceError && tile.hasRenderableContent) { return false; } - var childrenVisibility = updateChildren(tileset, tile, traversal.frameState); + var childrenVisibility = updateChildren(tileset, tile, this.frameState); var showAdditive = tile.refine === Cesium3DTileRefine.ADD; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; @@ -386,11 +391,16 @@ define([ // Continue traversing until we have renderable content. We want the first descendants with content of the root to load InternalBaseTraversal.prototype.shouldVisit = function(tile) { - return !tile.hasRenderableContent && isVisible(tile.visibilityPlaneMask); + return !tile.hasRenderableContent && isVisible(tile.visibilityPlaneMask); }; InternalBaseTraversal.prototype.getChildren = function(tile) { - if (hasVisibleChildren(this, tile, this.baseScreenSpaceError)) { + if (this.updateAndCheckChildren(tile, this.baseScreenSpaceError)) { + if (!childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return emptyArray; + } + var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { @@ -403,6 +413,8 @@ define([ return emptyArray; }; + InternalBaseTraversal.prototype.updateAndCheckChildren = BaseTraversal.prototype.updateAndCheckChildren; + function SkipTraversal(options) { this.tileset = undefined; this.frameState = undefined; @@ -497,6 +509,11 @@ define([ } if (showAdditive || showReplacement || tile.hasTilesetContent) { + if (!childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return emptyArray; + } + var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { @@ -546,7 +563,7 @@ define([ updateTransforms(children, tile.computedTransform); computeDistanceToCamera(children, frameState); - return computeChildrenVisibility(tile, frameState, true); + return computeChildrenVisibility(tile, frameState); } function visitTile(tileset, tile, frameState, outOfCore) { @@ -595,7 +612,7 @@ define([ } } - function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { + function computeChildrenVisibility(tile, frameState) { var flag = Cesium3DTileChildrenVisibility.NONE; var children = tile.children; var childrenLength = children.length; @@ -609,17 +626,15 @@ define([ flag |= Cesium3DTileChildrenVisibility.VISIBLE; } - if (checkViewerRequestVolume) { - if (!child.insideViewerRequestVolume(frameState)) { - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; - } - visibilityMask = CullingVolume.MASK_OUTSIDE; - } else { - flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; - } + if (!child.insideViewerRequestVolume(frameState)) { + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; + } + visibilityMask = CullingVolume.MASK_OUTSIDE; + } else { + flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; } } @@ -688,6 +703,11 @@ define([ return visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; } + function childrenAreVisible(tile) { + // optimization does not apply for additive refinement + return tile.refine === Cesium3DTileRefine.ADD || tile.children.length === 0 || tile.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE !== 0; + } + function DFS(root, options) { var stack = options.stack; From 2b858d69eea76cef51cae4f6e493dd133430d32a Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Tue, 2 May 2017 13:19:53 -0400 Subject: [PATCH 13/20] bugfixes --- Source/Scene/Cesium3DTilesetTraversal.js | 43 +++++++++++------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index f6bdbc050f46..8b06d5270b4c 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -301,11 +301,6 @@ define([ BaseTraversal.prototype.getChildren = function(tile) { if (this.updateAndCheckChildren(tile)) { - if (!childrenAreVisible(tile)) { - ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; - return emptyArray; - } - var children = tile.children; var childrenLength = children.length; var allReady = true; @@ -341,12 +336,12 @@ define([ } } - if (tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent) { + if (hasAdditiveContent(tile)) { tileset._desiredTiles.push(tile); } // stop traversal when we've attained the desired level of error - if (tile._screenSpaceError <= this.baseScreenSpaceError && tile.hasRenderableContent) { + if (tile._screenSpaceError <= this.baseScreenSpaceError) { return false; } @@ -362,8 +357,12 @@ define([ }; BaseTraversal.prototype.leafHandler = function(tile) { - // additive tiles have already been pushed to tileset._desiredTiles - if (tile.refine === Cesium3DTileRefine.REPLACE) { + // if skipLODs is off, leaves of the base traversal get pushed to tileset._desiredTiles. additive tiles have already been pushed + if (this.tileset.skipLODs || tile.refine === Cesium3DTileRefine.REPLACE) { + if (tile.refine === Cesium3DTileRefine.REPLACE && !childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return; + } this.leaves.push(tile); } }; @@ -396,11 +395,6 @@ define([ InternalBaseTraversal.prototype.getChildren = function(tile) { if (this.updateAndCheckChildren(tile, this.baseScreenSpaceError)) { - if (!childrenAreVisible(tile)) { - ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; - return emptyArray; - } - var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { @@ -450,7 +444,7 @@ define([ SkipTraversal.prototype.leafHandler = function(tile) { // additive tiles have already been pushed - if (tile.refine === Cesium3DTileRefine.REPLACE) { + if (!hasAdditiveContent(tile)) { this.tileset._desiredTiles.push(tile); } }; @@ -481,7 +475,7 @@ define([ var maximumScreenSpaceError = tileset._maximumScreenSpaceError; if (!tile.hasTilesetContent) { - if (tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent) { + if (hasAdditiveContent(tile)) { tileset._desiredTiles.push(tile); } @@ -500,7 +494,7 @@ define([ } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > maximumScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; // at least one child is visible, but is not in request volume. the parent must be selected @@ -509,11 +503,6 @@ define([ } if (showAdditive || showReplacement || tile.hasTilesetContent) { - if (!childrenAreVisible(tile)) { - ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; - return emptyArray; - } - var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { @@ -538,6 +527,10 @@ define([ InternalSkipTraversal.prototype.leafHandler = function(tile) { if (tile !== this.root) { + if (tile.refine === Cesium3DTileRefine.REPLACE && !childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return; + } if (!tile.hasEmptyContent) { if (this.tileset.loadSiblings) { var parent = tile.parent; @@ -551,7 +544,7 @@ define([ } } this.queue.push(tile); - } else if (tile.refine === Cesium3DTileRefine.REPLACE) { + } else if (!hasAdditiveContent(tile)) { // additive tiles have already been pushed this.tileset._desiredTiles.push(tile); } @@ -708,6 +701,10 @@ define([ return tile.refine === Cesium3DTileRefine.ADD || tile.children.length === 0 || tile.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE !== 0; } + function hasAdditiveContent(tile) { + return tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent; + } + function DFS(root, options) { var stack = options.stack; From 50ec7c83207adb40117f65ff918684cdab2636eb Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Wed, 3 May 2017 09:23:33 -0400 Subject: [PATCH 14/20] fix root of tileset of tilesets --- Source/Scene/Cesium3DTilesetTraversal.js | 34 +++++++++++++++--------- Specs/Scene/Cesium3DTilesetSpec.js | 5 ++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 8b06d5270b4c..ff738b832436 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -342,11 +342,15 @@ define([ // stop traversal when we've attained the desired level of error if (tile._screenSpaceError <= this.baseScreenSpaceError) { - return false; + // When skipping LODs, require an existing base level of content first + if (!tileset.skipLODs || tile.hasRenderableContent || defined(tile._ancestorWithContent)) { + computeChildrenVisibility(tile, this.frameState, false); + return false; + } } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > this.baseScreenSpaceError; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; return showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent); @@ -481,6 +485,7 @@ define([ // stop traversal when we've attained the desired level of error if (tile._screenSpaceError <= maximumScreenSpaceError) { + computeChildrenVisibility(tile, this.frameState, false); return emptyArray; } @@ -489,6 +494,7 @@ define([ (!tile.hasEmptyContent && tile.contentUnloaded) && defined(tile._ancestorWithLoadedContent) && this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { + computeChildrenVisibility(tile, this.frameState, false); return emptyArray; } } @@ -556,7 +562,7 @@ define([ updateTransforms(children, tile.computedTransform); computeDistanceToCamera(children, frameState); - return computeChildrenVisibility(tile, frameState); + return computeChildrenVisibility(tile, frameState, true); } function visitTile(tileset, tile, frameState, outOfCore) { @@ -605,7 +611,7 @@ define([ } } - function computeChildrenVisibility(tile, frameState) { + function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { var flag = Cesium3DTileChildrenVisibility.NONE; var children = tile.children; var childrenLength = children.length; @@ -619,15 +625,17 @@ define([ flag |= Cesium3DTileChildrenVisibility.VISIBLE; } - if (!child.insideViewerRequestVolume(frameState)) { - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; - } - visibilityMask = CullingVolume.MASK_OUTSIDE; - } else { - flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; + if (checkViewerRequestVolume) { + if (!child.insideViewerRequestVolume(frameState)) { + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; + } + visibilityMask = CullingVolume.MASK_OUTSIDE; + } else { + flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; + } } } diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index 1ba40d56d54f..54b97431ae30 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -1402,8 +1402,9 @@ defineSuite([ viewRootOnly(); // Root tiles are loaded initially var promises = [ - Cesium3DTilesTester.loadTileset(scene, tilesetUrl), - Cesium3DTilesTester.loadTileset(scene, tilesetUrl) + // skip LODs loads a base level of content first + Cesium3DTilesTester.loadTileset(scene, tilesetUrl, {skipLODs: false}), + Cesium3DTilesTester.loadTileset(scene, tilesetUrl, {skipLODs: false}) ]; return when.all(promises, function(tilesets) { From 1170c921ea4f33820bde024e0ab35ec3bacb933e Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 4 May 2017 09:36:19 -0400 Subject: [PATCH 15/20] test fixes and avoid adding twice to _desiredTiles --- Source/Scene/Cesium3DTileBatchTable.js | 6 +- Source/Scene/Cesium3DTilesetTraversal.js | 115 ++++++++++++++--------- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index 11ebcd70af61..02138213f185 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1261,14 +1261,16 @@ define([ derivedCommands.front = deriveTranslucentCommand(command, CullFace.BACK); } - var bivariateVisibilityTest = tileset._hasMixedContent && this.context.stencilBuffer; + var bivariateVisibilityTest = tileset.skipLODs && tileset._hasMixedContent && this.context.stencilBuffer; if (bivariateVisibilityTest) { if (!tile._finalResolution) { if (!defined(derivedCommands.zback)) { derivedCommands.zback = deriveZBackfaceCommand(command); } - tileset._backfaceCommands.push(derivedCommands.zback); + if (command.pass !== Pass.TRANSLUCENT) { + tileset._backfaceCommands.push(derivedCommands.zback); + } } if (!defined(derivedCommands.stencil) || tile._selectionDepth !== tile._lastSelectionDepth) { diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index ff738b832436..6b4341e3a157 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -65,7 +65,7 @@ define([ } if (root.contentUnloaded) { - root._requestHeap.insert(root); + loadTile(root, frameState); return; } @@ -110,7 +110,7 @@ define([ for (var i = 0; i < length; ++i) { var original = tiles.get(i); - if (original.refine === Cesium3DTileRefine.ADD && original.contentReady) { + if (hasAdditiveContent(original)) { original.selected = true; original._selectedFrame = frameState.frameNumber; continue; @@ -214,24 +214,22 @@ define([ tileset._hasMixedContent = true; } + lastAncestor = tile; + if (childrenLength === 0) { tile._finalResolution = true; - lastAncestor = tile; selectTile(tileset, tile, frameState); continue; } ancestorStack.push(tile); - lastAncestor = tile; tile._stackLength = stack.length; } } for (var i = 0; i < childrenLength; ++i) { var child = children[i]; - if (isVisible(child.visibilityPlaneMask)) { - stack.push(child); - } + stack.push(child); } } } @@ -295,22 +293,31 @@ define([ DFS(root, this); }; - BaseTraversal.prototype.visit = function(tile) { - visitTile(this.tileset, tile, this.frameState, this.outOfCore); + BaseTraversal.prototype.visitStart = function(tile) { + if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } }; + function visitEnd(tile) { + tile._lastVisitedFrame = this.frameState.frameNumber; + } + + BaseTraversal.prototype.visitEnd = visitEnd; + BaseTraversal.prototype.getChildren = function(tile) { if (this.updateAndCheckChildren(tile)) { var children = tile.children; var childrenLength = children.length; var allReady = true; + var replacementWithContent = tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent; for (var i = 0; i < childrenLength; ++i) { var child = children[i]; loadTile(child, this.frameState); touch(this.tileset, child, this.outOfCore); - // this content cannot be replaced until all of the nearest descendants with content are all loaded - if (tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent) { + // content cannot be replaced until all of the nearest descendants with content are all loaded + if (replacementWithContent) { if (!child.hasEmptyContent) { allReady = allReady && child.contentReady; } else { @@ -331,9 +338,11 @@ define([ if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content - if (!defined(tile._ancestorWithContent)) { - loadTile(tile, this.frameState); + loadTile(tile, this.frameState); + if (tile.contentReady) { + updateChildren(tileset, tile, this.frameState); } + return true; } if (hasAdditiveContent(tile)) { @@ -343,14 +352,14 @@ define([ // stop traversal when we've attained the desired level of error if (tile._screenSpaceError <= this.baseScreenSpaceError) { // When skipping LODs, require an existing base level of content first - if (!tileset.skipLODs || tile.hasRenderableContent || defined(tile._ancestorWithContent)) { - computeChildrenVisibility(tile, this.frameState, false); + // if (!tileset.skipLODs || tile.hasRenderableContent || defined(tile._ancestorWithContent)) { + updateChildren(tileset, tile, this.frameState); return false; - } + // } } var childrenVisibility = updateChildren(tileset, tile, this.frameState); - var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > this.baseScreenSpaceError; + var showAdditive = tile.refine === Cesium3DTileRefine.ADD; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; return showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent); @@ -362,7 +371,7 @@ define([ BaseTraversal.prototype.leafHandler = function(tile) { // if skipLODs is off, leaves of the base traversal get pushed to tileset._desiredTiles. additive tiles have already been pushed - if (this.tileset.skipLODs || tile.refine === Cesium3DTileRefine.REPLACE) { + if (this.tileset.skipLODs || !hasAdditiveContent(tile)) { if (tile.refine === Cesium3DTileRefine.REPLACE && !childrenAreVisible(tile)) { ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; return; @@ -385,13 +394,17 @@ define([ return this.allLoaded; }; - InternalBaseTraversal.prototype.visit = function(tile) { - visitTile(this.tileset, tile, this.frameState, this.outOfCore); + InternalBaseTraversal.prototype.visitStart = function(tile) { + if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } if (!tile.contentReady) { this.allLoaded = false; } }; + InternalBaseTraversal.prototype.visitEnd = visitEnd; + // Continue traversing until we have renderable content. We want the first descendants with content of the root to load InternalBaseTraversal.prototype.shouldVisit = function(tile) { return !tile.hasRenderableContent && isVisible(tile.visibilityPlaneMask); @@ -434,10 +447,14 @@ define([ this.queue2.length = 0; }; - SkipTraversal.prototype.visit = function(tile) { - visitTile(this.tileset, tile, this.frameState, this.outOfCore); + SkipTraversal.prototype.visitStart = function(tile) { + if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } }; + SkipTraversal.prototype.visitEnd = visitEnd; + var scratchQueue = []; SkipTraversal.prototype.getChildren = function(tile) { @@ -448,7 +465,7 @@ define([ SkipTraversal.prototype.leafHandler = function(tile) { // additive tiles have already been pushed - if (!hasAdditiveContent(tile)) { + if (!hasAdditiveContent(tile) && tile._lastVisitedFrame !== this.frameState.frameNumber) { this.tileset._desiredTiles.push(tile); } }; @@ -470,22 +487,26 @@ define([ DFS(root, this); }; - InternalSkipTraversal.prototype.visit = function(tile) { - visitTile(this.tileset, tile, this.frameState, this.outOfCore); + InternalSkipTraversal.prototype.visitStart = function(tile) { + if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } }; + InternalSkipTraversal.prototype.visitEnd = visitEnd; + InternalSkipTraversal.prototype.getChildren = function(tile) { var tileset = this.tileset; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - if (!tile.hasTilesetContent) { + if (!tile.hasTilesetContent) { if (hasAdditiveContent(tile)) { tileset._desiredTiles.push(tile); } // stop traversal when we've attained the desired level of error if (tile._screenSpaceError <= maximumScreenSpaceError) { - computeChildrenVisibility(tile, this.frameState, false); + updateChildren(this.tileset, tile, this.frameState); return emptyArray; } @@ -494,7 +515,7 @@ define([ (!tile.hasEmptyContent && tile.contentUnloaded) && defined(tile._ancestorWithLoadedContent) && this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { - computeChildrenVisibility(tile, this.frameState, false); + updateChildren(this.tileset, tile, this.frameState); return emptyArray; } } @@ -524,7 +545,7 @@ define([ var maximumScreenSpaceError = this.tileset._maximumScreenSpaceError; var parent = tile.parent; if (!defined(parent)) { - return true; + return isVisible(tile.visibilityPlaneMask); } var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._screenSpaceError > maximumScreenSpaceError; @@ -544,9 +565,11 @@ define([ var length = tiles.length; for (var i = 0; i < length; ++i) { loadTile(tiles[i], this.frameState); + touch(this.tileset, tiles[i], this.outOfCore); } } else { loadTile(tile, this.frameState); + touch(this.tileset, tile, this.outOfCore); } } this.queue.push(tile); @@ -567,22 +590,18 @@ define([ function visitTile(tileset, tile, frameState, outOfCore) { // because the leaves of one tree traversal are the root of the subsequent traversal, avoid double visitation - if (tile._lastVisitedFrame !== frameState.frameNumber) { - tile._lastVisitedFrame = frameState.frameNumber; - - ++tileset._statistics.visited; - tile.selected = false; - tile._finalResolution = false; - computeSSE(tile, frameState); - touch(tileset, tile, outOfCore); - tile._ancestorWithContent = undefined; - tile._ancestorWithLoadedContent = undefined; - var parent = tile.parent; - if (defined(parent)) { - var replace = parent.refine === Cesium3DTileRefine.REPLACE; - tile._ancestorWithContent = (replace && parent.hasRenderableContent) ? parent : parent._ancestorWithContent; - tile._ancestorWithLoadedContent = (replace && parent.hasRenderableContent && parent.contentReady) ? parent : parent._ancestorWithLoadedContent; - } + ++tileset._statistics.visited; + tile.selected = false; + tile._finalResolution = false; + computeSSE(tile, frameState); + touch(tileset, tile, outOfCore); + tile._ancestorWithContent = undefined; + tile._ancestorWithLoadedContent = undefined; + var parent = tile.parent; + if (defined(parent)) { + var replace = parent.refine === Cesium3DTileRefine.REPLACE; + tile._ancestorWithContent = (replace && parent.hasRenderableContent) ? parent : parent._ancestorWithContent; + tile._ancestorWithLoadedContent = (replace && parent.hasRenderableContent && parent.contentReady) ? parent : parent._ancestorWithLoadedContent; } } @@ -725,7 +744,7 @@ define([ maxLength = Math.max(maxLength, stack.length); var node = stack.pop(); - options.visit(node); + options.visitStart(node); var children = options.getChildren(node); var length = children.length; for (var i = 0; i < length; ++i) { @@ -739,6 +758,7 @@ define([ if (length === 0 && defined(options.leafHandler)) { options.leafHandler(node); } + options.visitEnd(node); } if (defined(stack.trim)) { @@ -761,7 +781,7 @@ define([ for (var i = 0; i < length; ++i) { var node = queue1.get(i); - options.visit(node); + options.visitStart(node); var children = options.getChildren(node); var childrenLength = children.length; for (var j = 0; j < childrenLength; ++j) { @@ -775,6 +795,7 @@ define([ if (childrenLength === 0 && defined(options.leafHandler)) { options.leafHandler(node); } + options.visitEnd(node); } queue1.length = 0; From 82570f133430d1cdcaa04115bc38039c8613b2be Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 4 May 2017 10:36:52 -0400 Subject: [PATCH 16/20] do not put translucent commands in CESIUM_3D_TILE render pass --- Source/Scene/Cesium3DTileBatchTable.js | 5 ++++- Source/Scene/Cesium3DTilesetTraversal.js | 12 +++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index 02138213f185..5cda9b1ca3f8 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1342,13 +1342,16 @@ define([ function deriveCommand(command) { var derivedCommand = DrawCommand.shallowClone(command); - derivedCommand.pass = Pass.CESIUM_3D_TILE; // Add a uniform to indicate if the original command was translucent so // the shader knows not to cull vertices that were originally transparent // even though their style is opaque. var translucentCommand = (derivedCommand.pass === Pass.TRANSLUCENT); + if (!translucentCommand) { + derivedCommand.pass = Pass.CESIUM_3D_TILE; + } + derivedCommand.uniformMap = defined(derivedCommand.uniformMap) ? derivedCommand.uniformMap : {}; derivedCommand.uniformMap.tile_translucentCommand = function() { return translucentCommand; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 6b4341e3a157..e3fae7e11673 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -299,11 +299,9 @@ define([ } }; - function visitEnd(tile) { + BaseTraversal.prototype.visitEnd = function(tile) { tile._lastVisitedFrame = this.frameState.frameNumber; - } - - BaseTraversal.prototype.visitEnd = visitEnd; + }; BaseTraversal.prototype.getChildren = function(tile) { if (this.updateAndCheckChildren(tile)) { @@ -403,7 +401,7 @@ define([ } }; - InternalBaseTraversal.prototype.visitEnd = visitEnd; + InternalBaseTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; // Continue traversing until we have renderable content. We want the first descendants with content of the root to load InternalBaseTraversal.prototype.shouldVisit = function(tile) { @@ -453,7 +451,7 @@ define([ } }; - SkipTraversal.prototype.visitEnd = visitEnd; + SkipTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; var scratchQueue = []; @@ -493,7 +491,7 @@ define([ } }; - InternalSkipTraversal.prototype.visitEnd = visitEnd; + InternalSkipTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; InternalSkipTraversal.prototype.getChildren = function(tile) { var tileset = this.tileset; From 1326d5be6564852ccb415a19eaaf6ee769070bb4 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 4 May 2017 11:05:24 -0400 Subject: [PATCH 17/20] use ManagedArray for temporary BFS front --- Source/Scene/Cesium3DTilesetTraversal.js | 38 +++++++++++------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index e3fae7e11673..1c67beef33c4 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -350,10 +350,8 @@ define([ // stop traversal when we've attained the desired level of error if (tile._screenSpaceError <= this.baseScreenSpaceError) { // When skipping LODs, require an existing base level of content first - // if (!tileset.skipLODs || tile.hasRenderableContent || defined(tile._ancestorWithContent)) { - updateChildren(tileset, tile, this.frameState); - return false; - // } + updateChildren(tileset, tile, this.frameState); + return false; } var childrenVisibility = updateChildren(tileset, tile, this.frameState); @@ -431,6 +429,8 @@ define([ this.queue1 = new ManagedArray(); this.queue2 = new ManagedArray(); this.internalDFS = new InternalSkipTraversal(options.selectionHeuristic); + this.maxChildrenLength = 0; + this.scratchQueue = new ManagedArray(); } SkipTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { @@ -440,9 +440,12 @@ define([ this.internalDFS.frameState = frameState; this.internalDFS.outOfCore = outOfCore; + this.maxChildrenLength = 0; BFS(root, this); this.queue1.length = 0; this.queue2.length = 0; + this.scratchQueue.length = 0; + this.scratchQueue.trim(this.maxChildrenLength); }; SkipTraversal.prototype.visitStart = function(tile) { @@ -453,12 +456,11 @@ define([ SkipTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; - var scratchQueue = []; - SkipTraversal.prototype.getChildren = function(tile) { - scratchQueue.length = 0; - this.internalDFS.execute(tile, scratchQueue); - return scratchQueue; + this.scratchQueue.length = 0; + this.internalDFS.execute(tile, this.scratchQueue); + this.maxChildrenLength = Math.max(this.maxChildrenLength, this.scratchQueue.length); + return this.scratchQueue; }; SkipTraversal.prototype.leafHandler = function(tile) { @@ -744,9 +746,10 @@ define([ var node = stack.pop(); options.visitStart(node); var children = options.getChildren(node); + var isNativeArray = !defined(children.get); var length = children.length; for (var i = 0; i < length; ++i) { - var child = children[i]; + var child = isNativeArray ? children[i] : children.get(i); if (!defined(options.shouldVisit) || options.shouldVisit(child)) { stack.push(child); @@ -759,9 +762,7 @@ define([ options.visitEnd(node); } - if (defined(stack.trim)) { - stack.trim(maxLength); - } + stack.trim(maxLength); } function BFS(root, options) { @@ -781,9 +782,10 @@ define([ var node = queue1.get(i); options.visitStart(node); var children = options.getChildren(node); + var isNativeArray = !defined(children.get); var childrenLength = children.length; for (var j = 0; j < childrenLength; ++j) { - var child = children[j]; + var child = isNativeArray ? children[j] : children.get(j); if (!defined(options.shouldVisit) || options.shouldVisit(child)) { queue2.push(child); @@ -807,12 +809,8 @@ define([ queue1.length = 0; queue2.length = 0; - if (defined(queue1.trim)) { - queue1.trim(maxLength); - } - if (defined(queue2.trim)) { - queue2.trim(maxLength); - } + queue1.trim(maxLength); + queue2.trim(maxLength); } Cesium3DTilesetTraversal.selectTiles = selectTiles; From 80806ea6a572422b70299a150cbd0255536858ad Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Thu, 4 May 2017 12:11:19 -0400 Subject: [PATCH 18/20] optimizations --- Source/Scene/Cesium3DTilesetTraversal.js | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 1c67beef33c4..5cfeb85b78b7 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -304,7 +304,7 @@ define([ }; BaseTraversal.prototype.getChildren = function(tile) { - if (this.updateAndCheckChildren(tile)) { + if (baseUpdateAndCheckChildren(this.tileset, tile, this.baseScreenSpaceError, this.frameState)) { var children = tile.children; var childrenLength = children.length; var allReady = true; @@ -331,14 +331,12 @@ define([ return emptyArray; }; - BaseTraversal.prototype.updateAndCheckChildren = function(tile) { - var tileset = this.tileset; - + function baseUpdateAndCheckChildren(tileset, tile, baseScreenSpaceError, frameState) { if (tile.hasTilesetContent) { // load any tilesets of tilesets now because at this point we still have not achieved a base level of content - loadTile(tile, this.frameState); + loadTile(tile, frameState); if (tile.contentReady) { - updateChildren(tileset, tile, this.frameState); + updateChildren(tileset, tile, frameState); } return true; } @@ -348,18 +346,18 @@ define([ } // stop traversal when we've attained the desired level of error - if (tile._screenSpaceError <= this.baseScreenSpaceError) { + if (tile._screenSpaceError <= baseScreenSpaceError) { // When skipping LODs, require an existing base level of content first - updateChildren(tileset, tile, this.frameState); + updateChildren(tileset, tile, frameState); return false; } - var childrenVisibility = updateChildren(tileset, tile, this.frameState); + var childrenVisibility = updateChildren(tileset, tile, frameState); var showAdditive = tile.refine === Cesium3DTileRefine.ADD; var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; return showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent); - }; + } BaseTraversal.prototype.shouldVisit = function(tile) { return isVisible(tile.visibilityPlaneMask); @@ -407,7 +405,7 @@ define([ }; InternalBaseTraversal.prototype.getChildren = function(tile) { - if (this.updateAndCheckChildren(tile, this.baseScreenSpaceError)) { + if (baseUpdateAndCheckChildren(this.tileset, tile, this.baseScreenSpaceError, this.frameState)) { var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { @@ -580,12 +578,16 @@ define([ }; function updateChildren(tileset, tile, frameState) { + if (tile._lastVisitedFrame === frameState.frameNumber) { + return tile.childrenVisibility; + } + var children = tile.children; updateTransforms(children, tile.computedTransform); computeDistanceToCamera(children, frameState); - return computeChildrenVisibility(tile, frameState, true); + return computeChildrenVisibility(tile, frameState); } function visitTile(tileset, tile, frameState, outOfCore) { @@ -630,7 +632,7 @@ define([ } } - function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { + function computeChildrenVisibility(tile, frameState) { var flag = Cesium3DTileChildrenVisibility.NONE; var children = tile.children; var childrenLength = children.length; @@ -644,17 +646,15 @@ define([ flag |= Cesium3DTileChildrenVisibility.VISIBLE; } - if (checkViewerRequestVolume) { - if (!child.insideViewerRequestVolume(frameState)) { - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; - } - visibilityMask = CullingVolume.MASK_OUTSIDE; - } else { - flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; - if (isVisible(visibilityMask)) { - flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; - } + if (!child.insideViewerRequestVolume(frameState)) { + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; + } + visibilityMask = CullingVolume.MASK_OUTSIDE; + } else { + flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; } } From 538ab35aba617bb8bc8bec750c0e58fd9dfcbe25 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 5 May 2017 10:50:12 -0400 Subject: [PATCH 19/20] backface commands for all tiles, not just unresolved tiles. fixes bugs when multiple final tiles have different selection depths --- Source/Scene/Cesium3DTileBatchTable.js | 12 +++++------- Specs/Scene/Cesium3DTilesetSpec.js | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index 5cda9b1ca3f8..fd0f5173531d 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1264,13 +1264,11 @@ define([ var bivariateVisibilityTest = tileset.skipLODs && tileset._hasMixedContent && this.context.stencilBuffer; if (bivariateVisibilityTest) { - if (!tile._finalResolution) { - if (!defined(derivedCommands.zback)) { - derivedCommands.zback = deriveZBackfaceCommand(command); - } - if (command.pass !== Pass.TRANSLUCENT) { - tileset._backfaceCommands.push(derivedCommands.zback); - } + if (!defined(derivedCommands.zback)) { + derivedCommands.zback = deriveZBackfaceCommand(command); + } + if (command.pass !== Pass.TRANSLUCENT) { + tileset._backfaceCommands.push(derivedCommands.zback); } if (!defined(derivedCommands.stencil) || tile._selectionDepth !== tile._lastSelectionDepth) { diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index d98331cf9332..533af15a49a9 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2380,8 +2380,8 @@ defineSuite([ scene.renderForSpecs(); - // 2 for root tile, 1 for child, 1 for stencil clear - expect(stats.numberOfCommands).toEqual(4); + // 2 for root tile, 2 for child, 1 for stencil clear + expect(stats.numberOfCommands).toEqual(5); expect(root.selected).toBe(true); expect(root._finalResolution).toBe(false); expect(root.children[0].children[0].children[3].selected).toBe(true); From 3bd95ab42efa3a896fc14c83df7fb6e378cc6e56 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Fri, 5 May 2017 13:42:32 -0400 Subject: [PATCH 20/20] updates --- Source/Scene/Cesium3DTilesetTraversal.js | 40 +++++++++++------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 5cfeb85b78b7..7badf1bc328f 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -192,7 +192,7 @@ define([ } var tile = stack.pop(); - if (!defined(tile) || tile._lastVisitedFrame !== frameState.frameNumber) { + if (!defined(tile) || !isVisited(tile, frameState)) { continue; } @@ -294,7 +294,7 @@ define([ }; BaseTraversal.prototype.visitStart = function(tile) { - if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + if (!isVisited(tile, this.frameState)) { visitTile(this.tileset, tile, this.frameState, this.outOfCore); } }; @@ -332,22 +332,13 @@ define([ }; function baseUpdateAndCheckChildren(tileset, tile, baseScreenSpaceError, frameState) { - if (tile.hasTilesetContent) { - // load any tilesets of tilesets now because at this point we still have not achieved a base level of content - loadTile(tile, frameState); - if (tile.contentReady) { - updateChildren(tileset, tile, frameState); - } - return true; - } - if (hasAdditiveContent(tile)) { tileset._desiredTiles.push(tile); } // stop traversal when we've attained the desired level of error - if (tile._screenSpaceError <= baseScreenSpaceError) { - // When skipping LODs, require an existing base level of content first + if (tile._screenSpaceError <= baseScreenSpaceError && !tile.hasTilesetContent) { + // update children so the leaf handler can check if any are visible for the children union bound optimization updateChildren(tileset, tile, frameState); return false; } @@ -380,6 +371,7 @@ define([ this.outOfCore = undefined; this.baseScreenSpaceError = undefined; this.stack = new ManagedArray(); + this.allLoaded = undefined; } InternalBaseTraversal.prototype.execute = function(root) { @@ -389,12 +381,9 @@ define([ }; InternalBaseTraversal.prototype.visitStart = function(tile) { - if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + if (!isVisited(tile, this.frameState)) { visitTile(this.tileset, tile, this.frameState, this.outOfCore); } - if (!tile.contentReady) { - this.allLoaded = false; - } }; InternalBaseTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; @@ -412,6 +401,9 @@ define([ var child = children[i]; loadTile(child, this.frameState); touch(this.tileset, child, this.outOfCore); + if (!tile.contentReady) { + this.allLoaded = false; + } } return children; } @@ -447,7 +439,7 @@ define([ }; SkipTraversal.prototype.visitStart = function(tile) { - if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + if (!isVisited(tile, this.frameState)) { visitTile(this.tileset, tile, this.frameState, this.outOfCore); } }; @@ -463,7 +455,7 @@ define([ SkipTraversal.prototype.leafHandler = function(tile) { // additive tiles have already been pushed - if (!hasAdditiveContent(tile) && tile._lastVisitedFrame !== this.frameState.frameNumber) { + if (!hasAdditiveContent(tile) && !isVisited(tile, this.frameState)) { this.tileset._desiredTiles.push(tile); } }; @@ -486,7 +478,7 @@ define([ }; InternalSkipTraversal.prototype.visitStart = function(tile) { - if (tile._lastVisitedFrame !== this.frameState.frameNumber) { + if (!isVisited(tile, this.frameState)) { visitTile(this.tileset, tile, this.frameState, this.outOfCore); } }; @@ -578,7 +570,7 @@ define([ }; function updateChildren(tileset, tile, frameState) { - if (tile._lastVisitedFrame === frameState.frameNumber) { + if (isVisited(tile, frameState)) { return tile.childrenVisibility; } @@ -590,8 +582,12 @@ define([ return computeChildrenVisibility(tile, frameState); } - function visitTile(tileset, tile, frameState, outOfCore) { + function isVisited(tile, frameState) { // because the leaves of one tree traversal are the root of the subsequent traversal, avoid double visitation + return tile._lastVisitedFrame === frameState.frameNumber; + } + + function visitTile(tileset, tile, frameState, outOfCore) { ++tileset._statistics.visited; tile.selected = false; tile._finalResolution = false;