You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1308 lines
33 KiB
JavaScript

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 (!defined(depth)) {
continue;
}
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(scratchRectangle);
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 (!defined(depth)) {
continue;
}
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;