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;