import ApproximateTerrainHeights from '../Core/ApproximateTerrainHeights.js';
|
import BoundingRectangle from '../Core/BoundingRectangle.js';
|
import Cartesian2 from '../Core/Cartesian2.js';
|
import Cartesian3 from '../Core/Cartesian3.js';
|
import Cartographic from '../Core/Cartographic.js';
|
import Check from '../Core/Check.js';
|
import Color from '../Core/Color.js';
|
import defaultValue from '../Core/defaultValue.js';
|
import defined from '../Core/defined.js';
|
import DeveloperError from '../Core/DeveloperError.js';
|
import Matrix4 from '../Core/Matrix4.js';
|
import OrthographicFrustum from '../Core/OrthographicFrustum.js';
|
import OrthographicOffCenterFrustum from '../Core/OrthographicOffCenterFrustum.js';
|
import PerspectiveFrustum from '../Core/PerspectiveFrustum.js';
|
import PerspectiveOffCenterFrustum from '../Core/PerspectiveOffCenterFrustum.js';
|
import Ray from '../Core/Ray.js';
|
import ShowGeometryInstanceAttribute from '../Core/ShowGeometryInstanceAttribute.js';
|
import when from '../ThirdParty/when.js';
|
import Camera from './Camera.js';
|
import Cesium3DTileFeature from './Cesium3DTileFeature.js';
|
import Cesium3DTilePass from './Cesium3DTilePass.js';
|
import Cesium3DTilePassState from './Cesium3DTilePassState.js';
|
import PickDepth from './PickDepth.js';
|
import PrimitiveCollection from './PrimitiveCollection.js';
|
import SceneMode from './SceneMode.js';
|
import SceneTransforms from './SceneTransforms.js';
|
import View from './View.js';
|
|
var offscreenDefaultWidth = 0.1;
|
|
var mostDetailedPreloadTilesetPassState = new Cesium3DTilePassState({
|
pass : Cesium3DTilePass.MOST_DETAILED_PRELOAD
|
});
|
|
var mostDetailedPickTilesetPassState = new Cesium3DTilePassState({
|
pass : Cesium3DTilePass.MOST_DETAILED_PICK
|
});
|
|
var pickTilesetPassState = new Cesium3DTilePassState({
|
pass : Cesium3DTilePass.PICK
|
});
|
|
/**
|
* @private
|
*/
|
function Picking(scene) {
|
this._mostDetailedRayPicks = [];
|
this.pickRenderStateCache = {};
|
this._pickPositionCache = {};
|
this._pickPositionCacheDirty = false;
|
|
var pickOffscreenViewport = new BoundingRectangle(0, 0, 1, 1);
|
var pickOffscreenCamera = new Camera(scene);
|
pickOffscreenCamera.frustum = new OrthographicFrustum({
|
width: offscreenDefaultWidth,
|
aspectRatio: 1.0,
|
near: 0.1
|
});
|
|
this._pickOffscreenView = new View(scene, pickOffscreenCamera, pickOffscreenViewport);
|
}
|
|
Picking.prototype.update = function() {
|
this._pickPositionCacheDirty = true;
|
};
|
|
Picking.prototype.getPickDepth = function(scene, index) {
|
var pickDepths = scene.view.pickDepths;
|
var pickDepth = pickDepths[index];
|
if (!defined(pickDepth)) {
|
pickDepth = new PickDepth();
|
pickDepths[index] = pickDepth;
|
}
|
return pickDepth;
|
};
|
|
var scratchOrthoPickingFrustum = new OrthographicOffCenterFrustum();
|
var scratchOrthoOrigin = new Cartesian3();
|
var scratchOrthoDirection = new Cartesian3();
|
var scratchOrthoPixelSize = new Cartesian2();
|
var scratchOrthoPickVolumeMatrix4 = new Matrix4();
|
|
function getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height, viewport) {
|
var camera = scene.camera;
|
var frustum = camera.frustum;
|
if (defined(frustum._offCenterFrustum)) {
|
frustum = frustum._offCenterFrustum;
|
}
|
|
var x = 2.0 * (drawingBufferPosition.x - viewport.x) / viewport.width - 1.0;
|
x *= (frustum.right - frustum.left) * 0.5;
|
var y = 2.0 * (viewport.height - drawingBufferPosition.y - viewport.y) / viewport.height - 1.0;
|
y *= (frustum.top - frustum.bottom) * 0.5;
|
|
var transform = Matrix4.clone(camera.transform, scratchOrthoPickVolumeMatrix4);
|
camera._setTransform(Matrix4.IDENTITY);
|
|
var origin = Cartesian3.clone(camera.position, scratchOrthoOrigin);
|
Cartesian3.multiplyByScalar(camera.right, x, scratchOrthoDirection);
|
Cartesian3.add(scratchOrthoDirection, origin, origin);
|
Cartesian3.multiplyByScalar(camera.up, y, scratchOrthoDirection);
|
Cartesian3.add(scratchOrthoDirection, origin, origin);
|
|
camera._setTransform(transform);
|
|
if (scene.mode === SceneMode.SCENE2D) {
|
Cartesian3.fromElements(origin.z, origin.x, origin.y, origin);
|
}
|
|
var pixelSize = frustum.getPixelDimensions(viewport.width, viewport.height, 1.0, 1.0, scratchOrthoPixelSize);
|
|
var ortho = scratchOrthoPickingFrustum;
|
ortho.right = pixelSize.x * 0.5;
|
ortho.left = -ortho.right;
|
ortho.top = pixelSize.y * 0.5;
|
ortho.bottom = -ortho.top;
|
ortho.near = frustum.near;
|
ortho.far = frustum.far;
|
|
return ortho.computeCullingVolume(origin, camera.directionWC, camera.upWC);
|
}
|
|
var scratchPerspPickingFrustum = new PerspectiveOffCenterFrustum();
|
var scratchPerspPixelSize = new Cartesian2();
|
|
function getPickPerspectiveCullingVolume(scene, drawingBufferPosition, width, height, viewport) {
|
var camera = scene.camera;
|
var frustum = camera.frustum;
|
var near = frustum.near;
|
|
var tanPhi = Math.tan(frustum.fovy * 0.5);
|
var tanTheta = frustum.aspectRatio * tanPhi;
|
|
var x = 2.0 * (drawingBufferPosition.x - viewport.x) / viewport.width - 1.0;
|
var y = 2.0 * (viewport.height - drawingBufferPosition.y - viewport.y) / viewport.height - 1.0;
|
|
var xDir = x * near * tanTheta;
|
var yDir = y * near * tanPhi;
|
|
var pixelSize = frustum.getPixelDimensions(viewport.width, viewport.height, 1.0, 1.0, scratchPerspPixelSize);
|
var pickWidth = pixelSize.x * width * 0.5;
|
var pickHeight = pixelSize.y * height * 0.5;
|
|
var offCenter = scratchPerspPickingFrustum;
|
offCenter.top = yDir + pickHeight;
|
offCenter.bottom = yDir - pickHeight;
|
offCenter.right = xDir + pickWidth;
|
offCenter.left = xDir - pickWidth;
|
offCenter.near = near;
|
offCenter.far = frustum.far;
|
|
return offCenter.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC);
|
}
|
|
function getPickCullingVolume(scene, drawingBufferPosition, width, height, viewport) {
|
var frustum = scene.camera.frustum;
|
if (frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum) {
|
return getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height, viewport);
|
}
|
|
return getPickPerspectiveCullingVolume(scene, drawingBufferPosition, width, height, viewport);
|
}
|
|
// pick rectangle width and height, assumed odd
|
var scratchRectangleWidth = 3.0;
|
var scratchRectangleHeight = 3.0;
|
var scratchRectangle = new BoundingRectangle(0.0, 0.0, scratchRectangleWidth, scratchRectangleHeight);
|
var scratchPosition = new Cartesian2();
|
var scratchColorZero = new Color(0.0, 0.0, 0.0, 0.0);
|
|
Picking.prototype.pick = function(scene, windowPosition, width, height) {
|
//>>includeStart('debug', pragmas.debug);
|
if (!defined(windowPosition)) {
|
throw new DeveloperError('windowPosition is undefined.');
|
}
|
//>>includeEnd('debug');
|
|
scratchRectangleWidth = defaultValue(width, 3.0);
|
scratchRectangleHeight = defaultValue(height, scratchRectangleWidth);
|
|
var context = scene.context;
|
var us = context.uniformState;
|
var frameState = scene.frameState;
|
|
var view = scene.defaultView;
|
scene.view = view;
|
|
var viewport = view.viewport;
|
viewport.x = 0;
|
viewport.y = 0;
|
viewport.width = context.drawingBufferWidth;
|
viewport.height = context.drawingBufferHeight;
|
|
var passState = view.passState;
|
passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
|
|
var drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(scene, windowPosition, scratchPosition);
|
|
scene.jobScheduler.disableThisFrame();
|
|
scene.updateFrameState();
|
frameState.cullingVolume = getPickCullingVolume(scene, drawingBufferPosition, scratchRectangleWidth, scratchRectangleHeight, viewport);
|
frameState.invertClassification = false;
|
frameState.passes.pick = true;
|
frameState.tilesetPassState = pickTilesetPassState;
|
|
us.update(frameState);
|
|
scene.updateEnvironment();
|
|
scratchRectangle.x = drawingBufferPosition.x - ((scratchRectangleWidth - 1.0) * 0.5);
|
scratchRectangle.y = (scene.drawingBufferHeight - drawingBufferPosition.y) - ((scratchRectangleHeight - 1.0) * 0.5);
|
scratchRectangle.width = scratchRectangleWidth;
|
scratchRectangle.height = scratchRectangleHeight;
|
passState = view.pickFramebuffer.begin(scratchRectangle, view.viewport);
|
|
scene.updateAndExecuteCommands(passState, scratchColorZero);
|
scene.resolveFramebuffers(passState);
|
|
var object = view.pickFramebuffer.end(scratchRectangle);
|
context.endFrame();
|
return object;
|
};
|
|
function renderTranslucentDepthForPick(scene, drawingBufferPosition) {
|
// PERFORMANCE_IDEA: render translucent only and merge with the previous frame
|
var context = scene.context;
|
var frameState = scene.frameState;
|
var environmentState = scene.environmentState;
|
|
var view = scene.defaultView;
|
scene.view = view;
|
|
var viewport = view.viewport;
|
viewport.x = 0;
|
viewport.y = 0;
|
viewport.width = context.drawingBufferWidth;
|
viewport.height = context.drawingBufferHeight;
|
|
var passState = view.passState;
|
passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
|
|
scene.clearPasses(frameState.passes);
|
frameState.passes.pick = true;
|
frameState.passes.depth = true;
|
frameState.cullingVolume = getPickCullingVolume(scene, drawingBufferPosition, 1, 1, viewport);
|
frameState.tilesetPassState = pickTilesetPassState;
|
|
scene.updateEnvironment();
|
environmentState.renderTranslucentDepthForPick = true;
|
passState = view.pickDepthFramebuffer.update(context, drawingBufferPosition, viewport);
|
|
scene.updateAndExecuteCommands(passState, scratchColorZero);
|
scene.resolveFramebuffers(passState);
|
|
context.endFrame();
|
}
|
|
var scratchPerspectiveFrustum = new PerspectiveFrustum();
|
var scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum();
|
var scratchOrthographicFrustum = new OrthographicFrustum();
|
var scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum();
|
|
Picking.prototype.pickPositionWorldCoordinates = function(scene, windowPosition, result) {
|
if (!scene.useDepthPicking) {
|
return undefined;
|
}
|
|
//>>includeStart('debug', pragmas.debug);
|
if (!defined(windowPosition)) {
|
throw new DeveloperError('windowPosition is undefined.');
|
}
|
if (!scene.context.depthTexture) {
|
throw new DeveloperError('Picking from the depth buffer is not supported. Check pickPositionSupported.');
|
}
|
//>>includeEnd('debug');
|
|
var cacheKey = windowPosition.toString();
|
|
if (this._pickPositionCacheDirty){
|
this._pickPositionCache = {};
|
this._pickPositionCacheDirty = false;
|
} else if (this._pickPositionCache.hasOwnProperty(cacheKey)){
|
return Cartesian3.clone(this._pickPositionCache[cacheKey], result);
|
}
|
|
var frameState = scene.frameState;
|
var context = scene.context;
|
var uniformState = context.uniformState;
|
|
var view = scene.defaultView;
|
scene.view = view;
|
|
var drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(scene, windowPosition, scratchPosition);
|
if (scene.pickTranslucentDepth) {
|
renderTranslucentDepthForPick(scene, drawingBufferPosition);
|
} else {
|
scene.updateFrameState();
|
uniformState.update(frameState);
|
scene.updateEnvironment();
|
}
|
drawingBufferPosition.y = scene.drawingBufferHeight - drawingBufferPosition.y;
|
|
var camera = scene.camera;
|
|
// Create a working frustum from the original camera frustum.
|
var frustum;
|
if (defined(camera.frustum.fov)) {
|
frustum = camera.frustum.clone(scratchPerspectiveFrustum);
|
} else if (defined(camera.frustum.infiniteProjectionMatrix)){
|
frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum);
|
} else if (defined(camera.frustum.width)) {
|
frustum = camera.frustum.clone(scratchOrthographicFrustum);
|
} else {
|
frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum);
|
}
|
|
var frustumCommandsList = view.frustumCommandsList;
|
var numFrustums = frustumCommandsList.length;
|
for (var i = 0; i < numFrustums; ++i) {
|
var pickDepth = this.getPickDepth(scene, i);
|
var depth = pickDepth.getDepth(context, drawingBufferPosition.x, drawingBufferPosition.y);
|
if (depth > 0.0 && depth < 1.0) {
|
var renderedFrustum = frustumCommandsList[i];
|
var height2D;
|
if (scene.mode === SceneMode.SCENE2D) {
|
height2D = camera.position.z;
|
camera.position.z = height2D - renderedFrustum.near + 1.0;
|
frustum.far = Math.max(1.0, renderedFrustum.far - renderedFrustum.near);
|
frustum.near = 1.0;
|
uniformState.update(frameState);
|
uniformState.updateFrustum(frustum);
|
} else {
|
frustum.near = renderedFrustum.near * (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0);
|
frustum.far = renderedFrustum.far;
|
uniformState.updateFrustum(frustum);
|
}
|
|
result = SceneTransforms.drawingBufferToWgs84Coordinates(scene, drawingBufferPosition, depth, result);
|
|
if (scene.mode === SceneMode.SCENE2D) {
|
camera.position.z = height2D;
|
uniformState.update(frameState);
|
}
|
|
this._pickPositionCache[cacheKey] = Cartesian3.clone(result);
|
return result;
|
}
|
}
|
|
this._pickPositionCache[cacheKey] = undefined;
|
return undefined;
|
};
|
|
var scratchPickPositionCartographic = new Cartographic();
|
|
Picking.prototype.pickPosition = function(scene, windowPosition, result) {
|
result = this.pickPositionWorldCoordinates(scene, windowPosition, result);
|
if (defined(result) && scene.mode !== SceneMode.SCENE3D) {
|
Cartesian3.fromElements(result.y, result.z, result.x, result);
|
|
var projection = scene.mapProjection;
|
var ellipsoid = projection.ellipsoid;
|
|
var cart = projection.unproject(result, scratchPickPositionCartographic);
|
ellipsoid.cartographicToCartesian(cart, result);
|
}
|
|
return result;
|
};
|
|
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.
|
var i;
|
var attributes;
|
var result = [];
|
var pickedPrimitives = [];
|
var pickedAttributes = [];
|
var pickedFeatures = [];
|
if (!defined(limit)) {
|
limit = Number.MAX_VALUE;
|
}
|
|
var pickedResult = pickCallback();
|
while (defined(pickedResult)) {
|
var object = pickedResult.object;
|
var position = pickedResult.position;
|
var exclude = pickedResult.exclude;
|
|
if (defined(position) && !defined(object)) {
|
result.push(pickedResult);
|
break;
|
}
|
|
if (!defined(object) || !defined(object.primitive)) {
|
break;
|
}
|
|
if (!exclude) {
|
result.push(pickedResult);
|
if (0 >= --limit) {
|
break;
|
}
|
}
|
|
var primitive = object.primitive;
|
var hasShowAttribute = false;
|
|
// If the picked object has a show attribute, use it.
|
if (typeof primitive.getGeometryInstanceAttributes === 'function') {
|
if (defined(object.id)) {
|
attributes = primitive.getGeometryInstanceAttributes(object.id);
|
if (defined(attributes) && defined(attributes.show)) {
|
hasShowAttribute = true;
|
attributes.show = ShowGeometryInstanceAttribute.toValue(false, attributes.show);
|
pickedAttributes.push(attributes);
|
}
|
}
|
}
|
|
if (object instanceof Cesium3DTileFeature) {
|
hasShowAttribute = true;
|
object.show = false;
|
pickedFeatures.push(object);
|
}
|
|
// Otherwise, hide the entire primitive
|
if (!hasShowAttribute) {
|
primitive.show = false;
|
pickedPrimitives.push(primitive);
|
}
|
|
pickedResult = pickCallback();
|
}
|
|
// Unhide everything we hid while drill picking
|
for (i = 0; i < pickedPrimitives.length; ++i) {
|
pickedPrimitives[i].show = true;
|
}
|
|
for (i = 0; i < pickedAttributes.length; ++i) {
|
attributes = pickedAttributes[i];
|
attributes.show = ShowGeometryInstanceAttribute.toValue(true, attributes.show);
|
}
|
|
for (i = 0; i < pickedFeatures.length; ++i) {
|
pickedFeatures[i].show = true;
|
}
|
|
return result;
|
}
|
|
Picking.prototype.drillPick = function(scene, windowPosition, limit, width, height) {
|
var that = this;
|
var pickCallback = function() {
|
var object = that.pick(scene, windowPosition, width, height);
|
if (defined(object)) {
|
return {
|
object : object,
|
position : undefined,
|
exclude : false
|
};
|
}
|
};
|
var objects = drillPick(limit, pickCallback);
|
return objects.map(function(element) {
|
return element.object;
|
});
|
};
|
|
var scratchRight = new Cartesian3();
|
var scratchUp = new Cartesian3();
|
|
function MostDetailedRayPick(ray, width, tilesets) {
|
this.ray = ray;
|
this.width = width;
|
this.tilesets = tilesets;
|
this.ready = false;
|
this.deferred = when.defer();
|
this.promise = this.deferred.promise;
|
}
|
|
function updateOffscreenCameraFromRay(picking, ray, width, camera) {
|
var direction = ray.direction;
|
var orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight);
|
var right = Cartesian3.cross(direction, orthogonalAxis, scratchRight);
|
var up = Cartesian3.cross(direction, right, scratchUp);
|
|
camera.position = ray.origin;
|
camera.direction = direction;
|
camera.up = up;
|
camera.right = right;
|
|
camera.frustum.width = defaultValue(width, offscreenDefaultWidth);
|
return camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC);
|
}
|
|
function updateMostDetailedRayPick(picking, scene, rayPick) {
|
var frameState = scene.frameState;
|
|
var ray = rayPick.ray;
|
var width = rayPick.width;
|
var tilesets = rayPick.tilesets;
|
|
var camera = picking._pickOffscreenView.camera;
|
var cullingVolume = updateOffscreenCameraFromRay(picking, ray, width, camera);
|
|
var tilesetPassState = mostDetailedPreloadTilesetPassState;
|
tilesetPassState.camera = camera;
|
tilesetPassState.cullingVolume = cullingVolume;
|
|
var ready = true;
|
var tilesetsLength = tilesets.length;
|
for (var i = 0; i < tilesetsLength; ++i) {
|
var tileset = tilesets[i];
|
if (tileset.show && scene.primitives.contains(tileset)) {
|
// Only update tilesets that are still contained in the scene's primitive collection and are still visible
|
// Update tilesets continually until all tilesets are ready. This way tiles are never removed from the cache.
|
tileset.updateForPass(frameState, tilesetPassState);
|
ready = (ready && tilesetPassState.ready);
|
}
|
}
|
|
if (ready) {
|
rayPick.deferred.resolve();
|
}
|
|
return ready;
|
}
|
|
Picking.prototype.updateMostDetailedRayPicks = function(scene) {
|
// Modifies array during iteration
|
var rayPicks = this._mostDetailedRayPicks;
|
for (var i = 0; i < rayPicks.length; ++i) {
|
if (updateMostDetailedRayPick(this, scene, rayPicks[i])) {
|
rayPicks.splice(i--, 1);
|
}
|
}
|
};
|
|
function getTilesets(primitives, objectsToExclude, tilesets) {
|
var length = primitives.length;
|
for (var i = 0; i < length; ++i) {
|
var primitive = primitives.get(i);
|
if (primitive.show) {
|
if (defined(primitive.isCesium3DTileset)) {
|
if (!defined(objectsToExclude) || objectsToExclude.indexOf(primitive) === -1) {
|
tilesets.push(primitive);
|
}
|
} else if (primitive instanceof PrimitiveCollection) {
|
getTilesets(primitive, objectsToExclude, tilesets);
|
}
|
}
|
}
|
}
|
|
function launchMostDetailedRayPick(picking, scene, ray, objectsToExclude, width, callback) {
|
var tilesets = [];
|
getTilesets(scene.primitives, objectsToExclude, tilesets);
|
if (tilesets.length === 0) {
|
return when.resolve(callback());
|
}
|
|
var rayPick = new MostDetailedRayPick(ray, width, tilesets);
|
picking._mostDetailedRayPicks.push(rayPick);
|
return rayPick.promise.then(function() {
|
return callback();
|
});
|
}
|
|
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(picking, scene, ray, objectsToExclude, width, requirePosition, mostDetailed) {
|
var context = scene.context;
|
var uniformState = context.uniformState;
|
var frameState = scene.frameState;
|
|
var view = picking._pickOffscreenView;
|
scene.view = view;
|
|
updateOffscreenCameraFromRay(picking, ray, width, view.camera);
|
|
scratchRectangle = BoundingRectangle.clone(view.viewport, scratchRectangle);
|
|
var passState = view.pickFramebuffer.begin(scratchRectangle, view.viewport);
|
|
scene.jobScheduler.disableThisFrame();
|
|
scene.updateFrameState();
|
frameState.invertClassification = false;
|
frameState.passes.pick = true;
|
frameState.passes.offscreen = true;
|
|
if (mostDetailed) {
|
frameState.tilesetPassState = mostDetailedPickTilesetPassState;
|
} else {
|
frameState.tilesetPassState = pickTilesetPassState;
|
}
|
|
uniformState.update(frameState);
|
|
scene.updateEnvironment();
|
scene.updateAndExecuteCommands(passState, scratchColorZero);
|
scene.resolveFramebuffers(passState);
|
|
var position;
|
var object = view.pickFramebuffer.end(context);
|
|
if (scene.context.depthTexture) {
|
var numFrustums = view.frustumCommandsList.length;
|
for (var i = 0; i < numFrustums; ++i) {
|
var pickDepth = picking.getPickDepth(scene, i);
|
var depth = pickDepth.getDepth(context, 0, 0);
|
if (depth > 0.0 && depth < 1.0) {
|
var renderedFrustum = view.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;
|
}
|
}
|
}
|
|
scene.view = scene.defaultView;
|
context.endFrame();
|
|
if (defined(object) || defined(position)) {
|
return {
|
object : object,
|
position : position,
|
exclude : (!defined(position) && requirePosition) || isExcluded(object, objectsToExclude)
|
};
|
}
|
}
|
|
function getRayIntersections(picking, scene, ray, limit, objectsToExclude, width, requirePosition, mostDetailed) {
|
var pickCallback = function() {
|
return getRayIntersection(picking, scene, ray, objectsToExclude, width, requirePosition, mostDetailed);
|
};
|
return drillPick(limit, pickCallback);
|
}
|
|
function pickFromRay(picking, scene, ray, objectsToExclude, width, requirePosition, mostDetailed) {
|
var results = getRayIntersections(picking, scene, ray, 1, objectsToExclude, width, requirePosition, mostDetailed);
|
if (results.length > 0) {
|
return results[0];
|
}
|
}
|
|
function drillPickFromRay(picking, scene, ray, limit, objectsToExclude, width, requirePosition, mostDetailed) {
|
return getRayIntersections(picking, scene, ray, limit, objectsToExclude, width, requirePosition, mostDetailed);
|
}
|
|
function deferPromiseUntilPostRender(scene, promise) {
|
// Resolve promise after scene's postRender in case entities are created when the promise resolves.
|
// Entities can't be created between viewer._onTick and viewer._postRender.
|
var deferred = when.defer();
|
promise.then(function(result) {
|
var removeCallback = scene.postRender.addEventListener(function() {
|
deferred.resolve(result);
|
removeCallback();
|
});
|
scene.requestRender();
|
}).otherwise(function(error) {
|
deferred.reject(error);
|
});
|
return deferred.promise;
|
}
|
|
Picking.prototype.pickFromRay = function(scene, ray, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('ray', ray);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
|
}
|
//>>includeEnd('debug');
|
|
return pickFromRay(this, scene, ray, objectsToExclude, width, false, false);
|
};
|
|
Picking.prototype.drillPickFromRay = function(scene, ray, limit, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('ray', ray);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
|
}
|
//>>includeEnd('debug');
|
|
return drillPickFromRay(this, scene, ray, limit, objectsToExclude, width,false, false);
|
};
|
|
Picking.prototype.pickFromRayMostDetailed = function(scene, ray, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('ray', ray);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
|
}
|
//>>includeEnd('debug');
|
|
var that = this;
|
ray = Ray.clone(ray);
|
objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
|
return deferPromiseUntilPostRender(scene, launchMostDetailedRayPick(that, scene, ray, objectsToExclude, width, function() {
|
return pickFromRay(that, scene, ray, objectsToExclude, width, false, true);
|
}));
|
};
|
|
Picking.prototype.drillPickFromRayMostDetailed = function(scene, ray, limit, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('ray', ray);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('Ray intersections are only supported in 3D mode.');
|
}
|
//>>includeEnd('debug');
|
|
var that = this;
|
ray = Ray.clone(ray);
|
objectsToExclude = defined(objectsToExclude) ? objectsToExclude.slice() : objectsToExclude;
|
return deferPromiseUntilPostRender(scene, launchMostDetailedRayPick(that, scene, ray, objectsToExclude, width, function() {
|
return drillPickFromRay(that, scene, ray, limit, objectsToExclude, width, false, true);
|
}));
|
};
|
|
var scratchSurfacePosition = new Cartesian3();
|
var scratchSurfaceNormal = new Cartesian3();
|
var scratchSurfaceRay = new Ray();
|
var scratchCartographic = new Cartographic();
|
|
function getRayForSampleHeight(scene, cartographic) {
|
var globe = scene.globe;
|
var ellipsoid = defined(globe) ? globe.ellipsoid : scene.mapProjection.ellipsoid;
|
var height = ApproximateTerrainHeights._defaultMaxTerrainHeight;
|
var surfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic(cartographic, scratchSurfaceNormal);
|
var surfacePosition = Cartographic.toCartesian(cartographic, ellipsoid, scratchSurfacePosition);
|
var surfaceRay = scratchSurfaceRay;
|
surfaceRay.origin = surfacePosition;
|
surfaceRay.direction = surfaceNormal;
|
var ray = new Ray();
|
Ray.getPoint(surfaceRay, height, ray.origin);
|
Cartesian3.negate(surfaceNormal, ray.direction);
|
return ray;
|
}
|
|
function getRayForClampToHeight(scene, cartesian) {
|
var globe = scene.globe;
|
var ellipsoid = defined(globe) ? globe.ellipsoid : scene.mapProjection.ellipsoid;
|
var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic);
|
return getRayForSampleHeight(scene, cartographic);
|
}
|
|
function getHeightFromCartesian(scene, cartesian) {
|
var globe = scene.globe;
|
var ellipsoid = defined(globe) ? globe.ellipsoid : scene.mapProjection.ellipsoid;
|
var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchCartographic);
|
return cartographic.height;
|
}
|
|
function sampleHeightMostDetailed(picking, scene, cartographic, objectsToExclude, width) {
|
var ray = getRayForSampleHeight(scene, cartographic);
|
return launchMostDetailedRayPick(picking, scene, ray, objectsToExclude, width, function() {
|
var pickResult = pickFromRay(picking, scene, ray, objectsToExclude, width, true, true);
|
if (defined(pickResult)) {
|
return getHeightFromCartesian(scene, pickResult.position);
|
}
|
});
|
}
|
|
function clampToHeightMostDetailed(picking, scene, cartesian, objectsToExclude, width, result) {
|
var ray = getRayForClampToHeight(scene, cartesian);
|
return launchMostDetailedRayPick(picking, scene, ray, objectsToExclude, width, function() {
|
var pickResult = pickFromRay(picking, scene, ray, objectsToExclude, width, true, true);
|
if (defined(pickResult)) {
|
return Cartesian3.clone(pickResult.position, result);
|
}
|
});
|
}
|
|
Picking.prototype.sampleHeight = function(scene, position, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('position', position);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('sampleHeight is only supported in 3D mode.');
|
}
|
if (!scene.sampleHeightSupported) {
|
throw new DeveloperError('sampleHeight requires depth texture support. Check sampleHeightSupported.');
|
}
|
//>>includeEnd('debug');
|
|
var ray = getRayForSampleHeight(scene, position);
|
var pickResult = pickFromRay(this, scene, ray, objectsToExclude, width, true, false);
|
if (defined(pickResult)) {
|
return getHeightFromCartesian(scene, pickResult.position);
|
}
|
};
|
|
Picking.prototype.clampToHeight = function(scene, cartesian, objectsToExclude, width, result) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('cartesian', cartesian);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('clampToHeight is only supported in 3D mode.');
|
}
|
if (!scene.clampToHeightSupported) {
|
throw new DeveloperError('clampToHeight requires depth texture support. Check clampToHeightSupported.');
|
}
|
//>>includeEnd('debug');
|
|
var ray = getRayForClampToHeight(scene, cartesian);
|
var pickResult = pickFromRay(this, scene, ray, objectsToExclude, width, true, false);
|
if (defined(pickResult)) {
|
return Cartesian3.clone(pickResult.position, result);
|
}
|
};
|
|
Picking.prototype.sampleHeightMostDetailed = function(scene, positions, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('positions', positions);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('sampleHeightMostDetailed is only supported in 3D mode.');
|
}
|
if (!scene.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, scene, positions[i], objectsToExclude, width);
|
}
|
return deferPromiseUntilPostRender(scene, when.all(promises).then(function(heights) {
|
var length = heights.length;
|
for (var i = 0; i < length; ++i) {
|
positions[i].height = heights[i];
|
}
|
return positions;
|
}));
|
};
|
|
Picking.prototype.clampToHeightMostDetailed = function(scene, cartesians, objectsToExclude, width) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.defined('cartesians', cartesians);
|
if (scene.mode !== SceneMode.SCENE3D) {
|
throw new DeveloperError('clampToHeightMostDetailed is only supported in 3D mode.');
|
}
|
if (!scene.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, scene, cartesians[i], objectsToExclude, width, cartesians[i]);
|
}
|
return deferPromiseUntilPostRender(scene, when.all(promises).then(function(clampedCartesians) {
|
var length = clampedCartesians.length;
|
for (var i = 0; i < length; ++i) {
|
cartesians[i] = clampedCartesians[i];
|
}
|
return cartesians;
|
}));
|
};
|
|
Picking.prototype.destroy = function() {
|
this._pickOffscreenView = this._pickOffscreenView && this._pickOffscreenView.destroy();
|
};
|
export default Picking;
|