diff --git a/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html
new file mode 100644
index 000000000000..c8ee45d89b89
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+
Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.jpg b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.jpg
new file mode 100644
index 000000000000..bda74e9bdfb4
Binary files /dev/null and b/Apps/Sandcastle/gallery/Sample Height from 3D Tiles.jpg differ
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)
diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js
index e0449e75ae1b..8b394390f0b6 100644
--- a/Source/Scene/Cesium3DTileset.js
+++ b/Source/Scene/Cesium3DTileset.js
@@ -29,6 +29,7 @@ define([
'./Cesium3DTileContentState',
'./Cesium3DTileOptimizations',
'./Cesium3DTileRefine',
+ './Cesium3DTilesetAsyncTraversal',
'./Cesium3DTilesetCache',
'./Cesium3DTilesetStatistics',
'./Cesium3DTilesetTraversal',
@@ -73,6 +74,7 @@ define([
Cesium3DTileContentState,
Cesium3DTileOptimizations,
Cesium3DTileRefine,
+ Cesium3DTilesetAsyncTraversal,
Cesium3DTilesetCache,
Cesium3DTilesetStatistics,
Cesium3DTilesetTraversal,
@@ -204,6 +206,7 @@ define([
this._statistics = new Cesium3DTilesetStatistics();
this._statisticsLastRender = new Cesium3DTilesetStatistics();
this._statisticsLastPick = new Cesium3DTilesetStatistics();
+ this._statisticsLastAsync = new Cesium3DTilesetStatistics();
this._tilesLoaded = false;
this._initialTilesLoaded = false;
@@ -1707,9 +1710,9 @@ define([
function updateTiles(tileset, frameState) {
tileset._styleEngine.applyStyle(tileset, frameState);
+ var statistics = tileset._statistics;
var passes = frameState.passes;
var isRender = passes.render;
- var statistics = tileset._statistics;
var commandList = frameState.commandList;
var numberOfInitialCommands = commandList.length;
var selectedTiles = tileset._selectedTiles;
@@ -1899,69 +1902,76 @@ define([
///////////////////////////////////////////////////////////////////////////
- /**
- * @private
- */
- Cesium3DTileset.prototype.update = function(frameState) {
+ function update(tileset, frameState) {
if (frameState.mode === SceneMode.MORPHING) {
- return;
+ return false;
}
- if (!this.show || !this.ready) {
- return;
+ 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;
- this._clippingPlanesOriginMatrixDirty = true;
+ var clippingPlanes = tileset._clippingPlanes;
+ tileset._clippingPlanesOriginMatrixDirty = true;
if (defined(clippingPlanes) && clippingPlanes.enabled) {
clippingPlanes.update(frameState);
}
- 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.
var passes = frameState.passes;
var isRender = passes.render;
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(tileset, frameState);
+ } else {
+ ready = Cesium3DTilesetTraversal.selectTiles(tileset, frameState);
}
- Cesium3DTilesetTraversal.selectTiles(this, frameState);
+ if (isRender || isAsync) {
+ requestTiles(tileset);
+ }
if (isRender) {
- requestTiles(this);
- 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++) {
@@ -1972,8 +1982,24 @@ define([
}
// Update last statistics
- var statisticsLast = 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/Cesium3DTilesetAsyncTraversal.js b/Source/Scene/Cesium3DTilesetAsyncTraversal.js
new file mode 100644
index 000000000000..604961041135
--- /dev/null
+++ b/Source/Scene/Cesium3DTilesetAsyncTraversal.js
@@ -0,0 +1,137 @@
+define([
+ '../Core/Intersect',
+ '../Core/ManagedArray',
+ './Cesium3DTileRefine'
+ ], function(
+ Intersect,
+ ManagedArray,
+ Cesium3DTileRefine) {
+ 'use strict';
+
+ /**
+ * Traversal that loads all leaves that intersect the camera frustum.
+ * Used to determine ray-tileset intersections during a pickFromRayMostDetailed call.
+ *
+ * @private
+ */
+ function Cesium3DTilesetAsyncTraversal() {
+ }
+
+ var asyncTraversal = {
+ stack : new ManagedArray(),
+ stackMaximumLength : 0
+ };
+
+ Cesium3DTilesetAsyncTraversal.selectTiles = function(tileset, frameState) {
+ tileset._selectedTiles.length = 0;
+ tileset._requestedTiles.length = 0;
+ tileset._hasMixedContent = false;
+
+ var ready = true;
+
+ var root = tileset.root;
+ root.updateVisibility(frameState);
+
+ if (!isVisible(root)) {
+ return ready;
+ }
+
+ var stack = asyncTraversal.stack;
+ stack.push(tileset.root);
+
+ while (stack.length > 0) {
+ 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 traverse = canTraverse(tileset, 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(tileset);
+ touchTile(tileset, tile);
+ }
+
+ asyncTraversal.stack.trim(asyncTraversal.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, 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 true; // Keep traversing until a leave is hit
+ }
+
+ 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) {
+ tile._priority = 0.0; // Highest priority
+ tileset._requestedTiles.push(tile);
+ }
+ }
+
+ function touchTile(tileset, tile) {
+ tileset._cache.touch(tile);
+ }
+
+ function visitTile(tileset) {
+ ++tileset.statistics.visited;
+ }
+
+ function selectDesiredTile(tileset, tile, frameState) {
+ if (tile.contentAvailable && (tile.contentVisibility(frameState) !== Intersect.OUTSIDE)) {
+ tileset._selectedTiles.push(tile);
+ }
+ }
+
+ return Cesium3DTilesetAsyncTraversal;
+});
diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js
index 23c4e52a018f..e9c0a0e962fe 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, frameState) {
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/Scene.js b/Source/Scene/Scene.js
index 504883467866..be9e08da12ae 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -49,9 +49,11 @@ define([
'../Renderer/ShaderProgram',
'../Renderer/ShaderSource',
'../Renderer/Texture',
+ '../ThirdParty/when',
'./BrdfLutGenerator',
'./Camera',
'./Cesium3DTileFeature',
+ './Cesium3DTileset',
'./CreditDisplay',
'./DebugCameraPrimitive',
'./DepthPlane',
@@ -128,9 +130,11 @@ define([
ShaderProgram,
ShaderSource,
Texture,
+ when,
BrdfLutGenerator,
Camera,
Cesium3DTileFeature,
+ Cesium3DTileset,
CreditDisplay,
DebugCameraPrimitive,
DepthPlane,
@@ -166,6 +170,14 @@ define([
};
};
+ function AsyncRayPick(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 +291,8 @@ define([
this._primitives = new PrimitiveCollection();
this._groundPrimitives = new PrimitiveCollection();
+ this._asyncRayPicks = [];
+
this._logDepthBuffer = context.fragmentDepth;
this._logDepthBufferDirty = true;
@@ -885,13 +899,14 @@ define([
},
/**
- * Returns true
if the {@link Scene#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() {
@@ -900,13 +915,14 @@ define([
},
/**
- * Returns true
if the {@link Scene#clampToHeight} function is supported.
+ * Returns true
if the {@link Scene#clampToHeight} and {@link Scene#clampToHeightMostDetailed} functions are supported.
* @memberof Scene.prototype
*
* @type {Boolean}
* @readonly
*
* @see Scene#clampToHeight
+ * @see Scene#clampToHeightMostDetailed
*/
clampToHeightSupported : {
get : function() {
@@ -1586,6 +1602,7 @@ define([
passes.depth = false;
passes.postProcess = false;
passes.offscreen = false;
+ passes.async = false;
}
function updateFrameNumber(scene, frameNumber, time) {
@@ -3019,6 +3036,8 @@ define([
scene.globe.update(frameState);
}
+ updateAsyncRayPicks(scene);
+
frameState.creditDisplay.update();
}
@@ -3660,6 +3679,85 @@ define([
camera.right = right;
}
+ function updateAsyncRayPick(scene, asyncRayPick) {
+ var context = scene._context;
+ var uniformState = context.uniformState;
+ var frameState = scene._frameState;
+
+ var view = scene._pickOffscreenView;
+ scene._view = view;
+
+ var ray = asyncRayPick.ray;
+ var primitives = asyncRayPick.primitives;
+
+ updateCameraFromRay(ray, view.camera);
+
+ updateFrameState(scene);
+ frameState.passes.offscreen = true;
+ frameState.passes.async = true;
+
+ 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) {
+ var primitive = primitives[i];
+ if (primitive.show && scene.primitives.contains(primitive)) {
+ // Only update primitives that are still contained in the scene's primitive collection and are still visible
+ // 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);
+ }
+ }
+
+ // Ignore commands pushed during async pass
+ commandList.length = commandsLength;
+
+ scene._view = scene._defaultView;
+
+ if (ready) {
+ asyncRayPick.deferred.resolve();
+ }
+
+ return ready;
+ }
+
+ function updateAsyncRayPicks(scene) {
+ // Modifies array during iteration
+ var asyncRayPicks = scene._asyncRayPicks;
+ for (var i = 0; i < asyncRayPicks.length; ++i) {
+ if (updateAsyncRayPick(scene, asyncRayPicks[i])) {
+ asyncRayPicks.splice(i--, 1);
+ }
+ }
+ }
+
+ function launchAsyncRayPick(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) && primitive.show) {
+ if (!defined(objectsToExclude) || objectsToExclude.indexOf(primitive) === -1) {
+ asyncPrimitives.push(primitive);
+ }
+ }
+ }
+ if (asyncPrimitives.length === 0) {
+ return when.resolve(callback());
+ }
+
+ var asyncRayPick = new AsyncRayPick(ray, asyncPrimitives);
+ scene._asyncRayPicks.push(asyncRayPick);
+ return asyncRayPick.promise.then(function() {
+ return callback();
+ });
+ }
+
function isExcluded(object, objectsToExclude) {
if (!defined(object) || !defined(objectsToExclude) || objectsToExclude.length === 0) {
return false;
@@ -3669,7 +3767,7 @@ define([
(objectsToExclude.indexOf(object.id) > -1);
}
- function getRayIntersection(scene, ray, objectsToExclude, requirePosition) {
+ function getRayIntersection(scene, ray, objectsToExclude, requirePosition, async) {
var context = scene._context;
var uniformState = context.uniformState;
var frameState = scene._frameState;
@@ -3689,6 +3787,7 @@ define([
frameState.invertClassification = false;
frameState.passes.pick = true;
frameState.passes.offscreen = true;
+ frameState.passes.async = async;
uniformState.update(frameState);
@@ -3727,22 +3826,22 @@ define([
}
}
- function getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition) {
+ function getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition, async) {
var pickCallback = function() {
- return getRayIntersection(scene, ray, objectsToExclude, requirePosition);
+ return getRayIntersection(scene, ray, objectsToExclude, requirePosition, async);
};
return drillPick(limit, pickCallback);
}
- function pickFromRay(scene, ray, objectsToExclude, requirePosition) {
- var results = getRayIntersections(scene, ray, 1, objectsToExclude, requirePosition);
+ function pickFromRay(scene, ray, objectsToExclude, requirePosition, async) {
+ var results = getRayIntersections(scene, ray, 1, objectsToExclude, requirePosition, async);
if (results.length > 0) {
return results[0];
}
}
- function drillPickFromRay(scene, ray, limit, objectsToExclude, requirePosition) {
- return getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition);
+ function drillPickFromRay(scene, ray, limit, objectsToExclude, requirePosition, async) {
+ return getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition, async);
}
/**
@@ -3758,7 +3857,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.
@@ -3770,7 +3869,7 @@ define([
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
}
//>>includeEnd('debug');
- return pickFromRay(this, ray, objectsToExclude, false);
+ return pickFromRay(this, ray, objectsToExclude, false, false);
};
/**
@@ -3788,7 +3887,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.
@@ -3800,7 +3899,62 @@ define([
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
}
//>>includeEnd('debug');
- return drillPickFromRay(this, ray, limit, objectsToExclude, false);
+ return drillPickFromRay(this, ray, limit, objectsToExclude, false, 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 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.
+ */
+ 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 = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
+ return launchAsyncRayPick(this, ray, objectsToExclude, function() {
+ return pickFromRay(that, ray, objectsToExclude, false, 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 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.
+ */
+ 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 = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
+ return launchAsyncRayPick(this, ray, objectsToExclude, function() {
+ return drillPickFromRay(that, ray, limit, objectsToExclude, false, true);
+ });
};
var scratchSurfacePosition = new Cartesian3();
@@ -3837,19 +3991,47 @@ define([
return cartographic.height;
}
+ function sampleHeightMostDetailed(scene, cartographic, objectsToExclude) {
+ var ray = getRayForSampleHeight(scene, cartographic);
+ return launchAsyncRayPick(scene, ray, objectsToExclude, function() {
+ var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true);
+ if (defined(pickResult)) {
+ return getHeightFromCartesian(scene, pickResult.position);
+ }
+ });
+ }
+
+ function clampToHeightMostDetailed(scene, cartesian, objectsToExclude, result) {
+ var ray = getRayForClampToHeight(scene, cartesian);
+ return launchAsyncRayPick(scene, ray, objectsToExclude, function() {
+ var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true);
+ if (defined(pickResult)) {
+ return Cartesian3.clone(pickResult.position, result);
+ }
+ });
+ }
+
/**
* 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.
*
*
* @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
*
* @exception {DeveloperError} sampleHeight is only supported in 3D mode.
* @exception {DeveloperError} sampleHeight requires depth texture support. Check sampleHeightSupported.
@@ -3865,7 +4047,7 @@ define([
}
//>>includeEnd('debug');
var ray = getRayForSampleHeight(this, position);
- var pickResult = pickFromRay(this, ray, objectsToExclude, true);
+ var pickResult = pickFromRay(this, ray, objectsToExclude, true, false);
if (defined(pickResult)) {
return getHeightFromCartesian(this, pickResult.position);
}
@@ -3881,11 +4063,18 @@ 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
*
* @exception {DeveloperError} clampToHeight is only supported in 3D mode.
* @exception {DeveloperError} clampToHeight requires depth texture support. Check clampToHeightSupported.
@@ -3901,12 +4090,115 @@ define([
}
//>>includeEnd('debug');
var ray = getRayForClampToHeight(this, cartesian);
- var pickResult = pickFromRay(this, ray, objectsToExclude, true);
+ var pickResult = pickFromRay(this, ray, objectsToExclude, true, false);
if (defined(pickResult)) {
return Cartesian3.clone(pickResult.position, result);
}
};
+ /**
+ * 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. 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.
+ * @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.
+ * @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 requires depth texture support. Check sampleHeightSupported.');
+ }
+ //>>includeEnd('debug');
+ objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
+ 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) {
+ var length = heights.length;
+ for (var i = 0; i < length; ++i) {
+ positions[i].height = heights[i];
+ }
+ return positions;
+ });
+ };
+
+ /**
+ * 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 to update with clamped positions.
+ * @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.
+ * @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 requires depth texture support. Check clampToHeightSupported.');
+ }
+ //>>includeEnd('debug');
+ objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
+ var length = cartesians.length;
+ var promises = new Array(length);
+ for (var i = 0; i < length; ++i) {
+ promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude, cartesians[i]);
+ }
+ return when.all(promises).then(function(clampedCartesians) {
+ var length = clampedCartesians.length;
+ for (var i = 0; i < length; ++i) {
+ cartesians[i] = clampedCartesians[i];
+ }
+ return 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/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm
new file mode 100644
index 000000000000..00277d8462cb
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/0_0_0.b3dm differ
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 000000000000..3514e4cd2a2a
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_0.b3dm differ
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 000000000000..99b4ac8569fa
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_1.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_2.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_2.b3dm
new file mode 100644
index 000000000000..ec337a97d138
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_0_2.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_0.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_0.b3dm
new file mode 100644
index 000000000000..205dbd0eee18
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_0.b3dm differ
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 000000000000..f20b7dd20fd2
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_1.b3dm differ
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 000000000000..e32032e27e1d
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_1_2.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_0.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_0.b3dm
new file mode 100644
index 000000000000..8779e2254cfb
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_0.b3dm differ
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 000000000000..fc503c3b3a9f
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_1.b3dm differ
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 000000000000..1775fc0f1d4b
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/1_2_2.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_3.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_3.b3dm
new file mode 100644
index 000000000000..b93e84028c3f
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_3.b3dm differ
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 000000000000..2d5bc41455a0
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_4.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_5.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_5.b3dm
new file mode 100644
index 000000000000..2e32e9129cc8
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_3_5.b3dm differ
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 000000000000..31e75dfb7b04
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_3.b3dm differ
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 000000000000..89cb54de483a
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_4.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_5.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_5.b3dm
new file mode 100644
index 000000000000..b5f7696a731c
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_4_5.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_3.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_3.b3dm
new file mode 100644
index 000000000000..d73f354f9b03
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_3.b3dm differ
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 000000000000..66984df1e913
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_4.b3dm differ
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 000000000000..4c970cc081da
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetUniform/2_5_5.b3dm differ
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 1db93a2124ca..6876b86da7f5 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';
@@ -3297,4 +3308,185 @@ defineSuite([
expect(offsetMatrix).toEqualEpsilon(boundingSphereEastNorthUp, CesiumMath.EPSILON3);
});
});
+
+ 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 33de07c814fd..0ee351508385 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 those tests when using the WebGL stub
+ var webglStub = !!window.webglStub;
+
var scene;
var primitives;
var camera;
@@ -1153,67 +1157,1077 @@ defineSuite([
});
});
- it('calls multiple picking functions within the same frame', function() {
- if (!scene.clampToHeightSupported || !scene.pickPositionSupported) {
- return;
- }
+ 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;
+ });
+ }
- 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 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;
+ });
+ }
- scene.camera.setView({ destination : offscreenRectangle });
+ 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 render. Lays down depth for the pickPosition call
- scene.renderForSpecs();
+ 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 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);
+ 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) {
+ 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 pickPosition
- expect(scene).toPickPositionAndCall(function(cartesian) {
- var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle));
- expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5);
+ 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();
+ });
+ });
});
- // Call clampToHeight again
- expect(scene).toClampToHeightAndCall(function(cartesian) {
- var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0);
- expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5);
- }, cartesian);
+ 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();
+ });
+ });
+ });
- // Call pick
- expect(scene).toPickPrimitive(offscreenRectanglePrimitive);
+ it('picks a primitive', function() {
+ if (webglStub) {
+ return;
+ }
+ 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 clampToHeight again
- expect(scene).toClampToHeightAndCall(function(cartesian) {
- var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0);
- expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5);
- }, cartesian);
+ expect(primitive).toBe(rectangle);
- // 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();
+ if (scene.context.depthTexture) {
+ var expectedPosition = Cartesian3.fromRadians(0.0, 0.0);
+ expect(position).toEqualEpsilon(expectedPosition, 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('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) {
+ expect(result).toBeUndefined();
+ });
+ });
- // 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('picks the top primitive', function() {
+ if (webglStub) {
+ return;
+ }
+ 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);
+ });
+ });
+
+ it('excludes objects', function() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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() {
+ if (webglStub) {
+ return;
+ }
+ 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();
});
});