From e70d371dbb11f6d3c483ecc60ec49e26d1fe3ad4 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 26 Nov 2018 17:54:40 -0500 Subject: [PATCH] Configurable frustum width for ray-pick functions --- CHANGES.md | 4 ++ Source/Scene/Scene.js | 97 ++++++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 38 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 33a24e094fee..c48a0316a0e0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,9 @@ Change Log ##### Breaking Changes :mega: * `TerrainProviders` that implement `availability` must now also implement the `loadTileDataAvailability` method. +##### Deprecated :hourglass_flowing_sand: +* `Scene.clampToHeight` now takes an optional `width` argument before the `result` argument. The previous function definition will no longer work in 1.53. [#7287](https://github.com/AnalyticalGraphicsInc/cesium/pull/7287) + ##### 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. @@ -15,6 +18,7 @@ Change Log * Added `computeLineSegmentLineSegmentIntersection` to `Intersections2D`. [#7228](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7228) * Added ability to load availability progressively from a quantized mesh extension instead of upfront. This will speed up load time and reduce memory usage. [#7196](https://github.com/AnalyticalGraphicsInc/cesium/pull/7196) * Added the ability to apply styles to 3D Tilesets that don't contain features. [#7255](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7255) +* Added the ability to specify the width of the intersection volume for `Scene.sampleHeight`, `Scene.clampToHeight`, `Scene.sampleHeightMostDetailed`, and `Scene.clampToHeightMostDetailed`. [#7287](https://github.com/AnalyticalGraphicsInc/cesium/pull/7287) ##### Fixes :wrench: * Fixed issue causing polyline to look wavy depending on the position of the camera [#7209](https://github.com/AnalyticalGraphicsInc/cesium/pull/7209) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 4741c2d2ca3a..f5cca5cc8702 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -170,8 +170,9 @@ define([ }; }; - function AsyncRayPick(ray, primitives) { + function AsyncRayPick(ray, width, primitives) { this.ray = ray; + this.width = width; this.primitives = primitives; this.ready = false; this.deferred = when.defer(); @@ -775,16 +776,18 @@ define([ camera.frustum.far = 10000000000.0; } + var pickOffscreenDefaultWidth = 0.1; var pickOffscreenViewport = new BoundingRectangle(0, 0, 1, 1); var pickOffscreenCamera = new Camera(this); pickOffscreenCamera.frustum = new OrthographicFrustum({ - width: 0.01, + width: pickOffscreenDefaultWidth, aspectRatio: 1.0, near: 0.1 }); this._view = new View(this, camera, viewport); this._pickOffscreenView = new View(this, pickOffscreenCamera, pickOffscreenViewport); + this._pickOffscreenDefaultWidth = pickOffscreenDefaultWidth; this._defaultView = new View(this, camera, viewport); this._view = this._defaultView; @@ -3758,7 +3761,7 @@ define([ var scratchRight = new Cartesian3(); var scratchUp = new Cartesian3(); - function updateCameraFromRay(ray, camera) { + function updateOffscreenCameraFromRay(scene, ray, width, camera) { var direction = ray.direction; var orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight); var right = Cartesian3.cross(direction, orthogonalAxis, scratchRight); @@ -3768,6 +3771,8 @@ define([ camera.direction = direction; camera.up = up; camera.right = right; + + camera.frustum.width = defaultValue(width, scene._pickOffscreenDefaultWidth); } function updateAsyncRayPick(scene, asyncRayPick) { @@ -3779,9 +3784,10 @@ define([ scene._view = view; var ray = asyncRayPick.ray; + var width = asyncRayPick.width; var primitives = asyncRayPick.primitives; - updateCameraFromRay(ray, view.camera); + updateOffscreenCameraFromRay(scene, ray, width, view.camera); updateFrameState(scene); frameState.passes.offscreen = true; @@ -3826,7 +3832,7 @@ define([ } } - function launchAsyncRayPick(scene, ray, objectsToExclude, callback) { + function launchAsyncRayPick(scene, ray, objectsToExclude, width, callback) { var asyncPrimitives = []; var primitives = scene.primitives; var length = primitives.length; @@ -3842,7 +3848,7 @@ define([ return when.resolve(callback()); } - var asyncRayPick = new AsyncRayPick(ray, asyncPrimitives); + var asyncRayPick = new AsyncRayPick(ray, width, asyncPrimitives); scene._asyncRayPicks.push(asyncRayPick); return asyncRayPick.promise.then(function() { return callback(); @@ -3858,7 +3864,7 @@ define([ (objectsToExclude.indexOf(object.id) > -1); } - function getRayIntersection(scene, ray, objectsToExclude, requirePosition, async) { + function getRayIntersection(scene, ray, objectsToExclude, width, requirePosition, async) { var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3866,7 +3872,7 @@ define([ var view = scene._pickOffscreenView; scene._view = view; - updateCameraFromRay(ray, view.camera); + updateOffscreenCameraFromRay(scene, ray, width, view.camera); scratchRectangle = BoundingRectangle.clone(view.viewport, scratchRectangle); @@ -3917,22 +3923,22 @@ define([ } } - function getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition, async) { + function getRayIntersections(scene, ray, limit, objectsToExclude, width, requirePosition, async) { var pickCallback = function() { - return getRayIntersection(scene, ray, objectsToExclude, requirePosition, async); + return getRayIntersection(scene, ray, objectsToExclude, width, requirePosition, async); }; return drillPick(limit, pickCallback); } - function pickFromRay(scene, ray, objectsToExclude, requirePosition, async) { - var results = getRayIntersections(scene, ray, 1, objectsToExclude, requirePosition, async); + function pickFromRay(scene, ray, objectsToExclude, width, requirePosition, async) { + var results = getRayIntersections(scene, ray, 1, objectsToExclude, width, requirePosition, async); if (results.length > 0) { return results[0]; } } - function drillPickFromRay(scene, ray, limit, objectsToExclude, requirePosition, async) { - return getRayIntersections(scene, ray, limit, objectsToExclude, requirePosition, async); + function drillPickFromRay(scene, ray, limit, objectsToExclude, width, requirePosition, async) { + return getRayIntersections(scene, ray, limit, objectsToExclude, width, requirePosition, async); } /** @@ -3949,18 +3955,19 @@ define([ * * @param {Ray} ray The ray. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @returns {Object} An object containing the object and position of the first intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ - Scene.prototype.pickFromRay = function(ray, objectsToExclude) { + Scene.prototype.pickFromRay = function(ray, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (this._mode !== SceneMode.SCENE3D) { throw new DeveloperError('Ray intersections are only supported in 3D mode.'); } //>>includeEnd('debug'); - return pickFromRay(this, ray, objectsToExclude, false, false); + return pickFromRay(this, ray, objectsToExclude, width, false, false); }; /** @@ -3979,18 +3986,19 @@ 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 3D Tiles features to exclude from the ray intersection. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @returns {Object[]} List of objects containing the object and position of each intersection. * * @exception {DeveloperError} Ray intersections are only supported in 3D mode. */ - Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude) { + Scene.prototype.drillPickFromRay = function(ray, limit, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (this._mode !== SceneMode.SCENE3D) { throw new DeveloperError('Ray intersections are only supported in 3D mode.'); } //>>includeEnd('debug'); - return drillPickFromRay(this, ray, limit, objectsToExclude, false, false); + return drillPickFromRay(this, ray, limit, objectsToExclude, width,false, false); }; /** @@ -4001,11 +4009,12 @@ define([ * * @param {Ray} ray The ray. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to exclude from the ray intersection. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @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) { + Scene.prototype.pickFromRayMostDetailed = function(ray, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (this._mode !== SceneMode.SCENE3D) { @@ -4015,8 +4024,8 @@ define([ 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); + return launchAsyncRayPick(this, ray, objectsToExclude, width, function() { + return pickFromRay(that, ray, objectsToExclude, width, false, true); }); }; @@ -4029,11 +4038,12 @@ 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 3D Tiles features to exclude from the ray intersection. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @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) { + Scene.prototype.drillPickFromRayMostDetailed = function(ray, limit, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('ray', ray); if (this._mode !== SceneMode.SCENE3D) { @@ -4043,8 +4053,8 @@ define([ 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); + return launchAsyncRayPick(this, ray, objectsToExclude, width, function() { + return drillPickFromRay(that, ray, limit, objectsToExclude, width, false, true); }); }; @@ -4082,20 +4092,20 @@ define([ return cartographic.height; } - function sampleHeightMostDetailed(scene, cartographic, objectsToExclude) { + function sampleHeightMostDetailed(scene, cartographic, objectsToExclude, width) { var ray = getRayForSampleHeight(scene, cartographic); - return launchAsyncRayPick(scene, ray, objectsToExclude, function() { - var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); + return launchAsyncRayPick(scene, ray, objectsToExclude, width, function() { + var pickResult = pickFromRay(scene, ray, objectsToExclude, width, true, true); if (defined(pickResult)) { return getHeightFromCartesian(scene, pickResult.position); } }); } - function clampToHeightMostDetailed(scene, cartesian, objectsToExclude, result) { + function clampToHeightMostDetailed(scene, cartesian, objectsToExclude, width, result) { var ray = getRayForClampToHeight(scene, cartesian); - return launchAsyncRayPick(scene, ray, objectsToExclude, function() { - var pickResult = pickFromRay(scene, ray, objectsToExclude, true, true); + return launchAsyncRayPick(scene, ray, objectsToExclude, width, function() { + var pickResult = pickFromRay(scene, ray, objectsToExclude, width, true, true); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); } @@ -4113,6 +4123,7 @@ define([ * * @param {Cartographic} position The cartographic position to sample height from. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not sample height from. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @returns {Number} The height. This may be undefined if there was no scene geometry to sample height from. * * @example @@ -4127,7 +4138,7 @@ define([ * @exception {DeveloperError} sampleHeight is only supported in 3D mode. * @exception {DeveloperError} sampleHeight requires depth texture support. Check sampleHeightSupported. */ - Scene.prototype.sampleHeight = function(position, objectsToExclude) { + Scene.prototype.sampleHeight = function(position, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('position', position); if (this._mode !== SceneMode.SCENE3D) { @@ -4138,7 +4149,7 @@ define([ } //>>includeEnd('debug'); var ray = getRayForSampleHeight(this, position); - var pickResult = pickFromRay(this, ray, objectsToExclude, true, false); + var pickResult = pickFromRay(this, ray, objectsToExclude, width, true, false); if (defined(pickResult)) { return getHeightFromCartesian(this, pickResult.position); } @@ -4155,6 +4166,7 @@ define([ * * @param {Cartesian3} cartesian The cartesian position. * @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not clamp to. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @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. * @@ -4170,7 +4182,7 @@ define([ * @exception {DeveloperError} clampToHeight is only supported in 3D mode. * @exception {DeveloperError} clampToHeight requires depth texture support. Check clampToHeightSupported. */ - Scene.prototype.clampToHeight = function(cartesian, objectsToExclude, result) { + Scene.prototype.clampToHeight = function(cartesian, objectsToExclude, width, result) { //>>includeStart('debug', pragmas.debug); Check.defined('cartesian', cartesian); if (this._mode !== SceneMode.SCENE3D) { @@ -4180,8 +4192,15 @@ define([ throw new DeveloperError('clampToHeight requires depth texture support. Check clampToHeightSupported.'); } //>>includeEnd('debug'); + + if (width instanceof Cartesian3) { + result = width; + width = undefined; + deprecationWarning('clampToHeight-parameter-change', 'clampToHeight now takes an optional width argument before the result argument in Cesium 1.52. The previous function definition will no longer work in 1.53.'); + } + var ray = getRayForClampToHeight(this, cartesian); - var pickResult = pickFromRay(this, ray, objectsToExclude, true, false); + var pickResult = pickFromRay(this, ray, objectsToExclude, width, true, false); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); } @@ -4196,6 +4215,7 @@ define([ * * @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. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * * @example @@ -4214,7 +4234,7 @@ define([ * @exception {DeveloperError} sampleHeightMostDetailed is only supported in 3D mode. * @exception {DeveloperError} sampleHeightMostDetailed requires depth texture support. Check sampleHeightSupported. */ - Scene.prototype.sampleHeightMostDetailed = function(positions, objectsToExclude) { + Scene.prototype.sampleHeightMostDetailed = function(positions, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('positions', positions); if (this._mode !== SceneMode.SCENE3D) { @@ -4228,7 +4248,7 @@ define([ var length = positions.length; var promises = new Array(length); for (var i = 0; i < length; ++i) { - promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude); + promises[i] = sampleHeightMostDetailed(this, positions[i], objectsToExclude, width); } return when.all(promises).then(function(heights) { var length = heights.length; @@ -4247,6 +4267,7 @@ define([ * * @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. + * @param {Number} [width=0.1] Width of the intersection volume in meters. * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed. * * @example @@ -4265,7 +4286,7 @@ define([ * @exception {DeveloperError} clampToHeightMostDetailed is only supported in 3D mode. * @exception {DeveloperError} clampToHeightMostDetailed requires depth texture support. Check clampToHeightSupported. */ - Scene.prototype.clampToHeightMostDetailed = function(cartesians, objectsToExclude) { + Scene.prototype.clampToHeightMostDetailed = function(cartesians, objectsToExclude, width) { //>>includeStart('debug', pragmas.debug); Check.defined('cartesians', cartesians); if (this._mode !== SceneMode.SCENE3D) { @@ -4279,7 +4300,7 @@ define([ 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]); + promises[i] = clampToHeightMostDetailed(this, cartesians[i], objectsToExclude, width, cartesians[i]); } return when.all(promises).then(function(clampedCartesians) { var length = clampedCartesians.length;