From eb4b83da5674f32ce8b8f84d6d37a5a2b62401c1 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 18 Sep 2018 16:12:34 -0400 Subject: [PATCH 01/32] Move common code into Cesium3DTile --- Source/Scene/Cesium3DTile.js | 71 ++++++++++++++++++++++ Source/Scene/Cesium3DTilesetTraversal.js | 76 ++---------------------- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 2e24ba16c617..ffe28b757ab3 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -17,6 +17,7 @@ define([ '../Core/Matrix3', '../Core/Matrix4', '../Core/OrientedBoundingBox', + '../Core/OrthographicFrustum', '../Core/Rectangle', '../Core/Request', '../Core/RequestScheduler', @@ -53,6 +54,7 @@ define([ Matrix3, Matrix4, OrientedBoundingBox, + OrthographicFrustum, Rectangle, Request, RequestScheduler, @@ -607,6 +609,69 @@ define([ var scratchJulianDate = new JulianDate(); + /** + * Get the tile's screen space error. + * + * @private + */ + Cesium3DTile.prototype.getScreenSpaceError = function(frameState, useParentGeometricError) { + var tileset = this._tileset; + var parentGeometricError = defined(this.parent) ? this.parent.geometricError : tileset._geometricError; + var geometricError = useParentGeometricError ? parentGeometricError : this.geometricError; + if (geometricError === 0.0) { + // Leaf tiles do not have any error so save the computation + return 0.0; + } + + var camera = frameState.camera; + var frustum = camera.frustum; + var context = frameState.context; + var width = context.drawingBufferWidth; + var height = context.drawingBufferHeight; + + var error; + if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { + if (defined(frustum._offCenterFrustum)) { + frustum = frustum._offCenterFrustum; + } + var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); + error = geometricError / pixelSize; + } else { + // Avoid divide by zero when viewer is inside the tile + var distance = Math.max(this._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; + }; + + /** + * Update the tile's visibility. + * + * @private + */ + Cesium3DTile.prototype.updateVisibility = function(frameState) { + var parent = this.parent; + var parentTransform = defined(parent) ? parent.computedTransform : this._tileset.modelMatrix; + var parentVisibilityPlaneMask = defined(parent) ? parent._visibilityPlaneMask : CullingVolume.MASK_INDETERMINATE; + + this.updateTransform(parentTransform); + this._distanceToCamera = this.distanceToTile(frameState); + this._centerZDepth = this.distanceToTileCenter(frameState); + this._screenSpaceError = this.getScreenSpaceError(frameState); + this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask); // Use parent's plane mask to speed up visibility test + this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; + this._inRequestVolume = this.insideViewerRequestVolume(frameState); + }; + /** * Update whether the tile has expired. * @@ -848,6 +913,12 @@ define([ return Intersect.INSIDE; } + if (this._visibilityPlaneMask === CullingVolume.MASK_INSIDE) { + // The tile's bounding volume is completely inside the culling volume so + // the content bounding volume must also be inside. + return Intersect.INSIDE; + } + // PERFORMANCE_IDEA: is it possible to burn less CPU on this test since we know the // tile's (not the content's) bounding volume intersects the culling volume? var cullingVolume = frameState.cullingVolume; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 882d82aef852..6acee56ed1fb 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -1,27 +1,15 @@ define([ - '../Core/CullingVolume', - '../Core/defaultValue', '../Core/defined', - '../Core/freezeObject', '../Core/Intersect', '../Core/ManagedArray', - '../Core/Math', - '../Core/OrthographicFrustum', './Cesium3DTileOptimizationHint', - './Cesium3DTileRefine', - './SceneMode' + './Cesium3DTileRefine' ], function( - CullingVolume, - defaultValue, defined, - freezeObject, Intersect, ManagedArray, - CesiumMath, - OrthographicFrustum, Cesium3DTileOptimizationHint, - Cesium3DTileRefine, - SceneMode) { + Cesium3DTileRefine) { 'use strict'; /** @@ -77,7 +65,7 @@ define([ } // The tileset doesn't meet the SSE requirement, therefore the tree does not need to be rendered - if (getScreenSpaceError(tileset, tileset._geometricError, root, frameState) <= tileset._maximumScreenSpaceError) { + if (root.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError) { return; } @@ -124,13 +112,8 @@ define([ tileset._emptyTiles.push(tile); } - function contentVisible(tile, frameState) { - return (tile._visibilityPlaneMask === CullingVolume.MASK_INSIDE) || - (tile.contentVisibility(frameState) !== Intersect.OUTSIDE); - } - function selectTile(tileset, tile, frameState) { - if (contentVisible(tile, frameState)) { + if (tile.contentVisibility(frameState) !== Intersect.OUTSIDE) { var tileContent = tile.content; if (tileContent.featurePropertiesDirty) { // A feature's property in this tile changed, the tile needs to be re-styled. @@ -229,42 +212,6 @@ define([ } } - function getScreenSpaceError(tileset, geometricError, tile, frameState) { - if (geometricError === 0.0) { - // Leaf tiles do not have any error so save the computation - return 0.0; - } - - 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 { - // Avoid divide by zero when viewer is inside the tile - 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 updateVisibility(tileset, tile, frameState) { if (tile._updatedVisibilityFrame === frameState.frameNumber) { // Return early if visibility has already been checked during the traversal. @@ -272,17 +219,7 @@ define([ return; } - var parent = tile.parent; - var parentTransform = defined(parent) ? parent.computedTransform : tileset._modelMatrix; - var parentVisibilityPlaneMask = defined(parent) ? parent._visibilityPlaneMask : CullingVolume.MASK_INDETERMINATE; - - tile.updateTransform(parentTransform); - tile._distanceToCamera = tile.distanceToTile(frameState); - tile._centerZDepth = tile.distanceToTileCenter(frameState); - tile._screenSpaceError = getScreenSpaceError(tileset, tile.geometricError, tile, frameState); - tile._visibilityPlaneMask = tile.visibility(frameState, parentVisibilityPlaneMask); // Use parent's plane mask to speed up visibility test - tile._visible = tile._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; - tile._inRequestVolume = tile.insideViewerRequestVolume(frameState); + tile.updateVisibility(frameState); tile._updatedVisibilityFrame = frameState.frameNumber; } @@ -305,8 +242,7 @@ define([ } // Use parent's geometric error with child's box to see if the tile already meet the SSE - var sse = getScreenSpaceError(tileset, parent.geometricError, tile, frameState); - return sse <= tileset._maximumScreenSpaceError; + return tile.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError; } function updateTileVisibility(tileset, tile, frameState) { From 75d297c650a9b65fd29cff853d2d614c1cced9d9 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 19 Sep 2018 12:05:00 -0400 Subject: [PATCH 02/32] Pass statistics around in preparation for offscreen statistics --- Source/Scene/Cesium3DTileset.js | 86 +++++++++---------- Source/Scene/Cesium3DTilesetCache.js | 8 +- Source/Scene/Cesium3DTilesetTraversal.js | 66 +++++++------- .../Cesium3DTilesInspectorViewModel.js | 2 +- Specs/Scene/Cesium3DTilesetSpec.js | 4 +- 5 files changed, 81 insertions(+), 85 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index cc8bca4faf01..456180a889d9 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -196,7 +196,8 @@ define([ this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY); this._statistics = new Cesium3DTilesetStatistics(); - this._statisticsLastColor = new Cesium3DTilesetStatistics(); + this._statisticsPick = new Cesium3DTilesetStatistics(); + this._statisticsLastRender = new Cesium3DTilesetStatistics(); this._statisticsLastPick = new Cesium3DTilesetStatistics(); this._tilesLoaded = false; @@ -1381,12 +1382,11 @@ define([ /////////////////////////////////////////////////////////////////////////// - function requestContent(tileset, tile) { + function requestContent(tileset, tile, statistics) { if (tile.hasEmptyContent) { return; } - var statistics = tileset._statistics; var expired = tile.contentExpired; var requested = tile.requestContent(); @@ -1397,51 +1397,51 @@ define([ if (expired) { if (tile.hasTilesetContent) { - destroySubtree(tileset, tile); + destroySubtree(tileset, tile, statistics); } else { statistics.decrementLoadCounts(tile.content); - --tileset._statistics.numberOfTilesWithContentReady; + --statistics.numberOfTilesWithContentReady; } } ++statistics.numberOfPendingRequests; - tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile)); - tile.contentReadyPromise.then(handleTileSuccess(tileset, tile)).otherwise(handleTileFailure(tileset, tile)); + tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile, statistics)); + tile.contentReadyPromise.then(handleTileSuccess(tileset, tile, statistics)).otherwise(handleTileFailure(tileset, tile, statistics)); } function sortRequestByPriority(a, b) { return a._priority - b._priority; } - function requestTiles(tileset) { + function requestTiles(tileset, statistics) { // Sort requests by priority before making any requests. // This makes it less likely that requests will be cancelled after being issued. var requestedTiles = tileset._requestedTiles; var length = requestedTiles.length; requestedTiles.sort(sortRequestByPriority); for (var i = 0; i < length; ++i) { - requestContent(tileset, requestedTiles[i]); + requestContent(tileset, requestedTiles[i], statistics); } } - function addToProcessingQueue(tileset, tile) { + function addToProcessingQueue(tileset, tile, statistics) { return function() { tileset._processingQueue.push(tile); - --tileset._statistics.numberOfPendingRequests; - ++tileset._statistics.numberOfTilesProcessing; + --statistics.numberOfPendingRequests; + ++statistics.numberOfTilesProcessing; }; } - function handleTileFailure(tileset, tile) { + function handleTileFailure(tileset, tile, statistics) { return function(error) { if (tileset._processingQueue.indexOf(tile) >= 0) { // Failed during processing - --tileset._statistics.numberOfTilesProcessing; + --statistics.numberOfTilesProcessing; } else { // Failed when making request - --tileset._statistics.numberOfPendingRequests; + --statistics.numberOfPendingRequests; } var url = tile._contentResource.url; @@ -1458,15 +1458,15 @@ define([ }; } - function handleTileSuccess(tileset, tile) { + function handleTileSuccess(tileset, tile, statistics) { return function() { - --tileset._statistics.numberOfTilesProcessing; + --statistics.numberOfTilesProcessing; if (!tile.hasTilesetContent) { // RESEARCH_IDEA: ability to unload tiles (without content) for an // external tileset when all the tiles are unloaded. - tileset._statistics.incrementLoadCounts(tile.content); - ++tileset._statistics.numberOfTilesWithContentReady; + statistics.incrementLoadCounts(tile.content); + ++statistics.numberOfTilesWithContentReady; // Add to the tile cache. Previously expired tiles are already in the cache and won't get re-added. tileset._cache.add(tile); @@ -1626,10 +1626,9 @@ define([ pass : Pass.CESIUM_3D_TILE }); - function updateTiles(tileset, frameState) { + function updateTiles(tileset, frameState, statistics) { tileset._styleEngine.applyStyle(tileset, frameState); - var statistics = tileset._statistics; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; var selectedTiles = tileset._selectedTiles; @@ -1731,9 +1730,8 @@ define([ var scratchStack = []; - function destroySubtree(tileset, tile) { + function destroySubtree(tileset, tile, statistics) { var root = tile; - var statistics = tileset._statistics; var stack = scratchStack; stack.push(tile); while (stack.length > 0) { @@ -1744,27 +1742,27 @@ define([ stack.push(children[i]); } if (tile !== root) { - destroyTile(tileset, tile); + destroyTile(tileset, tile, statistics); --statistics.numberOfTilesTotal; } } root.children = []; } - function unloadTile(tileset, tile) { + function unloadTile(tileset, tile, statistics) { tileset.tileUnload.raiseEvent(tile); - tileset._statistics.decrementLoadCounts(tile.content); - --tileset._statistics.numberOfTilesWithContentReady; + statistics.decrementLoadCounts(tile.content); + --statistics.numberOfTilesWithContentReady; tile.unloadContent(); } - function destroyTile(tileset, tile) { - tileset._cache.unloadTile(tileset, tile, unloadTile); + function destroyTile(tileset, tile, statistics) { + tileset._cache.unloadTile(tileset, tile, statistics, unloadTile); tile.destroy(); } - function unloadTiles(tileset) { - tileset._cache.unloadTiles(tileset, unloadTile); + function unloadTiles(tileset, statistics) { + tileset._cache.unloadTiles(tileset, statistics, unloadTile); } /** @@ -1782,9 +1780,7 @@ define([ /////////////////////////////////////////////////////////////////////////// - function raiseLoadProgressEvent(tileset, frameState) { - var statistics = tileset._statistics; - var statisticsLast = tileset._statisticsLastColor; + function raiseLoadProgressEvent(tileset, frameState, statistics, statisticsLast) { var numberOfPendingRequests = statistics.numberOfPendingRequests; var numberOfTilesProcessing = statistics.numberOfTilesProcessing; var lastNumberOfPendingRequest = statisticsLast.numberOfPendingRequests; @@ -1848,7 +1844,8 @@ define([ var isPick = passes.pick; var outOfCore = isRender; - var statistics = this._statistics; + var statistics = isPick ? this._statisticsPick : this._statistics; + var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastRender; statistics.clear(); if (this.dynamicScreenSpaceError) { @@ -1860,26 +1857,25 @@ define([ } this._requestedTiles.length = 0; - Cesium3DTilesetTraversal.selectTiles(this, frameState); + Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); if (outOfCore) { - requestTiles(this); + requestTiles(this, statistics); processTiles(this, frameState); } - updateTiles(this, frameState); + updateTiles(this, frameState, statistics); if (outOfCore) { - unloadTiles(this); - } + unloadTiles(this, statistics); - // Events are raised (added to the afterRender queue) here since promises - // may resolve outside of the update loop that then raise events, e.g., - // model's readyPromise. - raiseLoadProgressEvent(this, frameState); + // Events are raised (added to the afterRender queue) here since promises + // may resolve outside of the update loop that then raise events, e.g., + // model's readyPromise. + raiseLoadProgressEvent(this, frameState, statistics, statisticsLast); + } // Update last statistics - var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastColor; Cesium3DTilesetStatistics.clone(statistics, statisticsLast); if (statistics.selected !== 0) { diff --git a/Source/Scene/Cesium3DTilesetCache.js b/Source/Scene/Cesium3DTilesetCache.js index 848a41da2529..f159c90d1470 100644 --- a/Source/Scene/Cesium3DTilesetCache.js +++ b/Source/Scene/Cesium3DTilesetCache.js @@ -39,7 +39,7 @@ define([ } }; - Cesium3DTilesetCache.prototype.unloadTile = function(tileset, tile, unloadCallback) { + Cesium3DTilesetCache.prototype.unloadTile = function(tileset, tile, statistics, unloadCallback) { var node = tile.cacheNode; if (!defined(node)) { return; @@ -47,10 +47,10 @@ define([ this._list.remove(node); tile.cacheNode = undefined; - unloadCallback(tileset, tile); + unloadCallback(tileset, tile, statistics); }; - Cesium3DTilesetCache.prototype.unloadTiles = function(tileset, unloadCallback) { + Cesium3DTilesetCache.prototype.unloadTiles = function(tileset, statistics, unloadCallback) { var trimTiles = this._trimTiles; this._trimTiles = false; @@ -67,7 +67,7 @@ define([ while ((node !== sentinel) && ((tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes) || trimTiles)) { var tile = node.item; node = node.next; - this.unloadTile(tileset, tile, unloadCallback); + this.unloadTile(tileset, tile, statistics, unloadCallback); } }; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 6acee56ed1fb..14d5d1590c3c 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -46,7 +46,7 @@ define([ var descendantSelectionDepth = 2; - Cesium3DTilesetTraversal.selectTiles = function(tileset, frameState) { + Cesium3DTilesetTraversal.selectTiles = function(tileset, statistics, frameState) { if (tileset.debugFreezeFrame) { return; } @@ -57,7 +57,7 @@ define([ tileset._hasMixedContent = false; var root = tileset.root; - updateTile(tileset, root, frameState); + updateTile(tileset, root, statistics, frameState); // The root tile is not visible if (!isVisible(root)) { @@ -70,11 +70,11 @@ define([ } if (!skipLevelOfDetail(tileset)) { - executeBaseTraversal(tileset, root, frameState); + executeBaseTraversal(tileset, root, statistics, frameState); } else if (tileset.immediatelyLoadDesiredLevelOfDetail) { - executeSkipTraversal(tileset, root, frameState); + executeSkipTraversal(tileset, root, statistics, frameState); } else { - executeBaseAndSkipTraversal(tileset, root, frameState); + executeBaseAndSkipTraversal(tileset, root, statistics, frameState); } traversal.stack.trim(traversal.stackMaximumLength); @@ -84,23 +84,23 @@ define([ selectionTraversal.ancestorStack.trim(selectionTraversal.ancestorStackMaximumLength); }; - function executeBaseTraversal(tileset, root, frameState) { + function executeBaseTraversal(tileset, root, statistics, frameState) { var baseScreenSpaceError = tileset._maximumScreenSpaceError; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); } - function executeSkipTraversal(tileset, root, frameState) { + function executeSkipTraversal(tileset, root, statistics, frameState) { var baseScreenSpaceError = Number.MAX_VALUE; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); traverseAndSelect(tileset, root, frameState); } - function executeBaseAndSkipTraversal(tileset, root, frameState) { + function executeBaseAndSkipTraversal(tileset, root, statistics, frameState) { var baseScreenSpaceError = Math.max(tileset.baseScreenSpaceError, tileset.maximumScreenSpaceError); var maximumScreenSpaceError = tileset.maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); traverseAndSelect(tileset, root, frameState); } @@ -129,7 +129,7 @@ define([ } } - function selectDescendants(tileset, root, frameState) { + function selectDescendants(tileset, root, statistics, frameState) { var stack = descendantTraversal.stack; stack.push(root); while (stack.length > 0) { @@ -141,7 +141,7 @@ define([ var child = children[i]; if (isVisible(child)) { if (child.contentAvailable) { - updateTile(tileset, child, frameState); + updateTile(tileset, child, statistics, frameState); touchTile(tileset, child, frameState); selectTile(tileset, child, frameState); } else if (child._depth - root._depth < descendantSelectionDepth) { @@ -153,7 +153,7 @@ define([ } } - function selectDesiredTile(tileset, tile, frameState) { + function selectDesiredTile(tileset, tile, statistics, frameState) { if (!skipLevelOfDetail(tileset)) { if (tile.contentAvailable) { // The tile can be selected right away and does not require traverseAndSelect @@ -170,12 +170,12 @@ define([ } else { // If no ancestors are ready traverse down and select tiles to minimize empty regions. // This happens often for immediatelyLoadDesiredLevelOfDetail where parent tiles are not necessarily loaded before zooming out. - selectDescendants(tileset, tile, frameState); + selectDescendants(tileset, tile, statistics, frameState); } } - function visitTile(tileset, tile, frameState) { - ++tileset._statistics.visited; + function visitTile(tileset, tile, statistics, frameState) { + ++statistics.visited; tile._visitedFrame = frameState.frameNumber; } @@ -245,7 +245,7 @@ define([ return tile.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError; } - function updateTileVisibility(tileset, tile, frameState) { + function updateTileVisibility(tileset, tile, statistics, frameState) { updateVisibility(tileset, tile, frameState); if (!isVisible(tile)) { @@ -263,15 +263,15 @@ define([ var hasChildren = tile.children.length > 0; if (replace && useOptimization && hasChildren) { if (!anyChildrenVisible(tileset, tile, frameState)) { - ++tileset._statistics.numberOfTilesCulledWithChildrenUnion; + ++statistics.numberOfTilesCulledWithChildrenUnion; tile._visible = false; return; } } } - function updateTile(tileset, tile, frameState) { - updateTileVisibility(tileset, tile, frameState); + function updateTile(tileset, tile, statistics, frameState) { + updateTileVisibility(tileset, tile, statistics, frameState); tile.updateExpiration(); tile._shouldSelect = false; @@ -315,14 +315,14 @@ define([ return b._distanceToCamera - a._distanceToCamera; } - function updateAndPushChildren(tileset, tile, stack, frameState) { + function updateAndPushChildren(tileset, tile, stack, statistics, frameState) { var i; var replace = tile.refine === Cesium3DTileRefine.REPLACE; var children = tile.children; var length = children.length; for (i = 0; i < length; ++i) { - updateTile(tileset, children[i], frameState); + updateTile(tileset, children[i], statistics, frameState); } // Sort by distance to take advantage of early Z and reduce artifacts for skipLevelOfDetail @@ -350,7 +350,7 @@ define([ if (!child._inRequestVolume) { childRefines = false; } else if (hasEmptyContent(child)) { - childRefines = executeEmptyTraversal(tileset, child, frameState); + childRefines = executeEmptyTraversal(tileset, child, statistics, frameState); } else { childRefines = child.contentAvailable; } @@ -395,7 +395,7 @@ define([ return tile._screenSpaceError > tileset._maximumScreenSpaceError; } - function executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState) { + function executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState) { // Depth-first traversal that traverses all visible tiles and marks tiles for selection. // If skipLevelOfDetail is off then a tile does not refine until all children are loaded. // This is the traditional replacement refinement approach and is called the base traversal. @@ -417,7 +417,7 @@ define([ var refines = false; if (canTraverse(tileset, tile)) { - refines = updateAndPushChildren(tileset, tile, stack, frameState) && parentRefines; + refines = updateAndPushChildren(tileset, tile, stack, statistics, frameState) && parentRefines; } var stoppedRefining = !refines && parentRefines; @@ -429,11 +429,11 @@ define([ addEmptyTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); if (stoppedRefining) { - selectDesiredTile(tileset, tile, frameState); + selectDesiredTile(tileset, tile, statistics, frameState); } } else if (add) { // Additive tiles are always loaded and selected - selectDesiredTile(tileset, tile, frameState); + selectDesiredTile(tileset, tile, statistics, frameState); loadTile(tileset, tile, frameState); } else if (replace) { if (baseTraversal) { @@ -441,11 +441,11 @@ define([ // Select tiles that can't refine further loadTile(tileset, tile, frameState); if (stoppedRefining) { - selectDesiredTile(tileset, tile, frameState); + selectDesiredTile(tileset, tile, statistics, frameState); } } else if (stoppedRefining) { // In skip traversal, load and select tiles that can't refine further - selectDesiredTile(tileset, tile, frameState); + selectDesiredTile(tileset, tile, statistics, frameState); loadTile(tileset, tile, frameState); } else if (reachedSkippingThreshold(tileset, tile)) { // In skip traversal, load tiles that aren't skipped. In practice roughly half the tiles stay unloaded. @@ -453,14 +453,14 @@ define([ } } - visitTile(tileset, tile, frameState); + visitTile(tileset, tile, statistics, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass } } - function executeEmptyTraversal(tileset, root, frameState) { + function executeEmptyTraversal(tileset, root, statistics, frameState) { // Depth-first traversal that checks if all nearest descendants with content are loaded. Ignores visibility. var allDescendantsLoaded = true; var stack = emptyTraversal.stack; @@ -482,7 +482,7 @@ define([ allDescendantsLoaded = false; } - updateTile(tileset, tile, frameState); + updateTile(tileset, tile, statistics, frameState); if (!isVisible(tile)) { // Load tiles that aren't visible since they are still needed for the parent to refine loadTile(tileset, tile, frameState); diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js index d80ba01ddb0e..bca09a0834cb 100644 --- a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js +++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js @@ -71,7 +71,7 @@ define([ return ''; } - var statistics = isPick ? tileset._statisticsLastPick : tileset._statisticsLastColor; + var statistics = isPick ? tileset._statisticsLastPick : tileset._statisticsLastRender; // Since the pick pass uses a smaller frustum around the pixel of interest, // the statistics will be different than the normal render pass. diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index 0a9da710c59b..923848e6a746 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2050,9 +2050,9 @@ defineSuite([ return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { tileset.style = new Cesium3DTileStyle({color: 'color("red")'}); scene.renderForSpecs(); - expect(tileset.statistics.numberOfTilesStyled).toBe(1); + expect(tileset._statistics.numberOfTilesStyled).toBe(1); scene.pickForSpecs(); - expect(tileset.statistics.numberOfTilesStyled).toBe(0); + expect(tileset._statisticsPick.numberOfTilesStyled).toBe(0); }); }); From c3896292f0dcad80eb318980e7610495b40426ad Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 1 Oct 2018 10:32:14 -0400 Subject: [PATCH 03/32] Offscreen picking --- CHANGES.md | 5 + Source/Core/Ray.js | 20 ++ Source/Scene/Cesium3DTile.js | 4 + Source/Scene/Cesium3DTileset.js | 85 ++++-- .../Cesium3DTilesetOffscreenTraversal.js | 138 +++++++++ Source/Scene/Cesium3DTilesetTraversal.js | 10 +- Source/Scene/FrameState.js | 9 +- Source/Scene/PointCloudEyeDomeLighting.js | 4 +- Source/Scene/Scene.js | 277 ++++++++++++++++-- Specs/Scene/Cesium3DTilesetSpec.js | 4 +- 10 files changed, 512 insertions(+), 44 deletions(-) create mode 100644 Source/Scene/Cesium3DTilesetOffscreenTraversal.js diff --git a/CHANGES.md b/CHANGES.md index 20e0d0415daa..d65dd03dec55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ Change Log ========== +### 1.51 - 2018-11-01 + +##### Additions :tada: +* Added `Ray.clone`. [#TODO](TODO) + ### 1.50 - 2018-10-01 ##### Breaking Changes :mega: diff --git a/Source/Core/Ray.js b/Source/Core/Ray.js index e8e202f3bbeb..a7867a3d1dc1 100644 --- a/Source/Core/Ray.js +++ b/Source/Core/Ray.js @@ -38,6 +38,26 @@ define([ this.direction = direction; } + /** + * Duplicates a Ray instance. + * + * @param {Ray} ray The ray to duplicate. + * @param {Ray} [result] The object onto which to store the result. + * @returns {Ray} The modified result parameter or a new Ray instance if one was not provided. (Returns undefined if ray is undefined) + */ + Ray.clone = function(ray, result) { + if (!defined(ray)) { + return undefined; + } + if (!defined(result)) { + return new Ray(ray.origin, ray.direction); + } + + result.origin = ray.origin; + result.direction = ray.direction; + return result; + }; + /** * Computes the point along the ray given by r(t) = o + t*d, * where o is the origin of the ray and d is the direction. diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 59a416da7044..c0fbc6861961 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -1126,6 +1126,10 @@ define([ }; function applyDebugSettings(tile, tileset, frameState) { + if (!frameState.passes.render) { + return; + } + var hasContentBoundingVolume = defined(tile._header.content) && defined(tile._header.content.boundingVolume); var empty = tile.hasEmptyContent || tile.hasTilesetContent; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index fe0fdc63864f..c74c5bbc2781 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -31,6 +31,7 @@ define([ './Cesium3DTilesetCache', './Cesium3DTilesetStatistics', './Cesium3DTilesetTraversal', + './Cesium3DTilesetOffscreenTraversal', './Cesium3DTileStyleEngine', './ClippingPlaneCollection', './LabelCollection', @@ -74,6 +75,7 @@ define([ Cesium3DTilesetCache, Cesium3DTilesetStatistics, Cesium3DTilesetTraversal, + Cesium3DTilesetOffscreenTraversal, Cesium3DTileStyleEngine, ClippingPlaneCollection, LabelCollection, @@ -183,6 +185,8 @@ define([ this._timeSinceLoad = 0.0; this._extras = undefined; + this._offscreenCache = new Cesium3DTilesetCache(); + this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true); this._allTilesAdditive = true; @@ -199,8 +203,10 @@ define([ this._statistics = new Cesium3DTilesetStatistics(); this._statisticsPick = new Cesium3DTilesetStatistics(); + this._statisticsAsync = new Cesium3DTilesetStatistics(); this._statisticsLastRender = new Cesium3DTilesetStatistics(); this._statisticsLastPick = new Cesium3DTilesetStatistics(); + this._statisticsLastAsync = new Cesium3DTilesetStatistics(); this._tilesLoaded = false; this._initialTilesLoaded = false; @@ -1435,12 +1441,13 @@ define([ return a._priority - b._priority; } - function requestTiles(tileset, statistics) { - // Sort requests by priority before making any requests. - // This makes it less likely that requests will be cancelled after being issued. - var requestedTiles = tileset._requestedTiles; + function requestTiles(tileset, requestedTiles, sortByPriority, statistics) { + if (sortByPriority) { + // Sort requests by priority before making any requests. + // This makes it less likely that requests will be cancelled after being issued. + requestedTiles.sort(sortRequestByPriority); + } var length = requestedTiles.length; - requestedTiles.sort(sortRequestByPriority); for (var i = 0; i < length; ++i) { requestContent(tileset, requestedTiles[i], statistics); } @@ -1649,6 +1656,7 @@ define([ function updateTiles(tileset, frameState, statistics) { tileset._styleEngine.applyStyle(tileset, frameState); + var isRender = frameState.passes.render; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; var selectedTiles = tileset._selectedTiles; @@ -1672,7 +1680,9 @@ define([ tile = selectedTiles[i]; // Raise the tileVisible event before update in case the tileVisible event // handler makes changes that update needs to apply to WebGL resources - tileVisible.raiseEvent(tile); + if (isRender) { + tileVisible.raiseEvent(tile); + } tile.update(tileset, frameState); statistics.incrementSelectionCounts(tile.content); ++statistics.selected; @@ -1738,13 +1748,15 @@ define([ tileset._pointCloudEyeDomeLighting.update(frameState, numberOfInitialCommands, tileset.pointCloudShading); } - if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl) { - if (!defined(tileset._tileDebugLabels)) { - tileset._tileDebugLabels = new LabelCollection(); + if (isRender) { + if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl) { + if (!defined(tileset._tileDebugLabels)) { + tileset._tileDebugLabels = new LabelCollection(); + } + updateTileDebugLabels(tileset, frameState); + } else { + tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); } - updateTileDebugLabels(tileset, frameState); - } else { - tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); } } @@ -1865,10 +1877,11 @@ define([ var passes = frameState.passes; var isRender = passes.render; var isPick = passes.pick; + var isAsync = passes.async; var outOfCore = isRender; - var statistics = isPick ? this._statisticsPick : this._statistics; - var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastRender; + var statistics = isAsync ? this._statisticsAsync : (isPick ? this._statisticsPick : this._statistics); + var statisticsLast = isAsync ? this._statisticsLastAsync : (isPick ? this._statisticsLastPick : this._statisticsLastRender); statistics.clear(); if (this.dynamicScreenSpaceError) { @@ -1879,16 +1892,24 @@ define([ this._cache.reset(); } - this._requestedTiles.length = 0; - Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); + if (isAsync) { + Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); + } else { + Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); + } if (outOfCore) { - requestTiles(this, statistics); + requestTiles(this, this._requestedTiles, true, statistics); processTiles(this, frameState); } updateTiles(this, frameState, statistics); + // TODO - remove + if (passes.offscreen) { + printSelectedTiles(this); + } + if (outOfCore) { unloadTiles(this, statistics); @@ -1912,6 +1933,36 @@ define([ } }; + // TODO : remove + function printSelectedTiles(tileset) { + // console.log('Printing selected tiles:'); + // var selectedTiles = tileset._selectedTiles; + // var length = selectedTiles.length; + // for (var i = 0; i < length; ++i) { + // var tile = selectedTiles[i]; + // console.log(tile._header.content.uri); + // } + } + + /** + * TODO private - who calls this? + * @param {FrameState} frameState The ray. + * @returns {Boolean} true when the primitive is ready + * + * @private + */ + Cesium3DTileset.prototype.updateAsync = function(frameState) { + var statistics = this._statisticsAsync; + var ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); + + // TODO - remove + if (ready) { + printSelectedTiles(this); + } + requestTiles(this, this._requestedTiles, false, statistics); // Tiles are requested now but processed in main update + return ready; + }; + /** * true if the tileset JSON file lists the extension in extensionsUsed; otherwise, false. * @param {String} extensionName The name of the extension to check. diff --git a/Source/Scene/Cesium3DTilesetOffscreenTraversal.js b/Source/Scene/Cesium3DTilesetOffscreenTraversal.js new file mode 100644 index 000000000000..5e4c1e64a00e --- /dev/null +++ b/Source/Scene/Cesium3DTilesetOffscreenTraversal.js @@ -0,0 +1,138 @@ +define([ + '../Core/Intersect', + '../Core/ManagedArray', + './Cesium3DTileRefine' + ], function( + Intersect, + ManagedArray, + Cesium3DTileRefine) { + 'use strict'; + + /** + * @private + */ + function Cesium3DTilesetOffscreenTraversal() { + } + + var offscreenTraversal = { + stack : new ManagedArray(), + stackMaximumLength : 0 + }; + + Cesium3DTilesetOffscreenTraversal.selectTiles = function(tileset, statistics, frameState) { + tileset._selectedTiles.length = 0; + tileset._requestedTiles.length = 0; + tileset._hasMixedContent = false; + var minimumGeometricError = 0; + + var ready = true; + + var root = tileset.root; + root.updateVisibility(frameState); + + if (!isVisible(root)) { + return ready; + } + + if (tileset._geometricError <= minimumGeometricError) { + return ready; + } + + var stack = offscreenTraversal.stack; + stack.push(tileset.root); + + while (stack.length > 0) { + offscreenTraversal.stackMaximumLength = Math.max(offscreenTraversal.stackMaximumLength, stack.length); + + var tile = stack.pop(); + var add = tile.refine === Cesium3DTileRefine.ADD; + var replace = tile.refine === Cesium3DTileRefine.REPLACE; + var traverse = canTraverse(tileset, minimumGeometricError, tile); + + if (traverse) { + updateAndPushChildren(tileset, tile, stack, frameState); + } + + if (add || (replace && !traverse)) { + loadTile(tileset, tile); + selectDesiredTile(tileset, tile, frameState); + + if (!hasEmptyContent(tile) && !tile.contentAvailable) { + ready = false; + } + } + + visitTile(statistics); + touchTile(tileset, tile); + } + + offscreenTraversal.stack.trim(offscreenTraversal.stackMaximumLength); + + return ready; + }; + + function isVisible(tile) { + return tile._visible && tile._inRequestVolume; + } + + function hasEmptyContent(tile) { + return tile.hasEmptyContent || tile.hasTilesetContent; + } + + function hasUnloadedContent(tile) { + return !hasEmptyContent(tile) && tile.contentUnloaded; + } + + function canTraverse(tileset, minimumGeometricError, tile) { + if (tile.children.length === 0) { + return false; + } + + if (tile.hasTilesetContent) { + // Traverse external tileset to visit its root tile + // Don't traverse if the subtree is expired because it will be destroyed + return !tile.contentExpired; + } + + if (tile.hasEmptyContent) { + return true; + } + + return tile.geometricError >= minimumGeometricError; + } + + function updateAndPushChildren(tileset, tile, stack, frameState) { + var children = tile.children; + var length = children.length; + + for (var i = 0; i < length; ++i) { + var child = children[i]; + child.updateVisibility(frameState); + if (isVisible(child)) { + stack.push(child); + } + } + } + + function loadTile(tileset, tile) { + if (hasUnloadedContent(tile) || tile.contentExpired) { + tileset._requestedTiles.push(tile); + } + } + + function touchTile(tileset, tile) { + tileset._cache.touch(tile); + } + + function visitTile(statistics) { + ++statistics.visited; + } + + function selectDesiredTile(tileset, tile, frameState) { + if (tile.contentAvailable && (tile.contentVisibility(frameState) !== Intersect.OUTSIDE)) { + tileset._selectedTiles.push(tile); + } + } + + return Cesium3DTilesetOffscreenTraversal; +}); diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 14d5d1590c3c..37acdd81ee06 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -47,6 +47,8 @@ define([ var descendantSelectionDepth = 2; Cesium3DTilesetTraversal.selectTiles = function(tileset, statistics, frameState) { + tileset._requestedTiles.length = 0; + if (tileset.debugFreezeFrame) { return; } @@ -58,6 +60,7 @@ define([ var root = tileset.root; updateTile(tileset, root, statistics, frameState); + postUpdateTile(root); // The root tile is not visible if (!isVisible(root)) { @@ -290,6 +293,11 @@ define([ } } + function postUpdateTile(tile) { + // Reset so visibility is checked during the next pass which may use a different camera + tile._updatedVisibilityFrame = 0; + } + function hasEmptyContent(tile) { return tile.hasEmptyContent || tile.hasTilesetContent; } @@ -456,7 +464,7 @@ define([ visitTile(tileset, tile, statistics, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; - tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass + postUpdateTile(tile); } } diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index a4796c2352be..e886cf2085de 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -166,7 +166,14 @@ define([ * @type {Boolean} * @default false */ - offscreen : false + offscreen : false, + + /** + * true if the primitive should update for an async pass, false otherwise. + * @type {Boolean} + * @default false + */ + async : false }; /** diff --git a/Source/Scene/PointCloudEyeDomeLighting.js b/Source/Scene/PointCloudEyeDomeLighting.js index a2a8de574de5..96ad5ff01b12 100644 --- a/Source/Scene/PointCloudEyeDomeLighting.js +++ b/Source/Scene/PointCloudEyeDomeLighting.js @@ -236,9 +236,7 @@ define([ } PointCloudEyeDomeLighting.prototype.update = function(frameState, commandStart, pointCloudShading) { - var passes = frameState.passes; - var isPick = (passes.pick && !passes.render); - if (!isSupported(frameState.context) || isPick) { + if (!isSupported(frameState.context) || !frameState.passes.render) { return; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index fe0e922db839..7430009d155d 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -23,6 +23,7 @@ define([ '../Core/GeometryInstance', '../Core/GeometryPipeline', '../Core/Intersect', + '../Core/isArray', '../Core/JulianDate', '../Core/Math', '../Core/Matrix4', @@ -49,9 +50,11 @@ define([ '../Renderer/ShaderProgram', '../Renderer/ShaderSource', '../Renderer/Texture', + '../ThirdParty/when', './BrdfLutGenerator', './Camera', './Cesium3DTileFeature', + './Cesium3DTileset', './CreditDisplay', './DebugCameraPrimitive', './DepthPlane', @@ -102,6 +105,7 @@ define([ GeometryInstance, GeometryPipeline, Intersect, + isArray, JulianDate, CesiumMath, Matrix4, @@ -128,9 +132,11 @@ define([ ShaderProgram, ShaderSource, Texture, + when, BrdfLutGenerator, Camera, Cesium3DTileFeature, + Cesium3DTileset, CreditDisplay, DebugCameraPrimitive, DepthPlane, @@ -166,6 +172,14 @@ define([ }; }; + function AsyncLoader(ray, primitives) { + this.ray = ray; + this.primitives = primitives; + this.ready = false; + this.deferred = when.defer(); + this.promise = this.deferred.promise; + } + /** * The container for all 3D graphical objects and state in a Cesium virtual scene. Generally, * a scene is not created directly; instead, it is implicitly created by {@link CesiumWidget}. @@ -279,6 +293,8 @@ define([ this._primitives = new PrimitiveCollection(); this._groundPrimitives = new PrimitiveCollection(); + this._asyncLoaders = []; + this._logDepthBuffer = context.fragmentDepth; this._logDepthBufferDirty = true; @@ -869,7 +885,7 @@ define([ }, /** - * Returns true if the pickPosition function is supported. + * Returns true if the {@link Scene#pickPosition} function is supported. * @memberof Scene.prototype * * @type {Boolean} @@ -884,13 +900,14 @@ define([ }, /** - * Returns true if the sampleHeight function is supported. + * Returns true if the {@link Scene#sampleHeight} and {@link Scene#sampleHeightMostDetailed} functions are supported. * @memberof Scene.prototype * * @type {Boolean} * @readonly * * @see Scene#sampleHeight + * @see Scene#sampleHeightMostDetailed */ sampleHeightSupported : { get : function() { @@ -899,13 +916,14 @@ define([ }, /** - * Returns true if the clampToHeight function is supported. + * Returns true if the {@link clampToHeight} and {@link Scene#clampToHeightMostDetailed} functions are supported. * @memberof Scene.prototype * * @type {Boolean} * @readonly * * @see Scene#clampToHeight + * @see Scene#clampToHeightMostDetailed */ clampToHeightSupported : { get : function() { @@ -1570,6 +1588,7 @@ define([ passes.depth = false; passes.postProcess = false; passes.offscreen = false; + passes.async = false; } function updateFrameState(scene, frameNumber, time) { @@ -2999,6 +3018,8 @@ define([ scene.globe.update(frameState); } + updateAsyncLoaders(scene); + frameState.creditDisplay.update(); } @@ -3323,7 +3344,6 @@ define([ var object = view.pickFramebuffer.end(scratchRectangle); context.endFrame(); - callAfterRenderFunctions(this); return object; }; @@ -3634,13 +3654,88 @@ define([ var orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight); var right = Cartesian3.cross(direction, orthogonalAxis, scratchRight); var up = Cartesian3.cross(direction, right, scratchUp); + camera.position = ray.origin; camera.direction = direction; camera.up = up; camera.right = right; } - function getRayIntersection(scene, ray) { + function updateAsyncLoader(scene, asyncLoader) { + var context = scene._context; + var uniformState = context.uniformState; + var frameState = scene._frameState; + + var view = scene._pickOffscreenView; + scene._view = view; + + var ray = asyncLoader.ray; + var primitives = asyncLoader.primitives; + + updateCameraFromRay(ray, view.camera); + + // Update with previous frame's number and time, assuming that render is called first. + updateFrameState(scene, frameState.frameNumber, frameState.time); + frameState.passes.offscreen = true; + frameState.passes.async = true; + + uniformState.update(frameState); + + var ready = true; + var primitivesLength = primitives.length; + for (var i = 0; i < primitivesLength; ++i) { + var primitive = primitives[i]; + if (scene.primitives.contains(primitive)) { + // Only update primitives that are still contained in the scene's primitive collection + ready = primitive.updateAsync(frameState) && ready; + } + } + + scene._view = scene._defaultView; + + if (ready) { + frameState.afterRender.push(function() { + asyncLoader.deferred.resolve(); + }); + } + + return ready; + } + + function updateAsyncLoaders(scene) { + var asyncLoaders = scene._asyncLoaders; + for (var i = 0; i < asyncLoaders.length; ++i) { + var ready = updateAsyncLoader(scene, asyncLoaders[i]); + if (ready) { + asyncLoaders.splice(i--, 1); + } + } + } + + function launchAsyncLoader(scene, ray, objectsToExclude, callback) { + var asyncPrimitives = []; + var primitives = scene.primitives; + var length = primitives.length; + for (var i = 0; i < length; ++i) { + var primitive = primitives.get(i); + if (primitive instanceof Cesium3DTileset) { + if (!defined(objectsToExclude) || objectsToExclude.indexOf(primitive) === -1) { + asyncPrimitives.push(primitive); + } + } + } + if (asyncPrimitives.length === 0) { + return when.resolve(callback()); + } + + var asyncLoader = new AsyncLoader(ray, asyncPrimitives); + scene._asyncLoaders.push(asyncLoader); + return asyncLoader.promise.then(function() { + return callback(); + }); + } + + function getRayIntersection(scene, ray, async) { var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3661,6 +3756,7 @@ define([ frameState.invertClassification = false; frameState.passes.pick = true; frameState.passes.offscreen = true; + frameState.passes.async = async; uniformState.update(frameState); @@ -3689,7 +3785,6 @@ define([ scene._view = scene._defaultView; context.endFrame(); - callAfterRenderFunctions(scene); if (defined(object) || defined(position)) { return { @@ -3699,7 +3794,7 @@ define([ } } - function getRayIntersections(scene, ray, limit, objectsToExclude) { + function getRayIntersections(scene, ray, limit, objectsToExclude, async) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (scene._mode !== SceneMode.SCENE3D) { @@ -3707,11 +3802,22 @@ define([ } //>>includeEnd('debug'); var pickCallback = function() { - return getRayIntersection(scene, ray); + return getRayIntersection(scene, ray, async); }; return drillPick(limit, pickCallback, objectsToExclude); } + function pickFromRay(scene, ray, objectsToExclude, async) { + var results = getRayIntersections(scene, ray, 1, objectsToExclude, async); + if (results.length > 0) { + return results[0]; + } + } + + function drillPickFromRay(scene, ray, limit, objectsToExclude, async) { + return getRayIntersections(scene, ray, limit, objectsToExclude, async); + } + /** * Returns an object containing the first object intersected by the ray and the position of intersection, * or undefined if there were no intersections. The intersected object has a primitive @@ -3727,10 +3833,7 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.pickFromRay = function(ray, objectsToExclude) { - var results = getRayIntersections(this, ray, 1, objectsToExclude); - if (results.length > 0) { - return results[0]; - } + return pickFromRay(this, ray, objectsToExclude, false); }; /** @@ -3750,7 +3853,50 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude) { - return getRayIntersections(this, ray, limit, objectsToExclude); + return drillPickFromRay(this, ray, limit, objectsToExclude, false); + }; + + /** + * Initiates an asynchronous {@link Scene#pickFromRay} request using the maximum level of detail for 3D Tilesets + * regardless of visibility. + * + * @private + * + * @param {Ray} ray The ray. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @returns {Promise.} A promise that resolves to an object containing the object and position of the first intersection. + * + * @exception {DeveloperError} Ray intersections are only supported in 3D mode. + */ + Scene.prototype.pickFromRayMostDetailed = function(ray, objectsToExclude) { + var that = this; + ray = Ray.clone(ray); + objectsToExclude = objectsToExclude.slice(); + return launchAsyncLoader(this, ray, objectsToExclude, function() { + return pickFromRay(that, ray, objectsToExclude, true); + }); + }; + + /** + * Initiates an asynchronous {@link Scene#drillPickFromRay} request using the maximum level of detail for 3D Tilesets + * regardless of visibility. + * + * @private + * + * @param {Ray} ray The ray. + * @param {Number} [limit=Number.MAX_VALUE] If supplied, stop finding intersections after this many intersections. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @returns {Promise.} A promise that resolves to a list of objects containing the object and position of each intersection. + * + * @exception {DeveloperError} Ray intersections are only supported in 3D mode. + */ + Scene.prototype.drillPickFromRayMostDetailed = function(ray, limit, objectsToExclude) { + var that = this; + ray = Ray.clone(ray); + objectsToExclude = objectsToExclude.slice(); + return launchAsyncLoader(this, ray, objectsToExclude, function() { + return drillPickFromRay(that, ray, limit, objectsToExclude, true); + }); }; var scratchSurfacePosition = new Cartesian3(); @@ -3780,6 +3926,33 @@ define([ return getRayForSampleHeight(scene, cartographic); } + function getHeightFromCartesian(scene, cartesian) { + var globe = scene.globe; + var ellipsoid = defined(globe) ? globe.ellipsoid : scene.mapProjection.ellipsoid; + var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic); + return cartographic.height; + } + + function sampleHeightMostDetailed(scene, position, objectsToExclude) { + var ray = getRayForSampleHeight(scene, position); + return launchAsyncLoader(scene, ray, objectsToExclude, function() { + var pickResult = pickFromRay(scene, ray, objectsToExclude, true); + if (defined(pickResult)) { + return getHeightFromCartesian(scene, pickResult.position); + } + }); + } + + function clampToHeightMostDetailed(scene, cartesian, objectsToExclude) { + var ray = getRayForClampToHeight(scene, cartesian); + return launchAsyncLoader(scene, ray, objectsToExclude, function() { + var pickResult = pickFromRay(scene, ray, objectsToExclude, true); + if (defined(pickResult)) { + return pickResult.position; + } + }); + } + /** * Returns the height of scene geometry at the given cartographic position or undefined if there was no * scene geometry to sample height from. May be used to clamp objects to the globe, 3D Tiles, or primitives in the scene. @@ -3793,6 +3966,8 @@ define([ * @returns {Number} The height. This may be undefined if there was no scene geometry to sample height from. * * @see Scene#clampToHeight + * @see Scene#clampToHeightMostDetailed + * @see Scene#sampleHeightMostDetailed * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. * @exception {DeveloperError} sampleHeight required depth texture support. Check sampleHeightSupported. @@ -3805,13 +3980,9 @@ define([ } //>>includeEnd('debug'); var ray = getRayForSampleHeight(this, position); - var pickResult = this.pickFromRay(ray, objectsToExclude); + var pickResult = pickFromRay(this, ray, objectsToExclude, false); if (defined(pickResult)) { - var cartesian = pickResult.position; - var globe = this.globe; - var ellipsoid = defined(globe) ? globe.ellipsoid : this.mapProjection.ellipsoid; - var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic); - return cartographic.height; + return getHeightFromCartesian(this, pickResult.position); } }; @@ -3830,6 +4001,8 @@ define([ * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. This may be undefined if there was no scene geometry to clamp to. * * @see Scene#sampleHeight + * @see Scene#sampleHeightMostDetailed + * @see Scene#clampToHeightMostDetailed * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. * @exception {DeveloperError} clampToHeight required depth texture support. Check clampToHeightSupported. @@ -3842,12 +4015,76 @@ define([ } //>>includeEnd('debug'); var ray = getRayForClampToHeight(this, cartesian); - var pickResult = this.pickFromRay(ray, objectsToExclude); + var pickResult = pickFromRay(this, ray, objectsToExclude, false); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); } }; + /** + * Initiates an asynchronous {@link Scene#sampleHeight} request using the maximum level of detail for 3D Tilesets + * regardless of visibility. + * + * @param {Cartographic|Cartographic[]} positions The cartographic position(s) to sample height from. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not sample height from. + * @returns {Promise.} A promise that resolves to the height(s), or undefined if there was no scene geometry to sample height from. + * + * @see Scene#sampleHeight + * + * @exception {DeveloperError} Ray intersections are only supported in 3D mode. + * @exception {DeveloperError} sampleHeightMostDetailed required depth texture support. Check sampleHeightSupported. + */ + Scene.prototype.sampleHeightMostDetailed = function(positions, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('positions', positions); + if (!this.sampleHeightSupported) { + throw new DeveloperError('sampleHeightMostDetailed required depth texture support. Check sampleHeightSupported.'); + } + //>>includeEnd('debug'); + objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; + positions = isArray(positions) ? positions : [positions]; + var length = positions.length; + var promises = new Array(length); + for (var i = 0; i < length; ++i) { + promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude); + } + return when.all(promises).then(function(heights) { + return (length === 1) ? heights[0] : heights; + }); + }; + + /** + * Initiates an asynchronous {@link Scene#clampToHeight} request using the maximum level of detail for 3D Tilesets + * regardless of visibility. + * + * @param {Cartesian3} cartesians The cartesian positions. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not clamp to. + * @returns {Promise.} A promise that resolves to the clamped cartesian position(s), or undefined if there was no scene geometry to clamp to. + * + * @see Scene#clampToHeight + * + * @exception {DeveloperError} Ray intersections are only supported in 3D mode. + * @exception {DeveloperError} clampToHeightMostDetailed required depth texture support. Check clampToHeightSupported. + */ + Scene.prototype.clampToHeightMostDetailed = function(cartesians, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('cartesian', cartesians); + if (!this.clampToHeightSupported) { + throw new DeveloperError('clampToHeightMostDetailed required depth texture support. Check clampToHeightSupported.'); + } + //>>includeEnd('debug'); + objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; + cartesians = isArray(cartesians) ? cartesians : [cartesians]; + var length = cartesians.length; + var promises = new Array(length); + for (var i = 0; i < length; ++i) { + promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude); + } + return when.all(promises).then(function(cartesians) { + return (length === 1) ? cartesians[0] : cartesians; + }); + }; + /** * Transforms a position in cartesian coordinates to canvas coordinates. This is commonly used to place an * HTML element at the same screen position as an object in the scene. diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index 9491ce9ce42c..d1953bc520c7 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2052,9 +2052,9 @@ defineSuite([ return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { tileset.style = new Cesium3DTileStyle({color: 'color("red")'}); scene.renderForSpecs(); - expect(tileset._statistics.numberOfTilesStyled).toBe(1); + expect(tileset._statisticsLastRender.numberOfTilesStyled).toBe(1); scene.pickForSpecs(); - expect(tileset._statisticsPick.numberOfTilesStyled).toBe(0); + expect(tileset._statisticsLastPick.numberOfTilesStyled).toBe(0); }); }); From 8b88951f55941781fae771d4964956d409a7161e Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 3 Oct 2018 18:50:09 -0400 Subject: [PATCH 04/32] Added Sandcastle demo --- .../gallery/Clamp to 3D Tiles Offscreen.html | 104 ++++++++++++++++++ .../gallery/Clamp to 3D Tiles Offscreen.jpg | Bin 0 -> 16217 bytes 2 files changed, 104 insertions(+) create mode 100644 Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html create mode 100644 Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.jpg diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html b/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html new file mode 100644 index 000000000000..85f17c14817b --- /dev/null +++ b/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html @@ -0,0 +1,104 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.jpg b/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bda74e9bdfb44d70eaa69ab867ed932959268e35 GIT binary patch literal 16217 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 literal 0 HcmV?d00001 From 8273d8eb74e399ac6e24300e845352723186c5fb Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 5 Oct 2018 10:05:49 -0400 Subject: [PATCH 05/32] Cleaned up Ray.clone --- CHANGES.md | 2 +- Source/Core/Ray.js | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d65dd03dec55..ae1266ec2187 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,7 @@ Change Log ### 1.51 - 2018-11-01 ##### Additions :tada: -* Added `Ray.clone`. [#TODO](TODO) +* Added `Ray.clone`. [#7115](https://github.com/AnalyticalGraphicsInc/cesium/pull/7115) ### 1.50 - 2018-10-01 diff --git a/Source/Core/Ray.js b/Source/Core/Ray.js index a7867a3d1dc1..4371d5a1821b 100644 --- a/Source/Core/Ray.js +++ b/Source/Core/Ray.js @@ -1,13 +1,13 @@ define([ './Cartesian3', + './Check', './defaultValue', - './defined', - './DeveloperError' + './defined' ], function( Cartesian3, + Check, defaultValue, - defined, - DeveloperError) { + defined) { 'use strict'; /** @@ -43,12 +43,13 @@ define([ * * @param {Ray} ray The ray to duplicate. * @param {Ray} [result] The object onto which to store the result. - * @returns {Ray} The modified result parameter or a new Ray instance if one was not provided. (Returns undefined if ray is undefined) + * @returns {Ray} The modified result parameter or a new Ray instance if one was not provided. */ Ray.clone = function(ray, result) { - if (!defined(ray)) { - return undefined; - } + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('ray', ray); + //>>includeEnd('debug'); + if (!defined(result)) { return new Ray(ray.origin, ray.direction); } @@ -74,12 +75,8 @@ define([ */ Ray.getPoint = function(ray, t, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(ray)){ - throw new DeveloperError('ray is requred'); - } - if (typeof t !== 'number') { - throw new DeveloperError('t is a required number'); - } + Check.typeOf.object('ray', ray); + Check.typeOf.number('t', t); //>>includeEnd('debug'); if (!defined(result)) { From eaa858b9bcf5f355513a1a0d1231c5e9c8f5c6f3 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 5 Oct 2018 16:54:25 -0400 Subject: [PATCH 06/32] Update frame number and time in main render loop so changes are reflected in updates. --- Source/Scene/Cesium3DTilesetTraversal.js | 8 +---- Source/Scene/Scene.js | 45 ++++++++++++++---------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 37acdd81ee06..13bb968bf036 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -60,7 +60,6 @@ define([ var root = tileset.root; updateTile(tileset, root, statistics, frameState); - postUpdateTile(root); // The root tile is not visible if (!isVisible(root)) { @@ -293,11 +292,6 @@ define([ } } - function postUpdateTile(tile) { - // Reset so visibility is checked during the next pass which may use a different camera - tile._updatedVisibilityFrame = 0; - } - function hasEmptyContent(tile) { return tile.hasEmptyContent || tile.hasTilesetContent; } @@ -464,7 +458,7 @@ define([ visitTile(tileset, tile, statistics, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; - postUpdateTile(tile); + tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass which may use a different camera } } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 7430009d155d..ad3ee9943f55 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -792,7 +792,8 @@ define([ this._view = this._defaultView; // Give frameState, camera, and screen space camera controller initial state before rendering - updateFrameState(this, 0.0, JulianDate.now()); + updateFrameNumber(this, 0.0, JulianDate.now()); + updateFrameState(this); this.initializeFrame(); } @@ -1591,7 +1592,13 @@ define([ passes.async = false; } - function updateFrameState(scene, frameNumber, time) { + function updateFrameNumber(scene, frameNumber, time) { + var frameState = scene._frameState; + frameState.frameNumber = frameNumber; + frameState.time = JulianDate.clone(time, frameState.time); + } + + function updateFrameState(scene) { var camera = scene.camera; var frameState = scene._frameState; @@ -1602,8 +1609,6 @@ define([ frameState.mode = scene._mode; frameState.morphTime = scene.morphTime; frameState.mapProjection = scene.mapProjection; - frameState.frameNumber = frameNumber; - frameState.time = JulianDate.clone(time, frameState.time); frameState.camera = camera; frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); frameState.occluder = getOccluder(scene); @@ -3023,7 +3028,7 @@ define([ frameState.creditDisplay.update(); } - function render(scene, time) { + function render(scene) { scene._pickPositionCacheDirty = true; var context = scene.context; @@ -3033,8 +3038,7 @@ define([ var view = scene._defaultView; scene._view = view; - var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0); - updateFrameState(scene, frameNumber, time); + updateFrameState(scene); frameState.passes.render = true; frameState.passes.postProcess = scene.postProcessStages.hasSelected; @@ -3092,9 +3096,9 @@ define([ context.endFrame(); } - function tryAndCatchError(scene, time, functionToExecute) { + function tryAndCatchError(scene, functionToExecute) { try { - functionToExecute(scene, time); + functionToExecute(scene); } catch (error) { scene._renderError.raiseEvent(scene, error); @@ -3115,13 +3119,9 @@ define([ time = JulianDate.now(); } + var frameState = this._frameState; this._jobScheduler.resetBudgets(); - // Update - this._preUpdate.raiseEvent(this, time); - tryAndCatchError(this, time, update); - this._postUpdate.raiseEvent(this, time); - var cameraChanged = this._view.checkForCameraUpdates(this); var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || this._logDepthBufferDirty || (this.mode === SceneMode.MORPHING); if (!shouldRender && defined(this.maximumRenderTimeChange) && defined(this._lastRenderTime)) { @@ -3133,10 +3133,19 @@ define([ this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime); this._renderRequested = false; this._logDepthBufferDirty = false; + var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0); + updateFrameNumber(this, frameNumber, time); + } + // Update + this._preUpdate.raiseEvent(this, time); + tryAndCatchError(this, update); + this._postUpdate.raiseEvent(this, time); + + if (shouldRender) { // Render this._preRender.raiseEvent(this, time); - tryAndCatchError(this, time, render); + tryAndCatchError(this, render); RequestScheduler.update(); } @@ -3324,7 +3333,7 @@ define([ this._jobScheduler.disableThisFrame(); // Update with previous frame's number and time, assuming that render is called before picking. - updateFrameState(this, frameState.frameNumber, frameState.time); + updateFrameState(this); frameState.cullingVolume = getPickCullingVolume(this, drawingBufferPosition, rectangleWidth, rectangleHeight, viewport); frameState.invertClassification = false; frameState.passes.pick = true; @@ -3675,7 +3684,7 @@ define([ updateCameraFromRay(ray, view.camera); // Update with previous frame's number and time, assuming that render is called first. - updateFrameState(scene, frameState.frameNumber, frameState.time); + updateFrameState(scene); frameState.passes.offscreen = true; frameState.passes.async = true; @@ -3752,7 +3761,7 @@ define([ scene._jobScheduler.disableThisFrame(); // Update with previous frame's number and time, assuming that render is called before picking. - updateFrameState(scene, frameState.frameNumber, frameState.time); + updateFrameState(scene); frameState.invertClassification = false; frameState.passes.pick = true; frameState.passes.offscreen = true; From 93061df46e7fe4f37596e279aeb3b779e71d45d7 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 15 Oct 2018 15:12:27 -0400 Subject: [PATCH 07/32] Fix #7120 by picking through primitives that don't write depth for sampleHeight and clampToHeight --- Source/Scene/Scene.js | 88 ++++++++++++++++++++++------------------- Specs/Scene/PickSpec.js | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 41 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index ad3ee9943f55..f862a479b648 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3529,16 +3529,7 @@ define([ return result; }; - function isExcluded(object, objectsToExclude) { - if (!defined(objectsToExclude) || objectsToExclude.length === 0) { - return false; - } - return (objectsToExclude.indexOf(object) > -1) || - (objectsToExclude.indexOf(object.primitive) > -1) || - (objectsToExclude.indexOf(object.id) > -1); - } - - function drillPick(limit, pickCallback, objectsToExclude) { + function drillPick(limit, pickCallback) { // PERFORMANCE_IDEA: This function calls each primitive's update for each pass. Instead // we could update the primitive once, and then just execute their commands for each pass, // and cull commands for picked primitives. e.g., base on the command's owner. @@ -3556,6 +3547,7 @@ define([ while (defined(pickedResult)) { var object = pickedResult.object; var position = pickedResult.position; + var exclude = pickedResult.exclude; if (defined(position) && !defined(object)) { result.push(pickedResult); @@ -3566,7 +3558,7 @@ define([ break; } - if (!isExcluded(object, objectsToExclude)) { + if (!exclude) { result.push(pickedResult); if (0 >= --limit) { break; @@ -3645,7 +3637,9 @@ define([ var object = that.pick(windowPosition, width, height); if (defined(object)) { return { - object : object + object : object, + position : undefined, + exclude : false }; } }; @@ -3744,7 +3738,16 @@ define([ }); } - function getRayIntersection(scene, ray, async) { + function isExcluded(object, objectsToExclude) { + if (!defined(object) || !defined(objectsToExclude) || objectsToExclude.length === 0) { + return false; + } + return (objectsToExclude.indexOf(object) > -1) || + (objectsToExclude.indexOf(object.primitive) > -1) || + (objectsToExclude.indexOf(object.id) > -1); + } + + function getRayIntersection(scene, ray, objectsToExclude, async, requirePosition) { var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3798,12 +3801,13 @@ define([ if (defined(object) || defined(position)) { return { object : object, - position : position + position : position, + exclude : (!defined(position) && requirePosition) || isExcluded(object, objectsToExclude) }; } } - function getRayIntersections(scene, ray, limit, objectsToExclude, async) { + function getRayIntersections(scene, ray, limit, objectsToExclude, async, requirePosition) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (scene._mode !== SceneMode.SCENE3D) { @@ -3811,20 +3815,20 @@ define([ } //>>includeEnd('debug'); var pickCallback = function() { - return getRayIntersection(scene, ray, async); + return getRayIntersection(scene, ray, objectsToExclude, async, requirePosition); }; - return drillPick(limit, pickCallback, objectsToExclude); + return drillPick(limit, pickCallback); } - function pickFromRay(scene, ray, objectsToExclude, async) { - var results = getRayIntersections(scene, ray, 1, objectsToExclude, async); + function pickFromRay(scene, ray, objectsToExclude, async, requirePosition) { + var results = getRayIntersections(scene, ray, 1, objectsToExclude, async, requirePosition); if (results.length > 0) { return results[0]; } } - function drillPickFromRay(scene, ray, limit, objectsToExclude, async) { - return getRayIntersections(scene, ray, limit, objectsToExclude, async); + function drillPickFromRay(scene, ray, limit, objectsToExclude, async, requirePosition) { + return getRayIntersections(scene, ray, limit, objectsToExclude, async, requirePosition); } /** @@ -3832,6 +3836,10 @@ define([ * or undefined if there were no intersections. The intersected object has a primitive * property that contains the intersected primitive. Other properties may be set depending on the type of primitive * and may be used to further identify the picked object. The ray must be given in world coordinates. + *

+ * This function only picks globe tiles and 3D Tiles that are rendered in the current view. Picks all other + * primitives regardless of their visibility. + *

* * @private * @@ -3842,7 +3850,7 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.pickFromRay = function(ray, objectsToExclude) { - return pickFromRay(this, ray, objectsToExclude, false); + return pickFromRay(this, ray, objectsToExclude, false, false); }; /** @@ -3851,6 +3859,10 @@ define([ * properties may also be set depending on the type of primitive and may be used to further identify the picked object. * The primitives in the list are ordered by first intersection to last intersection. The ray must be given in * world coordinates. + *

+ * This function only picks globe tiles and 3D Tiles that are rendered in the current view. Picks all other + * primitives regardless of their visibility. + *

* * @private * @@ -3862,7 +3874,7 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude) { - return drillPickFromRay(this, ray, limit, objectsToExclude, false); + return drillPickFromRay(this, ray, limit, objectsToExclude, false, false); }; /** @@ -3882,7 +3894,7 @@ define([ ray = Ray.clone(ray); objectsToExclude = objectsToExclude.slice(); return launchAsyncLoader(this, ray, objectsToExclude, function() { - return pickFromRay(that, ray, objectsToExclude, true); + return pickFromRay(that, ray, objectsToExclude, true, false); }); }; @@ -3904,7 +3916,7 @@ define([ ray = Ray.clone(ray); objectsToExclude = objectsToExclude.slice(); return launchAsyncLoader(this, ray, objectsToExclude, function() { - return drillPickFromRay(that, ray, limit, objectsToExclude, true); + return drillPickFromRay(that, ray, limit, objectsToExclude, true, false); }); }; @@ -3945,7 +3957,7 @@ define([ function sampleHeightMostDetailed(scene, position, objectsToExclude) { var ray = getRayForSampleHeight(scene, position); return launchAsyncLoader(scene, ray, objectsToExclude, function() { - var pickResult = pickFromRay(scene, ray, objectsToExclude, true); + var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { return getHeightFromCartesian(scene, pickResult.position); } @@ -3955,7 +3967,7 @@ define([ function clampToHeightMostDetailed(scene, cartesian, objectsToExclude) { var ray = getRayForClampToHeight(scene, cartesian); return launchAsyncLoader(scene, ray, objectsToExclude, function() { - var pickResult = pickFromRay(scene, ray, objectsToExclude, true); + var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { return pickResult.position; } @@ -3989,7 +4001,7 @@ define([ } //>>includeEnd('debug'); var ray = getRayForSampleHeight(this, position); - var pickResult = pickFromRay(this, ray, objectsToExclude, false); + var pickResult = pickFromRay(this, ray, objectsToExclude, false, true); if (defined(pickResult)) { return getHeightFromCartesian(this, pickResult.position); } @@ -4024,7 +4036,7 @@ define([ } //>>includeEnd('debug'); var ray = getRayForClampToHeight(this, cartesian); - var pickResult = pickFromRay(this, ray, objectsToExclude, false); + var pickResult = pickFromRay(this, ray, objectsToExclude, false, true); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); } @@ -4034,9 +4046,9 @@ define([ * Initiates an asynchronous {@link Scene#sampleHeight} request using the maximum level of detail for 3D Tilesets * regardless of visibility. * - * @param {Cartographic|Cartographic[]} positions The cartographic position(s) to sample height from. + * @param {Cartographic[]} positions The cartographic positions to sample height from. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not sample height from. - * @returns {Promise.} A promise that resolves to the height(s), or undefined if there was no scene geometry to sample height from. + * @returns {Promise.} A promise that resolves to the heights, or undefined if there was no scene geometry to sample height from. * * @see Scene#sampleHeight * @@ -4051,24 +4063,21 @@ define([ } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - positions = isArray(positions) ? positions : [positions]; var length = positions.length; var promises = new Array(length); for (var i = 0; i < length; ++i) { promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude); } - return when.all(promises).then(function(heights) { - return (length === 1) ? heights[0] : heights; - }); + return when.all(promises); }; /** * Initiates an asynchronous {@link Scene#clampToHeight} request using the maximum level of detail for 3D Tilesets * regardless of visibility. * - * @param {Cartesian3} cartesians The cartesian positions. + * @param {Cartesian3[]} cartesians The cartesian positions. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not clamp to. - * @returns {Promise.} A promise that resolves to the clamped cartesian position(s), or undefined if there was no scene geometry to clamp to. + * @returns {Promise.} A promise that resolves to the clamped cartesian positions, or undefined if there was no scene geometry to clamp to. * * @see Scene#clampToHeight * @@ -4083,15 +4092,12 @@ define([ } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - cartesians = isArray(cartesians) ? cartesians : [cartesians]; var length = cartesians.length; var promises = new Array(length); for (var i = 0; i < length; ++i) { promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude); } - return when.all(promises).then(function(cartesians) { - return (length === 1) ? cartesians[0] : cartesians; - }); + return when.all(promises); }; /** diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 26812206a792..05669fd72fb2 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -17,6 +17,7 @@ defineSuite([ 'Scene/Cesium3DTileStyle', 'Scene/EllipsoidSurfaceAppearance', 'Scene/Globe', + 'Scene/PointPrimitiveCollection', 'Scene/Primitive', 'Scene/Scene', 'Scene/SceneMode', @@ -43,6 +44,7 @@ defineSuite([ Cesium3DTileStyle, EllipsoidSurfaceAppearance, Globe, + PointPrimitiveCollection, Primitive, Scene, SceneMode, @@ -541,6 +543,19 @@ defineSuite([ }, primitiveRay); }); + it('picks primitive that doesn\'t write depth', function() { + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.fromRadians(0.0, 0.0, 100.0), + disableDepthTestDistance : Number.POSITIVE_INFINITY + }); + + expect(scene).toPickFromRayAndCall(function(result) { + expect(result.object.primitive).toBe(point); + expect(result.position).toBeUndefined(); + }, primitiveRay); + }); + it('throws if ray is undefined', function() { expect(function() { scene.pickFromRay(undefined); @@ -889,6 +904,35 @@ defineSuite([ }, cartographic, [rectangle2, rectangle3]); }); + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var rectangle = createSmallRectangle(0.0); + + var height = 100.0; + var cartographic = new Cartographic(0.0, 0.0, height); + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.toCartesian(cartographic) + }); + + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toEqualEpsilon(height, CesiumMath.EPSILON3); + }, cartographic); + + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); + }, cartographic); + + rectangle.show = false; + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toBeUndefined(); + }, cartographic); + }); + it('throws if position is undefined', function() { if (!scene.sampleHeightSupported) { return; @@ -1027,6 +1071,34 @@ defineSuite([ }, cartesian, [rectangle2, rectangle3]); }); + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var rectangle = createSmallRectangle(0.0); + + var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100.0); + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : cartesian + }); + + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }, cartesian); + + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }, cartesian); + + rectangle.show = false; + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toBeUndefined(); + }, cartesian); + }); + it('throws if cartesian is undefined', function() { if (!scene.clampToHeightSupported) { return; From af109b2e69cf2efa43577e4a6d7f30d2e5045199 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 15 Oct 2018 15:58:27 -0400 Subject: [PATCH 08/32] Added #7120 to CHANGES --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d86b5124e1fe..ab44c5578c73 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ Change Log ##### Fixes :wrench: * Fixed an issue where `pickPosition` would return incorrect results when called after `sampleHeight` or `clampToHeight`. [#7113](https://github.com/AnalyticalGraphicsInc/cesium/pull/7113) +* Fixed an issue where `sampleHeight` and `clampToHeight` would crash if picking a primitive that doesn't write depth. [#7120](https://github.com/AnalyticalGraphicsInc/cesium/issues/7120) ### 1.50 - 2018-10-01 From 320bf99b6242b1fb006a146e6e5046e97c9216b8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 15 Oct 2018 18:38:52 -0400 Subject: [PATCH 09/32] Modify arrays in place --- Source/Scene/Scene.js | 52 ++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 181d92d8fe92..98f5c19a6056 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3957,8 +3957,8 @@ define([ return cartographic.height; } - function sampleHeightMostDetailed(scene, position, objectsToExclude) { - var ray = getRayForSampleHeight(scene, position); + function sampleHeightMostDetailed(scene, cartographic, objectsToExclude) { + var ray = getRayForSampleHeight(scene, cartographic); return launchAsyncLoader(scene, ray, objectsToExclude, function() { var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { @@ -3967,12 +3967,12 @@ define([ }); } - function clampToHeightMostDetailed(scene, cartesian, objectsToExclude) { + function clampToHeightMostDetailed(scene, cartesian, objectsToExclude, result) { var ray = getRayForClampToHeight(scene, cartesian); return launchAsyncLoader(scene, ray, objectsToExclude, function() { var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { - return pickResult.position; + return Cartesian3.clone(pickResult.position, result); } }); } @@ -4046,12 +4046,14 @@ define([ }; /** - * Initiates an asynchronous {@link Scene#sampleHeight} request using the maximum level of detail for 3D Tilesets - * regardless of visibility. + * Initiates an asynchronous {@link Scene#sampleHeight} query for an array of {@link Cartographic} positions + * using the maximum level of detail for 3D Tilesets in the scene. Returns a promise that is resolved when + * the query completes. Each point height is modified in place. If a height cannot be determined because no + * geometry can be sampled at that location, or another error occurs, the height is set to undefined. * - * @param {Cartographic[]} positions The cartographic positions to sample height from. + * @param {Cartographic[]} positions The cartographic positions to update with sampled heights. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not sample height from. - * @returns {Promise.} A promise that resolves to the heights, or undefined if there was no scene geometry to sample height from. + * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * * @see Scene#sampleHeight * @@ -4066,21 +4068,29 @@ define([ } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; + var i; var length = positions.length; var promises = new Array(length); - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude); } - return when.all(promises); + return when.all(promises).then(function(heights) { + for (i = 0; i < length; ++i) { + positions[i].height = heights[i]; + } + return positions; + }); }; /** - * Initiates an asynchronous {@link Scene#clampToHeight} request using the maximum level of detail for 3D Tilesets - * regardless of visibility. + * Initiates an asynchronous {@link Scene#clampToHeight} query for an array of {@link Cartesian3} positions + * using the maximum level of detail for 3D Tilesets in the scene. Returns a promise that is resolved when + * the query completes. Each position is modified in place. If a position cannot be clamped because no geometry + * can be sampled at that location, or another error occurs, the element in the array is set to undefined. * - * @param {Cartesian3[]} cartesians The cartesian positions. + * @param {Cartesian3[]} cartesians The cartesian positions to update with clamped positions. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not clamp to. - * @returns {Promise.} A promise that resolves to the clamped cartesian positions, or undefined if there was no scene geometry to clamp to. + * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * * @see Scene#clampToHeight * @@ -4089,18 +4099,24 @@ define([ */ Scene.prototype.clampToHeightMostDetailed = function(cartesians, objectsToExclude) { //>>includeStart('debug', pragmas.debug); - Check.defined('cartesian', cartesians); + Check.defined('cartesians', cartesians); if (!this.clampToHeightSupported) { throw new DeveloperError('clampToHeightMostDetailed required depth texture support. Check clampToHeightSupported.'); } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; + var i; var length = cartesians.length; var promises = new Array(length); - for (var i = 0; i < length; ++i) { - promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude); + for (i = 0; i < length; ++i) { + promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude, cartesians[i]); } - return when.all(promises); + return when.all(promises).then(function(clampedCartesians) { + for (i = 0; i < length; ++i) { + cartesians[i] = clampedCartesians[i]; + } + return cartesians; + }); }; /** From 751b1382ad027a5e2f9039f153cf3fe92878446b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 16 Oct 2018 13:43:45 -0400 Subject: [PATCH 10/32] Rename demo --- ...screen.html => Sample Height from 3D Tiles.html} | 0 ...ffscreen.jpg => Sample Height from 3D Tiles.jpg} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename Apps/Sandcastle/gallery/{Clamp to 3D Tiles Offscreen.html => Sample Height from 3D Tiles.html} (100%) rename Apps/Sandcastle/gallery/{Clamp to 3D Tiles Offscreen.jpg => Sample Height from 3D Tiles.jpg} (100%) diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html similarity index 100% rename from Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.html rename to Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.jpg b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.jpg similarity index 100% rename from Apps/Sandcastle/gallery/Clamp to 3D Tiles Offscreen.jpg rename to Apps/Sandcastle/gallery/Sample Height from 3D Tiles.jpg From b605450937574305fa6c540822d243c2fd2f2c44 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 16 Oct 2018 14:15:07 -0400 Subject: [PATCH 11/32] Fix tests --- Source/Scene/Cesium3DTilesetTraversal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index dde15f82dd22..37cb47841a4b 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -260,7 +260,7 @@ define([ // The root tile may be culled by the children bounds optimization in which // case this tile should also be culled. var child = tile.children[0]; - updateTileVisibility(tileset, child, frameState); + updateTileVisibility(tileset, child, statistics, frameState); tile._visible = child._visible; return; } From bdd2d784eeaf94dfbed55f2588a84fa39f833c01 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 18 Oct 2018 19:40:38 -0400 Subject: [PATCH 12/32] Added pick tests --- Source/Scene/Scene.js | 70 ++- Specs/Scene/PickSpec.js | 1036 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 1037 insertions(+), 69 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 98f5c19a6056..11db7e271e94 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3811,12 +3811,6 @@ define([ } function getRayIntersections(scene, ray, limit, objectsToExclude, async, requirePosition) { - //>>includeStart('debug', pragmas.debug); - Check.defined('ray', ray); - if (scene._mode !== SceneMode.SCENE3D) { - throw new DeveloperError('Ray intersections are only supported in 3D mode.'); - } - //>>includeEnd('debug'); var pickCallback = function() { return getRayIntersection(scene, ray, objectsToExclude, async, requirePosition); }; @@ -3853,6 +3847,12 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.pickFromRay = function(ray, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); + } + //>>includeEnd('debug'); return pickFromRay(this, ray, objectsToExclude, false, false); }; @@ -3877,6 +3877,12 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); + } + //>>includeEnd('debug'); return drillPickFromRay(this, ray, limit, objectsToExclude, false, false); }; @@ -3893,9 +3899,15 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.pickFromRayMostDetailed = function(ray, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); + } + //>>includeEnd('debug'); var that = this; ray = Ray.clone(ray); - objectsToExclude = objectsToExclude.slice(); + objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; return launchAsyncLoader(this, ray, objectsToExclude, function() { return pickFromRay(that, ray, objectsToExclude, true, false); }); @@ -3915,9 +3927,15 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.drillPickFromRayMostDetailed = function(ray, limit, objectsToExclude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); + } + //>>includeEnd('debug'); var that = this; ray = Ray.clone(ray); - objectsToExclude = objectsToExclude.slice(); + objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; return launchAsyncLoader(this, ray, objectsToExclude, function() { return drillPickFromRay(that, ray, limit, objectsToExclude, true, false); }); @@ -3993,14 +4011,17 @@ define([ * @see Scene#clampToHeightMostDetailed * @see Scene#sampleHeightMostDetailed * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} sampleHeight required depth texture support. Check sampleHeightSupported. + * @exception {DeveloperError} sampleHeight is only supported in 3D mode. + * @exception {DeveloperError} sampleHeight requires depth texture support. Check sampleHeightSupported. */ Scene.prototype.sampleHeight = function(position, objectsToExclude) { //>>includeStart('debug', pragmas.debug); Check.defined('position', position); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('sampleHeight is only supported in 3D mode.'); + } if (!this.sampleHeightSupported) { - throw new DeveloperError('sampleHeight required depth texture support. Check sampleHeightSupported.'); + throw new DeveloperError('sampleHeight requires depth texture support. Check sampleHeightSupported.'); } //>>includeEnd('debug'); var ray = getRayForSampleHeight(this, position); @@ -4028,14 +4049,17 @@ define([ * @see Scene#sampleHeightMostDetailed * @see Scene#clampToHeightMostDetailed * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} clampToHeight required depth texture support. Check clampToHeightSupported. + * @exception {DeveloperError} clampToHeight is only supported in 3D mode. + * @exception {DeveloperError} clampToHeight requires depth texture support. Check clampToHeightSupported. */ Scene.prototype.clampToHeight = function(cartesian, objectsToExclude, result) { //>>includeStart('debug', pragmas.debug); Check.defined('cartesian', cartesian); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('sampleHeight is only supported in 3D mode.'); + } if (!this.clampToHeightSupported) { - throw new DeveloperError('clampToHeight required depth texture support. Check clampToHeightSupported.'); + throw new DeveloperError('clampToHeight requires depth texture support. Check clampToHeightSupported.'); } //>>includeEnd('debug'); var ray = getRayForClampToHeight(this, cartesian); @@ -4057,14 +4081,17 @@ define([ * * @see Scene#sampleHeight * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} sampleHeightMostDetailed required depth texture support. Check sampleHeightSupported. + * @exception {DeveloperError} sampleHeightMostDetailed is only supported in 3D mode. + * @exception {DeveloperError} sampleHeightMostDetailed requires depth texture support. Check sampleHeightSupported. */ Scene.prototype.sampleHeightMostDetailed = function(positions, objectsToExclude) { //>>includeStart('debug', pragmas.debug); Check.defined('positions', positions); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('sampleHeightMostDetailed is only supported in 3D mode.'); + } if (!this.sampleHeightSupported) { - throw new DeveloperError('sampleHeightMostDetailed required depth texture support. Check sampleHeightSupported.'); + throw new DeveloperError('sampleHeightMostDetailed requires depth texture support. Check sampleHeightSupported.'); } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; @@ -4094,14 +4121,17 @@ define([ * * @see Scene#clampToHeight * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} clampToHeightMostDetailed required depth texture support. Check clampToHeightSupported. + * @exception {DeveloperError} clampToHeightMostDetailed is only supported in 3D mode. + * @exception {DeveloperError} clampToHeightMostDetailed requires depth texture support. Check clampToHeightSupported. */ Scene.prototype.clampToHeightMostDetailed = function(cartesians, objectsToExclude) { //>>includeStart('debug', pragmas.debug); Check.defined('cartesians', cartesians); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('clampToHeightMostDetailed is only supported in 3D mode.'); + } if (!this.clampToHeightSupported) { - throw new DeveloperError('clampToHeightMostDetailed required depth texture support. Check clampToHeightSupported.'); + throw new DeveloperError('clampToHeightMostDetailed requires depth texture support. Check clampToHeightSupported.'); } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index bffb584afb74..077733111ec0 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -1010,7 +1010,7 @@ defineSuite([ }); it('clamps to the globe', function() { - if (!scene.sampleHeightSupported) { + if (!scene.clampToHeightSupported) { return; } @@ -1156,67 +1156,1005 @@ defineSuite([ }); }); - it('calls multiple picking functions within the same frame', function() { - if (!scene.clampToHeightSupported || !scene.pickPositionSupported) { - return; - } + // TODO : how to write default matchers for these since they return promises? Do they fail when run in WebGL stub? - createSmallRectangle(0.0); - var offscreenRectanglePrimitive = createRectangle(0.0, offscreenRectangle); - offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); + function pickFromRayMostDetailed(ray, objectsToExclude) { + var result; + var completed = false; + scene.pickFromRayMostDetailed(ray, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } - scene.camera.setView({ destination : offscreenRectangle }); + function drillPickFromRayMostDetailed(ray, limit, objectsToExclude) { + var result; + var completed = false; + scene.drillPickFromRayMostDetailed(ray, limit, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } - // Call render. Lays down depth for the pickPosition call - scene.renderForSpecs(); + function sampleHeightMostDetailed(cartographics, objectsToExclude) { + var result; + var completed = false; + scene.sampleHeightMostDetailed(cartographics, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } - // Call clampToHeight - var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); - expect(scene).toClampToHeightAndCall(function(cartesian) { - var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); - }, cartesian); + function clampToHeightMostDetailed(cartesians, objectsToExclude) { + var result; + var completed = false; + scene.clampToHeightMostDetailed(cartesians, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } - // Call pickPosition - expect(scene).toPickPositionAndCall(function(cartesian) { - var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + describe('pickFromRayMostDetailed', function() { + it('picks a tileset', function() { + scene.camera.setView({ destination : offscreenRectangle }); + return createTileset().then(function(tileset) { + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + var primitive = result.object.primitive; + var position = result.position; + + expect(primitive).toBe(tileset); + + if (scene.context.depthTexture) { + var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; + var maximumHeight = minimumHeight + 20.0; // Rough height of tile + expect(position.x).toBeGreaterThan(minimumHeight); + expect(position.x).toBeLessThan(maximumHeight); + expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + } + }); + }); }); - // Call clampToHeight again - expect(scene).toClampToHeightAndCall(function(cartesian) { - var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); - }, cartesian); + it('picks a primitive', function() { + var rectangle = createSmallRectangle(0.0); + scene.camera.setView({ destination : offscreenRectangle }); + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + var primitive = result.object.primitive; + var position = result.position; - // Call pick - expect(scene).toPickPrimitive(offscreenRectanglePrimitive); + expect(primitive).toBe(rectangle); - // Call clampToHeight again - expect(scene).toClampToHeightAndCall(function(cartesian) { - var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); - }, cartesian); + if (scene.context.depthTexture) { + var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); + expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); + } + }); + }); - // Call pickPosition on translucent primitive and returns undefined - offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 0.5); - scene.renderForSpecs(); - expect(scene).toPickPositionAndCall(function(cartesian) { - expect(cartesian).toBeUndefined(); + it('returns undefined if no primitives are picked', function() { + createLargeRectangle(0.0); + scene.camera.setView({ destination : offscreenRectangle }); + return pickFromRayMostDetailed(offscreenRay).then(function(result) { + expect(result).toBeUndefined(); + }); }); - // Call clampToHeight again - expect(scene).toClampToHeightAndCall(function(cartesian) { - var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); - }, cartesian); + it('picks the top primitive', function() { + createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + scene.camera.setView({ destination : offscreenRectangle }); + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + expect(result.object.primitive).toBe(rectangle2); + }); + }); - // Call pickPosition on translucent primitive with pickTranslucentDepth - scene.pickTranslucentDepth = true; - scene.renderForSpecs(); - expect(scene).toPickPositionAndCall(function(cartesian) { - var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); - expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + it('excludes objects', function() { + var rectangle1 = createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + var rectangle3 = createLargeRectangle(2.0); + var rectangle4 = createLargeRectangle(3.0); + rectangle4.show = false; + + scene.camera.setView({ destination : offscreenRectangle }); + return pickFromRayMostDetailed(primitiveRay, [rectangle2, rectangle3, rectangle4]).then(function(result) { + expect(result.object.primitive).toBe(rectangle1); + }).then(function() { + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + expect(result.object.primitive).toBe(rectangle3); + }); + }); + }); + + it('picks primitive that doesn\'t write depth', function() { + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.fromRadians(0.0, 0.0, 100.0), + disableDepthTestDistance : Number.POSITIVE_INFINITY + }); + + scene.camera.setView({ destination : offscreenRectangle }); + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + expect(result.object.primitive).toBe(point); + expect(result.position).toBeUndefined(); + }); + }); + + it('throws if ray is undefined', function() { + expect(function() { + scene.pickFromRayMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in 2D', function() { + scene.morphTo2D(0.0); + expect(function() { + scene.pickFromRayMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in CV', function() { + scene.morphToColumbusView(0.0); + expect(function() { + scene.pickFromRayMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + }); + + describe('drillPickFromRayMostDetailed', function() { + it('drill picks a primitive', function() { + var rectangle = createSmallRectangle(0.0); + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toBe(1); + + var primitive = results[0].object.primitive; + var position = results[0].position; + + expect(primitive).toBe(rectangle); + + if (scene.context.depthTexture) { + var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); + expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); + } else { + expect(position).toBeUndefined(); + } + }); + }); + + it('drill picks multiple primitives', function() { + var rectangle1 = createSmallRectangle(0.0); + var rectangle2 = createSmallRectangle(1.0); + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toBe(2); + + // rectangle2 is picked before rectangle1 + expect(results[0].object.primitive).toBe(rectangle2); + expect(results[1].object.primitive).toBe(rectangle1); + + if (scene.context.depthTexture) { + var rectangleCenter1 = Cartesian3.fromRadians(0.0, 0.0, 0.0); + var rectangleCenter2 = Cartesian3.fromRadians(0.0, 0.0, 1.0); + expect(results[0].position).toEqualEpsilon(rectangleCenter2, CesiumMath.EPSILON5); + expect(results[1].position).toEqualEpsilon(rectangleCenter1, CesiumMath.EPSILON5); + } else { + expect(results[0].position).toBeUndefined(); + expect(results[1].position).toBeUndefined(); + } + }); + }); + + it('does not drill pick when show is false', function() { + var rectangle1 = createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + rectangle2.show = false; + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].object.primitive).toEqual(rectangle1); + }); + }); + + it('does not drill pick when alpha is zero', function() { + var rectangle1 = createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + rectangle2.appearance.material.uniforms.color.alpha = 0.0; + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].object.primitive).toEqual(rectangle1); + }); + }); + + it('returns empty array if no primitives are picked', function() { + createLargeRectangle(0.0); + createLargeRectangle(1.0); + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(offscreenRay).then(function(results) { + expect(results.length).toEqual(0); + }); + }); + + it('can drill pick batched Primitives with show attribute', function() { + var geometry = new RectangleGeometry({ + rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), + granularity : CesiumMath.toRadians(20.0), + vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, + height : 0.0 + }); + + var geometryWithHeight = new RectangleGeometry({ + rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), + granularity : CesiumMath.toRadians(20.0), + vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, + height : 1.0 + }); + + var instance1 = new GeometryInstance({ + id : 1, + geometry : geometry, + attributes : { + show : new ShowGeometryInstanceAttribute(true) + } + }); + + var instance2 = new GeometryInstance({ + id : 2, + geometry : geometry, + attributes : { + show : new ShowGeometryInstanceAttribute(false) + } + }); + + var instance3 = new GeometryInstance({ + id : 3, + geometry : geometryWithHeight, + attributes : { + show : new ShowGeometryInstanceAttribute(true) + } + }); + + var primitive = primitives.add(new Primitive({ + geometryInstances : [instance1, instance2, instance3], + asynchronous : false, + appearance : new EllipsoidSurfaceAppearance() + })); + + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toEqual(2); + expect(results[0].object.primitive).toEqual(primitive); + expect(results[0].object.id).toEqual(3); + expect(results[1].object.primitive).toEqual(primitive); + expect(results[1].object.id).toEqual(1); + }); + }); + + it('can drill pick without ID', function() { + var geometry = new RectangleGeometry({ + rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), + granularity : CesiumMath.toRadians(20.0), + vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT + }); + + var instance1 = new GeometryInstance({ + geometry : geometry, + attributes : { + show : new ShowGeometryInstanceAttribute(true) + } + }); + + var instance2 = new GeometryInstance({ + geometry : geometry, + attributes : { + show : new ShowGeometryInstanceAttribute(true) + } + }); + + var primitive = primitives.add(new Primitive({ + geometryInstances : [instance1, instance2], + asynchronous : false, + appearance : new EllipsoidSurfaceAppearance() + })); + + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].object.primitive).toEqual(primitive); + }); + }); + + it('can drill pick batched Primitives without show attribute', function() { + var geometry = new RectangleGeometry({ + rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), + granularity : CesiumMath.toRadians(20.0), + vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, + height : 0.0 + }); + + var geometryWithHeight = new RectangleGeometry({ + rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), + granularity : CesiumMath.toRadians(20.0), + vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, + height : 1.0 + }); + + var instance1 = new GeometryInstance({ + id : 1, + geometry : geometry + }); + + var instance2 = new GeometryInstance({ + id : 2, + geometry : geometry + }); + + var instance3 = new GeometryInstance({ + id : 3, + geometry : geometryWithHeight + }); + + var primitive = primitives.add(new Primitive({ + geometryInstances : [instance1, instance2, instance3], + asynchronous : false, + appearance : new EllipsoidSurfaceAppearance() + })); + + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].object.primitive).toEqual(primitive); + expect(results[0].object.id).toEqual(3); + }); + }); + + it('stops drill picking when the limit is reached.', function() { + createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + var rectangle3 = createLargeRectangle(2.0); + var rectangle4 = createLargeRectangle(3.0); + + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay, 3).then(function(results) { + expect(results.length).toEqual(3); + expect(results[0].object.primitive).toEqual(rectangle4); + expect(results[1].object.primitive).toEqual(rectangle3); + expect(results[2].object.primitive).toEqual(rectangle2); + }); + }); + + it('excludes objects', function() { + createLargeRectangle(0.0); + var rectangle2 = createLargeRectangle(1.0); + var rectangle3 = createLargeRectangle(2.0); + var rectangle4 = createLargeRectangle(3.0); + var rectangle5 = createLargeRectangle(4.0); + scene.camera.setView({ destination : offscreenRectangle }); + return drillPickFromRayMostDetailed(primitiveRay, 2, [rectangle5, rectangle3]).then(function(results) { + expect(results.length).toBe(2); + expect(results[0].object.primitive).toBe(rectangle4); + expect(results[1].object.primitive).toBe(rectangle2); + }); + }); + + it('throws if ray is undefined', function() { + expect(function() { + scene.drillPickFromRayMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in 2D', function() { + scene.morphTo2D(0.0); + expect(function() { + scene.drillPickFromRayMostDetailed(primitiveRay); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in CV', function() { + scene.morphToColumbusView(0.0); + expect(function() { + scene.drillPickFromRayMostDetailed(primitiveRay); + }).toThrowDeveloperError(); + }); + }); + + describe('sampleHeightMostDetailed', function() { + it('samples height from tileset', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var cartographics = [new Cartographic(0.0, 0.0)]; + return createTileset().then(function() { + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + var height = updatedCartographics[0].height; + expect(height).toBeGreaterThan(0.0); + expect(height).toBeLessThan(20.0); // Rough height of tile + }); + }); + }); + + it('samples height from the globe', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var cartographics = [ + new Cartographic(0.0, 0.0), + new Cartographic(0.0001, 0.0001), + new Cartographic(0.0002, 0.0002) + ]; + var clonedCartographics = [new Cartographic(0.0, 0.0), new Cartographic(0.0001, 0.0001), new Cartographic(0.0002, 0.0002)]; + return createGlobe().then(function() { + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics).toBe(cartographics); + expect(updatedCartographics.length).toBe(3); + var previousHeight; + for (var i = 0; i < 3; ++i) { + var longitude = updatedCartographics[i].longitude; + var latitude = updatedCartographics[i].latitude; + var height = updatedCartographics[i].height; + expect(longitude).toBe(clonedCartographics[i].longitude); + expect(latitude).toBe(clonedCartographics[i].latitude); + expect(height).toBeDefined(); + expect(height).not.toBe(previousHeight); + previousHeight = height; + } + }); + }); + }); + + it('does not sample offscreen globe tiles', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var cartographics = [new Cartographic(0.0, 0.0)]; + scene.camera.setView({ destination : offscreenRectangle }); + return createGlobe().then(function() { + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toBeUndefined(); + }); + }); + }); + + it('samples height from multiple primitives', function() { + if (!scene.sampleHeightSupported) { + return; + } + + createRectangle(0.0, smallRectangle); + createRectangle(0.0, offscreenRectangle); + + var cartographics = [ + Rectangle.center(smallRectangle), + Rectangle.center(offscreenRectangle), + new Cartographic(-2.0, -2.0) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toBeDefined(); + expect(updatedCartographics[1].height).toBeDefined(); + expect(updatedCartographics[2].height).toBeUndefined(); // No primitive occupies this space + }); + }); + + it('samples multiple heights from primitive', function() { + if (!scene.sampleHeightSupported) { + return; + } + + createSmallRectangle(0.0); + var cartographics = [ + new Cartographic(0.0, 0.0), + new Cartographic(-0.000001, -0.000001), + new Cartographic(0.0000005, 0.0000005) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + var previousHeight; + for (var i = 0; i < 3; ++i) { + var height = updatedCartographics[i].height; + expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); + expect(height).not.toBe(previousHeight); + previousHeight = height; + } + }); + }); + + it('samples height from the top primitive', function() { + if (!scene.sampleHeightSupported) { + return; + } + createSmallRectangle(0.0); + createSmallRectangle(1.0); + var cartographics = [new Cartographic(0.0, 0.0)]; + scene.camera.setView({ destination : offscreenRectangle }); + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toEqualEpsilon(1.0, CesiumMath.EPSILON3); + }); + }); + + it('excludes objects', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var rectangle1 = createRectangle(0.0, smallRectangle); + createRectangle(0.0, offscreenRectangle); + var rectangle3 = createRectangle(1.0, offscreenRectangle); + + var cartographics = [ + Rectangle.center(smallRectangle), + Rectangle.center(offscreenRectangle), + new Cartographic(-2.0, -2.0) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return sampleHeightMostDetailed(cartographics, [rectangle1, rectangle3]).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toBeUndefined(); // This rectangle was excluded + expect(updatedCartographics[1].height).toEqualEpsilon(0.0, CesiumMath.EPSILON2); + expect(updatedCartographics[2].height).toBeUndefined(); // No primitive occupies this space + }); + }); + + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var rectangle = createSmallRectangle(0.0); + + var height = 100.0; + var cartographics = [new Cartographic(0.0, 0.0, height)]; + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.toCartesian(cartographics[0]) + }); + + scene.camera.setView({ destination : offscreenRectangle }); + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toEqualEpsilon(height, CesiumMath.EPSILON3); + }).then(function() { + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); + }); + }).then(function() { + rectangle.show = false; + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics[0].height).toBeUndefined(); + }); + }); + }); + + it('handles empty array', function() { + if (!scene.sampleHeightSupported) { + return; + } + + var cartographics = []; + return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + expect(updatedCartographics.length).toBe(0); + }); + }); + + it('throws if positions is undefined', function() { + if (!scene.sampleHeightSupported) { + return; + } + + expect(function() { + scene.sampleHeightMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in 2D', function() { + if (!scene.sampleHeightSupported) { + return; + } + + scene.morphTo2D(0.0); + var cartographics = [new Cartographic(0.0, 0.0)]; + expect(function() { + scene.sampleHeightMostDetailed(cartographics); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in CV', function() { + if (!scene.sampleHeightSupported) { + return; + } + + scene.morphToColumbusView(0.0); + var cartographics = [new Cartographic(0.0, 0.0)]; + expect(function() { + scene.sampleHeightMostDetailed(cartographics); + }).toThrowDeveloperError(); + }); + + it('throws if sampleHeight is not supported', function() { + if (!scene.sampleHeightSupported) { + return; + } + // Disable extension + var depthTexture = scene.context._depthTexture; + scene.context._depthTexture = false; + + var cartographics = [new Cartographic(0.0, 0.0)]; + expect(function() { + scene.sampleHeightMostDetailed(cartographics); + }).toThrowDeveloperError(); + + // Re-enable extension + scene.context._depthTexture = depthTexture; + }); + }); + + describe('clampToHeightMostDetailed', function() { + it('clamps to tileset', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var cartesians = [Cartesian3.fromRadians(0.0, 0.0, 100000.0)]; + return createTileset().then(function() { + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; + var maximumHeight = minimumHeight + 20.0; // Rough height of tile + var position = updatedCartesians[0]; + expect(position.x).toBeGreaterThan(minimumHeight); + expect(position.x).toBeLessThan(maximumHeight); + expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + }); + }); + }); + + it('clamps to the globe', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var cartesians = [ + Cartesian3.fromRadians(0.0, 0.0, 100000.0), + Cartesian3.fromRadians(0.0001, 0.0001, 100000.0), + Cartesian3.fromRadians(0.0002, 0.0002, 100000.0) + ]; + var clonedCartesians = [ + Cartesian3.fromRadians(0.0, 0.0, 100000.0), + Cartesian3.fromRadians(0.0001, 0.0001, 100000.0), + Cartesian3.fromRadians(0.0002, 0.0002, 100000.0) + ]; + return createGlobe().then(function() { + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + expect(updatedCartesians).toBe(cartesians); + expect(updatedCartesians.length).toBe(3); + var previousCartesian; + for (var i = 0; i < 3; ++i) { + expect(updatedCartesians[i]).not.toEqual(clonedCartesians[i]); + expect(updatedCartesians[i]).not.toEqual(previousCartesian); + previousCartesian = updatedCartesians[i]; + } + }); + }); + }); + + it('does not clamp to offscreen globe tiles', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var cartesians = [Cartesian3.fromRadians(0.0, 0.0, 100000.0)]; + scene.camera.setView({ destination : offscreenRectangle }); + return createGlobe().then(function() { + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + expect(updatedCartesians[0]).toBeUndefined(); + }); + }); + }); + + it('clamps to multiple primitives', function() { + if (!scene.clampToHeightSupported) { + return; + } + + createRectangle(0.0, smallRectangle); + createRectangle(0.0, offscreenRectangle); + + var cartesians = [ + Cartographic.toCartesian(Rectangle.center(smallRectangle)), + Cartographic.toCartesian(Rectangle.center(offscreenRectangle)), + Cartesian3.fromRadians(-2.0, -2.0) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + expect(updatedCartesians[0]).toBeDefined(); + expect(updatedCartesians[1]).toBeDefined(); + expect(updatedCartesians[2]).toBeUndefined(); // No primitive occupies this space + }); + }); + + it('clamps to primitive', function() { + if (!scene.clampToHeightSupported) { + return; + } + + createSmallRectangle(0.0); + var cartesians = [ + Cartesian3.fromRadians(0.0, 0.0, 100000.0), + Cartesian3.fromRadians(-0.000001, -0.000001, 100000.0), + Cartesian3.fromRadians(0.0000005, 0.0000005, 100000.0) + ]; + var expectedCartesians = [ + Cartesian3.fromRadians(0.0, 0.0, 0.0), + Cartesian3.fromRadians(-0.000001, -0.000001, 0.0), + Cartesian3.fromRadians(0.0000005, 0.0000005, 0.0) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + var previousCartesian; + for (var i = 0; i < 3; ++i) { + expect(updatedCartesians[i]).toEqualEpsilon(expectedCartesians[i], CesiumMath.EPSILON5); + expect(updatedCartesians[i]).not.toEqual(previousCartesian); + previousCartesian = updatedCartesians[i]; + } + }); + }); + + it('clamps to top primitive', function() { + if (!scene.clampToHeightSupported) { + return; + } + createSmallRectangle(0.0); + createSmallRectangle(1.0); + var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; + scene.camera.setView({ destination : offscreenRectangle }); + return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0, 1.0); + expect(updatedCartesians[0]).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }); + }); + + it('excludes objects', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var rectangle1 = createRectangle(0.0, smallRectangle); + createRectangle(0.0, offscreenRectangle); + var rectangle3 = createRectangle(1.0, offscreenRectangle); + + var cartesians = [ + Cartographic.toCartesian(Rectangle.center(smallRectangle)), + Cartographic.toCartesian(Rectangle.center(offscreenRectangle)), + Cartesian3.fromRadians(-2.0, -2.0) + ]; + scene.camera.setView({ destination : offscreenRectangle }); + return clampToHeightMostDetailed(cartesians, [rectangle1, rectangle3]).then(function(updatedCartesians) { + var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); + expect(updatedCartesians[0]).toBeUndefined(); // This rectangle was excluded + expect(updatedCartesians[1]).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON2); + expect(updatedCartesians[2]).toBeUndefined(); // No primitive occupies this space + }); + }); + + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var rectangle = createSmallRectangle(0.0); + + var height = 100.0; + var cartesian = Cartesian3.fromRadians(0.0, 0.0, height); + var cartesians1 = [Cartesian3.clone(cartesian)]; + var cartesians2 = [Cartesian3.clone(cartesian)]; + var cartesians3 = [Cartesian3.clone(cartesian)]; + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : cartesian + }); + + scene.camera.setView({ destination : offscreenRectangle }); + return clampToHeightMostDetailed(cartesians1).then(function(updatedCartesians) { + expect(updatedCartesians[0]).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }).then(function() { + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + return clampToHeightMostDetailed(cartesians2).then(function(updatedCartesians) { + expect(updatedCartesians[0]).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }); + }).then(function() { + rectangle.show = false; + return clampToHeightMostDetailed(cartesians3).then(function(updatedCartesians) { + expect(updatedCartesians[0]).toBeUndefined(); + }); + }); + }); + + it('handles empty array', function() { + if (!scene.clampToHeightSupported) { + return; + } + + var cartesians = []; + return sampleHeightMostDetailed(cartesians).then(function(updatedCartesians) { + expect(updatedCartesians.length).toBe(0); + }); + }); + + it('throws if cartesians is undefined', function() { + if (!scene.clampToHeightSupported) { + return; + } + + expect(function() { + scene.clampToHeightMostDetailed(undefined); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in 2D', function() { + if (!scene.clampToHeightSupported) { + return; + } + + scene.morphTo2D(0.0); + var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; + expect(function() { + scene.clampToHeightMostDetailed(cartesians); + }).toThrowDeveloperError(); + }); + + it('throws if scene camera is in CV', function() { + if (!scene.clampToHeightSupported) { + return; + } + + scene.morphToColumbusView(0.0); + var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; + expect(function() { + scene.clampToHeightMostDetailed(cartesians); + }).toThrowDeveloperError(); + }); + + it('throws if clampToHeight is not supported', function() { + if (!scene.clampToHeightSupported) { + return; + } + // Disable extension + var depthTexture = scene.context._depthTexture; + scene.context._depthTexture = false; + + var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; + expect(function() { + scene.clampToHeightMostDetailed(cartesians); + }).toThrowDeveloperError(); + + // Re-enable extension + scene.context._depthTexture = depthTexture; + }); + }); + + it('calls multiple picking functions within the same frame', function() { + if (!scene.clampToHeightSupported || !scene.pickPositionSupported) { + return; + } + + createSmallRectangle(0.0); + var offscreenRectanglePrimitive = createRectangle(0.0, offscreenRectangle); + offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); + + scene.camera.setView({ destination : offscreenRectangle }); + + // Call render. Lays down depth for the pickPosition call + scene.renderForSpecs(); + + var cartographic = Cartographic.fromRadians(0.0, 0.0, 100000.0); + var cartesian = Cartographic.toCartesian(cartographic); + var cartesians = [Cartesian3.clone(cartesian)]; + var cartographics = [cartographic]; + + // Call clampToHeight + expect(scene).toClampToHeightAndCall(function(cartesian) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }, cartesian); + + // Call pickPosition + expect(scene).toPickPositionAndCall(function(cartesian) { + var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }); + + // Call clampToHeight again + expect(scene).toClampToHeightAndCall(function(cartesian) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }, cartesian); + + // Call pick + expect(scene).toPickPrimitive(offscreenRectanglePrimitive); + + // Call clampToHeight again + expect(scene).toClampToHeightAndCall(function(cartesian) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }, cartesian); + + // Call pickPosition on translucent primitive and returns undefined + offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 0.5); + scene.renderForSpecs(); + expect(scene).toPickPositionAndCall(function(cartesian) { + expect(cartesian).toBeUndefined(); + }); + + // Call clampToHeight again + expect(scene).toClampToHeightAndCall(function(cartesian) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }, cartesian); + + // Call pickPosition on translucent primitive with pickTranslucentDepth + scene.pickTranslucentDepth = true; + scene.renderForSpecs(); + expect(scene).toPickPositionAndCall(function(cartesian) { + var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }); + + // Mix async and sync requests + var results = []; + var completed = 0; + scene.clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { + results.push(updatedCartesians); + completed++; + }); + scene.sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { + results.push(updatedCartographics); + completed++; + }); + + // Call clampToHeight again + expect(scene).toClampToHeightAndCall(function(cartesian) { + var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); + expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); + }, cartesian); + + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed === 2; + }).then(function() { + expect(results[0][0].toBeDefined()); + expect(results[1][0].height.toBeDefined()); }); }); From 67d2a867a3456528acc8ea6192031cfa91022f1d Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 13:51:54 -0400 Subject: [PATCH 13/32] Remove updateAsync and use normal update --- Source/Scene/Cesium3DTileset.js | 86 +++++++++-------------- Source/Scene/Cesium3DTilesetTraversal.js | 2 + Source/Scene/PointCloudEyeDomeLighting.js | 2 +- Source/Scene/Scene.js | 8 +-- 4 files changed, 41 insertions(+), 57 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index c74c5bbc2781..356d7b0af8ec 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1656,7 +1656,10 @@ define([ function updateTiles(tileset, frameState, statistics) { tileset._styleEngine.applyStyle(tileset, frameState); - var isRender = frameState.passes.render; + var passes = frameState.passes; + var isRender = passes.render; + var isPick = passes.pick; + var isAsync = passes.async; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; var selectedTiles = tileset._selectedTiles; @@ -1692,8 +1695,7 @@ define([ tile.update(tileset, frameState); } - var lengthAfterUpdate = commandList.length; - var addedCommandsLength = lengthAfterUpdate - lengthBeforeUpdate; + var addedCommandsLength = commandList.length - lengthBeforeUpdate; tileset._backfaceCommands.trim(); @@ -1738,11 +1740,18 @@ define([ } } + if (isAsync && !isRender && !isPick) { + // Don't push commands for an async-only pass + commandList.length = numberOfInitialCommands; + } + // Number of commands added by each update above - statistics.numberOfCommands = (commandList.length - numberOfInitialCommands); + addedCommandsLength = commandList.length - numberOfInitialCommands; + statistics.numberOfCommands = addedCommandsLength; // Only run EDL if simple attenuation is on - if (tileset.pointCloudShading.attenuation && + if (isRender && + tileset.pointCloudShading.attenuation && tileset.pointCloudShading.eyeDomeLighting && (addedCommandsLength > 0)) { tileset._pointCloudEyeDomeLighting.update(frameState, numberOfInitialCommands, tileset.pointCloudShading); @@ -1848,11 +1857,11 @@ define([ */ Cesium3DTileset.prototype.update = function(frameState) { if (frameState.mode === SceneMode.MORPHING) { - return; + return false; } if (!this.show || !this.ready) { - return; + return false; } if (!defined(this._loadTimestamp)) { @@ -1878,7 +1887,6 @@ define([ var isRender = passes.render; var isPick = passes.pick; var isAsync = passes.async; - var outOfCore = isRender; var statistics = isAsync ? this._statisticsAsync : (isPick ? this._statisticsPick : this._statistics); var statisticsLast = isAsync ? this._statisticsLastAsync : (isPick ? this._statisticsLastPick : this._statisticsLastRender); @@ -1888,29 +1896,29 @@ define([ updateDynamicScreenSpaceError(this, frameState); } - if (outOfCore) { + if (isRender) { this._cache.reset(); } + var ready; + if (isAsync) { - Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); + ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); } else { - Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); + ready = Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); } - if (outOfCore) { + if (isRender || isAsync) { requestTiles(this, this._requestedTiles, true, statistics); + } + + if (isRender) { processTiles(this, frameState); } updateTiles(this, frameState, statistics); - // TODO - remove - if (passes.offscreen) { - printSelectedTiles(this); - } - - if (outOfCore) { + if (isRender) { unloadTiles(this, statistics); // Events are raised (added to the afterRender queue) here since promises @@ -1922,44 +1930,18 @@ define([ // Update last statistics Cesium3DTilesetStatistics.clone(statistics, statisticsLast); - if (statistics.selected !== 0) { - var credits = this._credits; - if (defined(credits)) { - var length = credits.length; - for (var i = 0; i < length; i++) { - frameState.creditDisplay.addCredit(credits[i]); + if (isRender) { + if (statistics.selected !== 0) { + var credits = this._credits; + if (defined(credits)) { + var length = credits.length; + for (var i = 0; i < length; i++) { + frameState.creditDisplay.addCredit(credits[i]); + } } } } - }; - - // TODO : remove - function printSelectedTiles(tileset) { - // console.log('Printing selected tiles:'); - // var selectedTiles = tileset._selectedTiles; - // var length = selectedTiles.length; - // for (var i = 0; i < length; ++i) { - // var tile = selectedTiles[i]; - // console.log(tile._header.content.uri); - // } - } - /** - * TODO private - who calls this? - * @param {FrameState} frameState The ray. - * @returns {Boolean} true when the primitive is ready - * - * @private - */ - Cesium3DTileset.prototype.updateAsync = function(frameState) { - var statistics = this._statisticsAsync; - var ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); - - // TODO - remove - if (ready) { - printSelectedTiles(this); - } - requestTiles(this, this._requestedTiles, false, statistics); // Tiles are requested now but processed in main update return ready; }; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 37cb47841a4b..77027e149128 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -84,6 +84,8 @@ define([ descendantTraversal.stack.trim(descendantTraversal.stackMaximumLength); selectionTraversal.stack.trim(selectionTraversal.stackMaximumLength); selectionTraversal.ancestorStack.trim(selectionTraversal.ancestorStackMaximumLength); + + return true; }; function executeBaseTraversal(tileset, root, statistics, frameState) { diff --git a/Source/Scene/PointCloudEyeDomeLighting.js b/Source/Scene/PointCloudEyeDomeLighting.js index 96ad5ff01b12..16d5daa5d46b 100644 --- a/Source/Scene/PointCloudEyeDomeLighting.js +++ b/Source/Scene/PointCloudEyeDomeLighting.js @@ -236,7 +236,7 @@ define([ } PointCloudEyeDomeLighting.prototype.update = function(frameState, commandStart, pointCloudShading) { - if (!isSupported(frameState.context) || !frameState.passes.render) { + if (!isSupported(frameState.context)) { return; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 11db7e271e94..a1ebb9032a04 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3691,9 +3691,9 @@ define([ var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { var primitive = primitives[i]; - if (scene.primitives.contains(primitive)) { - // Only update primitives that are still contained in the scene's primitive collection - ready = primitive.updateAsync(frameState) && ready; + if (scene.primitives.contains(primitive) && primitive.show) { + // Only update primitives that are still contained in the scene's primitive collection and are still visible + ready = primitive.update(frameState) && ready; } } @@ -3724,7 +3724,7 @@ define([ var length = primitives.length; for (var i = 0; i < length; ++i) { var primitive = primitives.get(i); - if (primitive instanceof Cesium3DTileset) { + if ((primitive instanceof Cesium3DTileset) && primitive.show) { if (!defined(objectsToExclude) || objectsToExclude.indexOf(primitive) === -1) { asyncPrimitives.push(primitive); } From 26a1a665a2fb68a2f685ac906ae2c57bffe60113 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 13:53:42 -0400 Subject: [PATCH 14/32] Fix tests --- Specs/Scene/PickSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 077733111ec0..582b8ecac1a6 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -2153,8 +2153,8 @@ defineSuite([ scene.render(); return completed === 2; }).then(function() { - expect(results[0][0].toBeDefined()); - expect(results[1][0].height.toBeDefined()); + expect(results[0][0]).toBeDefined(); + expect(results[1][0].height).toBeDefined(); }); }); From db53e33332e49fd6b4c1da7a3899adf5c2d6bd89 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 14:42:54 -0400 Subject: [PATCH 15/32] Minor Scene edits --- Source/Scene/Scene.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index a1ebb9032a04..26e7ea743410 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -917,7 +917,7 @@ define([ }, /** - * Returns true if the {@link clampToHeight} and {@link Scene#clampToHeightMostDetailed} functions are supported. + * Returns true if the {@link Scene#clampToHeight} and {@link Scene#clampToHeightMostDetailed} functions are supported. * @memberof Scene.prototype * * @type {Boolean} @@ -3332,7 +3332,6 @@ define([ this._jobScheduler.disableThisFrame(); - // Update with previous frame's number and time, assuming that render is called before picking. updateFrameState(this); frameState.cullingVolume = getPickCullingVolume(this, drawingBufferPosition, rectangleWidth, rectangleHeight, viewport); frameState.invertClassification = false; @@ -3680,7 +3679,6 @@ define([ updateCameraFromRay(ray, view.camera); - // Update with previous frame's number and time, assuming that render is called first. updateFrameState(scene); frameState.passes.offscreen = true; frameState.passes.async = true; @@ -3766,7 +3764,6 @@ define([ scene._jobScheduler.disableThisFrame(); - // Update with previous frame's number and time, assuming that render is called before picking. updateFrameState(scene); frameState.invertClassification = false; frameState.passes.pick = true; From a8710ff099d80aa3c9532f29202b4e8c3776d5e9 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 14:43:05 -0400 Subject: [PATCH 16/32] Undo statistics changes in Cesium3DTileset --- Source/Scene/Cesium3DTileset.js | 91 +++++++++---------- Source/Scene/Cesium3DTilesetCache.js | 8 +- .../Cesium3DTilesetOffscreenTraversal.js | 8 +- Source/Scene/Cesium3DTilesetTraversal.js | 68 +++++++------- 4 files changed, 87 insertions(+), 88 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 7d82ff0b1b4a..c706dbce5b6c 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -187,8 +187,6 @@ define([ this._timeSinceLoad = 0.0; this._extras = undefined; - this._offscreenCache = new Cesium3DTilesetCache(); - this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true); this._allTilesAdditive = true; @@ -204,8 +202,6 @@ define([ this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY); this._statistics = new Cesium3DTilesetStatistics(); - this._statisticsPick = new Cesium3DTilesetStatistics(); - this._statisticsAsync = new Cesium3DTilesetStatistics(); this._statisticsLastRender = new Cesium3DTilesetStatistics(); this._statisticsLastPick = new Cesium3DTilesetStatistics(); this._statisticsLastAsync = new Cesium3DTilesetStatistics(); @@ -1449,11 +1445,12 @@ define([ /////////////////////////////////////////////////////////////////////////// - function requestContent(tileset, tile, statistics) { + function requestContent(tileset, tile) { if (tile.hasEmptyContent) { return; } + var statistics = tileset._statistics; var expired = tile.contentExpired; var requested = tile.requestContent(); @@ -1464,7 +1461,7 @@ define([ if (expired) { if (tile.hasTilesetContent) { - destroySubtree(tileset, tile, statistics); + destroySubtree(tileset, tile); } else { statistics.decrementLoadCounts(tile.content); --statistics.numberOfTilesWithContentReady; @@ -1473,43 +1470,42 @@ define([ ++statistics.numberOfPendingRequests; - tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile, statistics)); - tile.contentReadyPromise.then(handleTileSuccess(tileset, tile, statistics)).otherwise(handleTileFailure(tileset, tile, statistics)); + tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile)); + tile.contentReadyPromise.then(handleTileSuccess(tileset, tile)).otherwise(handleTileFailure(tileset, tile)); } function sortRequestByPriority(a, b) { return a._priority - b._priority; } - function requestTiles(tileset, requestedTiles, sortByPriority, statistics) { - if (sortByPriority) { - // Sort requests by priority before making any requests. - // This makes it less likely that requests will be cancelled after being issued. - requestedTiles.sort(sortRequestByPriority); - } + function requestTiles(tileset) { + // Sort requests by priority before making any requests. + // This makes it less likely that requests will be cancelled after being issued. + var requestedTiles = tileset._requestedTiles; var length = requestedTiles.length; + requestedTiles.sort(sortRequestByPriority); for (var i = 0; i < length; ++i) { - requestContent(tileset, requestedTiles[i], statistics); + requestContent(tileset, requestedTiles[i]); } } - function addToProcessingQueue(tileset, tile, statistics) { + function addToProcessingQueue(tileset, tile) { return function() { tileset._processingQueue.push(tile); - --statistics.numberOfPendingRequests; - ++statistics.numberOfTilesProcessing; + --tileset._statistics.numberOfPendingRequests; + ++tileset._statistics.numberOfTilesProcessing; }; } - function handleTileFailure(tileset, tile, statistics) { + function handleTileFailure(tileset, tile) { return function(error) { if (tileset._processingQueue.indexOf(tile) >= 0) { // Failed during processing - --statistics.numberOfTilesProcessing; + --tileset._statistics.numberOfTilesProcessing; } else { // Failed when making request - --statistics.numberOfPendingRequests; + --tileset._statistics.numberOfPendingRequests; } var url = tile._contentResource.url; @@ -1526,15 +1522,15 @@ define([ }; } - function handleTileSuccess(tileset, tile, statistics) { + function handleTileSuccess(tileset, tile) { return function() { - --statistics.numberOfTilesProcessing; + --tileset._statistics.numberOfTilesProcessing; if (!tile.hasTilesetContent) { // RESEARCH_IDEA: ability to unload tiles (without content) for an // external tileset when all the tiles are unloaded. - statistics.incrementLoadCounts(tile.content); - ++statistics.numberOfTilesWithContentReady; + tileset._statistics.incrementLoadCounts(tile.content); + ++tileset._statistics.numberOfTilesWithContentReady; // Add to the tile cache. Previously expired tiles are already in the cache and won't get re-added. tileset._cache.add(tile); @@ -1693,9 +1689,10 @@ define([ pass : Pass.CESIUM_3D_TILE }); - function updateTiles(tileset, frameState, statistics) { + function updateTiles(tileset, frameState) { tileset._styleEngine.applyStyle(tileset, frameState); + var statistics = tileset._statistics; var passes = frameState.passes; var isRender = passes.render; var isPick = passes.pick; @@ -1811,7 +1808,7 @@ define([ var scratchStack = []; - function destroySubtree(tileset, tile, statistics) { + function destroySubtree(tileset, tile) { var root = tile; var stack = scratchStack; stack.push(tile); @@ -1823,27 +1820,27 @@ define([ stack.push(children[i]); } if (tile !== root) { - destroyTile(tileset, tile, statistics); - --statistics.numberOfTilesTotal; + destroyTile(tileset, tile); + --tileset._statistics.numberOfTilesTotal; } } root.children = []; } - function unloadTile(tileset, tile, statistics) { + function unloadTile(tileset, tile) { tileset.tileUnload.raiseEvent(tile); - statistics.decrementLoadCounts(tile.content); - --statistics.numberOfTilesWithContentReady; + tileset._statistics.decrementLoadCounts(tile.content); + --tileset._statistics.numberOfTilesWithContentReady; tile.unloadContent(); } - function destroyTile(tileset, tile, statistics) { - tileset._cache.unloadTile(tileset, tile, statistics, unloadTile); + function destroyTile(tileset, tile) { + tileset._cache.unloadTile(tileset, tile, unloadTile); tile.destroy(); } - function unloadTiles(tileset, statistics) { - tileset._cache.unloadTiles(tileset, statistics, unloadTile); + function unloadTiles(tileset) { + tileset._cache.unloadTiles(tileset, unloadTile); } /** @@ -1861,7 +1858,9 @@ define([ /////////////////////////////////////////////////////////////////////////// - function raiseLoadProgressEvent(tileset, frameState, statistics, statisticsLast) { + function raiseLoadProgressEvent(tileset, frameState) { + var statistics = tileset._statistics; + var statisticsLast = tileset._statisticsLastRender; var numberOfPendingRequests = statistics.numberOfPendingRequests; var numberOfTilesProcessing = statistics.numberOfTilesProcessing; var lastNumberOfPendingRequest = statisticsLast.numberOfPendingRequests; @@ -1921,14 +1920,14 @@ define([ this._skipLevelOfDetail = this.skipLevelOfDetail && !defined(this._classificationType) && !this._disableSkipLevelOfDetail && !this._allTilesAdditive; - // Do not do out-of-core operations (new content requests, cache removal, - // process new tiles) during the pick pass. + // Do out-of-core operations (new content requests, cache removal, + // process new tiles) only during the render pass. var passes = frameState.passes; var isRender = passes.render; var isPick = passes.pick; var isAsync = passes.async; - var statistics = isAsync ? this._statisticsAsync : (isPick ? this._statisticsPick : this._statistics); + var statistics = this._statistics; var statisticsLast = isAsync ? this._statisticsLastAsync : (isPick ? this._statisticsLastPick : this._statisticsLastRender); statistics.clear(); @@ -1943,28 +1942,28 @@ define([ var ready; if (isAsync) { - ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, statistics, frameState); + ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, frameState); } else { - ready = Cesium3DTilesetTraversal.selectTiles(this, statistics, frameState); + ready = Cesium3DTilesetTraversal.selectTiles(this, frameState); } if (isRender || isAsync) { - requestTiles(this, this._requestedTiles, true, statistics); + requestTiles(this); } if (isRender) { processTiles(this, frameState); } - updateTiles(this, frameState, statistics); + updateTiles(this, frameState); if (isRender) { - unloadTiles(this, statistics); + unloadTiles(this); // Events are raised (added to the afterRender queue) here since promises // may resolve outside of the update loop that then raise events, e.g., // model's readyPromise. - raiseLoadProgressEvent(this, frameState, statistics, statisticsLast); + raiseLoadProgressEvent(this, frameState); } // Update last statistics diff --git a/Source/Scene/Cesium3DTilesetCache.js b/Source/Scene/Cesium3DTilesetCache.js index f159c90d1470..848a41da2529 100644 --- a/Source/Scene/Cesium3DTilesetCache.js +++ b/Source/Scene/Cesium3DTilesetCache.js @@ -39,7 +39,7 @@ define([ } }; - Cesium3DTilesetCache.prototype.unloadTile = function(tileset, tile, statistics, unloadCallback) { + Cesium3DTilesetCache.prototype.unloadTile = function(tileset, tile, unloadCallback) { var node = tile.cacheNode; if (!defined(node)) { return; @@ -47,10 +47,10 @@ define([ this._list.remove(node); tile.cacheNode = undefined; - unloadCallback(tileset, tile, statistics); + unloadCallback(tileset, tile); }; - Cesium3DTilesetCache.prototype.unloadTiles = function(tileset, statistics, unloadCallback) { + Cesium3DTilesetCache.prototype.unloadTiles = function(tileset, unloadCallback) { var trimTiles = this._trimTiles; this._trimTiles = false; @@ -67,7 +67,7 @@ define([ while ((node !== sentinel) && ((tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes) || trimTiles)) { var tile = node.item; node = node.next; - this.unloadTile(tileset, tile, statistics, unloadCallback); + this.unloadTile(tileset, tile, unloadCallback); } }; diff --git a/Source/Scene/Cesium3DTilesetOffscreenTraversal.js b/Source/Scene/Cesium3DTilesetOffscreenTraversal.js index 5e4c1e64a00e..fcc32ab53c0b 100644 --- a/Source/Scene/Cesium3DTilesetOffscreenTraversal.js +++ b/Source/Scene/Cesium3DTilesetOffscreenTraversal.js @@ -19,7 +19,7 @@ define([ stackMaximumLength : 0 }; - Cesium3DTilesetOffscreenTraversal.selectTiles = function(tileset, statistics, frameState) { + Cesium3DTilesetOffscreenTraversal.selectTiles = function(tileset, frameState) { tileset._selectedTiles.length = 0; tileset._requestedTiles.length = 0; tileset._hasMixedContent = false; @@ -62,7 +62,7 @@ define([ } } - visitTile(statistics); + visitTile(tileset); touchTile(tileset, tile); } @@ -124,8 +124,8 @@ define([ tileset._cache.touch(tile); } - function visitTile(statistics) { - ++statistics.visited; + function visitTile(tileset) { + ++tileset.statistics.visited; } function selectDesiredTile(tileset, tile, frameState) { diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 77027e149128..e9c0a0e962fe 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -46,7 +46,7 @@ define([ var descendantSelectionDepth = 2; - Cesium3DTilesetTraversal.selectTiles = function(tileset, statistics, frameState) { + Cesium3DTilesetTraversal.selectTiles = function(tileset, frameState) { tileset._requestedTiles.length = 0; if (tileset.debugFreezeFrame) { @@ -59,7 +59,7 @@ define([ tileset._hasMixedContent = false; var root = tileset.root; - updateTile(tileset, root, statistics, frameState); + updateTile(tileset, root, frameState); // The root tile is not visible if (!isVisible(root)) { @@ -72,11 +72,11 @@ define([ } if (!skipLevelOfDetail(tileset)) { - executeBaseTraversal(tileset, root, statistics, frameState); + executeBaseTraversal(tileset, root, frameState); } else if (tileset.immediatelyLoadDesiredLevelOfDetail) { - executeSkipTraversal(tileset, root, statistics, frameState); + executeSkipTraversal(tileset, root, frameState); } else { - executeBaseAndSkipTraversal(tileset, root, statistics, frameState); + executeBaseAndSkipTraversal(tileset, root, frameState); } traversal.stack.trim(traversal.stackMaximumLength); @@ -88,23 +88,23 @@ define([ return true; }; - function executeBaseTraversal(tileset, root, statistics, frameState) { + function executeBaseTraversal(tileset, root, frameState) { var baseScreenSpaceError = tileset._maximumScreenSpaceError; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); } - function executeSkipTraversal(tileset, root, statistics, frameState) { + function executeSkipTraversal(tileset, root, frameState) { var baseScreenSpaceError = Number.MAX_VALUE; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); traverseAndSelect(tileset, root, frameState); } - function executeBaseAndSkipTraversal(tileset, root, statistics, frameState) { + function executeBaseAndSkipTraversal(tileset, root, frameState) { var baseScreenSpaceError = Math.max(tileset.baseScreenSpaceError, tileset.maximumScreenSpaceError); var maximumScreenSpaceError = tileset.maximumScreenSpaceError; - executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState); + executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState); traverseAndSelect(tileset, root, frameState); } @@ -133,7 +133,7 @@ define([ } } - function selectDescendants(tileset, root, statistics, frameState) { + function selectDescendants(tileset, root, frameState) { var stack = descendantTraversal.stack; stack.push(root); while (stack.length > 0) { @@ -145,7 +145,7 @@ define([ var child = children[i]; if (isVisible(child)) { if (child.contentAvailable) { - updateTile(tileset, child, statistics, frameState); + updateTile(tileset, child, frameState); touchTile(tileset, child, frameState); selectTile(tileset, child, frameState); } else if (child._depth - root._depth < descendantSelectionDepth) { @@ -157,7 +157,7 @@ define([ } } - function selectDesiredTile(tileset, tile, statistics, frameState) { + function selectDesiredTile(tileset, tile, frameState) { if (!skipLevelOfDetail(tileset)) { if (tile.contentAvailable) { // The tile can be selected right away and does not require traverseAndSelect @@ -174,12 +174,12 @@ define([ } else { // If no ancestors are ready traverse down and select tiles to minimize empty regions. // This happens often for immediatelyLoadDesiredLevelOfDetail where parent tiles are not necessarily loaded before zooming out. - selectDescendants(tileset, tile, statistics, frameState); + selectDescendants(tileset, tile, frameState); } } - function visitTile(tileset, tile, statistics, frameState) { - ++statistics.visited; + function visitTile(tileset, tile, frameState) { + ++tileset._statistics.visited; tile._visitedFrame = frameState.frameNumber; } @@ -249,7 +249,7 @@ define([ return tile.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError; } - function updateTileVisibility(tileset, tile, statistics, frameState) { + function updateTileVisibility(tileset, tile, frameState) { updateVisibility(tileset, tile, frameState); if (!isVisible(tile)) { @@ -262,7 +262,7 @@ define([ // The root tile may be culled by the children bounds optimization in which // case this tile should also be culled. var child = tile.children[0]; - updateTileVisibility(tileset, child, statistics, frameState); + updateTileVisibility(tileset, child, frameState); tile._visible = child._visible; return; } @@ -277,15 +277,15 @@ define([ var useOptimization = tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint.USE_OPTIMIZATION; if (replace && useOptimization && hasChildren) { if (!anyChildrenVisible(tileset, tile, frameState)) { - ++statistics.numberOfTilesCulledWithChildrenUnion; + ++tileset._statistics.numberOfTilesCulledWithChildrenUnion; tile._visible = false; return; } } } - function updateTile(tileset, tile, statistics, frameState) { - updateTileVisibility(tileset, tile, statistics, frameState); + function updateTile(tileset, tile, frameState) { + updateTileVisibility(tileset, tile, frameState); tile.updateExpiration(); tile._shouldSelect = false; @@ -329,14 +329,14 @@ define([ return b._distanceToCamera - a._distanceToCamera; } - function updateAndPushChildren(tileset, tile, stack, statistics, frameState) { + function updateAndPushChildren(tileset, tile, stack, frameState) { var i; var replace = tile.refine === Cesium3DTileRefine.REPLACE; var children = tile.children; var length = children.length; for (i = 0; i < length; ++i) { - updateTile(tileset, children[i], statistics, frameState); + updateTile(tileset, children[i], frameState); } // Sort by distance to take advantage of early Z and reduce artifacts for skipLevelOfDetail @@ -364,7 +364,7 @@ define([ if (!child._inRequestVolume) { childRefines = false; } else if (hasEmptyContent(child)) { - childRefines = executeEmptyTraversal(tileset, child, statistics, frameState); + childRefines = executeEmptyTraversal(tileset, child, frameState); } else { childRefines = child.contentAvailable; } @@ -409,7 +409,7 @@ define([ return tile._screenSpaceError > tileset._maximumScreenSpaceError; } - function executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, statistics, frameState) { + function executeTraversal(tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState) { // Depth-first traversal that traverses all visible tiles and marks tiles for selection. // If skipLevelOfDetail is off then a tile does not refine until all children are loaded. // This is the traditional replacement refinement approach and is called the base traversal. @@ -431,7 +431,7 @@ define([ var refines = false; if (canTraverse(tileset, tile)) { - refines = updateAndPushChildren(tileset, tile, stack, statistics, frameState) && parentRefines; + refines = updateAndPushChildren(tileset, tile, stack, frameState) && parentRefines; } var stoppedRefining = !refines && parentRefines; @@ -443,11 +443,11 @@ define([ addEmptyTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); if (stoppedRefining) { - selectDesiredTile(tileset, tile, statistics, frameState); + selectDesiredTile(tileset, tile, frameState); } } else if (add) { // Additive tiles are always loaded and selected - selectDesiredTile(tileset, tile, statistics, frameState); + selectDesiredTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); } else if (replace) { if (baseTraversal) { @@ -455,11 +455,11 @@ define([ // Select tiles that can't refine further loadTile(tileset, tile, frameState); if (stoppedRefining) { - selectDesiredTile(tileset, tile, statistics, frameState); + selectDesiredTile(tileset, tile, frameState); } } else if (stoppedRefining) { // In skip traversal, load and select tiles that can't refine further - selectDesiredTile(tileset, tile, statistics, frameState); + selectDesiredTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); } else if (reachedSkippingThreshold(tileset, tile)) { // In skip traversal, load tiles that aren't skipped. In practice roughly half the tiles stay unloaded. @@ -467,14 +467,14 @@ define([ } } - visitTile(tileset, tile, statistics, frameState); + visitTile(tileset, tile, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass which may use a different camera } } - function executeEmptyTraversal(tileset, root, statistics, frameState) { + function executeEmptyTraversal(tileset, root, frameState) { // Depth-first traversal that checks if all nearest descendants with content are loaded. Ignores visibility. var allDescendantsLoaded = true; var stack = emptyTraversal.stack; @@ -496,7 +496,7 @@ define([ allDescendantsLoaded = false; } - updateTile(tileset, tile, statistics, frameState); + updateTile(tileset, tile, frameState); if (!isVisible(tile)) { // Load tiles that aren't visible since they are still needed for the parent to refine loadTile(tileset, tile, frameState); From ebd65d943a95f47ad10a229cfb6514f0f5252c67 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 15:45:29 -0400 Subject: [PATCH 17/32] Cleanup and fixes from #7115 --- CHANGES.md | 2 + Source/Core/Ray.js | 35 ++-- Source/Scene/Cesium3DTile.js | 69 ++++++++ Source/Scene/Cesium3DTileset.js | 82 ++++----- Source/Scene/Cesium3DTilesetTraversal.js | 80 +-------- Source/Scene/PointCloudEyeDomeLighting.js | 4 +- Source/Scene/Scene.js | 162 ++++++++++++------ .../Cesium3DTilesInspectorViewModel.js | 2 +- Specs/Core/RaySpec.js | 21 +++ Specs/Scene/Cesium3DTilesetSpec.js | 4 +- Specs/Scene/PickSpec.js | 63 ++++++- 11 files changed, 340 insertions(+), 184 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 40ce73e6a6d2..34d76c63947a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ Change Log ### 1.51 - 2018-11-01 ##### Additions :tada: +* Added `Ray.clone`. [#7115](https://github.com/AnalyticalGraphicsInc/cesium/pull/7115) * Shrink minified and gzipped Cesium.js by 27 KB (~3.7%) by delay loading seldom-used third-party dependencies. [#7140](https://github.com/AnalyticalGraphicsInc/cesium/pull/7140) * Added WMS-T (time) support in WebMapServiceImageryProvider [#2581](https://github.com/AnalyticalGraphicsInc/cesium/issues/2581) * Added `Transforms.fixedFrameToHeadingPitchRoll`, a helper function for extracting a `HeadingPitchRoll` from a fixed frame transform [#7164](https://github.com/AnalyticalGraphicsInc/cesium/pull/7164) @@ -13,6 +14,7 @@ Change Log ##### Fixes :wrench: * Fixed an issue where `pickPosition` would return incorrect results when called after `sampleHeight` or `clampToHeight`. [#7113](https://github.com/AnalyticalGraphicsInc/cesium/pull/7113) +* Fixed an issue where `sampleHeight` and `clampToHeight` would crash if picking a primitive that doesn't write depth. [#7120](https://github.com/AnalyticalGraphicsInc/cesium/issues/7120) * Fixed crash when updating polyline attributes twice in one frame [#7155](https://github.com/AnalyticalGraphicsInc/cesium/pull/7155) * Fixed a crash when using `BingMapsGeocoderService` [#7143](https://github.com/AnalyticalGraphicsInc/cesium/issues/7143) diff --git a/Source/Core/Ray.js b/Source/Core/Ray.js index e8e202f3bbeb..3aab72cb5625 100644 --- a/Source/Core/Ray.js +++ b/Source/Core/Ray.js @@ -1,13 +1,13 @@ define([ './Cartesian3', + './Check', './defaultValue', - './defined', - './DeveloperError' + './defined' ], function( Cartesian3, + Check, defaultValue, - defined, - DeveloperError) { + defined) { 'use strict'; /** @@ -38,6 +38,25 @@ define([ this.direction = direction; } + /** + * Duplicates a Ray instance. + * + * @param {Ray} ray The ray to duplicate. + * @param {Ray} [result] The object onto which to store the result. + * @returns {Ray} The modified result parameter or a new Ray instance if one was not provided. (Returns undefined if ray is undefined) + */ + Ray.clone = function(ray, result) { + if (!defined(ray)) { + return undefined; + } + if (!defined(result)) { + return new Ray(ray.origin, ray.direction); + } + result.origin = ray.origin; + result.direction = ray.direction; + return result; + }; + /** * Computes the point along the ray given by r(t) = o + t*d, * where o is the origin of the ray and d is the direction. @@ -54,12 +73,8 @@ define([ */ Ray.getPoint = function(ray, t, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(ray)){ - throw new DeveloperError('ray is requred'); - } - if (typeof t !== 'number') { - throw new DeveloperError('t is a required number'); - } + Check.typeOf.object('ray', ray); + Check.typeOf.number('t', t); //>>includeEnd('debug'); if (!defined(result)) { diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 113fc0d7acc2..1dd025dc5e65 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -17,6 +17,7 @@ define([ '../Core/Matrix3', '../Core/Matrix4', '../Core/OrientedBoundingBox', + '../Core/OrthographicFrustum', '../Core/Rectangle', '../Core/Request', '../Core/RequestScheduler', @@ -54,6 +55,7 @@ define([ Matrix3, Matrix4, OrientedBoundingBox, + OrthographicFrustum, Rectangle, Request, RequestScheduler, @@ -607,6 +609,63 @@ define([ var scratchJulianDate = new JulianDate(); + /** + * Get the tile's screen space error. + * + * @private + */ + Cesium3DTile.prototype.getScreenSpaceError = function(frameState, useParentGeometricError) { + var tileset = this._tileset; + var parentGeometricError = defined(this.parent) ? this.parent.geometricError : tileset._geometricError; + var geometricError = useParentGeometricError ? parentGeometricError : this.geometricError; + if (geometricError === 0.0) { + // Leaf tiles do not have any error so save the computation + return 0.0; + } + var camera = frameState.camera; + var frustum = camera.frustum; + var context = frameState.context; + var width = context.drawingBufferWidth; + var height = context.drawingBufferHeight; + var error; + if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { + if (defined(frustum._offCenterFrustum)) { + frustum = frustum._offCenterFrustum; + } + var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); + error = geometricError / pixelSize; + } else { + // Avoid divide by zero when viewer is inside the tile + var distance = Math.max(this._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; + }; + /** + * Update the tile's visibility. + * + * @private + */ + Cesium3DTile.prototype.updateVisibility = function(frameState) { + var parent = this.parent; + var parentTransform = defined(parent) ? parent.computedTransform : this._tileset.modelMatrix; + var parentVisibilityPlaneMask = defined(parent) ? parent._visibilityPlaneMask : CullingVolume.MASK_INDETERMINATE; + this.updateTransform(parentTransform); + this._distanceToCamera = this.distanceToTile(frameState); + this._centerZDepth = this.distanceToTileCenter(frameState); + this._screenSpaceError = this.getScreenSpaceError(frameState); + this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask); // Use parent's plane mask to speed up visibility test + this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; + this._inRequestVolume = this.insideViewerRequestVolume(frameState); + }; + /** * Update whether the tile has expired. * @@ -846,6 +905,12 @@ define([ return Intersect.INSIDE; } + if (this._visibilityPlaneMask === CullingVolume.MASK_INSIDE) { + // The tile's bounding volume is completely inside the culling volume so + // the content bounding volume must also be inside. + return Intersect.INSIDE; + } + // PERFORMANCE_IDEA: is it possible to burn less CPU on this test since we know the // tile's (not the content's) bounding volume intersects the culling volume? var cullingVolume = frameState.cullingVolume; @@ -1055,6 +1120,10 @@ define([ }; function applyDebugSettings(tile, tileset, frameState) { + if (!frameState.passes.render) { + return; + } + var hasContentBoundingVolume = defined(tile._header.content) && defined(tile._header.content.boundingVolume); var empty = tile.hasEmptyContent || tile.hasTilesetContent; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 154d24a78c45..cb9839b98190 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -200,7 +200,7 @@ define([ this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY); this._statistics = new Cesium3DTilesetStatistics(); - this._statisticsLastColor = new Cesium3DTilesetStatistics(); + this._statisticsLastRender = new Cesium3DTilesetStatistics(); this._statisticsLastPick = new Cesium3DTilesetStatistics(); this._tilesLoaded = false; @@ -1461,7 +1461,7 @@ define([ destroySubtree(tileset, tile); } else { statistics.decrementLoadCounts(tile.content); - --tileset._statistics.numberOfTilesWithContentReady; + --statistics.numberOfTilesWithContentReady; } } @@ -1689,6 +1689,8 @@ define([ function updateTiles(tileset, frameState) { tileset._styleEngine.applyStyle(tileset, frameState); + var passes = frameState.passes; + var isRender = passes.render; var statistics = tileset._statistics; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; @@ -1713,7 +1715,9 @@ define([ tile = selectedTiles[i]; // Raise the tileVisible event before update in case the tileVisible event // handler makes changes that update needs to apply to WebGL resources - tileVisible.raiseEvent(tile); + if (isRender) { + tileVisible.raiseEvent(tile); + } tile.update(tileset, frameState); statistics.incrementSelectionCounts(tile.content); ++statistics.selected; @@ -1723,8 +1727,7 @@ define([ tile.update(tileset, frameState); } - var lengthAfterUpdate = commandList.length; - var addedCommandsLength = lengthAfterUpdate - lengthBeforeUpdate; + var addedCommandsLength = commandList.length - lengthBeforeUpdate; tileset._backfaceCommands.trim(); @@ -1770,22 +1773,26 @@ define([ } // Number of commands added by each update above - statistics.numberOfCommands = (commandList.length - numberOfInitialCommands); + addedCommandsLength = commandList.length - numberOfInitialCommands; + statistics.numberOfCommands = addedCommandsLength; // Only run EDL if simple attenuation is on - if (tileset.pointCloudShading.attenuation && + if (isRender && + tileset.pointCloudShading.attenuation && tileset.pointCloudShading.eyeDomeLighting && (addedCommandsLength > 0)) { tileset._pointCloudEyeDomeLighting.update(frameState, numberOfInitialCommands, tileset.pointCloudShading); } - if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl) { - if (!defined(tileset._tileDebugLabels)) { - tileset._tileDebugLabels = new LabelCollection(); + if (isRender) { + if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl) { + if (!defined(tileset._tileDebugLabels)) { + tileset._tileDebugLabels = new LabelCollection(); + } + updateTileDebugLabels(tileset, frameState); + } else { + tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); } - updateTileDebugLabels(tileset, frameState); - } else { - tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); } } @@ -1793,7 +1800,6 @@ define([ function destroySubtree(tileset, tile) { var root = tile; - var statistics = tileset._statistics; var stack = scratchStack; stack.push(tile); while (stack.length > 0) { @@ -1805,7 +1811,7 @@ define([ } if (tile !== root) { destroyTile(tileset, tile); - --statistics.numberOfTilesTotal; + --tileset._statistics.numberOfTilesTotal; } } root.children = []; @@ -1844,7 +1850,7 @@ define([ function raiseLoadProgressEvent(tileset, frameState) { var statistics = tileset._statistics; - var statisticsLast = tileset._statisticsLastColor; + var statisticsLast = tileset._statisticsLastRender; var numberOfPendingRequests = statistics.numberOfPendingRequests; var numberOfTilesProcessing = statistics.numberOfTilesProcessing; var lastNumberOfPendingRequest = statisticsLast.numberOfPendingRequests; @@ -1904,12 +1910,11 @@ define([ this._skipLevelOfDetail = this.skipLevelOfDetail && !defined(this._classificationType) && !this._disableSkipLevelOfDetail && !this._allTilesAdditive; - // Do not do out-of-core operations (new content requests, cache removal, - // process new tiles) during the pick pass. + // Do out-of-core operations (new content requests, cache removal, + // process new tiles) only during the render pass. var passes = frameState.passes; var isRender = passes.render; var isPick = passes.pick; - var outOfCore = isRender; var statistics = this._statistics; statistics.clear(); @@ -1918,42 +1923,41 @@ define([ updateDynamicScreenSpaceError(this, frameState); } - if (outOfCore) { + if (isRender) { this._cache.reset(); } - this._requestedTiles.length = 0; Cesium3DTilesetTraversal.selectTiles(this, frameState); - if (outOfCore) { + if (isRender) { requestTiles(this); processTiles(this, frameState); } updateTiles(this, frameState); - if (outOfCore) { + if (isRender) { unloadTiles(this); - } - // Events are raised (added to the afterRender queue) here since promises - // may resolve outside of the update loop that then raise events, e.g., - // model's readyPromise. - raiseLoadProgressEvent(this, frameState); - - // Update last statistics - var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastColor; - Cesium3DTilesetStatistics.clone(statistics, statisticsLast); - - if (statistics.selected !== 0) { - var credits = this._credits; - if (defined(credits)) { - var length = credits.length; - for (var i = 0; i < length; i++) { - frameState.creditDisplay.addCredit(credits[i]); + // Events are raised (added to the afterRender queue) here since promises + // may resolve outside of the update loop that then raise events, e.g., + // model's readyPromise. + raiseLoadProgressEvent(this, frameState); + + if (statistics.selected !== 0) { + var credits = this._credits; + if (defined(credits)) { + var length = credits.length; + for (var i = 0; i < length; i++) { + frameState.creditDisplay.addCredit(credits[i]); + } } } } + + // Update last statistics + var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastRender; + Cesium3DTilesetStatistics.clone(statistics, statisticsLast); }; /** diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 332a729a684a..23c4e52a018f 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -1,27 +1,15 @@ define([ - '../Core/CullingVolume', - '../Core/defaultValue', '../Core/defined', - '../Core/freezeObject', '../Core/Intersect', '../Core/ManagedArray', - '../Core/Math', - '../Core/OrthographicFrustum', './Cesium3DTileOptimizationHint', - './Cesium3DTileRefine', - './SceneMode' + './Cesium3DTileRefine' ], function( - CullingVolume, - defaultValue, defined, - freezeObject, Intersect, ManagedArray, - CesiumMath, - OrthographicFrustum, Cesium3DTileOptimizationHint, - Cesium3DTileRefine, - SceneMode) { + Cesium3DTileRefine) { 'use strict'; /** @@ -59,6 +47,8 @@ define([ var descendantSelectionDepth = 2; Cesium3DTilesetTraversal.selectTiles = function(tileset, frameState) { + tileset._requestedTiles.length = 0; + if (tileset.debugFreezeFrame) { return; } @@ -77,7 +67,7 @@ define([ } // The tileset doesn't meet the SSE requirement, therefore the tree does not need to be rendered - if (getScreenSpaceError(tileset, tileset._geometricError, root, frameState) <= tileset._maximumScreenSpaceError) { + if (root.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError) { return; } @@ -124,13 +114,8 @@ define([ tileset._emptyTiles.push(tile); } - function contentVisible(tile, frameState) { - return (tile._visibilityPlaneMask === CullingVolume.MASK_INSIDE) || - (tile.contentVisibility(frameState) !== Intersect.OUTSIDE); - } - function selectTile(tileset, tile, frameState) { - if (contentVisible(tile, frameState)) { + if (tile.contentVisibility(frameState) !== Intersect.OUTSIDE) { var tileContent = tile.content; if (tileContent.featurePropertiesDirty) { // A feature's property in this tile changed, the tile needs to be re-styled. @@ -229,42 +214,6 @@ define([ } } - function getScreenSpaceError(tileset, geometricError, tile, frameState) { - if (geometricError === 0.0) { - // Leaf tiles do not have any error so save the computation - return 0.0; - } - - 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 { - // Avoid divide by zero when viewer is inside the tile - 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 updateVisibility(tileset, tile, frameState) { if (tile._updatedVisibilityFrame === frameState.frameNumber) { // Return early if visibility has already been checked during the traversal. @@ -272,17 +221,7 @@ define([ return; } - var parent = tile.parent; - var parentTransform = defined(parent) ? parent.computedTransform : tileset._modelMatrix; - var parentVisibilityPlaneMask = defined(parent) ? parent._visibilityPlaneMask : CullingVolume.MASK_INDETERMINATE; - - tile.updateTransform(parentTransform); - tile._distanceToCamera = tile.distanceToTile(frameState); - tile._centerZDepth = tile.distanceToTileCenter(frameState); - tile._screenSpaceError = getScreenSpaceError(tileset, tile.geometricError, tile, frameState); - tile._visibilityPlaneMask = tile.visibility(frameState, parentVisibilityPlaneMask); // Use parent's plane mask to speed up visibility test - tile._visible = tile._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; - tile._inRequestVolume = tile.insideViewerRequestVolume(frameState); + tile.updateVisibility(frameState); tile._updatedVisibilityFrame = frameState.frameNumber; } @@ -305,8 +244,7 @@ define([ } // Use parent's geometric error with child's box to see if the tile already meet the SSE - var sse = getScreenSpaceError(tileset, parent.geometricError, tile, frameState); - return sse <= tileset._maximumScreenSpaceError; + return tile.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError; } function updateTileVisibility(tileset, tile, frameState) { @@ -530,7 +468,7 @@ define([ visitTile(tileset, tile, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; - tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass + tile._updatedVisibilityFrame = 0; // Reset so visibility is checked during the next pass which may use a different camera } } diff --git a/Source/Scene/PointCloudEyeDomeLighting.js b/Source/Scene/PointCloudEyeDomeLighting.js index a2a8de574de5..16d5daa5d46b 100644 --- a/Source/Scene/PointCloudEyeDomeLighting.js +++ b/Source/Scene/PointCloudEyeDomeLighting.js @@ -236,9 +236,7 @@ define([ } PointCloudEyeDomeLighting.prototype.update = function(frameState, commandStart, pointCloudShading) { - var passes = frameState.passes; - var isPick = (passes.pick && !passes.render); - if (!isSupported(frameState.context) || isPick) { + if (!isSupported(frameState.context)) { return; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index d094ef78216f..e9ee16137c01 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -776,7 +776,8 @@ define([ this._view = this._defaultView; // Give frameState, camera, and screen space camera controller initial state before rendering - updateFrameState(this, 0.0, JulianDate.now()); + updateFrameNumber(this, 0.0, JulianDate.now()); + updateFrameState(this); this.initializeFrame(); } @@ -869,7 +870,7 @@ define([ }, /** - * Returns true if the pickPosition function is supported. + * Returns true if the {@link Scene#pickPosition} function is supported. * @memberof Scene.prototype * * @type {Boolean} @@ -884,7 +885,7 @@ define([ }, /** - * Returns true if the sampleHeight function is supported. + * Returns true if the {@link Scene#sampleHeight} function is supported. * @memberof Scene.prototype * * @type {Boolean} @@ -899,7 +900,7 @@ define([ }, /** - * Returns true if the clampToHeight function is supported. + * Returns true if the {@link Scene#clampToHeight} function is supported. * @memberof Scene.prototype * * @type {Boolean} @@ -1572,7 +1573,13 @@ define([ passes.offscreen = false; } - function updateFrameState(scene, frameNumber, time) { + function updateFrameNumber(scene, frameNumber, time) { + var frameState = scene._frameState; + frameState.frameNumber = frameNumber; + frameState.time = JulianDate.clone(time, frameState.time); + } + + function updateFrameState(scene) { var camera = scene.camera; var frameState = scene._frameState; @@ -1583,8 +1590,6 @@ define([ frameState.mode = scene._mode; frameState.morphTime = scene.morphTime; frameState.mapProjection = scene.mapProjection; - frameState.frameNumber = frameNumber; - frameState.time = JulianDate.clone(time, frameState.time); frameState.camera = camera; frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); frameState.occluder = getOccluder(scene); @@ -3002,7 +3007,7 @@ define([ frameState.creditDisplay.update(); } - function render(scene, time) { + function render(scene) { scene._pickPositionCacheDirty = true; var context = scene.context; @@ -3012,8 +3017,7 @@ define([ var view = scene._defaultView; scene._view = view; - var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0); - updateFrameState(scene, frameNumber, time); + updateFrameState(scene); frameState.passes.render = true; frameState.passes.postProcess = scene.postProcessStages.hasSelected; @@ -3071,9 +3075,9 @@ define([ context.endFrame(); } - function tryAndCatchError(scene, time, functionToExecute) { + function tryAndCatchError(scene, functionToExecute) { try { - functionToExecute(scene, time); + functionToExecute(scene); } catch (error) { scene._renderError.raiseEvent(scene, error); @@ -3094,13 +3098,9 @@ define([ time = JulianDate.now(); } + var frameState = this._frameState; this._jobScheduler.resetBudgets(); - // Update - this._preUpdate.raiseEvent(this, time); - tryAndCatchError(this, time, update); - this._postUpdate.raiseEvent(this, time); - var cameraChanged = this._view.checkForCameraUpdates(this); var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || this._logDepthBufferDirty || (this.mode === SceneMode.MORPHING); if (!shouldRender && defined(this.maximumRenderTimeChange) && defined(this._lastRenderTime)) { @@ -3112,10 +3112,19 @@ define([ this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime); this._renderRequested = false; this._logDepthBufferDirty = false; + var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0); + updateFrameNumber(this, frameNumber, time); + } + + // Update + this._preUpdate.raiseEvent(this, time); + tryAndCatchError(this, update); + this._postUpdate.raiseEvent(this, time); + if (shouldRender) { // Render this._preRender.raiseEvent(this, time); - tryAndCatchError(this, time, render); + tryAndCatchError(this, render); RequestScheduler.update(); } @@ -3302,8 +3311,7 @@ define([ this._jobScheduler.disableThisFrame(); - // Update with previous frame's number and time, assuming that render is called before picking. - updateFrameState(this, frameState.frameNumber, frameState.time); + updateFrameState(this); frameState.cullingVolume = getPickCullingVolume(this, drawingBufferPosition, rectangleWidth, rectangleHeight, viewport); frameState.invertClassification = false; frameState.passes.pick = true; @@ -3323,7 +3331,6 @@ define([ var object = view.pickFramebuffer.end(scratchRectangle); context.endFrame(); - callAfterRenderFunctions(this); return object; }; @@ -3503,16 +3510,7 @@ define([ return result; }; - function isExcluded(object, objectsToExclude) { - if (!defined(objectsToExclude) || objectsToExclude.length === 0) { - return false; - } - return (objectsToExclude.indexOf(object) > -1) || - (objectsToExclude.indexOf(object.primitive) > -1) || - (objectsToExclude.indexOf(object.id) > -1); - } - - function drillPick(limit, pickCallback, objectsToExclude) { + function drillPick(limit, pickCallback) { // PERFORMANCE_IDEA: This function calls each primitive's update for each pass. Instead // we could update the primitive once, and then just execute their commands for each pass, // and cull commands for picked primitives. e.g., base on the command's owner. @@ -3530,6 +3528,7 @@ define([ while (defined(pickedResult)) { var object = pickedResult.object; var position = pickedResult.position; + var exclude = pickedResult.exclude; if (defined(position) && !defined(object)) { result.push(pickedResult); @@ -3540,7 +3539,7 @@ define([ break; } - if (!isExcluded(object, objectsToExclude)) { + if (!exclude) { result.push(pickedResult); if (0 >= --limit) { break; @@ -3619,7 +3618,9 @@ define([ var object = that.pick(windowPosition, width, height); if (defined(object)) { return { - object : object + object : object, + position : undefined, + exclude : false }; } }; @@ -3637,13 +3638,23 @@ define([ var orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight); var right = Cartesian3.cross(direction, orthogonalAxis, scratchRight); var up = Cartesian3.cross(direction, right, scratchUp); + camera.position = ray.origin; camera.direction = direction; camera.up = up; camera.right = right; } - function getRayIntersection(scene, ray) { + function isExcluded(object, objectsToExclude) { + if (!defined(object) || !defined(objectsToExclude) || objectsToExclude.length === 0) { + return false; + } + return (objectsToExclude.indexOf(object) > -1) || + (objectsToExclude.indexOf(object.primitive) > -1) || + (objectsToExclude.indexOf(object.id) > -1); + } + + function getRayIntersection(scene, ray, objectsToExclude, requirePosition) { var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3660,7 +3671,7 @@ define([ scene._jobScheduler.disableThisFrame(); // Update with previous frame's number and time, assuming that render is called before picking. - updateFrameState(scene, frameState.frameNumber, frameState.time); + updateFrameState(scene); frameState.invertClassification = false; frameState.passes.pick = true; frameState.passes.offscreen = true; @@ -3692,17 +3703,17 @@ define([ scene._view = scene._defaultView; context.endFrame(); - callAfterRenderFunctions(scene); if (defined(object) || defined(position)) { return { object : object, - position : position + position : position, + exclude : (!defined(position) && requirePosition) || isExcluded(object, objectsToExclude) }; } } - function getRayIntersections(scene, ray, limit, objectsToExclude) { + function getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (scene._mode !== SceneMode.SCENE3D) { @@ -3710,9 +3721,20 @@ define([ } //>>includeEnd('debug'); var pickCallback = function() { - return getRayIntersection(scene, ray); + return getRayIntersection(scene, ray, objectsToExclude, requirePosition); }; - return drillPick(limit, pickCallback, objectsToExclude); + return drillPick(limit, pickCallback); + } + + function pickFromRay(scene, ray, objectsToExclude, requirePosition) { + var results = getRayIntersections(scene, ray, 1, objectsToExclude, requirePosition); + if (results.length > 0) { + return results[0]; + } + } + + function drillPickFromRay(scene, ray, limit, objectsToExclude, requirePosition) { + return getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition); } /** @@ -3720,6 +3742,10 @@ define([ * or undefined if there were no intersections. The intersected object has a primitive * property that contains the intersected primitive. Other properties may be set depending on the type of primitive * and may be used to further identify the picked object. The ray must be given in world coordinates. + *

+ * This function only picks globe tiles and 3D Tiles that are rendered in the current view. Picks all other + * primitives regardless of their visibility. + *

* * @private * @@ -3730,10 +3756,13 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.pickFromRay = function(ray, objectsToExclude) { - var results = getRayIntersections(this, ray, 1, objectsToExclude); - if (results.length > 0) { - return results[0]; + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); } + //>>includeEnd('debug'); + return pickFromRay(this, ray, objectsToExclude, false); }; /** @@ -3742,6 +3771,10 @@ define([ * properties may also be set depending on the type of primitive and may be used to further identify the picked object. * The primitives in the list are ordered by first intersection to last intersection. The ray must be given in * world coordinates. + *

+ * This function only picks globe tiles and 3D Tiles that are rendered in the current view. Picks all other + * primitives regardless of their visibility. + *

* * @private * @@ -3753,7 +3786,13 @@ define([ * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude) { - return getRayIntersections(this, ray, limit, objectsToExclude); + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('Ray intersections are only supported in 3D mode.'); + } + //>>includeEnd('debug'); + return drillPickFromRay(this, ray, limit, objectsToExclude, false); }; var scratchSurfacePosition = new Cartesian3(); @@ -3783,6 +3822,13 @@ define([ return getRayForSampleHeight(scene, cartographic); } + function getHeightFromCartesian(scene, cartesian) { + var globe = scene.globe; + var ellipsoid = defined(globe) ? globe.ellipsoid : scene.mapProjection.ellipsoid; + var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic); + return cartographic.height; + } + /** * Returns the height of scene geometry at the given cartographic position or undefined if there was no * scene geometry to sample height from. May be used to clamp objects to the globe, 3D Tiles, or primitives in the scene. @@ -3797,24 +3843,23 @@ define([ * * @see Scene#clampToHeight * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} sampleHeight required depth texture support. Check sampleHeightSupported. + * @exception {DeveloperError} sampleHeight is only supported in 3D mode. + * @exception {DeveloperError} sampleHeight requires depth texture support. Check sampleHeightSupported. */ Scene.prototype.sampleHeight = function(position, objectsToExclude) { //>>includeStart('debug', pragmas.debug); Check.defined('position', position); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('sampleHeight is only supported in 3D mode.'); + } if (!this.sampleHeightSupported) { - throw new DeveloperError('sampleHeight required depth texture support. Check sampleHeightSupported.'); + throw new DeveloperError('sampleHeight requires depth texture support. Check sampleHeightSupported.'); } //>>includeEnd('debug'); var ray = getRayForSampleHeight(this, position); - var pickResult = this.pickFromRay(ray, objectsToExclude); + var pickResult = pickFromRay(this, ray, objectsToExclude, true); if (defined(pickResult)) { - var cartesian = pickResult.position; - var globe = this.globe; - var ellipsoid = defined(globe) ? globe.ellipsoid : this.mapProjection.ellipsoid; - var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic); - return cartographic.height; + return getHeightFromCartesian(this, pickResult.position); } }; @@ -3834,18 +3879,21 @@ define([ * * @see Scene#sampleHeight * - * @exception {DeveloperError} Ray intersections are only supported in 3D mode. - * @exception {DeveloperError} clampToHeight required depth texture support. Check clampToHeightSupported. + * @exception {DeveloperError} clampToHeight is only supported in 3D mode. + * @exception {DeveloperError} clampToHeight requires depth texture support. Check clampToHeightSupported. */ Scene.prototype.clampToHeight = function(cartesian, objectsToExclude, result) { //>>includeStart('debug', pragmas.debug); Check.defined('cartesian', cartesian); + if (this._mode !== SceneMode.SCENE3D) { + throw new DeveloperError('sampleHeight is only supported in 3D mode.'); + } if (!this.clampToHeightSupported) { - throw new DeveloperError('clampToHeight required depth texture support. Check clampToHeightSupported.'); + throw new DeveloperError('clampToHeight requires depth texture support. Check clampToHeightSupported.'); } //>>includeEnd('debug'); var ray = getRayForClampToHeight(this, cartesian); - var pickResult = this.pickFromRay(ray, objectsToExclude); + var pickResult = pickFromRay(this, ray, objectsToExclude, true); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); } diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js index d80ba01ddb0e..bca09a0834cb 100644 --- a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js +++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js @@ -71,7 +71,7 @@ define([ return ''; } - var statistics = isPick ? tileset._statisticsLastPick : tileset._statisticsLastColor; + var statistics = isPick ? tileset._statisticsLastPick : tileset._statisticsLastRender; // Since the pick pass uses a smaller frustum around the pixel of interest, // the statistics will be different than the normal render pass. diff --git a/Specs/Core/RaySpec.js b/Specs/Core/RaySpec.js index 91ea5084b816..815f6f24070f 100644 --- a/Specs/Core/RaySpec.js +++ b/Specs/Core/RaySpec.js @@ -28,6 +28,27 @@ defineSuite([ expect(ray.direction).toEqual(Cartesian3.UNIT_X); }); + it('clone with a result parameter', function() { + var direction = Cartesian3.normalize(new Cartesian3(1, 2, 3), new Cartesian3()); + var ray = new Ray(Cartesian3.UNIT_X, direction); + var result = new Ray(); + var returnedResult = Ray.clone(ray, result); + expect(ray).not.toBe(result); + expect(result).toBe(returnedResult); + expect(ray).toEqual(result); + }); + + it('clone works with a result parameter that is an input parameter', function() { + var direction = Cartesian3.normalize(new Cartesian3(1, 2, 3), new Cartesian3()); + var ray = new Ray(Cartesian3.UNIT_X, direction); + var returnedResult = Ray.clone(ray, ray); + expect(ray).toBe(returnedResult); + }); + + it('clone returns undefined if ray is undefined', function() { + expect(Ray.clone()).toBeUndefined(); + }); + it('getPoint along ray works without a result parameter', function() { var direction = Cartesian3.normalize(new Cartesian3(1, 2, 3), new Cartesian3()); var ray = new Ray(Cartesian3.UNIT_X, direction); diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index e68374ff6c5e..f14180e0bc06 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2095,9 +2095,9 @@ defineSuite([ return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { tileset.style = new Cesium3DTileStyle({color: 'color("red")'}); scene.renderForSpecs(); - expect(tileset.statistics.numberOfTilesStyled).toBe(1); + expect(tileset._statisticsLastRender.numberOfTilesStyled).toBe(1); scene.pickForSpecs(); - expect(tileset.statistics.numberOfTilesStyled).toBe(0); + expect(tileset._statisticsLastPick.numberOfTilesStyled).toBe(0); }); }); diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 34021fbc10ad..a3c5d886ee3d 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -18,6 +18,7 @@ defineSuite([ 'Scene/Cesium3DTileStyle', 'Scene/EllipsoidSurfaceAppearance', 'Scene/Globe', + 'Scene/PointPrimitiveCollection', 'Scene/Primitive', 'Scene/Scene', 'Scene/SceneMode', @@ -45,6 +46,7 @@ defineSuite([ Cesium3DTileStyle, EllipsoidSurfaceAppearance, Globe, + PointPrimitiveCollection, Primitive, Scene, SceneMode, @@ -546,6 +548,18 @@ defineSuite([ }, primitiveRay); }); + it('picks primitive that doesn\'t write depth', function() { + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.fromRadians(0.0, 0.0, 100.0), + disableDepthTestDistance : Number.POSITIVE_INFINITY + }); + expect(scene).toPickFromRayAndCall(function(result) { + expect(result.object.primitive).toBe(point); + expect(result.position).toBeUndefined(); + }, primitiveRay); + }); + it('throws if ray is undefined', function() { expect(function() { scene.pickFromRay(undefined); @@ -894,6 +908,30 @@ defineSuite([ }, cartographic, [rectangle2, rectangle3]); }); + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.sampleHeightSupported) { + return; + } + var rectangle = createSmallRectangle(0.0); + var height = 100.0; + var cartographic = new Cartographic(0.0, 0.0, height); + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : Cartographic.toCartesian(cartographic) + }); + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toEqualEpsilon(height, CesiumMath.EPSILON3); + }, cartographic); + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); + }, cartographic); + rectangle.show = false; + expect(scene).toSampleHeightAndCall(function(height) { + expect(height).toBeUndefined(); + }, cartographic); + }); + it('throws if position is undefined', function() { if (!scene.sampleHeightSupported) { return; @@ -966,7 +1004,7 @@ defineSuite([ }); it('clamps to the globe', function() { - if (!scene.sampleHeightSupported) { + if (!scene.clampToHeightSupported) { return; } @@ -1032,6 +1070,29 @@ defineSuite([ }, cartesian, [rectangle2, rectangle3]); }); + it('excludes primitive that doesn\'t write depth', function() { + if (!scene.clampToHeightSupported) { + return; + } + var rectangle = createSmallRectangle(0.0); + var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100.0); + var collection = scene.primitives.add(new PointPrimitiveCollection()); + var point = collection.add({ + position : cartesian + }); + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }, cartesian); + point.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); + }, cartesian); + rectangle.show = false; + expect(scene).toClampToHeightAndCall(function(clampedCartesian) { + expect(clampedCartesian).toBeUndefined(); + }, cartesian); + }); + it('throws if cartesian is undefined', function() { if (!scene.clampToHeightSupported) { return; From 9e8910869a79530621c9188a3ab2efcdad7b5ac5 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 17:52:06 -0400 Subject: [PATCH 18/32] Renamed Cesium3DTilesetOffscreenTraversal to Cesium3DTilesetAsyncTraversal --- Source/Scene/Cesium3DTileset.js | 6 +++--- ...rsal.js => Cesium3DTilesetAsyncTraversal.js} | 17 ++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) rename Source/Scene/{Cesium3DTilesetOffscreenTraversal.js => Cesium3DTilesetAsyncTraversal.js} (85%) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 663a94bae4ef..9a1c195e4acf 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -28,10 +28,10 @@ define([ './Cesium3DTileContentState', './Cesium3DTileOptimizations', './Cesium3DTileRefine', + './Cesium3DTilesetAsyncTraversal', './Cesium3DTilesetCache', './Cesium3DTilesetStatistics', './Cesium3DTilesetTraversal', - './Cesium3DTilesetOffscreenTraversal', './Cesium3DTileStyleEngine', './ClippingPlaneCollection', './LabelCollection', @@ -72,10 +72,10 @@ define([ Cesium3DTileContentState, Cesium3DTileOptimizations, Cesium3DTileRefine, + Cesium3DTilesetAsyncTraversal, Cesium3DTilesetCache, Cesium3DTilesetStatistics, Cesium3DTilesetTraversal, - Cesium3DTilesetOffscreenTraversal, Cesium3DTileStyleEngine, ClippingPlaneCollection, LabelCollection, @@ -1943,7 +1943,7 @@ define([ var ready; if (isAsync) { - ready = Cesium3DTilesetOffscreenTraversal.selectTiles(this, frameState); + ready = Cesium3DTilesetAsyncTraversal.selectTiles(this, frameState); } else { ready = Cesium3DTilesetTraversal.selectTiles(this, frameState); } diff --git a/Source/Scene/Cesium3DTilesetOffscreenTraversal.js b/Source/Scene/Cesium3DTilesetAsyncTraversal.js similarity index 85% rename from Source/Scene/Cesium3DTilesetOffscreenTraversal.js rename to Source/Scene/Cesium3DTilesetAsyncTraversal.js index fcc32ab53c0b..3cc1bd7768c7 100644 --- a/Source/Scene/Cesium3DTilesetOffscreenTraversal.js +++ b/Source/Scene/Cesium3DTilesetAsyncTraversal.js @@ -9,17 +9,20 @@ define([ 'use strict'; /** + * Traversal that loads all leaves that intersect the camera frustum. + * Used to determine ray-tileset intersections during a pickFromRayMostDetailed call. + * * @private */ - function Cesium3DTilesetOffscreenTraversal() { + function Cesium3DTilesetAsyncTraversal() { } - var offscreenTraversal = { + var asyncTraversal = { stack : new ManagedArray(), stackMaximumLength : 0 }; - Cesium3DTilesetOffscreenTraversal.selectTiles = function(tileset, frameState) { + Cesium3DTilesetAsyncTraversal.selectTiles = function(tileset, frameState) { tileset._selectedTiles.length = 0; tileset._requestedTiles.length = 0; tileset._hasMixedContent = false; @@ -38,11 +41,11 @@ define([ return ready; } - var stack = offscreenTraversal.stack; + var stack = asyncTraversal.stack; stack.push(tileset.root); while (stack.length > 0) { - offscreenTraversal.stackMaximumLength = Math.max(offscreenTraversal.stackMaximumLength, stack.length); + asyncTraversal.stackMaximumLength = Math.max(asyncTraversal.stackMaximumLength, stack.length); var tile = stack.pop(); var add = tile.refine === Cesium3DTileRefine.ADD; @@ -66,7 +69,7 @@ define([ touchTile(tileset, tile); } - offscreenTraversal.stack.trim(offscreenTraversal.stackMaximumLength); + asyncTraversal.stack.trim(asyncTraversal.stackMaximumLength); return ready; }; @@ -134,5 +137,5 @@ define([ } } - return Cesium3DTilesetOffscreenTraversal; + return Cesium3DTilesetAsyncTraversal; }); From 0a805d5f6702783ca18646819422bb17ea23c0d6 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 18:01:41 -0400 Subject: [PATCH 19/32] eslint fix --- Source/Scene/Cesium3DTileset.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 9a1c195e4acf..7acc1d57f0bf 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1692,8 +1692,6 @@ define([ function updateTiles(tileset, frameState) { tileset._styleEngine.applyStyle(tileset, frameState); - var passes = frameState.passes; - var isRender = passes.render; var statistics = tileset._statistics; var passes = frameState.passes; var isRender = passes.render; From ab58195ac92932ef26a818001658f136ce0e5534 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 19 Oct 2018 18:16:37 -0400 Subject: [PATCH 20/32] Remove unused include --- Source/Scene/Scene.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index b31f365d4b9f..05d7dee59bad 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -23,7 +23,6 @@ define([ '../Core/GeometryInstance', '../Core/GeometryPipeline', '../Core/Intersect', - '../Core/isArray', '../Core/JulianDate', '../Core/Math', '../Core/Matrix4', @@ -105,7 +104,6 @@ define([ GeometryInstance, GeometryPipeline, Intersect, - isArray, JulianDate, CesiumMath, Matrix4, From 12e92b5a162e69eb9fd9be433511bd8ac430841f Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 22 Oct 2018 09:42:08 -0400 Subject: [PATCH 21/32] Ignore async commands in different place --- Source/Scene/Cesium3DTileset.js | 7 ------- Source/Scene/Scene.js | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 7acc1d57f0bf..5819bb06f6a6 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1695,8 +1695,6 @@ define([ var statistics = tileset._statistics; var passes = frameState.passes; var isRender = passes.render; - var isPick = passes.pick; - var isAsync = passes.async; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; var selectedTiles = tileset._selectedTiles; @@ -1777,11 +1775,6 @@ define([ } } - if (isAsync && !isRender && !isPick) { - // Don't push commands for an async-only pass - commandList.length = numberOfInitialCommands; - } - // Number of commands added by each update above addedCommandsLength = commandList.length - numberOfInitialCommands; statistics.numberOfCommands = addedCommandsLength; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 05d7dee59bad..d6be9e84bf5b 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3683,6 +3683,9 @@ define([ uniformState.update(frameState); + var commandList = frameState.commandList; + var commandsLength = commandList.length; + var ready = true; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { @@ -3693,6 +3696,9 @@ define([ } } + // Ignore commands pushed during async pass + commandList.length = commandsLength; + scene._view = scene._defaultView; if (ready) { From a406d3aa11d29eb20765f5badc60e5f91e0149ed Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 22 Oct 2018 15:34:05 -0400 Subject: [PATCH 22/32] Fix WebGL stub tests --- Specs/Scene/PickSpec.js | 54 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 1ba51ac37529..40951bc2ccf3 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -56,6 +56,10 @@ defineSuite([ pollToPromise) { 'use strict'; + // It's not easily possible to mock the asynchronous pick functions + // so don't run the tests if running with the WebGL stub + var webglStub = !!window.webglStub; + var scene; var primitives; var camera; @@ -1153,8 +1157,6 @@ defineSuite([ }); }); - // TODO : how to write default matchers for these since they return promises? Do they fail when run in WebGL stub? - function pickFromRayMostDetailed(ray, objectsToExclude) { var result; var completed = false; @@ -1221,6 +1223,9 @@ defineSuite([ describe('pickFromRayMostDetailed', function() { it('picks a tileset', function() { + if (webglStub) { + return; + } scene.camera.setView({ destination : offscreenRectangle }); return createTileset().then(function(tileset) { return pickFromRayMostDetailed(primitiveRay).then(function(result) { @@ -1242,6 +1247,9 @@ defineSuite([ }); it('picks a primitive', function() { + if (webglStub) { + return; + } var rectangle = createSmallRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(primitiveRay).then(function(result) { @@ -1258,6 +1266,9 @@ defineSuite([ }); it('returns undefined if no primitives are picked', function() { + if (webglStub) { + return; + } createLargeRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(offscreenRay).then(function(result) { @@ -1266,6 +1277,9 @@ defineSuite([ }); it('picks the top primitive', function() { + if (webglStub) { + return; + } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); @@ -1275,6 +1289,9 @@ defineSuite([ }); it('excludes objects', function() { + if (webglStub) { + return; + } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); @@ -1292,6 +1309,9 @@ defineSuite([ }); it('picks primitive that doesn\'t write depth', function() { + if (webglStub) { + return; + } var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : Cartographic.fromRadians(0.0, 0.0, 100.0), @@ -1328,6 +1348,9 @@ defineSuite([ describe('drillPickFromRayMostDetailed', function() { it('drill picks a primitive', function() { + if (webglStub) { + return; + } var rectangle = createSmallRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { @@ -1348,6 +1371,9 @@ defineSuite([ }); it('drill picks multiple primitives', function() { + if (webglStub) { + return; + } var rectangle1 = createSmallRectangle(0.0); var rectangle2 = createSmallRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); @@ -1371,6 +1397,9 @@ defineSuite([ }); it('does not drill pick when show is false', function() { + if (webglStub) { + return; + } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.show = false; @@ -1382,6 +1411,9 @@ defineSuite([ }); it('does not drill pick when alpha is zero', function() { + if (webglStub) { + return; + } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.appearance.material.uniforms.color.alpha = 0.0; @@ -1393,6 +1425,9 @@ defineSuite([ }); it('returns empty array if no primitives are picked', function() { + if (webglStub) { + return; + } createLargeRectangle(0.0); createLargeRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); @@ -1402,6 +1437,9 @@ defineSuite([ }); it('can drill pick batched Primitives with show attribute', function() { + if (webglStub) { + return; + } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), @@ -1457,6 +1495,9 @@ defineSuite([ }); it('can drill pick without ID', function() { + if (webglStub) { + return; + } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), @@ -1491,6 +1532,9 @@ defineSuite([ }); it('can drill pick batched Primitives without show attribute', function() { + if (webglStub) { + return; + } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), @@ -1535,6 +1579,9 @@ defineSuite([ }); it('stops drill picking when the limit is reached.', function() { + if (webglStub) { + return; + } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); @@ -1550,6 +1597,9 @@ defineSuite([ }); it('excludes objects', function() { + if (webglStub) { + return; + } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); From 66e626c0f9d567f58ead7922d965490a5034e895 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 08:32:37 -0500 Subject: [PATCH 23/32] Added tileset tests --- Source/Scene/Cesium3DTileset.js | 2 +- Source/Scene/Cesium3DTilesetAsyncTraversal.js | 1 + Source/Scene/Scene.js | 4 +- .../Tilesets/TilesetUniform/0_0_0.b3dm | Bin 0 -> 8688 bytes .../Tilesets/TilesetUniform/1_0_0.b3dm | Bin 0 -> 8708 bytes .../Tilesets/TilesetUniform/1_0_1.b3dm | Bin 0 -> 8708 bytes .../Tilesets/TilesetUniform/1_0_2.b3dm | Bin 0 -> 8712 bytes .../Tilesets/TilesetUniform/1_1_0.b3dm | Bin 0 -> 8700 bytes .../Tilesets/TilesetUniform/1_1_1.b3dm | Bin 0 -> 8708 bytes .../Tilesets/TilesetUniform/1_1_2.b3dm | Bin 0 -> 8712 bytes .../Tilesets/TilesetUniform/1_2_0.b3dm | Bin 0 -> 8696 bytes .../Tilesets/TilesetUniform/1_2_1.b3dm | Bin 0 -> 8712 bytes .../Tilesets/TilesetUniform/1_2_2.b3dm | Bin 0 -> 8704 bytes .../Tilesets/TilesetUniform/2_3_3.b3dm | Bin 0 -> 8720 bytes .../Tilesets/TilesetUniform/2_3_4.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/2_3_5.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/2_4_3.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/2_4_4.b3dm | Bin 0 -> 8720 bytes .../Tilesets/TilesetUniform/2_4_5.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/2_5_3.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/2_5_4.b3dm | Bin 0 -> 8724 bytes .../Tilesets/TilesetUniform/2_5_5.b3dm | Bin 0 -> 8716 bytes .../Tilesets/TilesetUniform/tileset.json | 77 +++++ .../Tilesets/TilesetUniform/tileset2.json | 311 ++++++++++++++++++ Specs/Scene/Cesium3DTilesetSpec.js | 192 +++++++++++ Specs/Scene/PickSpec.js | 28 +- 26 files changed, 610 insertions(+), 5 deletions(-) create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_0.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_1.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_2.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_0.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_1.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_2.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_0.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_1.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_2.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_3.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_4.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_5.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_3.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_4.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_5.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_3.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_4.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_5.b3dm create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset.json create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset2.json diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 5819bb06f6a6..0379babd1a9d 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1969,7 +1969,7 @@ define([ } // Update last statistics - var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastRender; + var statisticsLast = isAsync ? this._statisticsLastAsync : (isPick ? this._statisticsLastPick : this._statisticsLastRender); Cesium3DTilesetStatistics.clone(statistics, statisticsLast); return ready; diff --git a/Source/Scene/Cesium3DTilesetAsyncTraversal.js b/Source/Scene/Cesium3DTilesetAsyncTraversal.js index 3cc1bd7768c7..76b46189f348 100644 --- a/Source/Scene/Cesium3DTilesetAsyncTraversal.js +++ b/Source/Scene/Cesium3DTilesetAsyncTraversal.js @@ -119,6 +119,7 @@ define([ function loadTile(tileset, tile) { if (hasUnloadedContent(tile) || tile.contentExpired) { + tile._priority = 0.0; // Highest priority tileset._requestedTiles.push(tile); } } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index d6be9e84bf5b..7e75e887d381 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3702,9 +3702,7 @@ define([ scene._view = scene._defaultView; if (ready) { - frameState.afterRender.push(function() { - asyncLoader.deferred.resolve(); - }); + asyncLoader.deferred.resolve(); } return ready; diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..00277d8462cb556f94dadde82c14dcbb2392cff8 GIT binary patch literal 8688 zcmeHM`+FQ^6@DooiYO?ETC0Q+rQkH~%sOHw00kc$^UsdB%lfT(x_Qqd}+;thZG2mb*71kd?q&gSgqV&q|c;CZs`ob%54-g(cN z^PO+^>C@5dc=k>a}ZS5ZJ*|EQGTmMDFJFKqaTt~;O6^&b6do#9U zyLQ&j*?HTu3${OETYc5aWE9VghqS)Q>C8ICf|GNyu9qu%g?!%rA7hzJu29VTML(0z z%CoDCdA{enUfwMhvy%3|gt+`5mg+N|UeWh+1<%jI}zA9G|)e51!H4jyLI-*-Huhu)745?iH8qP(`e-#*W=(!g_d21 zaBSk%o^3r@%eKltbS0f{RIP0f#>{^1HKR+zYg;uT${L-Paw;OB4DD%9nw|=FRk^R$!0zsg zFWE+$IbNL^ErmnWZTj_@pfW<+3d>Qw9vukfVfs;`KW4PbVSOr0ORYuaC?;BR8^p9{ zqcbtz&RV+%hI)s42l~yX^$!g0>h7c6w5|Q-hp+cS`i7y3qRMzg-&eHt3w8{L9p75D8uzm-H@!YaC&R27l&DJ{e$%;* zjq8D}z5OU2zVj$w8;(8R{V#uC6Xtu4^SZ`-&%gA-7tHsJg)ec<7Y=*ATJthj_*zG> z)Wi3JTQw!G=r8rGaCl8)(bW1IjydyEXAABYKE*Yj7ft3X+|4-Ab_`BxT+B)1MpHN{ zm(kQZf;C5BovR6Jj>1M?@~ZkxAJSY6OuVi9Eb}!co{2MGW9Epa#>|oa2o_D@>$qqN zN9q?%S~q#MrsfE4)zrB(N3iHiUg2|Ga~Sjfa~&x*XGt(WFC2}T&zd|_8Z#%2&nvtf z$LxpXQZ<-*me4VpQjg~AT&-C692cx>ZpE4-n0u6B(=UbTKItL%LSZwXNh~=#DK@j6 z=q2ZC1rFyDO~&&}=u@nrW3c8ZEO}X{32TnR%hlghgYc=hX0BLUW9qHMc~2XdIWj*Q zGe`DaVWTO09aowuJ>yP0bP9swsIzf2n7MqibUpJ*`-C7<2CAb7-?5U{&}T z#a{uhN=xTnK}#|oQXG!GmUFz8YnT0K=N_!}hvr+&dn;b+&su-v95&6u3NzE-6Q}8! z&9bsWe3iNjN-JnBm+w`SW34}H=Sya}X%<#FqfO5&H}5*WN?oam{r@9}&i6`UuDzWX zwotWgyta^bJFi2e-_Gj@>9_NGmh{_s{gd?Dd4d0p)W+*LtfK_$a6Ddz6LA7gB7Xv2 zk2m0CvM1w>cr)IFQ*bKzQ}9;21*hS3@~6@N%fAh0l06e|$2;&&tS7r3@4|cVZfw9= z`d>YfJlb=S685|&c09W8j9K=;*ufi-ogE?G{&yv3yhwwREL-rb6iyLq~ zuEXcaUxzQ^3%C(qB7Y;kjIZD@*~7RAH{+|gh3qZ36<@>cxD8(?e;dAmZ{iNJci>KZ z8{fiRxSRZ4_%6PKBe;kB5!{RWa6j4m@c zL;M&&!jt$3`6ux+{1i{&=j5NlFYrq|P4;O#gJ&_1qhycbSNIKnjpuNT{B!s%euwAD kK9AqykN5*#z@Ny!fWP3+coBak|04c|zvCscFX12f4_}hsVE_OC literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_0.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_0.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..3514e4cd2a2af276357fa40ba20af57fa7095b34 GIT binary patch literal 8708 zcmeHM`*&Pb75=D21W{2GwW8E3YSr6t@11u4gEHBCwqDq7PzNp6yh^N2Gyg)&4d zf&%gqt06MhAGMSpD>DSg&I<)vVreqbM;TdmG(1!E|sl+ z$a=QUNz%K*XnH1=nrFI_UdH@o-RGERO!{ZKFuGIQ>*#sF-cy0yBSV8VCqFR}R;%So zRUV_}jL%I?g_V6#c!ksLxz70EI2@Xqs)q8AvYaRvXUnCq6ptRBl~zmnL0X-2CD{&q z!ExR1?Ay_oa9pPtm9!soL6`JI^Dpf#_jppwV*W~HC%rCP$&7?(=UZ0av3YlC`u;PN zzAs%Yf6WYidH?)n(SoNPZKSVvpm(@guEkPhmjy|zA&77qHR`PMV;&eNS$*plz2&-GG`QohF#ZA{a6jrKHS#H<~x;$rIQmgmQLLZg# zMQMz1QqJ(zSDzJ$Ib1OlwmnlDTwacOFdo(3!gsu}7qiQue6!NhAOv%4`YZb%lOxQ@Z z5*4FZXba1HEO$0O7wfX->>V2EAMGC+tY6yT(D0t#0l7`rIavQG?7v99VR~^_V4DEX0snRU-Xnk)M&~KY62dVO{kXTr#a|HQKjQF2{o^t3|I) z$eU5$t)m5X#?dF0uh`97HUZoF2QhQO11ELVPXBk$lLwA3!Mawz?pv&D{q-lGSl5cl zr{D6)QLCdhu5!t@I>gLJ*Tk)wj7$A?XUWk$i>Yb#?|$&6W}X(G{m)u62aAKa?mtemJ{Bv7nieaE z^&!?A+HurRO>%5LtvXsC@)a{r`k9Z7Yxx%2d|I*1L9y0SV<^_zTdaALW3h5HAM%Nn zqgXYGSK_Fjn&dDR^{o!EYUx@->!X_BQ$STRu7D zYYx=0`RG}zLu@(3t(uH$bGF##(~7C7eqyb!V$IWHttmMcD~CB)tQE4CbB>N75LQ@`a9Yiz~Jw^-NYSgagsTC5z_hgfYpj{0@9##Ju) zYNM+`@O&*vD= z=iGT8oxFqf`bdA9es9I=^;xeE-@_%nuu9J~`NCOpW*c`_@vl*LQE3&emEt{zVyxF^ z{rzGuFX@Ff_UMu`EA_keuThsdseLwL*m%z**4#Te&?DJ)aP)|FC&v-d@8mcp`kfq) zh<+!>zeK;21HC{e$0^t#0XEz%AMe9u*e~`n zd;nuOAoBqn#0QbbxXk02z!XAEVp{AZW)R_WnJ>qOP(l#}l*JZM!G|%6s@Pe?n8P8N z58(=2iNm-`=BrS{M=+18@lml?;|M;6Yh=C#AIB$f9j?VE#a@dWaRaW$r^H^5n{YF3 zk@*(firesM+%EI&_zXUW&*CWV5PKA##}{y?%y;68_%gnPyYLmUcj2qJ8^`c9vB&Ur zd;|B$d=I{fZ{gcGF7t7G2j9bYaWB3v_Fnt|Kg4}9--jRJC-^Z=;HP3w;AeOM_v7bc z@5e9jARdzWAv}yn@JpPO`6PaY-{9AH6u%YwD1L|E<1v{Z!yoV`{1K1i&tf0PU+^TJ Tz+c5afxqGJcuMA{@DKbK(dEAn literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_1.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_1.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..99b4ac8569fa8995368ddef7c241d3b6ed966a7a GIT binary patch literal 8708 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 literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_1.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_1.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..f20b7dd20fd2aed506cc0130a6f5ebd635d3d00d GIT binary patch literal 8708 zcmeHM`+FQ^6@FWaT0kkH1uIAysZ^Q9-PwBpyJ>n$Nt2R zBBEA>aw{MO6cq6Wh+NxkMU;EFh`;&|@IQFYw|kPamxjp0^3dnWX6KxD&b;4y&YbVe z?9-=1k&)~I0E;Z(bL9DxURd!ZYfZ;M=f>^b>v}c}Y_!@_Q_Gi^EPupm-xjjNwqr-^ zs2#K8cEU~$+E#ZyH|7VEBOYB}6>f`!Q}I+dl1Rj&sbnggu>a3kGM;qeF(;LZ#AN9l zLynV(#X_l6A{m`t`93i&-wmYtP+L5eOj2?u=_L4*w5y?HA`(x<&8HelI8HPckC~6K zKDvce_eRej8xN@GiMD7cp}tA`V-^{t`5DU$tmpCCcMh=SL|{{YZ%@f`hljmlF<&Us zZIrB`$$b$8K<`il{}G1AcA!7018{P2fVewYTC zzSGr(>G=ycWOF`nw*JnJ?vB3ccFnXQ*^neMha|+Q?nuOmPVdMulQ`kn;1u1PTQr+o zvc|leS8#)T!D_c6BdY>G;}usG-RuPWnTxiy(<>DHJngXMw1uoGD$UGJWsY)gmIkC6 zqI=^(>h^MD!MN2Pr^lF6&di#gz3I6VI*(4neFtt~jE6E3O~tEM`-6hdFTk)fGYNAL ztH^&)y=0tvte=utnBIKWEOOCVs*6XHwQBJYN3>dmJC}6>ui(3xO5Y}i3Y)#a&1C%H zzWn6ac#fWDtDPPOx9D}|Gx@@Lci8ObHZ!_3ytY*kqFke=C0iAdP>A-_%}k8DoAcaP zt9MJsrI)Rv%^b;34rRQ4>NfozjZzt&ZFyP0SoC*#vY39R=+BH+)+>&C)lv(7)(?nQ zIductv!Tg=PfOO8-u|wEuHGKAX+6Dtn>)H`H*IUX`6=vLOYaz}$j^=V^v0sCpXq~H zku_CDr%cWqom6sAuL?fDRl;0Q^^cxthB{;(aH`a3RmZoG&j&p$%S|s1)01JY8dOo2 zJbb70ie0{P8L+0S2i@au9Ok36_`URdEB4i3KIb@J*O<@w*I)R8`JA!vC9e6xVb4c> zUgip4>j;*5_*`(krsNgZuFmtNd z@FlMKievVpNnTS=6P<<{bZwIB82Sxsg|BN<_pWD@~o3_qv%*Lk&{D%4;+wuhMT=D;$-pp$69H>!f3zBi7THu9-M#5ffP0bNpuPJ#&zu8*h=-L>Irp7#D9OpXgFwI=0hdf&f zmqnlX3a1%!JRg(ma9MF`u;vJ^at5U8#xvN0CG4`!F%r-pT`M zs>>SD9tTLjl}DNMTY3DK^jmp=|LxVnV*wUYf`vE%pTLP&gin%Rgim2H zP9l2}PR3{OX`F&n$)AGH;xsJ5>ExHt|J8pUOUW+988{PPz%sJS@I`zHXX7lKL;fr* z$GNb`TKF=)f)zLq=aWAVU&B|i5*LtP2^*`>Mz#%KM+jj!WF15hM+{LU$VZVx3hiXu zaUs5ei*XTFlfMXSs5NPHl22nT)?q!__1J)o=)xwln{Wwwuo>OxCEtxLxDvygX9n3*Z2({BKr^?#&7XEJVN#n{2qVAAMhytME+6y8GpfJWFN!hcoKib z6ZjkXC-4lO##4Bf{8M-i&*LE3gE)jT4&w;fBltUB#6R!?{z?7?{0lGPWwI~h6}*nu d@G9OQ|0>?Xn>dQM$sff#_&44q`!4>2{{b?<>Ye}q literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_2.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_2.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..e32032e27e1d1e868126f9bcb9ed5b10bb89cdc2 GIT binary patch literal 8712 zcmeHM`*&Pb75*p>DIy{uXys9_fK_jYx%bZ8nF)_h8d{P%O-d39Skrow+$0y~5oc}+ zWeAj~sECMF6cGyYvVb5I1QiHR6;WP_Yw?r+0KfXPd*95S?3qkrWHBsQE0f%_zd7@r zZ+~Z>eI{M2!_H{o004&?z%dm0)xBaiR*m(YgI(P_avOTj8SFMXGSjP9SB+rQ=-3uF zEz>p~Ghrr8*G!q|A=Ai}isL~zHR{vnYps}LW!#MAq*87&o^)Kx{6FW?ZrXN}nYd$H zDOtPZIonPplTO@>ebDs87y zR?3cE!us$QlJ9Om7@r8K?5S8Ho>ISQ^CLDhL@gA(#`A+4xy^1q3CJA`Y#Qk6ts36Q zh+nCc$`#s6)fk={8}rLs1OF1EBW@bQyF$NjY^>tbPU-DPsW4e8`o(Z?*Ce%E(z0Ei zoGQ}CwnaFM=8dfzx*Wqa3PF+k(oV)Qc2fN&%tSnJ|Odahv>KN&Z1jr&C6!L?`#8Iow?5bda>rpke-*sSwa%x)Xu}P6ZP|0 zFo|s?ho)(7UL`u>sxj^t{jwL9%0`FbjIIrXykA*c@d}e1=RP!zoqo9zl<0g7I~F&l z$y(Govt3m53e+C8DcT#iQO+-phZ9DJ>t*mpA*Nm(K?y{iUh#i6Y%*qk}GkSMj?_`BHhKHxixawy1Tfc}=4%MEQ(vOQ9wr zQ*k;|FF!fqZ7y+LjlM0N=bpEL4s*0LHJtYcsM_>bX^d?6rr{TYN+sCo%VPRtML$t% z75vJCU$a^c3PDJ;*|%OuXEr<)^4qGhrEj2Tu&1v#I<(%t{>_~^I!)8q5&c2zIh(#= z$Wc%n4e0xdj()BV(TYOkGBF+HEYh3V98{}vz~3qs8>;=#jb^B)q6Pd@uXnDSI05{#&!rJaaT= zKK~0q_=1_kSokup`NH9~qA5D;LHso9h^FQUZq}5%qTgz*aI`n6gLn}Aqu;I8>!a~q z&)itAkH*XqeT|tT`x4B1Z^hwx(G-sM)2zduh0j=g%Dni|c{N{S?WY-Q4}#y^zOP;% zjVDffx?UfRnInEQW{&JjFz>w;hv!98INDFM4%b2WjK!zSiyxg=^EK9fnz8o4xVo}1 zBYj0UtFiPab2OHo6h3=nEPXC~nb&;9iTE`SI&b8swl>PEaSP9yqdjQ-h@;oG@}~B# zYceCf%6rsUdXo36vGkrHdYqgGG%@N$JDS5SLjkTX< zESfwoSo(^6YApTF-ZYl}6u$V8-WI;hYrb%Jt!Rpl_QPwnj$q9Z+^i{iMZeWr;b?EN zcjjv>eZ@XCmY!t3#?t@7mt4$YEPR>QeBtm~(G(roukf{wV9gQStSNa#ztvjdNUl~M zSf6>buifhe0ZYQqDgGjONm@GhB3hDhx8m^J$2rHx*}I%aJI`RbKGff;-<$DreU|GZ z_pqTC7U`KfpEwO`*2yP}%r8-QPH7RXh4OubaxB+p`F=?+H}t|1d$eKALjA7im#8bA zIDQy8biNM~v-fsB$dcPOK3URk=d+vi+xhGz{dPVxq~Ffx-=yEp2c1AWp98Rh60E?1 zI0OgdAbf)2K{ymEaTtw<;gdK5pTgnzG{wVl6h4C^aWusv>Hq6Li(_d#7RTXp_&iq8 zxC&pui8ukr<0Oj5V>P}AgGK{i!l^g~C*#W$PsUgA6|BM6D6WBtwTRIe!)b`af=#0h z2QHFGAVo2OG&1O*u>+^$ES!lmu#VyxSWl(NqKje{XJZ34(zp@lpc_5dMB^rW9lh9$ z9Qr8cum$I$pT>R+U@OkUAkL>ah;QIRT!3v9FTgi3gzYqL#}0f89)@Wg#t6pXV-({Q zM=^l_7tweTE=Car8EY21lRa4B}-G8!*K6<1&ym*Yx`mt!}s z!qqfhjcaf{uEVwX4#jKnJ$x58;QJJBz>W9;Zlduf{17+eN4SN?TkvE26hFZp+)8l| zeukgpHX3ikFK`EbiQ91}#oKW=?!sRDisD|}gL`owjrZYxJb+(gAC3F)Abx{~@em%N z_z)h&Z}Aw7kKu7Vi6^ihzoWPxzsJ*f3V)#Z6#j^3@GOnb;yKLVdAva53wRN);AOmo uKT&)Mf5u<%Dvht=ulPIuhS%^q#nw6q^6@IC$3ZjB&ky@n;C{-q5XLd6?8^Pv6FR5u#lC)M!S|`aQ8Q4qQos^a( zPzuP^QVI$pY7wnk@j|U~r%+Mu3SQuO_{2ZJS06um&Ln4YcG|?q!}cl9lg-ZWynE)I z_q@L|XLdhiAU#w#3BZ5O0X|8QU+#j*ST)*O``XuR?OfTlx^IoqoSRs-tZD>9M)T&B zX_>Z}HZx|E>6ls5?Kh3iQgI{*$A^46-(WSSt(=q1X6;Pc&86LxZT`Qxw3SU~QaQ(s zube~1aUI*v*tuLb7hm_ka_rp;#d@mI$+>P*CY!Qti(lqc#*cR}#slE2I9D z)pAe>LZVH6^g_C41LGlotQzaPdpr6%y1SyA*45p!uCG&+6cmR7 z`o5xDKi!AuibCWvGZED+(uc_!)T?s9-z)SJ693T?%^!3sdcetPq%}3Z* zNBn5a9NCv(I^T9I4(CNvINDEAhqy7HF>4B6*Ggi|5v=_rvF0$QzQ)fK^|A)bd@@I4 znMvXEUKq=q3t#e@FC5{s2hq`f_*$(aSaSp?HKnfhtg-f!#G=W0!Q?N(>{Da%6mv9Y zj(E_RIkGRoqA7fx7fs=4KS>?p#?KdHt}Fbcrmm$qg0<%))*QhytL#%_o>k#!EHf#5 z@guX%{sqs(;k;-HM{0?_))A~Zf_1GV)*Qj2FLlMg?44@~rn!rsDVnJoEHlX*jb$c< z&-#p+!&vx|*L>j!pFN8XW4>1ST1T+v2u^BBUCwKcV9}Sl!soo^2&O(pdFE?OeT?}U zGer=_&P6|!qI+`I-;pLf|HuMmgWf7o|9N}1j{V3PmN{%*_+195f2*6 zYzu!j4%ZS*U6-#FO|2t1=~?npOFU?O;pl6HFSU;4f%(jveC=Ez2$&N-toXCwIce$I zvuH`hor=S`_j8T+vv;{4^}Gl3{h|3*^Pa@>{h9BNJcl*2Fw4xueB#txvqH|ylAojQ zw9+hEGu1nRYRva%{`rzwu9<~7&S=dwGtIls&rw%AaXcP1biMZyv-f%)Xd$8K=;C3O<65 z;iEVeAE$UKK7rG58qT128vTF%Q&>#vVw{Oj<18$pbqPL$&*8IJinA#$#WI`&gH{9Q z;tTjZ&cpc>&%>ASMJ&e!6qmz90~%><#Dz$~f=#Q9G#oS`gDk}iT;$M9Ycsx#ui#=_ zgcTGoLL0TF1??1Da0ymo6|JkV8f(yjwY09qSJ8!a=tMWgPOQhJ=%KX-z1WBi=)>12 z_Td}&IyPZ5#Z9;j{n$e57Hq{g;bDN*0SsaoK87$taR{Rba5=4);|df}KprKEd6e-j zjG;nt3?as`jn-|r65Fu@SJ8SEs<;LdxEj||yc#=k9d^;W3)kb@xDhwtCW<%UR@{P{ zaT~>(aXY?)-L&q;9oU0Av6t4pxC`IIcX2oFp?Ej$#rJU^t@q&v_z`}H`|)Fn_v0sc z0Q>MD#eMiGeun+D?#IvZ3;YrXXgz>m;Wzj-9>Q-aK7`-lVH~9OAbyWO;tx24M<^b` z<9G~@;t7h6;z>M(r)hl}hw%)a#SvPM;7|Aq{*34FJjLhm0$#*Rw7!Iw@mIWxSMWEA Zui)=^9k1aZ6ko$X@h`kV>l=6z{{^%hQe*%C literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_1.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_1.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..fc503c3b3a9f6c611c7b347a3a281a98605aa7e8 GIT binary patch literal 8712 zcmeHM`*&Pr72Q-2il`_LwW8DuXw{ox=FZHWnTU4M(2~??Qj$;}HLW+vO>$u#aptB_ zhG<1lR6v9(im1g0Y5_qgPXz|V7ovao}=&JQ2@2viE=G_zt{KZXh0WbJ?t$cATt3cah0t?Pe;QNxC`Lyqc*D z*K*V5C9KzPBl#ZigPGZo+MbD};u-amwclWwaT+1>nkkNL;W4}Ebf9nyuyu5JsBU?a zlYXsMsn+OT>ej^k^t4~y5%`x|y>Z)`*c?8<>CU5Q7+@G{;s}z(;G!>8O{$yOc$4SR>PFnq>>^SwbSEBJ~rs&>yj0%2vCY-f;T{_8Ja%s}^LLX^+w zw3M16l8Vza^@?+|-nI(&)f(R3cm4&N>0wS)<|m5&D0Q2DD@~IP-?scxP^$%deA!ID ztmuy!t&(4x^_y0!K`97{w)oZy>6uN;hy1o~Z66*T92*=SG7oKNcw}2&fu5#q?KZy< z2hXK%7;+Sprvm!EqDQ~f2eYGOT&5OG%`&}NtUp_LcmhTk~!Bm9zGn zJaaT=KK~6s_=1_kSoo6HeBtn3(G(r_Abz4cqNzE8qnc7z^q1Q!9PLf|ARa`&zP8k0 z%swMHuQ(bvM4$Nz=NYp;rCD8`1Ng=_L}p>9F56?IY+|hT8x>)Soo6HeBlV6J&2C>6V(w-%@G{cl)Bop z#@bI5izep<%b8-I5p3q4z43X`eDT11h4YMsuksCr^BS|JuG`#e_*zG>_MmW^W>kN< zy^3RME$2b>ski3rFh^tRp~^3YjhVxF;dkP2Ez#6XmVb#oGJDh!RAb{H#uKT+z=0PzM6aUoELs4j_}!ouB-L6j^Lrv0pLvU~eH#SuQIGlSk*LX8~m*>&V zXRz8Inr}7lQM}rp)&9siY@3A+Gt=S|r)|$h`J_XBg}O^h9kh;A?+sLAwLh!pOJ=!k z7FIZ;ZF`P1?>fIiUGc>6^{Aoiy_T50ck@D?+;;KGlXf?+eWc&b>j3F@^J@QEb4+ zDQi#k;cUc_fJ3W;BwVDCLWW`rS>(`5YcD>5PvMg|2OBA#gH6<$Jo+i-aV|Dv z3$0sl9tJRot+Z~%r!jaqOaX z7k1;b@GwE^1ST;JA5)m2IE7gRxRlmQaT&@ep@<5_BC7Zt=1`+JhY<7FL+c(~jw`Si zSJHYV>i9esa238l@ha@Y7jZSMSK~|gGOoon_zJ~q@HKoD*Wv3Fufz4Y0XNcmBff!~ z@J-xI>&>_Y-^RDFAGcE6kMH2SxQ*7^@ICwh-^cCvA;sJABiw-l_%X!;_z8ZBJ88WW zKf}-Q3ml~NAnw91aS!gsy%g`pefSmbr}ch3fQRrP4&h;nhwvC4#UuDN#YgZPJdP)5 zeF9ISfv4~^txw~(_&t7yXYdD#&)|>v6P~5@S^OD)#b59oo~QU6{)QLv0{%|%1^fg5 L#7nfkgn!|`9@yzH literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_2.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_2.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..1775fc0f1d4b2a60c5d890c62988dc1f644d7c31 GIT binary patch literal 8704 zcmeHM{dXK?6@I9of~Y8pT2bnVT6Ge3XLo0JBif{)rKxFBl2Fl_)=4r+2KOWGP6}m- zRs=;wL_|?UEqG3%KNZUTu5@=MUG($$yqC=tQ@-!J|DP+9&S!F&R5q8+ zHg|5&=NEj>%jQ#FCeN+wyEOM8X6mVKzgQ^bbNO60lhrHdF1d=HpGjr($}YJIUOu1B zd&x!C)3;Ib-V;VMv#}IB-cp9*Gba<%Y1e23t zy*O@pPhr`p;^-!KumXp=;T(uHb;<2N1(rUT1=c{wR zBDcM?;5hL+2X^&m9M>sF6%oy*x@~pxzPs9!l6sbdD}`P3x^qRZ`SCq>wP#g%#~0j{ zsi&Ws)VtEs#qw!pmzH-gy(*gbq@#`Y_YL-qG)uKyhQg{KnGFOH&eCaQylnF{)(qmM zbK?v0Y(YIa;D$33R>E2kS8Gm>lbPy@qf%J!st4scwaYhM=SWzqM^!mp$Lmfx3zAyW zH;X+~3ChwOOD=gfZKA=jG84}_J-%P`HRN)i*}iM`9O>E!tDZX!YBSo9nQT$oBat7C zYmt8WrM=}&*nrrg{FBuy_)=s0hUC%(t@_&OW!K5h&lXmy`6=~SwTxD-9K>NQ3QCK0 zo13WZ3*(?viY7;@^E0y*d6%6Yc^QIw*k3JGYrBKV?`5X@ss5mZN$-IudfT{6xuL(pu%P zJ{vBjT8qk2EVRXC5X+fO%*VQHIQxf32gU}5hmu1Z8Xno#Hz=p+I){^=!GVk9`$aN} zDpQesTglNc*CE+aPO{7{Brz+pSd1aHszv&(lGcQl{^X5TXkEz*E}14;OYK{$R^uU+ z)uPuY<;_U$CTLNear8;`8+Q9vVCTRPuKTaEQ%B?U|MZ@`wRZ*9wfgnG#k$sCe)5TR zt(biJEuS2H|7tYzwD^wuZfNFUv2v(s zv2xU}+Qgb;JC6FPNe*)(-|7%s4zZ2ZiY<6l?7**1XBFSUH*x z`NYamteV7Yanw&uau|#HR)^Sfh;6J^Y&pc#XI$pbXSX#`tV8!I-(p=;(_-aN(_-bY zKE!IZ>I>`C&euN=kX z({K6Y=w514hq;k&b%-s8xK)#Jso!odIgHgV2i4d6>?_r?nEkIDi`k##Yb?d=ZSv{2 zd~(Rw9H?XS(Y;oO*m8(lH5u3DY_ZL!6;o6F#9Cj)ny1BDQ*ta;4s)ui*0zF{7ZkEjMw9hcmvMI zo5Y@vx8Tj#j<<^44i{bMmbn`jAcZtMnLT9SBZn;VVzVfqh#r}H@HV^yZ^wn$A@)M- zl+yH~Uu-Wf!Y=HVc{eV`9t>cw%zN=p3}GJzF)Vfv`*8_IWFEmN4&YLZ;ay_K@E*Jy zm*Jq;%kW-|=?9h{K)1ip*!<9oOZKM;Eteuy98Zkg}KkMUFd1Sjz`u_y6! z+>3kg3$gd$m$(o2%X~i`z=QY|9+LSX{2IT-Z}2dFC-!0d9)G|iGCzVp;?MXK9>rh8 ZK8nBMaXf~?0-_?K))!U82Z(~AyhKqRf-n4)b3Dg${0IEmdw28M+-x>6a##+Wlce8!KXd2v z{oFhEduKQ2bkrX&90OpZ1H4(h{`739`B`U2*Kl{wfxeyn=MDEb9oe}pTV|bT-09e# zaFed*`fkciyMf#8W=346uT-3j;+gSK?r%%B`N?dM$@nQh?WI$RwEO>sf{YiWJ>T=v znflJP#?pQwoA#24R1nnn{*RcJpNm<2qAkc~GC_MfXip{l_Jkj}3!zNAA7lglNi&qq zCZy7A;AQKfmu{of-4jNWQ?c~iYfA;$q@T_tyc%eCU#6jvxh<3V;axgrx1J6R9|!Cn z8t9*Oa${p*rBW(a37iq_KtEWz|Xc z+ZJ0Yy-n*-Esrg4^~jvN^cj*b(laEb0QpxhF)1%&a#=R-1!o)T?&|9rtoLlO7nx;A z{52#I&cd5(~kwp^up(zDKFSPaX#xKwsJ9DjUU9Oc8xwo0xrt$y*Q z>l_Trm8c}g?09VnXHH714p42Fin)SJ$wEk;O{b|ZEKbH#PDdc`w5D7fI6Hv#;UnEk zXO}lO&Xp&1GX0c%6x8I0;&P;)hDmR664oGAlYgOlGSs!~d{xrPjCOtH@=~i*7o;*v z)q;dZteUT#E9By^9Od%0zD64tW`Jm9V>%FO_%Y#;WJJ zzdE`yyslFwk@w_nDJ+OEl#nyc<)^1|drI0@XJBvF`4{Yz!yGTojON23>9%}1O-LD` z>x6}?CPLRgs!7fLNhg(wzU?J^h3nT^iGdO7Rt9T@5z?j7i_9$Npv z;GV8NIZf9&Q2k!)Jy$+xq@t)e9?8d+9Q|S+syhnRDpPaSoJF~)<&a*LBmHPeYC#LX z@#duC9liZ{{@?$3MAzW) zr*&>R>z_-oUTa+MTdddmmjL<1daamz#x0*5^|~7KDwlk#L(F>gnz&Jud8yxQFFAV8 zVrp9b!$%*e*J<&6|2kT)!D8i5(_-alT(ya{#%3IiQ<_W}W*m*{YRs!#^3_v^nDyv2F*(#^ZPahJmmIxk zF*U6|XI1qq=1eNrV$LM_)K`vT@)@^$a&#{>sl(dHw>rd@L)@szywq>DmmKD5R)gy6 zea@BYSVi$@->%Y&NlgsTRu7DYYo(~_2^!!Lu@(3jhf7BYqr?d(}=05aboSS zVy)9+?I}4HD~B~$tQBeoo3>N78E)41gjYi`BL zw^*;qu~<3Ov{*Up53%}Y9F6O0%&T1T)l-L<_2@M*In-or)Ni(z9KB~THL0)nYCrSa ziGVfXhYWueye2K1dlfBaoHrbey^?dhl56LAwDJtr`y=yh=DiWG_h-F7d=Hn*!YVUU z=L=`ap6$G|N_>sFi%P3#t(5O&lw-X=>+ct5dC4rSaYmQyS!v#Fe2u!SN&QQa!{&Q2 zvDV(og-)rqg{xDvTe;>%zm@BV=(ln`Ec&fn&x?L57rKB}u4Ax45^TV$a6FE~v3Rxk zWAPfi7AJ^30Vm@1cpXl{$>LAK8}SBg#GAx#l>fJX3pR<}gj4WVybYVhZpPbj8s33Z zak}_Zu?6phBi6wgcsJgKGw~krXX3p$3tRC%@mt|y8`{LS;r&P;2~VsC9|6)xp%V~4b+6W!uFaV~aZm)Ko64?XC`Zn3-ZA@pMp`Y<5A4|{Pw z2E`6y2>WmWhVfzX!}tg;!iCr`{z7~dBe+=X#W;YEA%{`1qZq>kLX2Zl{5Yl%;S#Zz z;NvKwfILd#^C;sJm_|kXG-Ax)pxA@B6qn%;E*E<_W^pCva0RXse+B08NgNh?7@xvt z@M&C)Ys6oT&*8JU7M~Y?Ew01$xIyd<_yTUkO}JU?&G;g|j4$C9d`0{%xD{W;ZDMc3 z*YFK|9k=6~;%~>d@hu#|cf=pTckw;kA@&X&#rN?8+$r`>{189Jk8l@$BK|J?6nEnu zvG?F-_yvBBdvTxmd+{s$68GZ)@%Q7`_zfNu`yd{|!*~RbihUHn#qaSuJch@`KZZZx nk9b1t6L=DT!qa#P&xn5tf5D&eEdDC~S^N!u$8%zz!$0s}NKlNd literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_4.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_4.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..2d5bc41455a0c8f9bdebf48ac1d4a019c0f3eada GIT binary patch literal 8716 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 literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_3.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_3.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..31e75dfb7b040388cb924753ef8c65c5055993f2 GIT binary patch literal 8716 zcmeHM`*&Pr72Q;*TC3u#YDI__)S@@zeayWx6VW6MDM?L}l7tG@P;Zi(bN^uI1Rv719&Y({^{FPjq}FN?$MsU0|UK-7mfBA9l3?Jws|9(G&=TM zrfoW=Yo<-l^v#T!9W#xAQgJ$p=O#n?yxmT@cFxbbnVjQUUOJsM|G$*uWIWGu9Y33^ z?_6)r%6M7NvaM_`Q{VePa%?|{#l4mCbJ?t)@%&8Max<3eo3&In_R$a+dEo zIp4|EQ_tN-^4%9k(=##k+)1VVob7s9%c+8l`8-XHEwoMNM|bg<9X$uwdj_z3WN2{S z2qq@NN~Kh;&~fIC@wus~u)Hq{uQWO=(-=P#heK0Sm5>fgZzoEH*-|kq#-oR3Y2-ZH z@i{qHq>mk&aGd0geZ4)dVH$;~$a9%Z`Dr_s@mwog1!lWzrD`}yI+kl%)Yn3A6>Vtb zyEIX5YPrYMY|n4f=dlHIRqvpucs4Hr+(~lxog1t~S!sJpd!Q;m5|< zyrxkWqI^cTrBD-*lts@p$j{CMdrI6_V`y*p#h3Kb!<;P5jpxG=>Nfpmnj#yaX@rHS zQi%?RvYCEi(Vt|r3Sng?tXVBbg(xOk^=%N-GaH|a`R%;1cW9)4w0~$Yd1!+}!+W|1 z=xLh9f#jEB|Aq7|Lyn^2WJKRt^yruSknAWVF4GH1%_6<2)}UUMBmQEs*--5d-Drk7 zm0WOgnrPL=w_Ga4gDlHUuT0R*NIp$aRbA@vNf+97YzKDs58_DgmXlBa`#&d#H=o`0 zuj?K=q4Bo!|B>XGqcL;%Ujf1w%pAtTm%Qc+hxdx6=&%Rz)2Jhwnj^STQ|gL-v%SL6 z-sJ4!LG%wFf2iIcjUV{u@p^wWW{&7<%p5tFU_N^@4(CNvINDF64to|pWAQ0@@uTZ% zzQ)>5Bi0@SpBpaL`=jx_eV5hyqcL;DkH*ZAa|!0NH{)<#G=-!6H0p34gwI%fN?!cv zx|*-C_S1;92gbvj?_83(BHXF5%qMd+mYEbjdt)qfE_}&rzTzbOng?As@l)HI)YZ6& zXU)+bw0^?Tdz*PvXV*Phl3C?5YAiF!=hawdQutDfIdT?-6U`=^PNk{qCVraeoZ>;w zt7|o4%@Mqse|7f6gYacm`Mes-OmcrTmYEd3oSivxE`<}#CY(;Csp~E&{iLqeY~opS z5=~u8_^OuHU*S!iJ>l?Py8B%+S9F)VHI_TY9F66^2%r5hmb)W-$!orFgwGyCNBiNu zT1T+v2yWDry4thG+D{`EP0kCJxniFh%lxx9jb%QCFMedUg)e!{7Y^?gP0`VQc(2wG ztT}=kHKnfTH`^;5?M=?ke2rzU*r&!alg!sx=3n?yi#d#iFL})u4(}CB(UJ2CU+W0g z9Kns6Qdjhw?G=vHYUY9UnOFT;>=Xp73oj}D8hBk=y7n4cQgKmnIQK%X@j~`4&!d&k zV52`Y-)i0)@kW0(`Xl#n#Vo8bGj%?3R_y7NPu9q}y8iao2%yUwpuS3GfiE^6p{&n9N?t$d-2+_v!5McS==Es}mKU&lzlm9G<|-^$n1 zq~FRHx`9@{PQxZDun8~1W}Jc3@nVXn<0W`0w$Qo-FT*SFa%{ybDQ?B9@hY5&*HAo@ z{?Gn(*hcF%ydH1B8?l|%?RXQ;#+z{#&Y^e~+VB<_v>G@UZ^K)09^OvzJiG(vV+YBO(kVlDP9%Xz4v#3ymBEEp5xRv5j+=egVc3N-8mvJY)f;;e4ig)1a_!^Gk8x)V>oA?&)qV+Bu z$G7nv+)eA<_%6PW@8KT&fZ{#)A@0R}wBCmw;V1Yp?#BZZ@5j&ZQ#^==C_adv;}>|C z)`zi#6F7-SXnh2~#INue9>uRIK8oMqw|Jb^$MHM-0l&u+_#?$9@Mru9PvS2WpTuAB MH#|k_Q}{dn3xHDpZU6uP literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_4.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_4.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..89cb54de483ae202542aee70705a7bcbe5ef4a46 GIT binary patch literal 8720 zcmeHM`+FQ^6@DpDv7*)+V&zgt(5ln8GqXFp8_^^UDNRk2l7x!ZP$$VG8Qe?UofJxl zROC_x5fM;srFcO_ydf&yNV&>gK*eAE&40slzL~Q<*=~2TaFFIbXkZ}``xBpLFW7JFhnJ$fP=61XObYS3kVC%@x;Jg(~ zOoa7%wN|G!&0FJhQ&VAWM-*OUb!Kd9d`}z>O-lE`jFH=Z>eHd4UYFiF2CVn&Bvs*o_l^$EL zmuBz0M%i;y({Cd^hmNDu63usb_ITc!4l7|Th^sZL)AA-a#!)G(Z>$I9S+<8B+Scx{R*$N*!Is;Ru@)#b(>h5T zRf010N9v-raUTtYmFalK>h%4h&mj-Bnr^*D+XC=(87V-Pg zaSwIEQHUk-uU3y5dTDO-UgD}RpI-I|+4pz!g5rvN4rCrOy64cV_K^m)@Q;r)ml`J zVxlFtK}>r#J{R-tytRF3q<^%3XwYoh;Lz~4o&nlT+uCKm8T-$sR}5toRVE{PVbRtf zs)LzPHd$sDOw0=1Br&K~wTNFU4kwiU(1~WKUFLvOrba7m-&(aA53(#5y*@!F!#p)8 zQI|Np(uED{fKB~_*w?q_A-;xJpVs}~s~9Kf zv6Ykhjizu^ETgG)1Z$4MI@S`bISLzniL3HAbx311F!8#1F7q`eUXN&M%pB3wm^tic zeZitBeC=o49GB;cCS&0XmVB5aSU4Q38EcMU(Pw@$)*QyX{~SBTT^jRz;b_bpUN85Q z#>`3miwZBt=~9|H?xNDvItEKVDo@QZnv$E+Z<#9`6{{r&iN!UN`ZQtF|A{X-Unw^I zNtm7|XD7v`w^NSb``FMPqwVJv*{Yrb%Ju4sym&Zk*NG&M(Xv!=uq{lm=_j?Rs- zXll&;!G6xO2~)o&HRRq>*z~0EB^GmJU4ob6q<*6*oMwGqm*@!A9ED9>(bV~9j>603 zZ|ac7YT)EPsgK4mA;Z{*xxk3Cqa53qOTe6^nQBh*Ct zlIyn8KP&yy=!Y&rN3MymR{AHY#LD|8t;3tRf8lmz}0IP5;R^xaahqsVD4kzHPSVQ9)ybbTb+i@b^ zN%lm%8}Gteyoc;s`hWZP;UpSQ!uxSDK7e&JuEPg$8cxM2IGyY%SdS0EqS3;K@e!Pf zGw@NeXW--b7&hP&WH-RZMs(2Fflnd>2QG~+Jov~Vi#*vZ3MitJ#!h?+pTVbb7P`ou zg-ukNZuFAv#@Xn@W*Rr+9Be^9w$iv2pT!`yVE{v92e2LIVwlEZj9>@O!zeySb`)R0 z=W#xEl06?^#27B1@dE6^mk?l_#&Jww3Lz#jO?DD9h;Si|7vjsPpo|i#WJ{>wE0{%{ z>?~r;VKC78!ov4Bf)8QDv*7hl8WG+vIc<0@Q`~*M6&u|Zo_u%LFC4PZ>@hh_T;@9{M?xXQO{1(5*?{Gi{;b9sd#v^zfkKs}LjqIa%0#D*88lS?`cn;6v8T_5> ZGk6g%;CcLm?DP01{)LxldoaerOSW7pWXY;?8(k_5+jRY!CFar&;I7j zcfS3defF7YSI3-*!bt!&8^D_>@=x!EYMe86bd7ZP?(gpzxL~B$XwS{JwappPgweh) zZCa*nI%dXnP0!4l{-|m6mx@zSJUbE6``fLQW92;G$>wa=&ia;P{(n=popoKy^L)R) zaBWlRtn0gJ*Y#|-zVd&}vG*Vr>*w!HD^p(1c5>-# zhBC<1i?@(`_lD8bbW8=eQyDL3Ij*0!s~~H>M5jini`v?z@*_LB&5oP_>^TM4H9R;l zX9VNpVWm9$Cla-M6OE1Sug_%+@EXE@TXQ<^| z%l3G3wn%T=bZADL%o}@qx*fwb3Q>{!(ocCAE0=Yhv|k0L<)oIXM&)ySGfkB(1XmEh zk?xpII#pX)mGIprg&v(Zm)o~qAo~{eH2tajv*;dLCuFmqcedf~uKuo}dbv~~PDtXc zAqjD6C*s(d`iZQV#J1egdD>f0Ne+3=mOw#QLEtZc6Yg&B@ZZ<@w| zuw02sbi#(6N*nWJE$N+V9~FZFHAroW_Qs9W9~P(LX`|iqavrB#>NVYa^}Z9j?dDD_ zZXA@SxG9}X&a1sU9G4^hAhhhIPFRCjRsQ+vQC}`U6W8*&>MPsJtm4kg_{-J2G|y-? zhbvbI;;%_HILPOt@uAY})O3;Vv(ZkMA*h7irF^NpGZ;_Kb6?WB)V!uq z7NWdIx1~@Mk*PGDX^@|p4tAHguEyY=t_v^fp~IXg&5q^6VX8KLFHMq-&@{qARH;M< zLRm~-SM-~-Rw1lRhc&C^s1U_OtG*0kI!UGq#E-_zk$nl~y*K0Vyl4tX`)SnSItZV!_>_6^qw{LM z#@bIK)*cuSZMWO+0Ik z_Mr6>j$Yf$o7%gs$)fZs?@?pvN#3u<(v!lMT+ETZD4b|E;dCfXoj38*MCSw#vR|F6 z5o?a%mHex{Cmw_^y~_L5SbCD{qp|d)@MZ7Jk$owgXg1+=C{3MrQRydnwPq8~nv-bi zT*6nmwEi-0YVQe$*V5hZl)j?7+@-PHDduP__eJ>Zhq2rp;mf?{3rG0uL3FeqUaNHk zYmVSXP06c0Ypne=V$tMz!O~akQ)B6W_NKA)r|`v(^tSM2Uh{>+YeiFZv>#rpbp&gU z;6_c!EBejW3P*dBy)$29=_~fBvGgSKHJ1JtzT{#KW8urZ<_m|{il*queub}f1Z$4q zMoq~p`pwn~M{+gu!1~Oq{w#C|0@j2V6@L}HCM}(N6)nlQpg27Da?bH`_Ackq$}?E6 z5B0a|_eQ*4pY{64JzUlctMp8rPn>0II^>;I=GUmZq_m3GO8H(wIo9j5e!rxbm-WIL zdvw{FmHJ)JuTfV#aeOgy=zK3EX7858^jrBHC;e7F&yjvB zAMn5VTKJrV4U}L5UV)7`1t;T`6i>#h@M>(LaT8vH*WtC;jMr1#j5p#9I2CWAcq;v$ z{adhw#w~a&-iEhhD~((64xEm6;xwE=@iesIOc*p8I1BH_yKpw%L-B097w2Fb-bZm8 zOl(Jr#uUy)8WwCCZ8-4YB7-c&41DC!PGdXXj}PJlI1e2Z&%+KXO((i3cH(^WU?+_` zaRGYKhg~%8!iO+`-RQ?4#eVF;g&3l72*cQmi!g!@QyjrZa0xEPK8hFPqZq}dG+v7R z_!t6=(Kv>2Od`YtrYKHe8WAp|@iKfIMHG-niDDjQd;&A5P@F-GSsbA804~QBIEX80 zyb^QxB<67yuBLbu7H|y?(Rc`-!e{VlT#M@{UW*%WJr3hWiidF%ZpJM%-h$8KR@{c$ zX}leu!x!**9KjbU9>JIJW!ypI9rz0F!dG!8zDDs*d;?#{QGAo)QG5&E#@#gDjbr!@ zzKeTkya(UI5Ac25iyux;@;ke8yoMCGaY0Dk2h&+#1p0e|M++1$z9X%Yg5<-j>fJKy`+JD>U7 z&-c##ZhB6Kozc=k09G5oF%o3|{-&L>6RB**ssNZe;yy+S_k<+5JR%{aM?&fV`H^|HcKBzDy7LvIVeYiyC-SnvX<>} za;i+XZHsUe=Z)=~Ivv9_N@1BqEvI$9Kh!9PCdA3K<~`Rcn+gep&7Gr2i7~YgBFVn(BH(NMMu#(pql-*s||E^^mOzm9h+}O{(vIR zGKvsq?xi@ko4k}oi`Z6nXomLY*W&j)ZHx!ypz24Js?laRqb*Tb3~DVkzck5l{-$Z{ z3aYiRLND2{TQkNCS&PS~K1yZ3L^Cp%qP_7f^#tYdXu@do=tOhL`Ek?ZmyDj!?QG$| z=0<*XoafSU>5HH)KM++z{w}oa`AJxYSY7_@>e2kxtII*jT6ymJ;`ZDn+<9)kQO(P6 zMyom8xso3R)zB~2`!+dT-5NxGu^5i@SEj}%%5hrs8bK+n z)xuqYJWRi_=ubRarJyzu%vr65r7$8|{i7ezI~$&g_~W#(t#6=vu)D7}erdgZ{aZVF z=xv(D&iJ=t_u2F{Lyp4oXh>gL^y=sP5I<3hUAi-I%`$zc*PvciL;hy5*wEY`I?)Vu zDn8)kG}f9M-)f~2^|CBCy*5H8BfcA>y1LZiozARZ3vBG}#r<6ubiDq*|3B4#=t=FT z1hWlz&FTM%^UTqh`TVZ{;R|LCW8q6)^M%9Dil*qW2l12E5lzhzoYs`OqQB6y!qMKO z58^@e_w0Wx>5s;b{C9uSAB~wK`WiDw_9dA2z7U7=qA48hC#}Pth0j=gN?!cvx|*-C z_LIiigJANP^haZ=o%Baz=7=ASnIroWO!qq$;&5Ixg`@qXb%>kr8MCJFb*(hk9KqU8 z8fy+?>T7bQsFw{`=94)Z%S;NN_rh4_T=!_R6R!I~pDttoZ2XN|R= zG!{+H3nqUtW}h09r-Y+1bHszj%#nQw7ER&nyl4tX`$_8%H#uL7xvucjn!1+e2-cp{ zSaSr+tg=syc~*s^vCO3K#gEK3`xm?zhx4K-9H}MxT1T+v2-daISaSr6zSI@}vUjc} znC31yQ#4ZzSZ0zr8p})ypY<6thq3S_uld3eK6@4&#{8`CwT@uT5uDbPx}4V>!J;p9 zh0l4-5lnrI^UT+n`k3%FW{zlT%pA`1UIdG#@O54^g`@qXbwpEh1gAB1EzJ?EJ*Tne z2$or5pBl^jvp0>IBOWxC*%tm%9Ihprx-LH}np#J2+Oy=PmUz(m!qLwPUurGnf%(j< z|IBU>1S|_bq4-PSWohZ!OK3^OS;gVp`?<#Z*}J@tX1;@!{?L4@c~9e&{;c#z&SAqW zEHN_)pEwQAY>+!k;Cue5~LV)fobHCFnwa=v7i8)jjdGurUXV)L%^%hVN59N&!^ zy52j9*?TiDw3FK=UhSma%xjkPn|bXc{bpWIl72I<*Ga#b7dn7uUI$?n6CVkIGN%JScgx-pw+-B_!LgVsrWR-Q*j1P$9jB*;(C~9K`X7T_$)H8VAE>Dfrl(y zr6fbBRBgZLuFL0pIna6WcWJRe`e5H6ziBJ9MM;bWNAVT@o50Y))SaTF5> zaWSnI;}VonLJ<{;MO1MqCQ+j}i3n5JMe8nHhRd-VSI~L|rtuZb;7WXz;+2@i*RY4y zJ@`7liErR4Tut#Rd<)m&8ho4LHTVv$!}YXYj~j3!zKfe^y$RpL5Ac2L#myA=;)l2e zx6*nmeuSUk$G8nYrFa{Dj-O#4enD{`eu-b&vM6|dn9x;!8c literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_4.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_4.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..66984df1e913ecd464562f4b2d44f20c137c44f3 GIT binary patch literal 8724 zcmeHM{dXK?6@I8twW16RfvEU}zjBV}=PT z?{n|m_r8<%bljUPodn=@7G31|vuj=5&s*ENM|=AA4)hLQINE1*78W*dp0}b&t8-7< zc5K)7?2MhYeLH98$82k$Ql5_Dxyg|3cQ`4}Dfqd9n{hG)-_Loz{cK~ooSVrM(rMSp zHrAeK%y-kdOy2YSg4h37K!s<-h*7*K79GaS{h4jSqb)r(5t(3!ZJi33D zMlb8oR#9-SOt)Q!aBSk%?%p2HvaM27CQ-*rEj5X{<>l>;e9F%_gxx_f}~2*n+*heCPQozr&%araz7P96FEAaoODGoNc71d!T!`(XORN zh1FVAp&ho|RN7jg(#-7C=cpW%Xh4z>JsS_wKv`Y zX^v<$k2_Zi;;6G(#OS z2b?N3TFLlUE0uVVWx46K2|5|(u0eHm$-_^|uh}h|fNlMQxVP`p?t{G>9vNPLX4l!_ z;xhct>mEPGapq{ueE!>j@C7r6vG66X`NH9~qA5C(OZZwxu;vJE)|9-WztURaNUoJ? z5dGoxcOGTToF=?gaWpT?&bWxmGLTYAp={AkP^(bSkZ9A|yOqA7eG7fs3a zIf6xB@(Q2hn#1^ke;jF?sRKtIT84>l&KGkurh3fz5; z!MX;8O|B_jhVx7;jhNwT%xJ-@>FqNzE8C9h~oUR|@st?K8vXlg8HiuIeYnMtmV;u@Rz6u#8M9C6cVg}Rll0;L zcz)zeJl~$*&+WX>MHRR4>LTrSUW=sP&g(GgxAQti`t7`)A^mn<;D7VA@j40XD8V|s z1nY4MPR2{gpNyB`<=8-W173kw;gvWQuO@#gPRDC-8eU8OH2S~$*JC5ujd%mzh&N#q z*-dye&cs`A2F@aX1~%iZu*h0C8|UI}I0tVhe-7S(^RNZ)B)F(Qix_j*M|K~sz?ImKtH@r3d3+QLxEj}xzZ#49 z7!Hsy$br|~Hq#P#G4;s$&MH|Hp5Z{gdxo9x~A4!(!);vRgT z{5|*q?!|p%@52xAWBdsB;{o#bhcBAH>h_b39D;VI0LVJc38bK8j!9mv|hH v;aB7z!>{ohJVEve{1(5*@9-r4K>kVm34g>>_%r#Z@E80QPm_Hbf5U$OV2zY6 literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_5.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_5.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..4c970cc081daae611d6365ebcc9aa7e6936416c2 GIT binary patch literal 8716 zcmeHM`*$3575`9qC|1E&)rwFD)S{EPvorgOXp@GPq^3zpLIrE6n`Dv-s|JWFiwt%0w#D*!e$VT7Eue^@+}OE}P8+scewRdO_ApyGx-|CKdRB{-hX6=aPO- z5+*W@(6hHu>h24p>6uu1p6m?LInPgJ6UjQrxX;nh*uu8y!su=tv!mw#`%VG&j0_FV zJNb!;uv#rws`5DV&iLHaR9M*`g;zRV3D+4v9EU?wQ`Jx&RxT&X#o2NxEXAXTXJzD4 zUNWu0xsu#Y%85B~E$-~!)$2R1Q;bR?>iM0^{gFobS+_+fA#E+@uM)nW=**@AFPBOA z2?g1d+ah#go5vR1mGyhiO#L1DF36uofu6jG-U-=~PdnR4Z_hx_aHC(#oyeY$#9v1e z;Vd1Ap9~sDvTBl~ml|7;XUkV>r#$aWho!KRkINOO%kd{W;;0Z-JF5BOtor4fu5&1? zRHL#Sv6JjfI15r*ZFuT~RLU1+LY6}EY&uB;VQD&^ak|p!Tv}5u51Spn#^8~jOyy2& zZk(@7>s0#kakX@JB(6mIN$4e)Ct)38b@`{OClk8zOj2G}yS{pP!5Y=2gX~JRbV4Im z&DYKq^Kn>-@`ZZeX2&ag!#H0kL=(g1x#^jbywFaUybSqj*jp}?E4%X(wevhs8(kS* z*Qt=md-ApvmqZv!$eHE~voraRvY!eUgd zMu$RfmhUV1t&LVOtj>f>rBnUCen#^++aoOkvOjr5Q94-M81ZE$FKZ_j|7 zrt2K6eJA!`C?7IXQB<0YmBn%2_zR?6jgP-V60)d_hsYIkd>uFgFAr1D+8eJilDe-L-~UDl&(_|&tz&kYw> zV7=D3-nUq<^=|>>6YI5N@)@^$a@6Z;&Z}JVtqw8k(QD#nP3EP3tG(ptJ&UPn_3!)F z@kX5%A36R&qXvtWLrsg7qjA+H)*4%JG)_%&SR46PhuCt6ZLVf)ImFawUi&PTL%iku ze>VDK@l*f)PoqB;D~FmED~J6d)*4!IG)_%&Y(337+8^>2vrfiYkIifO7TbE7v8_R| z_EK{w*4|sJb(3SUacj*QvX#w1(Dd`JO{LHu|&iesPvp%)&Znbj6<4=H15Esmq$wKN~r0zGo6^?d@FX zmTKF$x<$L4Yf<#uxsHi`JJ-Xa-_G@v=(lr0|NYd)brLp7f=zeNQzCuM;a*v$cPV+MGjqJ zyYP0r6YszU*dhJ`?3C7YqgQ-4F2pYE7P}i4p%4AoBX$qog+c7a0EWa5U>`2Vu-IXY zU_UOwDBdl86z{=hxD*G(UyAo)43~?&90&0}h<>^=A) zevBXCUfd`CUi=h4!ToqZ{QdYDevSvlK8T0#Fn)nY#6E&w;@9{U9>s6OKZ@VtcX&+f kWB5J(h(F+Q{7L-d_zV7wC-7JCPvCF(JDwE#B>sW_0!I;M1& literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset.json new file mode 100644 index 000000000000..1cd90ab10b46 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset.json @@ -0,0 +1,77 @@ +{ + "asset": { + "version": "1.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 8 + }, + "Longitude": { + "minimum": -1.3197141326496755, + "maximum": -1.3196686224299798 + }, + "Latitude": { + "minimum": 0.6988476848333334, + "maximum": 0.6988827717222222 + }, + "Height": { + "minimum": 2.4691358024691357, + "maximum": 22.22222222222222 + } + }, + "geometricError": 240, + "root": { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 22.22222222222222 + ] + }, + "geometricError": 120, + "content": { + "uri": "0_0_0.b3dm" + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 22.22222222222222 + ] + }, + "geometricError": 120, + "content": { + "uri": "tileset2.json" + } + } + ], + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "REPLACE" + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset2.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset2.json new file mode 100644 index 000000000000..b6560b4bd5ae --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset2.json @@ -0,0 +1,311 @@ +{ + "asset": { + "version": "1.0" + }, + "geometricError": 240, + "root": { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 22.22222222222222 + ] + }, + "geometricError": 120, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.319686826529935, + 0.6988687369666666, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_0_0.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988687369666666, + -1.319686826529935, + 0.6988792630333333, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_0_1.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988792630333334, + -1.319686826529935, + 0.6988897891, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_0_2.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988582109, + -1.3196731734700649, + 0.6988687369666666, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_1_0.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988687369666666, + -1.3196731734700649, + 0.6988792630333333, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_1_1.b3dm" + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988687369666666, + -1.3196822755099784, + 0.6988722456555555, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_3_3.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988722456555555, + -1.3196822755099784, + 0.6988757543444444, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_3_4.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988757543444445, + -1.3196822755099784, + 0.6988792630333334, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_3_5.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196822755099784, + 0.6988687369666666, + -1.3196777244900217, + 0.6988722456555555, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_4_3.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196822755099784, + 0.6988722456555555, + -1.3196777244900217, + 0.6988757543444444, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_4_4.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196822755099784, + 0.6988757543444445, + -1.3196777244900217, + 0.6988792630333334, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_4_5.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196777244900215, + 0.6988687369666666, + -1.3196731734700649, + 0.6988722456555555, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_5_3.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196777244900215, + 0.6988722456555555, + -1.3196731734700649, + 0.6988757543444444, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_5_4.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196777244900215, + 0.6988757543444445, + -1.3196731734700649, + 0.6988792630333334, + 0, + 2.4691358024691357 + ] + }, + "geometricError": 0, + "content": { + "uri": "2_5_5.b3dm" + } + } + ] + }, + { + "boundingVolume": { + "region": [ + -1.319686826529935, + 0.6988792630333334, + -1.3196731734700649, + 0.6988897891, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_1_2.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196731734700649, + 0.6988582109, + -1.3196595204101946, + 0.6988687369666666, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_2_0.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196731734700649, + 0.6988687369666666, + -1.3196595204101946, + 0.6988792630333333, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_2_1.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3196731734700649, + 0.6988792630333334, + -1.3196595204101946, + 0.6988897891, + 0, + 7.407407407407408 + ] + }, + "geometricError": 60, + "content": { + "uri": "1_2_2.b3dm" + } + } + ] + } +} diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index f14180e0bc06..e180a213f749 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'Scene/Cesium3DTileset', 'Core/Cartesian2', 'Core/Cartesian3', + 'Core/Cartographic', 'Core/Color', 'Core/defined', 'Core/CullingVolume', @@ -14,6 +15,7 @@ defineSuite([ 'Core/Matrix4', 'Core/PerspectiveFrustum', 'Core/PrimitiveType', + 'Core/Ray', 'Core/RequestScheduler', 'Core/Resource', 'Core/Transforms', @@ -35,6 +37,7 @@ defineSuite([ Cesium3DTileset, Cartesian2, Cartesian3, + Cartographic, Color, defined, CullingVolume, @@ -47,6 +50,7 @@ defineSuite([ Matrix4, PerspectiveFrustum, PrimitiveType, + Ray, RequestScheduler, Resource, Transforms, @@ -66,6 +70,10 @@ defineSuite([ when) { 'use strict'; + // It's not easily possible to mock the asynchronous pick functions + // so don't run those tests when using the WebGL stub + var webglStub = !!window.webglStub; + var scene; var centerLongitude = -1.31968; var centerLatitude = 0.698874; @@ -76,6 +84,9 @@ defineSuite([ // Parent tile with no content and four child tiles with content var tilesetEmptyRootUrl = 'Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/tileset.json'; + // Tileset with 3 levels of uniform subdivision + var tilesetUniform = 'Data/Cesium3DTiles/Tilesets/TilesetUniform/tileset.json'; + var tilesetReplacement1Url = 'Data/Cesium3DTiles/Tilesets/TilesetReplacement1/tileset.json'; var tilesetReplacement2Url = 'Data/Cesium3DTiles/Tilesets/TilesetReplacement2/tileset.json'; var tilesetReplacement3Url = 'Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset.json'; @@ -3280,4 +3291,185 @@ defineSuite([ }); }); + function sampleHeightMostDetailed(cartographics, objectsToExclude) { + var result; + var completed = false; + scene.sampleHeightMostDetailed(cartographics, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } + + function drillPickFromRayMostDetailed(ray, limit, objectsToExclude) { + var result; + var completed = false; + scene.drillPickFromRayMostDetailed(ray, limit, objectsToExclude).then(function(pickResult) { + result = pickResult; + completed = true; + }); + return pollToPromise(function() { + // Scene requires manual updates in the tests to move along the promise + scene.render(); + return completed; + }).then(function() { + return result; + }); + } + + describe('most detailed height queries', function() { + it('tileset is offscreen', function() { + if (webglStub) { + return; + } + + viewNothing(); + + // Tileset uses replacement refinement so only one tile should be loaded and selected during most detailed picking + var centerCartographic = new Cartographic(-1.3196799798348215, 0.6988740001506679, 2.4683731133709323); + var cartographics = [centerCartographic]; + + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + return sampleHeightMostDetailed(cartographics).then(function() { + expect(centerCartographic.height).toEqualEpsilon(2.47, CesiumMath.EPSILON1); + var statisticsAsync = tileset._statisticsLastAsync; + var statisticsRender = tileset._statisticsLastRender; + expect(statisticsAsync.numberOfCommands).toBe(1); + expect(statisticsAsync.numberOfTilesWithContentReady).toBe(1); + expect(statisticsAsync.selected).toBe(1); + expect(statisticsAsync.visited).toBeGreaterThan(1); + expect(statisticsAsync.numberOfTilesTotal).toBe(21); + expect(statisticsRender.selected).toBe(0); + }); + }); + }); + + it('tileset is onscreen', function() { + if (webglStub) { + return; + } + + viewAllTiles(); + + // Tileset uses replacement refinement so only one tile should be loaded and selected during most detailed picking + var centerCartographic = new Cartographic(-1.3196799798348215, 0.6988740001506679, 2.4683731133709323); + var cartographics = [centerCartographic]; + + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + return sampleHeightMostDetailed(cartographics).then(function() { + expect(centerCartographic.height).toEqualEpsilon(2.47, CesiumMath.EPSILON1); + var statisticsAsync = tileset._statisticsLastAsync; + var statisticsRender = tileset._statisticsLastRender; + expect(statisticsAsync.numberOfCommands).toBe(1); + expect(statisticsAsync.numberOfTilesWithContentReady).toBeGreaterThan(1); + expect(statisticsAsync.selected).toBe(1); + expect(statisticsAsync.visited).toBeGreaterThan(1); + expect(statisticsAsync.numberOfTilesTotal).toBe(21); + expect(statisticsRender.selected).toBeGreaterThan(0); + }); + }); + }); + + it('tileset uses additive refinement', function() { + if (webglStub) { + return; + } + + viewNothing(); + + var originalLoadJson = Cesium3DTileset.loadJson; + spyOn(Cesium3DTileset, 'loadJson').and.callFake(function(tilesetUrl) { + return originalLoadJson(tilesetUrl).then(function(tilesetJson) { + tilesetJson.root.refine = 'ADD'; + return tilesetJson; + }); + }); + + var offcenterCartographic = new Cartographic(-1.3196754112739246, 0.6988705057695633, 2.467395745774971); + var cartographics = [offcenterCartographic]; + + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + return sampleHeightMostDetailed(cartographics).then(function() { + var statistics = tileset._statisticsLastAsync; + expect(offcenterCartographic.height).toEqualEpsilon(7.407, CesiumMath.EPSILON1); + expect(statistics.numberOfCommands).toBe(3); // One for each level of the tree + expect(statistics.numberOfTilesWithContentReady).toBeGreaterThanOrEqualTo(3); + expect(statistics.selected).toBe(3); + expect(statistics.visited).toBeGreaterThan(3); + expect(statistics.numberOfTilesTotal).toBe(21); + }); + }); + }); + + it('drill picks multiple features when tileset uses additive refinement', function() { + if (webglStub) { + return; + } + + viewNothing(); + var ray = new Ray(scene.camera.positionWC, scene.camera.directionWC); + + var originalLoadJson = Cesium3DTileset.loadJson; + spyOn(Cesium3DTileset, 'loadJson').and.callFake(function(tilesetUrl) { + return originalLoadJson(tilesetUrl).then(function(tilesetJson) { + tilesetJson.root.refine = 'ADD'; + return tilesetJson; + }); + }); + + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + return drillPickFromRayMostDetailed(ray).then(function(results) { + expect(results.length).toBe(3); + expect(results[0].object.content.url.indexOf('0_0_0.b3dm') > -1).toBe(true); + expect(results[1].object.content.url.indexOf('1_1_1.b3dm') > -1).toBe(true); + expect(results[2].object.content.url.indexOf('2_4_4.b3dm') > -1).toBe(true); + console.log(results); + }); + }); + }); + + it('works when tileset cache is disabled', function() { + if (webglStub) { + return; + } + viewNothing(); + var centerCartographic = new Cartographic(-1.3196799798348215, 0.6988740001506679, 2.4683731133709323); + var cartographics = [centerCartographic]; + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + tileset.maximumMemoryUsage = 0; + return sampleHeightMostDetailed(cartographics).then(function() { + expect(centerCartographic.height).toEqualEpsilon(2.47, CesiumMath.EPSILON1); + }); + }); + }); + + it('multiple samples', function() { + if (webglStub) { + return; + } + + viewNothing(); + + var centerCartographic = new Cartographic(-1.3196799798348215, 0.6988740001506679); + var offcenterCartographic = new Cartographic(-1.3196754112739246, 0.6988705057695633); + var missCartographic = new Cartographic(-1.3196096042084076, 0.6988703290845706); + var cartographics = [centerCartographic, offcenterCartographic, missCartographic]; + + return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then(function(tileset) { + return sampleHeightMostDetailed(cartographics).then(function() { + expect(centerCartographic.height).toEqualEpsilon(2.47, CesiumMath.EPSILON1); + expect(offcenterCartographic.height).toEqualEpsilon(2.47, CesiumMath.EPSILON1); + expect(missCartographic.height).toBeUndefined(); + expect(tileset._statisticsLastAsync.numberOfTilesWithContentReady).toBe(2); + }); + }); + }); + }); + }, 'WebGL'); diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 40951bc2ccf3..0ee351508385 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -57,7 +57,7 @@ defineSuite([ 'use strict'; // It's not easily possible to mock the asynchronous pick functions - // so don't run the tests if running with the WebGL stub + // so don't run those tests when using the WebGL stub var webglStub = !!window.webglStub; var scene; @@ -1246,6 +1246,32 @@ defineSuite([ }); }); + it('excludes tileset in objectsToExclude list', function() { + if (webglStub) { + return; + } + scene.camera.setView({ destination : offscreenRectangle }); + return createTileset().then(function(tileset) { + var objectsToExclude = [tileset]; + return pickFromRayMostDetailed(primitiveRay, objectsToExclude).then(function(result) { + expect(result).toBeUndefined(); + }); + }); + }); + + it('excludes tileset whose show is false', function() { + if (webglStub) { + return; + } + scene.camera.setView({ destination : offscreenRectangle }); + return createTileset().then(function(tileset) { + tileset.show = false; + return pickFromRayMostDetailed(primitiveRay).then(function(result) { + expect(result).toBeUndefined(); + }); + }); + }); + it('picks a primitive', function() { if (webglStub) { return; From a5078f52b9996791045c31c8096ec83c927c1566 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:07:04 -0500 Subject: [PATCH 24/32] Added updateAsync function to Cesium3DTileset --- Source/Scene/Cesium3DTileset.js | 61 +++++++++++++++++++-------------- Source/Scene/Scene.js | 2 +- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 0379babd1a9d..0cf162c621fe 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1884,34 +1884,31 @@ define([ /////////////////////////////////////////////////////////////////////////// - /** - * @private - */ - Cesium3DTileset.prototype.update = function(frameState) { + function update(tileset, frameState) { if (frameState.mode === SceneMode.MORPHING) { return false; } - if (!this.show || !this.ready) { + if (!tileset.show || !tileset.ready) { return false; } - if (!defined(this._loadTimestamp)) { - this._loadTimestamp = JulianDate.clone(frameState.time); + if (!defined(tileset._loadTimestamp)) { + tileset._loadTimestamp = JulianDate.clone(frameState.time); } // Update clipping planes - var clippingPlanes = this._clippingPlanes; + var clippingPlanes = tileset._clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { clippingPlanes.update(frameState); - if (this._useBoundingSphereForClipping) { - this._clippingPlaneOffsetMatrix = Transforms.eastNorthUpToFixedFrame(this.boundingSphere.center); + if (tileset._useBoundingSphereForClipping) { + tileset._clippingPlaneOffsetMatrix = Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center); } } - this._timeSinceLoad = Math.max(JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000, 0.0); + tileset._timeSinceLoad = Math.max(JulianDate.secondsDifference(frameState.time, tileset._loadTimestamp) * 1000, 0.0); - this._skipLevelOfDetail = this.skipLevelOfDetail && !defined(this._classificationType) && !this._disableSkipLevelOfDetail && !this._allTilesAdditive; + tileset._skipLevelOfDetail = tileset.skipLevelOfDetail && !defined(tileset._classificationType) && !tileset._disableSkipLevelOfDetail && !tileset._allTilesAdditive; // Do out-of-core operations (new content requests, cache removal, // process new tiles) only during the render pass. @@ -1920,45 +1917,45 @@ define([ var isPick = passes.pick; var isAsync = passes.async; - var statistics = this._statistics; + var statistics = tileset._statistics; statistics.clear(); - if (this.dynamicScreenSpaceError) { - updateDynamicScreenSpaceError(this, frameState); + if (tileset.dynamicScreenSpaceError) { + updateDynamicScreenSpaceError(tileset, frameState); } if (isRender) { - this._cache.reset(); + tileset._cache.reset(); } var ready; if (isAsync) { - ready = Cesium3DTilesetAsyncTraversal.selectTiles(this, frameState); + ready = Cesium3DTilesetAsyncTraversal.selectTiles(tileset, frameState); } else { - ready = Cesium3DTilesetTraversal.selectTiles(this, frameState); + ready = Cesium3DTilesetTraversal.selectTiles(tileset, frameState); } if (isRender || isAsync) { - requestTiles(this); + requestTiles(tileset); } if (isRender) { - processTiles(this, frameState); + processTiles(tileset, frameState); } - updateTiles(this, frameState); + updateTiles(tileset, frameState); if (isRender) { - unloadTiles(this); + unloadTiles(tileset); // Events are raised (added to the afterRender queue) here since promises // may resolve outside of the update loop that then raise events, e.g., // model's readyPromise. - raiseLoadProgressEvent(this, frameState); + raiseLoadProgressEvent(tileset, frameState); if (statistics.selected !== 0) { - var credits = this._credits; + var credits = tileset._credits; if (defined(credits)) { var length = credits.length; for (var i = 0; i < length; i++) { @@ -1969,10 +1966,24 @@ define([ } // Update last statistics - var statisticsLast = isAsync ? this._statisticsLastAsync : (isPick ? this._statisticsLastPick : this._statisticsLastRender); + var statisticsLast = isAsync ? tileset._statisticsLastAsync : (isPick ? tileset._statisticsLastPick : tileset._statisticsLastRender); Cesium3DTilesetStatistics.clone(statistics, statisticsLast); return ready; + } + + /** + * @private + */ + Cesium3DTileset.prototype.update = function(frameState) { + update(this, frameState); + }; + + /** + * @private + */ + Cesium3DTileset.prototype.updateAsync = function(frameState) { + return update(this, frameState); }; /** diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 7e75e887d381..27f507c610e0 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3692,7 +3692,7 @@ define([ var primitive = primitives[i]; if (scene.primitives.contains(primitive) && primitive.show) { // Only update primitives that are still contained in the scene's primitive collection and are still visible - ready = primitive.update(frameState) && ready; + ready = primitive.updateAsync(frameState) && ready; } } From 470bd0ff8830c9d575bd4557ef9c57f43aaaa624 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:07:12 -0500 Subject: [PATCH 25/32] Add terrain to Sandcastle demo --- Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html index 85f17c14817b..c8ee45d89b89 100644 --- a/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html +++ b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html @@ -29,7 +29,9 @@ function startup(Cesium) { 'use strict'; //Sandcastle_Begin -var viewer = new Cesium.Viewer('cesiumContainer'); +var viewer = new Cesium.Viewer('cesiumContainer', { + terrainProvider : Cesium.createWorldTerrain() +}); var scene = viewer.scene; var tileset = scene.primitives.add( From d0c16e393e408a20a90cb33ac9b8f8d0e3c831b8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:14:42 -0500 Subject: [PATCH 26/32] Remove minimumGeometricError --- Source/Scene/Cesium3DTilesetAsyncTraversal.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetAsyncTraversal.js b/Source/Scene/Cesium3DTilesetAsyncTraversal.js index 76b46189f348..d795348e5c0d 100644 --- a/Source/Scene/Cesium3DTilesetAsyncTraversal.js +++ b/Source/Scene/Cesium3DTilesetAsyncTraversal.js @@ -26,7 +26,6 @@ define([ tileset._selectedTiles.length = 0; tileset._requestedTiles.length = 0; tileset._hasMixedContent = false; - var minimumGeometricError = 0; var ready = true; @@ -37,10 +36,6 @@ define([ return ready; } - if (tileset._geometricError <= minimumGeometricError) { - return ready; - } - var stack = asyncTraversal.stack; stack.push(tileset.root); @@ -50,7 +45,7 @@ define([ var tile = stack.pop(); var add = tile.refine === Cesium3DTileRefine.ADD; var replace = tile.refine === Cesium3DTileRefine.REPLACE; - var traverse = canTraverse(tileset, minimumGeometricError, tile); + var traverse = canTraverse(tileset, tile); if (traverse) { updateAndPushChildren(tileset, tile, stack, frameState); @@ -86,7 +81,7 @@ define([ return !hasEmptyContent(tile) && tile.contentUnloaded; } - function canTraverse(tileset, minimumGeometricError, tile) { + function canTraverse(tileset, tile) { if (tile.children.length === 0) { return false; } @@ -101,7 +96,7 @@ define([ return true; } - return tile.geometricError >= minimumGeometricError; + return true; // Keep traversing until a leave is hit } function updateAndPushChildren(tileset, tile, stack, frameState) { From e4572137c0db683d830f636a930e5f8e7eeda52d Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:17:07 -0500 Subject: [PATCH 27/32] Formatting --- Source/Scene/Cesium3DTilesetAsyncTraversal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Scene/Cesium3DTilesetAsyncTraversal.js b/Source/Scene/Cesium3DTilesetAsyncTraversal.js index d795348e5c0d..604961041135 100644 --- a/Source/Scene/Cesium3DTilesetAsyncTraversal.js +++ b/Source/Scene/Cesium3DTilesetAsyncTraversal.js @@ -43,8 +43,8 @@ define([ asyncTraversal.stackMaximumLength = Math.max(asyncTraversal.stackMaximumLength, stack.length); var tile = stack.pop(); - var add = tile.refine === Cesium3DTileRefine.ADD; - var replace = tile.refine === Cesium3DTileRefine.REPLACE; + var add = (tile.refine === Cesium3DTileRefine.ADD); + var replace = (tile.refine === Cesium3DTileRefine.REPLACE); var traverse = canTraverse(tileset, tile); if (traverse) { From 9a2e7eb2b5012c51ce4b8a8ad4066c88c4351a53 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:22:50 -0500 Subject: [PATCH 28/32] Renamed asyncLoaders to asyncRayPicks --- Source/Scene/Scene.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 27f507c610e0..46613243432f 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -170,7 +170,7 @@ define([ }; }; - function AsyncLoader(ray, primitives) { + function AsyncRayPick(ray, primitives) { this.ray = ray; this.primitives = primitives; this.ready = false; @@ -291,7 +291,7 @@ define([ this._primitives = new PrimitiveCollection(); this._groundPrimitives = new PrimitiveCollection(); - this._asyncLoaders = []; + this._asyncRayPicks = []; this._logDepthBuffer = context.fragmentDepth; this._logDepthBufferDirty = true; @@ -3021,7 +3021,7 @@ define([ scene.globe.update(frameState); } - updateAsyncLoaders(scene); + updateAsyncRayPicks(scene); frameState.creditDisplay.update(); } @@ -3664,7 +3664,7 @@ define([ camera.right = right; } - function updateAsyncLoader(scene, asyncLoader) { + function updateAsyncRayPick(scene, asyncRayPick) { var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3672,8 +3672,8 @@ define([ var view = scene._pickOffscreenView; scene._view = view; - var ray = asyncLoader.ray; - var primitives = asyncLoader.primitives; + var ray = asyncRayPick.ray; + var primitives = asyncRayPick.primitives; updateCameraFromRay(ray, view.camera); @@ -3702,23 +3702,23 @@ define([ scene._view = scene._defaultView; if (ready) { - asyncLoader.deferred.resolve(); + asyncRayPick.deferred.resolve(); } return ready; } - function updateAsyncLoaders(scene) { - var asyncLoaders = scene._asyncLoaders; - for (var i = 0; i < asyncLoaders.length; ++i) { - var ready = updateAsyncLoader(scene, asyncLoaders[i]); + function updateAsyncRayPicks(scene) { + var asyncRayPicks = scene._asyncRayPicks; + for (var i = 0; i < asyncRayPicks.length; ++i) { + var ready = updateAsyncRayPick(scene, asyncRayPicks[i]); if (ready) { - asyncLoaders.splice(i--, 1); + asyncRayPicks.splice(i--, 1); } } } - function launchAsyncLoader(scene, ray, objectsToExclude, callback) { + function launchAsyncRayPick(scene, ray, objectsToExclude, callback) { var asyncPrimitives = []; var primitives = scene.primitives; var length = primitives.length; @@ -3734,9 +3734,9 @@ define([ return when.resolve(callback()); } - var asyncLoader = new AsyncLoader(ray, asyncPrimitives); - scene._asyncLoaders.push(asyncLoader); - return asyncLoader.promise.then(function() { + var asyncRayPick = new AsyncRayPick(ray, asyncPrimitives); + scene._asyncRayPicks.push(asyncRayPick); + return asyncRayPick.promise.then(function() { return callback(); }); } @@ -3907,7 +3907,7 @@ define([ var that = this; ray = Ray.clone(ray); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - return launchAsyncLoader(this, ray, objectsToExclude, function() { + return launchAsyncRayPick(this, ray, objectsToExclude, function() { return pickFromRay(that, ray, objectsToExclude, false, true); }); }; @@ -3935,7 +3935,7 @@ define([ var that = this; ray = Ray.clone(ray); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - return launchAsyncLoader(this, ray, objectsToExclude, function() { + return launchAsyncRayPick(this, ray, objectsToExclude, function() { return drillPickFromRay(that, ray, limit, objectsToExclude, false, true); }); }; @@ -3976,7 +3976,7 @@ define([ function sampleHeightMostDetailed(scene, cartographic, objectsToExclude) { var ray = getRayForSampleHeight(scene, cartographic); - return launchAsyncLoader(scene, ray, objectsToExclude, function() { + return launchAsyncRayPick(scene, ray, objectsToExclude, function() { var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { return getHeightFromCartesian(scene, pickResult.position); @@ -3986,7 +3986,7 @@ define([ function clampToHeightMostDetailed(scene, cartesian, objectsToExclude, result) { var ray = getRayForClampToHeight(scene, cartesian); - return launchAsyncLoader(scene, ray, objectsToExclude, function() { + return launchAsyncRayPick(scene, ray, objectsToExclude, function() { var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); From 3d64294759eadb8391d4546f466fec90dcd582ad Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:50:19 -0500 Subject: [PATCH 29/32] Scene updates --- Source/Scene/Scene.js | 70 ++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 46613243432f..0a498ebbe5fb 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3690,9 +3690,11 @@ define([ var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { var primitive = primitives[i]; - if (scene.primitives.contains(primitive) && primitive.show) { + if (primitive.show && scene.primitives.contains(primitive)) { // Only update primitives that are still contained in the scene's primitive collection and are still visible - ready = primitive.updateAsync(frameState) && ready; + // Update primitives continually until all primitives are ready. This way tiles are never removed from the cache. + var primitiveReady = primitive.updateAsync(frameState); + ready = (ready && primitiveReady); } } @@ -3709,10 +3711,10 @@ define([ } function updateAsyncRayPicks(scene) { + // Modifies array during iteration var asyncRayPicks = scene._asyncRayPicks; for (var i = 0; i < asyncRayPicks.length; ++i) { - var ready = updateAsyncRayPick(scene, asyncRayPicks[i]); - if (ready) { + if (updateAsyncRayPick(scene, asyncRayPicks[i])) { asyncRayPicks.splice(i--, 1); } } @@ -3840,7 +3842,7 @@ define([ * @private * * @param {Ray} ray The ray. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. * @returns {Object} An object containing the object and position of the first intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. @@ -3870,7 +3872,7 @@ define([ * * @param {Ray} ray The ray. * @param {Number} [limit=Number.MAX_VALUE] If supplied, stop finding intersections after this many intersections. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. * @returns {Object[]} List of objects containing the object and position of each intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. @@ -3892,7 +3894,7 @@ define([ * @private * * @param {Ray} ray The ray. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. * @returns {Promise.} A promise that resolves to an object containing the object and position of the first intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. @@ -3920,7 +3922,7 @@ define([ * * @param {Ray} ray The ray. * @param {Number} [limit=Number.MAX_VALUE] If supplied, stop finding intersections after this many intersections. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to exclude from the ray intersection. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. * @returns {Promise.} A promise that resolves to a list of objects containing the object and position of each intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. @@ -4003,9 +4005,14 @@ define([ *

* * @param {Cartographic} position The cartographic position to sample height from. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not sample height from. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not sample height from. * @returns {Number} The height. This may be undefined if there was no scene geometry to sample height from. * + * @example + * var position = new Cesium.Cartographic(-1.31968, 0.698874); + * var height = viewer.scene.sampleHeight(position); + * console.log(height); + * * @see Scene#clampToHeight * @see Scene#clampToHeightMostDetailed * @see Scene#sampleHeightMostDetailed @@ -4040,10 +4047,15 @@ define([ *

* * @param {Cartesian3} cartesian The cartesian position. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not clamp to. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not clamp to. * @param {Cartesian3} [result] An optional object to return the clamped position. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. This may be undefined if there was no scene geometry to clamp to. * + * @example + * // Clamp an entity to the underlying scene geometry + * var position = entity.position.getValue(Cesium.JulianDate.now()); + * entity.position = viewer.scene.clampToHeight(position); + * * @see Scene#sampleHeight * @see Scene#sampleHeightMostDetailed * @see Scene#clampToHeightMostDetailed @@ -4075,9 +4087,20 @@ define([ * geometry can be sampled at that location, or another error occurs, the height is set to undefined. * * @param {Cartographic[]} positions The cartographic positions to update with sampled heights. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not sample height from. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not sample height from. * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * + * @example + * var positions = [ + * new Cesium.Cartographic(-1.31968, 0.69887), + * new Cesium.Cartographic(-1.10489, 0.83923) + * ]; + * var promise = viewer.scene.sampleHeightMostDetailed(positions); + * promise.then(function(updatedPosition) { + * // positions[0].height and positions[1].height have been updated. + * // updatedPositions is just a reference to positions. + * } + * * @see Scene#sampleHeight * * @exception {DeveloperError} sampleHeightMostDetailed is only supported in 3D mode. @@ -4094,14 +4117,14 @@ define([ } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - var i; var length = positions.length; var promises = new Array(length); - for (i = 0; i < length; ++i) { + for (var i = 0; i < length; ++i) { promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude); } return when.all(promises).then(function(heights) { - for (i = 0; i < length; ++i) { + var length = heights.length; + for (var i = 0; i < length; ++i) { positions[i].height = heights[i]; } return positions; @@ -4115,9 +4138,20 @@ define([ * can be sampled at that location, or another error occurs, the element in the array is set to undefined. * * @param {Cartesian3[]} cartesians The cartesian positions to update with clamped positions. - * @param {Object[]} [objectsToExclude] A list of primitives, entities, or features to not clamp to. + * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not clamp to. * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * + * @example + * var cartesians = [ + * entities[0].position.getValue(Cesium.JulianDate.now()), + * entities[1].position.getValue(Cesium.JulianDate.now()) + * ]; + * var promise = viewer.scene.clampToHeightMostDetailed(cartesians); + * promise.then(function(updatedCartesians) { + * entities[0].position = updatedCartesians[0]; + * entities[1].position = updatedCartesians[1]; + * } + * * @see Scene#clampToHeight * * @exception {DeveloperError} clampToHeightMostDetailed is only supported in 3D mode. @@ -4134,14 +4168,14 @@ define([ } //>>includeEnd('debug'); objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude; - var i; var length = cartesians.length; var promises = new Array(length); - for (i = 0; i < length; ++i) { + for (var i = 0; i < length; ++i) { promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude, cartesians[i]); } return when.all(promises).then(function(clampedCartesians) { - for (i = 0; i < length; ++i) { + var length = clampedCartesians.length; + for (var i = 0; i < length; ++i) { cartesians[i] = clampedCartesians[i]; } return cartesians; From b5cafd7281b15cfc7fb5ca7b906423097481c34e Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 18:58:46 -0500 Subject: [PATCH 30/32] Add to doc --- Source/Scene/Scene.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 980fee1febc9..be9e08da12ae 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -4013,7 +4013,8 @@ define([ /** * Returns the height of scene geometry at the given cartographic position or undefined if there was no - * scene geometry to sample height from. May be used to clamp objects to the globe, 3D Tiles, or primitives in the scene. + * scene geometry to sample height from. The height of the input position is ignored. May be used to clamp objects to + * the globe, 3D Tiles, or primitives in the scene. *

* This function only samples height from globe tiles and 3D Tiles that are rendered in the current view. Samples height * from all other primitives regardless of their visibility. @@ -4097,9 +4098,10 @@ define([ /** * Initiates an asynchronous {@link Scene#sampleHeight} query for an array of {@link Cartographic} positions - * using the maximum level of detail for 3D Tilesets in the scene. Returns a promise that is resolved when - * the query completes. Each point height is modified in place. If a height cannot be determined because no - * geometry can be sampled at that location, or another error occurs, the height is set to undefined. + * using the maximum level of detail for 3D Tilesets in the scene. The height of the input positions is ignored. + * Returns a promise that is resolved when the query completes. Each point height is modified in place. + * If a height cannot be determined because no geometry can be sampled at that location, or another error occurs, + * the height is set to undefined. * * @param {Cartographic[]} positions The cartographic positions to update with sampled heights. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not sample height from. From 3876766af8bd3d4df5216030b1e1bfd9e4dd324b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Nov 2018 19:01:06 -0500 Subject: [PATCH 31/32] Merge conflict fix --- Source/Scene/Cesium3DTileset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 11197eb74453..8b394390f0b6 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1917,7 +1917,7 @@ define([ // Update clipping planes var clippingPlanes = tileset._clippingPlanes; - this._clippingPlanesOriginMatrixDirty = true; + tileset._clippingPlanesOriginMatrixDirty = true; if (defined(clippingPlanes) && clippingPlanes.enabled) { clippingPlanes.update(frameState); } From d115e172a745b6e276b6bed80eef929b65ba453c Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 7 Nov 2018 16:07:44 -0500 Subject: [PATCH 32/32] Updated CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 77259ebefb55..832b23c0bc59 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,9 @@ Change Log ### 1.52 - 2018-12-03 ##### Additions :tada: +* Added functions to get the most detailed height of 3D Tiles on-screen or off-screen. [#7115](https://github.com/AnalyticalGraphicsInc/cesium/pull/7115) + * Added `Scene.sampleHeightMostDetailed`, an asynchronous version of `Scene.sampleHeight` that uses the maximum level of detail for 3D Tiles. + * Added `Scene.clampToHeightMostDetailed`, an asynchronous version of `Scene.clampToHeight` that uses the maximum level of detail for 3D Tiles. * Added `Scene.invertClassificationSupported` for checking if invert classification is supported. * Added `computeLineSegmentLineSegmentIntersection` to `Intersections2D`. [#7228](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7228)