diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js
index 985172d6c80d..8f5d99495f65 100644
--- a/Source/Scene/FrameState.js
+++ b/Source/Scene/FrameState.js
@@ -159,7 +159,14 @@ define([
* @type {Boolean}
* @default false
*/
- postProcess : false
+ postProcess : false,
+
+ /**
+ * true
if the primitive should update for an offscreen pass, false
otherwise.
+ * @type {Boolean}
+ * @default false
+ */
+ offscreen : false
};
/**
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index 8e75ee0c26b8..a6110d119f41 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -1,4 +1,5 @@
define([
+ '../Core/ApproximateTerrainHeights',
'../Core/BoundingRectangle',
'../Core/BoundingSphere',
'../Core/BoxGeometry',
@@ -6,6 +7,7 @@ define([
'../Core/Cartesian3',
'../Core/Cartesian4',
'../Core/Cartographic',
+ '../Core/Check',
'../Core/Color',
'../Core/ColorGeometryInstanceAttribute',
'../Core/createGuid',
@@ -34,6 +36,7 @@ define([
'../Core/PerspectiveFrustum',
'../Core/PerspectiveOffCenterFrustum',
'../Core/PixelFormat',
+ '../Core/Ray',
'../Core/RequestScheduler',
'../Core/ShowGeometryInstanceAttribute',
'../Core/TaskProcessor',
@@ -83,6 +86,7 @@ define([
'./SunPostProcess',
'./TweenCollection'
], function(
+ ApproximateTerrainHeights,
BoundingRectangle,
BoundingSphere,
BoxGeometry,
@@ -90,6 +94,7 @@ define([
Cartesian3,
Cartesian4,
Cartographic,
+ Check,
Color,
ColorGeometryInstanceAttribute,
createGuid,
@@ -118,6 +123,7 @@ define([
PerspectiveFrustum,
PerspectiveOffCenterFrustum,
PixelFormat,
+ Ray,
RequestScheduler,
ShowGeometryInstanceAttribute,
TaskProcessor,
@@ -852,8 +858,18 @@ define([
this._removeGlobeCallbacks = [];
var viewport = new BoundingRectangle(0, 0, context.drawingBufferWidth, context.drawingBufferHeight);
+ var pickOffscreenViewport = new BoundingRectangle(0, 0, 1, 1);
+
+ var pickOffscreenCamera = new Camera(this);
+ pickOffscreenCamera.frustum = new OrthographicFrustum({
+ width: 0.01,
+ aspectRatio: 1.0,
+ near: 0.1,
+ far: 500000000.0
+ });
this._view = new View(this, this._camera, viewport);
+ this._pickOffscreenView = new View(this, pickOffscreenCamera, pickOffscreenViewport);
// initial guess at frustums.
var near = camera.frustum.near;
@@ -1658,6 +1674,7 @@ define([
passes.pick = false;
passes.depth = false;
passes.postProcess = false;
+ passes.offscreen = false;
}
function updateFrameState(scene, frameNumber, time) {
@@ -2968,6 +2985,7 @@ define([
// Update celestial and terrestrial environment effects.
var environmentState = scene._environmentState;
var renderPass = frameState.passes.render;
+ var offscreenPass = frameState.passes.offscreen;
var skyAtmosphere = scene.skyAtmosphere;
var globe = scene.globe;
@@ -3009,7 +3027,7 @@ define([
}
environmentState.renderTranslucentDepthForPick = false;
- environmentState.useWebVR = scene._useWebVR && scene.mode !== SceneMode.SCENE2D;
+ environmentState.useWebVR = scene._useWebVR && scene.mode !== SceneMode.SCENE2D && !offscreenPass;
environmentState.frustumCommandsList = view.frustumCommandsList;
environmentState.view = view;
@@ -3822,6 +3840,8 @@ define([
* @returns {Cartesian3} The cartesian position.
*
* @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported.
+ *
+ * @see Scene#pickPositionFromRay
*/
Scene.prototype.pickPosition = function(windowPosition, result) {
result = this.pickPositionWorldCoordinates(windowPosition, result);
@@ -3838,37 +3858,10 @@ define([
return result;
};
- /**
- * Returns a list of objects, each containing a `primitive` property, for all primitives at
- * a particular window coordinate position. Other properties may also be set depending on the
- * type of primitive. The primitives in the list are ordered by their visual order in the
- * scene (front to back).
- *
- * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
- * @param {Number} [limit] If supplied, stop drilling after collecting this many picks.
- * @param {Number} [width=3] Width of the pick rectangle.
- * @param {Number} [height=3] Height of the pick rectangle.
- * @returns {Object[]} Array of objects, each containing 1 picked primitives.
- *
- * @exception {DeveloperError} windowPosition is undefined.
- *
- * @example
- * var pickedObjects = scene.drillPick(new Cesium.Cartesian2(100.0, 200.0));
- *
- * @see Scene#pick
- *
- */
- Scene.prototype.drillPick = function(windowPosition, limit, width, height) {
+ 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.
-
- //>>includeStart('debug', pragmas.debug);
- if (!defined(windowPosition)) {
- throw new DeveloperError('windowPosition is undefined.');
- }
- //>>includeEnd('debug');
-
var i;
var attributes;
var result = [];
@@ -3879,20 +3872,21 @@ define([
limit = Number.MAX_VALUE;
}
- var pickedResult = this.pick(windowPosition, width, height);
- while (defined(pickedResult) && defined(pickedResult.primitive)) {
+ var pickedResult = pickCallback();
+ while (defined(pickedResult) && defined(pickedResult.object) && defined(pickedResult.object.primitive)) {
result.push(pickedResult);
if (0 >= --limit) {
break;
}
- var primitive = pickedResult.primitive;
+ var object = pickedResult.object;
+ var primitive = object.primitive;
var hasShowAttribute = false;
- //If the picked object has a show attribute, use it.
+ // If the picked object has a show attribute, use it.
if (typeof primitive.getGeometryInstanceAttributes === 'function') {
- if (defined(pickedResult.id)) {
- attributes = primitive.getGeometryInstanceAttributes(pickedResult.id);
+ if (defined(object.id)) {
+ attributes = primitive.getGeometryInstanceAttributes(object.id);
if (defined(attributes) && defined(attributes.show)) {
hasShowAttribute = true;
attributes.show = ShowGeometryInstanceAttribute.toValue(false, attributes.show);
@@ -3901,10 +3895,10 @@ define([
}
}
- if (pickedResult instanceof Cesium3DTileFeature) {
+ if (object instanceof Cesium3DTileFeature) {
hasShowAttribute = true;
- pickedResult.show = false;
- pickedFeatures.push(pickedResult);
+ object.show = false;
+ pickedFeatures.push(object);
}
// Otherwise, hide the entire primitive
@@ -3913,7 +3907,7 @@ define([
pickedPrimitives.push(primitive);
}
- pickedResult = this.pick(windowPosition, width, height);
+ pickedResult = pickCallback();
}
// Unhide everything we hid while drill picking
@@ -3931,6 +3925,287 @@ define([
}
return result;
+ }
+
+ var scratchRight = new Cartesian3();
+ var scratchUp = new Cartesian3();
+
+ function pickFromRay(scene, ray, pickPosition, pickObject) {
+ var context = scene._context;
+ var uniformState = context.uniformState;
+ var frameState = scene._frameState;
+ var environmentState = scene._environmentState;
+ var view = scene._pickOffscreenView;
+
+ var direction = ray.direction;
+ var orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight);
+ var right = Cartesian3.cross(direction, orthogonalAxis, scratchRight);
+ var up = Cartesian3.cross(direction, right, scratchUp);
+
+ var pickOffscreenCamera = view.camera;
+ pickOffscreenCamera.position = ray.origin;
+ pickOffscreenCamera.direction = direction;
+ pickOffscreenCamera.up = up;
+ pickOffscreenCamera.right = right;
+
+ scratchRectangle = BoundingRectangle.clone(view.viewport, scratchRectangle);
+
+ // Switch out the scene's camera with the offscreen camera
+ var sceneCamera = scene._camera;
+ scene._camera = pickOffscreenCamera;
+
+ scene._jobScheduler.disableThisFrame();
+
+ // Update with previous frame's number and time, assuming that render is called before picking.
+ updateFrameState(scene, frameState.frameNumber, frameState.time);
+ frameState.invertClassification = false;
+ frameState.passes.pick = true;
+ frameState.passes.offscreen = true;
+
+ uniformState.update(frameState);
+
+ updateEnvironment(scene, view);
+ var passState = environmentState.pickFramebuffer.begin(scratchRectangle, view.viewport);
+
+ updateAndExecuteCommands(scene, passState, scratchColorZero);
+ resolveFramebuffers(scene, passState);
+
+ var position;
+ var object;
+
+ if (pickObject) {
+ object = environmentState.pickFramebuffer.end(context);
+ pickPosition = pickPosition && defined(object);
+ }
+
+ if (pickPosition) {
+ var numFrustums = environmentState.frustumCommandsList.length;
+ for (var i = 0; i < numFrustums; ++i) {
+ var pickDepth = getPickDepth(scene, i);
+ var depth = pickDepth.getDepth(context, 0, 0);
+ if (depth > 0.0 && depth < 1.0) {
+ var renderedFrustum = environmentState.frustumCommandsList[i];
+ var near = renderedFrustum.near * (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0);
+ var far = renderedFrustum.far;
+ var distance = near + depth * (far - near);
+ position = Ray.getPoint(ray, distance);
+ break;
+ }
+ }
+
+ if (defined(position) && (scene.mode !== SceneMode.SCENE3D)) {
+ Cartesian3.fromElements(position.y, position.z, position.x, position);
+
+ var projection = scene.mapProjection;
+ var ellipsoid = projection.ellipsoid;
+
+ var cartographic = projection.unproject(position, scratchPickPositionCartographic);
+ ellipsoid.cartographicToCartesian(cartographic, position);
+ }
+ }
+
+ scene._camera = sceneCamera;
+ context.endFrame();
+ callAfterRenderFunctions(scene);
+
+ if (!defined(object) && !defined(position)) {
+ return;
+ }
+
+ return {
+ object : object,
+ position : position
+ };
+ }
+
+ /**
+ * Returns an object with a `primitive` property that contains the first (top) primitive in the scene
+ * hit by the ray or undefined if nothing is hit. Other properties may potentially be set depending on the type of primitive.
+ *
+ * When a feature of a 3D Tiles tileset is picked, pick
returns a {@link Cesium3DTileFeature} object.
+ *
+ * This function only samples height from globe tiles and 3D Tiles that are rendered in the current view. All other + * primitives are sampled regardless of visibility. + *
+ * + * @param {Cartographic} position The position to sample from. + * @returns {Number} The height. + * + * @see sampleTerrain + * @see sampleTerrainMostDetailed + */ + Scene.prototype.sampleHeight = function(position) { + //>>includeStart('debug', pragmas.debug); + Check.defined('position', position); + //>>includeEnd('debug'); + var globe = this.globe; + var ellipsoid = defined(globe) ? globe.ellipsoid : this.mapProjection.ellipsoid; + var height = ApproximateTerrainHeights._defaultMaxTerrainHeight; + var surfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic(position, scratchSurfaceNormal); + var surfacePosition = Cartographic.toCartesian(position, ellipsoid, scratchSurfacePosition); + var surfaceRay = scratchSurfaceRay; + surfaceRay.origin = surfacePosition; + surfaceRay.direction = surfaceNormal; + var ray = scratchRay; + Ray.getPoint(surfaceRay, height, ray.origin); + Cartesian3.negate(surfaceNormal, ray.direction); + var pickPosition = this.pickPositionFromRay(ray, scratchPickPosition); + if (defined(pickPosition)) { + var pickCartographic = Cartographic.fromCartesian(pickPosition, ellipsoid, scratchPickCartographic); + return pickCartographic.height; + } }; /** @@ -4059,6 +4334,7 @@ define([ this._brdfLutGenerator = this._brdfLutGenerator && this._brdfLutGenerator.destroy(); this._view = this._view && this._view.destroy(); + this._pickOffscreenView = this._pickOffscreenView && this._pickOffscreenView.destroy(); if (this._removeCreditContainer) { this._canvas.parentNode.removeChild(this._creditContainer);