12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313 |
- 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 defer from "../Core/defer.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 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";
- const offscreenDefaultWidth = 0.1;
- const mostDetailedPreloadTilesetPassState = new Cesium3DTilePassState({
- pass: Cesium3DTilePass.MOST_DETAILED_PRELOAD,
- });
- const mostDetailedPickTilesetPassState = new Cesium3DTilePassState({
- pass: Cesium3DTilePass.MOST_DETAILED_PICK,
- });
- const pickTilesetPassState = new Cesium3DTilePassState({
- pass: Cesium3DTilePass.PICK,
- });
- /**
- * @private
- */
- function Picking(scene) {
- this._mostDetailedRayPicks = [];
- this.pickRenderStateCache = {};
- this._pickPositionCache = {};
- this._pickPositionCacheDirty = false;
- const pickOffscreenViewport = new BoundingRectangle(0, 0, 1, 1);
- const 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) {
- const pickDepths = scene.view.pickDepths;
- let pickDepth = pickDepths[index];
- if (!defined(pickDepth)) {
- pickDepth = new PickDepth();
- pickDepths[index] = pickDepth;
- }
- return pickDepth;
- };
- const scratchOrthoPickingFrustum = new OrthographicOffCenterFrustum();
- const scratchOrthoOrigin = new Cartesian3();
- const scratchOrthoDirection = new Cartesian3();
- const scratchOrthoPixelSize = new Cartesian2();
- const scratchOrthoPickVolumeMatrix4 = new Matrix4();
- function getPickOrthographicCullingVolume(
- scene,
- drawingBufferPosition,
- width,
- height,
- viewport
- ) {
- const camera = scene.camera;
- let frustum = camera.frustum;
- if (defined(frustum._offCenterFrustum)) {
- frustum = frustum._offCenterFrustum;
- }
- let x = (2.0 * (drawingBufferPosition.x - viewport.x)) / viewport.width - 1.0;
- x *= (frustum.right - frustum.left) * 0.5;
- let y =
- (2.0 * (viewport.height - drawingBufferPosition.y - viewport.y)) /
- viewport.height -
- 1.0;
- y *= (frustum.top - frustum.bottom) * 0.5;
- const transform = Matrix4.clone(
- camera.transform,
- scratchOrthoPickVolumeMatrix4
- );
- camera._setTransform(Matrix4.IDENTITY);
- const 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);
- }
- const pixelSize = frustum.getPixelDimensions(
- viewport.width,
- viewport.height,
- 1.0,
- 1.0,
- scratchOrthoPixelSize
- );
- const 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);
- }
- const scratchPerspPickingFrustum = new PerspectiveOffCenterFrustum();
- const scratchPerspPixelSize = new Cartesian2();
- function getPickPerspectiveCullingVolume(
- scene,
- drawingBufferPosition,
- width,
- height,
- viewport
- ) {
- const camera = scene.camera;
- const frustum = camera.frustum;
- const near = frustum.near;
- const tanPhi = Math.tan(frustum.fovy * 0.5);
- const tanTheta = frustum.aspectRatio * tanPhi;
- const x =
- (2.0 * (drawingBufferPosition.x - viewport.x)) / viewport.width - 1.0;
- const y =
- (2.0 * (viewport.height - drawingBufferPosition.y - viewport.y)) /
- viewport.height -
- 1.0;
- const xDir = x * near * tanTheta;
- const yDir = y * near * tanPhi;
- const pixelSize = frustum.getPixelDimensions(
- viewport.width,
- viewport.height,
- 1.0,
- 1.0,
- scratchPerspPixelSize
- );
- const pickWidth = pixelSize.x * width * 0.5;
- const pickHeight = pixelSize.y * height * 0.5;
- const 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
- ) {
- const 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
- let scratchRectangleWidth = 3.0;
- let scratchRectangleHeight = 3.0;
- let scratchRectangle = new BoundingRectangle(
- 0.0,
- 0.0,
- scratchRectangleWidth,
- scratchRectangleHeight
- );
- const scratchPosition = new Cartesian2();
- const 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);
- const context = scene.context;
- const us = context.uniformState;
- const frameState = scene.frameState;
- const view = scene.defaultView;
- scene.view = view;
- const viewport = view.viewport;
- viewport.x = 0;
- viewport.y = 0;
- viewport.width = context.drawingBufferWidth;
- viewport.height = context.drawingBufferHeight;
- let passState = view.passState;
- passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
- const 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);
- const object = view.pickFramebuffer.end(scratchRectangle);
- context.endFrame();
- return object;
- };
- function renderTranslucentDepthForPick(scene, drawingBufferPosition) {
- // PERFORMANCE_IDEA: render translucent only and merge with the previous frame
- const context = scene.context;
- const frameState = scene.frameState;
- const environmentState = scene.environmentState;
- const view = scene.defaultView;
- scene.view = view;
- const viewport = view.viewport;
- viewport.x = 0;
- viewport.y = 0;
- viewport.width = context.drawingBufferWidth;
- viewport.height = context.drawingBufferHeight;
- let 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();
- }
- const scratchPerspectiveFrustum = new PerspectiveFrustum();
- const scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum();
- const scratchOrthographicFrustum = new OrthographicFrustum();
- const 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');
- const cacheKey = windowPosition.toString();
- if (this._pickPositionCacheDirty) {
- this._pickPositionCache = {};
- this._pickPositionCacheDirty = false;
- } else if (this._pickPositionCache.hasOwnProperty(cacheKey)) {
- return Cartesian3.clone(this._pickPositionCache[cacheKey], result);
- }
- const frameState = scene.frameState;
- const context = scene.context;
- const uniformState = context.uniformState;
- const view = scene.defaultView;
- scene.view = view;
- const 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;
- const camera = scene.camera;
- // Create a working frustum from the original camera frustum.
- let 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);
- }
- const frustumCommandsList = view.frustumCommandsList;
- const numFrustums = frustumCommandsList.length;
- for (let i = 0; i < numFrustums; ++i) {
- const pickDepth = this.getPickDepth(scene, i);
- const depth = pickDepth.getDepth(
- context,
- drawingBufferPosition.x,
- drawingBufferPosition.y
- );
- if (!defined(depth)) {
- continue;
- }
- if (depth > 0.0 && depth < 1.0) {
- const renderedFrustum = frustumCommandsList[i];
- let 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;
- };
- const 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);
- const projection = scene.mapProjection;
- const ellipsoid = projection.ellipsoid;
- const 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.
- let i;
- let attributes;
- const result = [];
- const pickedPrimitives = [];
- const pickedAttributes = [];
- const pickedFeatures = [];
- if (!defined(limit)) {
- limit = Number.MAX_VALUE;
- }
- let pickedResult = pickCallback();
- while (defined(pickedResult)) {
- const object = pickedResult.object;
- const position = pickedResult.position;
- const 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;
- }
- }
- const primitive = object.primitive;
- let 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
- ) {
- const that = this;
- const pickCallback = function () {
- const object = that.pick(scene, windowPosition, width, height);
- if (defined(object)) {
- return {
- object: object,
- position: undefined,
- exclude: false,
- };
- }
- };
- const objects = drillPick(limit, pickCallback);
- return objects.map(function (element) {
- return element.object;
- });
- };
- const scratchRight = new Cartesian3();
- const scratchUp = new Cartesian3();
- function MostDetailedRayPick(ray, width, tilesets) {
- this.ray = ray;
- this.width = width;
- this.tilesets = tilesets;
- this.ready = false;
- this.deferred = defer();
- this.promise = this.deferred.promise;
- }
- function updateOffscreenCameraFromRay(picking, ray, width, camera) {
- const direction = ray.direction;
- const orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight);
- const right = Cartesian3.cross(direction, orthogonalAxis, scratchRight);
- const 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) {
- const frameState = scene.frameState;
- const ray = rayPick.ray;
- const width = rayPick.width;
- const tilesets = rayPick.tilesets;
- const camera = picking._pickOffscreenView.camera;
- const cullingVolume = updateOffscreenCameraFromRay(
- picking,
- ray,
- width,
- camera
- );
- const tilesetPassState = mostDetailedPreloadTilesetPassState;
- tilesetPassState.camera = camera;
- tilesetPassState.cullingVolume = cullingVolume;
- let ready = true;
- const tilesetsLength = tilesets.length;
- for (let i = 0; i < tilesetsLength; ++i) {
- const 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
- const rayPicks = this._mostDetailedRayPicks;
- for (let i = 0; i < rayPicks.length; ++i) {
- if (updateMostDetailedRayPick(this, scene, rayPicks[i])) {
- rayPicks.splice(i--, 1);
- }
- }
- };
- function getTilesets(primitives, objectsToExclude, tilesets) {
- const length = primitives.length;
- for (let i = 0; i < length; ++i) {
- const 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
- ) {
- const tilesets = [];
- getTilesets(scene.primitives, objectsToExclude, tilesets);
- if (tilesets.length === 0) {
- return Promise.resolve(callback());
- }
- const 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
- ) {
- const context = scene.context;
- const uniformState = context.uniformState;
- const frameState = scene.frameState;
- const view = picking._pickOffscreenView;
- scene.view = view;
- updateOffscreenCameraFromRay(picking, ray, width, view.camera);
- scratchRectangle = BoundingRectangle.clone(view.viewport, scratchRectangle);
- const 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);
- let position;
- const object = view.pickFramebuffer.end(scratchRectangle);
- if (scene.context.depthTexture) {
- const numFrustums = view.frustumCommandsList.length;
- for (let i = 0; i < numFrustums; ++i) {
- const pickDepth = picking.getPickDepth(scene, i);
- const depth = pickDepth.getDepth(context, 0, 0);
- if (!defined(depth)) {
- continue;
- }
- if (depth > 0.0 && depth < 1.0) {
- const renderedFrustum = view.frustumCommandsList[i];
- const near =
- renderedFrustum.near *
- (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0);
- const far = renderedFrustum.far;
- const 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
- ) {
- const pickCallback = function () {
- return getRayIntersection(
- picking,
- scene,
- ray,
- objectsToExclude,
- width,
- requirePosition,
- mostDetailed
- );
- };
- return drillPick(limit, pickCallback);
- }
- function pickFromRay(
- picking,
- scene,
- ray,
- objectsToExclude,
- width,
- requirePosition,
- mostDetailed
- ) {
- const 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.
- const deferred = defer();
- promise
- .then(function (result) {
- const removeCallback = scene.postRender.addEventListener(function () {
- deferred.resolve(result);
- removeCallback();
- });
- scene.requestRender();
- })
- .catch(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');
- const 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');
- const 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
- );
- }
- )
- );
- };
- const scratchSurfacePosition = new Cartesian3();
- const scratchSurfaceNormal = new Cartesian3();
- const scratchSurfaceRay = new Ray();
- const scratchCartographic = new Cartographic();
- function getRayForSampleHeight(scene, cartographic) {
- const globe = scene.globe;
- const ellipsoid = defined(globe)
- ? globe.ellipsoid
- : scene.mapProjection.ellipsoid;
- const height = ApproximateTerrainHeights._defaultMaxTerrainHeight;
- const surfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic(
- cartographic,
- scratchSurfaceNormal
- );
- const surfacePosition = Cartographic.toCartesian(
- cartographic,
- ellipsoid,
- scratchSurfacePosition
- );
- const surfaceRay = scratchSurfaceRay;
- surfaceRay.origin = surfacePosition;
- surfaceRay.direction = surfaceNormal;
- const ray = new Ray();
- Ray.getPoint(surfaceRay, height, ray.origin);
- Cartesian3.negate(surfaceNormal, ray.direction);
- return ray;
- }
- function getRayForClampToHeight(scene, cartesian) {
- const globe = scene.globe;
- const ellipsoid = defined(globe)
- ? globe.ellipsoid
- : scene.mapProjection.ellipsoid;
- const cartographic = Cartographic.fromCartesian(
- cartesian,
- ellipsoid,
- scratchCartographic
- );
- return getRayForSampleHeight(scene, cartographic);
- }
- function getHeightFromCartesian(scene, cartesian) {
- const globe = scene.globe;
- const ellipsoid = defined(globe)
- ? globe.ellipsoid
- : scene.mapProjection.ellipsoid;
- const cartographic = Cartographic.fromCartesian(
- cartesian,
- ellipsoid,
- scratchCartographic
- );
- return cartographic.height;
- }
- function sampleHeightMostDetailed(
- picking,
- scene,
- cartographic,
- objectsToExclude,
- width
- ) {
- const ray = getRayForSampleHeight(scene, cartographic);
- return launchMostDetailedRayPick(
- picking,
- scene,
- ray,
- objectsToExclude,
- width,
- function () {
- const 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
- ) {
- const ray = getRayForClampToHeight(scene, cartesian);
- return launchMostDetailedRayPick(
- picking,
- scene,
- ray,
- objectsToExclude,
- width,
- function () {
- const 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');
- const ray = getRayForSampleHeight(scene, position);
- const 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');
- const ray = getRayForClampToHeight(scene, cartesian);
- const 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;
- const length = positions.length;
- const promises = new Array(length);
- for (let i = 0; i < length; ++i) {
- promises[i] = sampleHeightMostDetailed(
- this,
- scene,
- positions[i],
- objectsToExclude,
- width
- );
- }
- return deferPromiseUntilPostRender(
- scene,
- Promise.all(promises).then(function (heights) {
- const length = heights.length;
- for (let 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;
- const length = cartesians.length;
- const promises = new Array(length);
- for (let i = 0; i < length; ++i) {
- promises[i] = clampToHeightMostDetailed(
- this,
- scene,
- cartesians[i],
- objectsToExclude,
- width,
- cartesians[i]
- );
- }
- return deferPromiseUntilPostRender(
- scene,
- Promise.all(promises).then(function (clampedCartesians) {
- const length = clampedCartesians.length;
- for (let 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;
|