智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683
  1. import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
  2. import BoundingRectangle from "../Core/BoundingRectangle.js";
  3. import Cartesian2 from "../Core/Cartesian2.js";
  4. import Cartesian3 from "../Core/Cartesian3.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import Check from "../Core/Check.js";
  7. import Color from "../Core/Color.js";
  8. import defined from "../Core/defined.js";
  9. import DeveloperError from "../Core/DeveloperError.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import oneTimeWarning from "../Core/oneTimeWarning.js";
  12. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  13. import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";
  14. import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";
  15. import PerspectiveOffCenterFrustum from "../Core/PerspectiveOffCenterFrustum.js";
  16. import Ray from "../Core/Ray.js";
  17. import ShowGeometryInstanceAttribute from "../Core/ShowGeometryInstanceAttribute.js";
  18. import Camera from "./Camera.js";
  19. import Cesium3DTileFeature from "./Cesium3DTileFeature.js";
  20. import Cesium3DTilePass from "./Cesium3DTilePass.js";
  21. import Cesium3DTilePassState from "./Cesium3DTilePassState.js";
  22. import MetadataPicking from "./MetadataPicking.js";
  23. import PickDepth from "./PickDepth.js";
  24. import PrimitiveCollection from "./PrimitiveCollection.js";
  25. import SceneMode from "./SceneMode.js";
  26. import SceneTransforms from "./SceneTransforms.js";
  27. import View from "./View.js";
  28. const offscreenDefaultWidth = 0.1;
  29. const mostDetailedPreloadTilesetPassState = new Cesium3DTilePassState({
  30. pass: Cesium3DTilePass.MOST_DETAILED_PRELOAD,
  31. });
  32. const mostDetailedPickTilesetPassState = new Cesium3DTilePassState({
  33. pass: Cesium3DTilePass.MOST_DETAILED_PICK,
  34. });
  35. const pickTilesetPassState = new Cesium3DTilePassState({
  36. pass: Cesium3DTilePass.PICK,
  37. });
  38. /**
  39. * @private
  40. */
  41. function Picking(scene) {
  42. this._mostDetailedRayPicks = [];
  43. this.pickRenderStateCache = {};
  44. this._pickPositionCache = {};
  45. this._pickPositionCacheDirty = false;
  46. const pickOffscreenViewport = new BoundingRectangle(0, 0, 1, 1);
  47. const pickOffscreenCamera = new Camera(scene);
  48. pickOffscreenCamera.frustum = new OrthographicFrustum({
  49. width: offscreenDefaultWidth,
  50. aspectRatio: 1.0,
  51. near: 0.1,
  52. });
  53. this._pickOffscreenView = new View(
  54. scene,
  55. pickOffscreenCamera,
  56. pickOffscreenViewport,
  57. );
  58. }
  59. Picking.prototype.update = function () {
  60. this._pickPositionCacheDirty = true;
  61. };
  62. Picking.prototype.getPickDepth = function (scene, index) {
  63. const pickDepths = scene.view.pickDepths;
  64. let pickDepth = pickDepths[index];
  65. if (!defined(pickDepth)) {
  66. pickDepth = new PickDepth();
  67. pickDepths[index] = pickDepth;
  68. }
  69. return pickDepth;
  70. };
  71. const scratchOrthoPickingFrustum = new OrthographicOffCenterFrustum();
  72. const scratchOrthoOrigin = new Cartesian3();
  73. const scratchOrthoDirection = new Cartesian3();
  74. const scratchOrthoPixelSize = new Cartesian2();
  75. const scratchOrthoPickVolumeMatrix4 = new Matrix4();
  76. function getPickOrthographicCullingVolume(
  77. scene,
  78. drawingBufferPosition,
  79. width,
  80. height,
  81. viewport,
  82. ) {
  83. const camera = scene.camera;
  84. let frustum = camera.frustum;
  85. const offCenterFrustum = frustum.offCenterFrustum;
  86. if (defined(offCenterFrustum)) {
  87. frustum = offCenterFrustum;
  88. }
  89. let x = (2.0 * (drawingBufferPosition.x - viewport.x)) / viewport.width - 1.0;
  90. x *= (frustum.right - frustum.left) * 0.5;
  91. let y =
  92. (2.0 * (viewport.height - drawingBufferPosition.y - viewport.y)) /
  93. viewport.height -
  94. 1.0;
  95. y *= (frustum.top - frustum.bottom) * 0.5;
  96. const transform = Matrix4.clone(
  97. camera.transform,
  98. scratchOrthoPickVolumeMatrix4,
  99. );
  100. camera._setTransform(Matrix4.IDENTITY);
  101. const origin = Cartesian3.clone(camera.position, scratchOrthoOrigin);
  102. Cartesian3.multiplyByScalar(camera.right, x, scratchOrthoDirection);
  103. Cartesian3.add(scratchOrthoDirection, origin, origin);
  104. Cartesian3.multiplyByScalar(camera.up, y, scratchOrthoDirection);
  105. Cartesian3.add(scratchOrthoDirection, origin, origin);
  106. camera._setTransform(transform);
  107. if (scene.mode === SceneMode.SCENE2D) {
  108. Cartesian3.fromElements(origin.z, origin.x, origin.y, origin);
  109. }
  110. const pixelSize = frustum.getPixelDimensions(
  111. viewport.width,
  112. viewport.height,
  113. 1.0,
  114. 1.0,
  115. scratchOrthoPixelSize,
  116. );
  117. const ortho = scratchOrthoPickingFrustum;
  118. ortho.right = pixelSize.x * 0.5;
  119. ortho.left = -ortho.right;
  120. ortho.top = pixelSize.y * 0.5;
  121. ortho.bottom = -ortho.top;
  122. ortho.near = frustum.near;
  123. ortho.far = frustum.far;
  124. return ortho.computeCullingVolume(origin, camera.directionWC, camera.upWC);
  125. }
  126. const scratchPerspPickingFrustum = new PerspectiveOffCenterFrustum();
  127. const scratchPerspPixelSize = new Cartesian2();
  128. function getPickPerspectiveCullingVolume(
  129. scene,
  130. drawingBufferPosition,
  131. width,
  132. height,
  133. viewport,
  134. ) {
  135. const camera = scene.camera;
  136. const frustum = camera.frustum;
  137. const near = frustum.near;
  138. const tanPhi = Math.tan(frustum.fovy * 0.5);
  139. const tanTheta = frustum.aspectRatio * tanPhi;
  140. const x =
  141. (2.0 * (drawingBufferPosition.x - viewport.x)) / viewport.width - 1.0;
  142. const y =
  143. (2.0 * (viewport.height - drawingBufferPosition.y - viewport.y)) /
  144. viewport.height -
  145. 1.0;
  146. const xDir = x * near * tanTheta;
  147. const yDir = y * near * tanPhi;
  148. const pixelSize = frustum.getPixelDimensions(
  149. viewport.width,
  150. viewport.height,
  151. 1.0,
  152. 1.0,
  153. scratchPerspPixelSize,
  154. );
  155. const pickWidth = pixelSize.x * width * 0.5;
  156. const pickHeight = pixelSize.y * height * 0.5;
  157. const offCenter = scratchPerspPickingFrustum;
  158. offCenter.top = yDir + pickHeight;
  159. offCenter.bottom = yDir - pickHeight;
  160. offCenter.right = xDir + pickWidth;
  161. offCenter.left = xDir - pickWidth;
  162. offCenter.near = near;
  163. offCenter.far = frustum.far;
  164. return offCenter.computeCullingVolume(
  165. camera.positionWC,
  166. camera.directionWC,
  167. camera.upWC,
  168. );
  169. }
  170. function getPickCullingVolume(
  171. scene,
  172. drawingBufferPosition,
  173. width,
  174. height,
  175. viewport,
  176. ) {
  177. const frustum = scene.camera.frustum;
  178. if (
  179. frustum instanceof OrthographicFrustum ||
  180. frustum instanceof OrthographicOffCenterFrustum
  181. ) {
  182. return getPickOrthographicCullingVolume(
  183. scene,
  184. drawingBufferPosition,
  185. width,
  186. height,
  187. viewport,
  188. );
  189. }
  190. return getPickPerspectiveCullingVolume(
  191. scene,
  192. drawingBufferPosition,
  193. width,
  194. height,
  195. viewport,
  196. );
  197. }
  198. // Pick position and rectangle, used in all picking functions,
  199. // filled in computePickingDrawingBufferRectangle and passed
  200. // the the FrameBuffer begin/end methods
  201. const scratchRectangle = new BoundingRectangle(0.0, 0.0, 3.0, 3.0);
  202. const scratchPosition = new Cartesian2();
  203. // Dummy color that is passed to updateAndExecuteCommands in
  204. // all picking functions, used as the "background color"
  205. const scratchColorZero = new Color(0.0, 0.0, 0.0, 0.0);
  206. /**
  207. * Compute the rectangle that describes the part of the drawing buffer
  208. * that is relevant for picking.
  209. *
  210. * @param {number} drawingBufferHeight The height of the drawing buffer
  211. * @param {Cartesian2} position The position inside the drawing buffer
  212. * @param {number|undefined} width The width of the rectangle, assumed to
  213. * be an odd integer number, default : 3.0
  214. * @param {number|undefined} height The height of the rectangle. If unspecified,
  215. * height will default to the value of <code>width</code>
  216. * @param {BoundingRectangle} result The result rectangle
  217. * @returns {BoundingRectangle} The result rectangle
  218. */
  219. function computePickingDrawingBufferRectangle(
  220. drawingBufferHeight,
  221. position,
  222. width,
  223. height,
  224. result,
  225. ) {
  226. result.width = width ?? 3.0;
  227. result.height = height ?? result.width;
  228. result.x = position.x - (result.width - 1.0) * 0.5;
  229. result.y = drawingBufferHeight - position.y - (result.height - 1.0) * 0.5;
  230. return result;
  231. }
  232. /**
  233. * Setup needed before picking.
  234. *
  235. * @param {Scene} scene
  236. * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
  237. * @param {BoundingRectangle} drawingBufferRectangle The output drawing buffer recangle.
  238. * @param {number} [width=3] Width of the pick rectangle.
  239. * @param {number} [height=3] Height of the pick rectangle.
  240. */
  241. function pickBegin(
  242. scene,
  243. windowPosition,
  244. drawingBufferRectangle,
  245. width,
  246. height,
  247. ) {
  248. const { context, frameState, defaultView } = scene;
  249. const { viewport, pickFramebuffer } = defaultView;
  250. scene.view = defaultView;
  251. viewport.x = 0;
  252. viewport.y = 0;
  253. viewport.width = context.drawingBufferWidth;
  254. viewport.height = context.drawingBufferHeight;
  255. let passState = defaultView.passState;
  256. passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
  257. const drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(
  258. scene,
  259. windowPosition,
  260. scratchPosition,
  261. );
  262. computePickingDrawingBufferRectangle(
  263. context.drawingBufferHeight,
  264. drawingBufferPosition,
  265. width,
  266. height,
  267. drawingBufferRectangle,
  268. );
  269. scene.jobScheduler.disableThisFrame();
  270. scene.updateFrameState();
  271. frameState.cullingVolume = getPickCullingVolume(
  272. scene,
  273. drawingBufferPosition,
  274. drawingBufferRectangle.width,
  275. drawingBufferRectangle.height,
  276. viewport,
  277. );
  278. frameState.invertClassification = false;
  279. frameState.passes.pick = true;
  280. frameState.tilesetPassState = pickTilesetPassState;
  281. context.uniformState.update(frameState);
  282. scene.updateEnvironment();
  283. passState = pickFramebuffer.begin(drawingBufferRectangle, viewport);
  284. scene.updateAndExecuteCommands(passState, scratchColorZero);
  285. scene.resolveFramebuffers(passState);
  286. }
  287. /**
  288. * Teardown needed after picking.
  289. *
  290. * @param {Scene} scene
  291. */
  292. function pickEnd(scene) {
  293. const { context } = scene;
  294. context.endFrame();
  295. }
  296. /**
  297. * Same operation as {@link Picking#pick}, but returns a Promise that resolves asynchronously without blocking the main render thread.
  298. * Requires WebGL2 else using synchronous fallback.
  299. *
  300. * @see Picking#pick
  301. *
  302. * @param {Scene} scene
  303. * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
  304. * @param {number} [width=3] Width of the pick rectangle.
  305. * @param {number} [height=3] Height of the pick rectangle.
  306. * @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
  307. * @returns {Promise<object[]>} List of objects containing the picked primitives.
  308. *
  309. * @exception {RuntimeError} Async Picking Request Timeout.
  310. */
  311. Picking.prototype.pickAsync = async function (
  312. scene,
  313. windowPosition,
  314. width,
  315. height,
  316. limit = 1,
  317. ) {
  318. //>>includeStart('debug', pragmas.debug);
  319. Check.defined("windowPosition", windowPosition);
  320. //>>includeEnd('debug');
  321. const { context, frameState, defaultView } = scene;
  322. const { pickFramebuffer } = defaultView;
  323. const drawingBufferRectangle = scratchRectangle;
  324. pickBegin(scene, windowPosition, drawingBufferRectangle, width, height);
  325. let pickedObjects;
  326. if (context.webgl2) {
  327. pickedObjects = pickFramebuffer.endAsync(
  328. drawingBufferRectangle,
  329. frameState,
  330. limit,
  331. ); // Promise<Object[]>
  332. } else {
  333. pickedObjects = pickFramebuffer.end(drawingBufferRectangle, limit); // Object[]
  334. pickedObjects = Promise.resolve(pickedObjects); // Promise<Object[]> Wrap as Promise
  335. oneTimeWarning(
  336. "picking-async-fallback",
  337. "Fallback to synchronous picking because async operation requires WebGL2 context.",
  338. );
  339. }
  340. pickEnd(scene);
  341. return pickedObjects;
  342. };
  343. /**
  344. * Returns a list of objects with a <code>primitive</code> property that contains the first (top) primitives
  345. * in the scene at a particular window coordinate. Other properties may potentially be set depending on the
  346. * type of primitive and may be used to further identify the picked object.
  347. * <p>
  348. * When a feature of a 3D Tiles tileset is picked, <code>pick</code> returns a {@link Cesium3DTileFeature} object.
  349. * </p>
  350. * @param {Scene} scene
  351. * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
  352. * @param {number} [width=3] Width of the pick rectangle.
  353. * @param {number} [height=3] Height of the pick rectangle.
  354. * @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
  355. * @returns {object[]} List of objects containing the picked primitives.
  356. *
  357. */
  358. Picking.prototype.pick = function (
  359. scene,
  360. windowPosition,
  361. width,
  362. height,
  363. limit = 1,
  364. ) {
  365. //>>includeStart('debug', pragmas.debug);
  366. Check.defined("windowPosition", windowPosition);
  367. //>>includeEnd('debug');
  368. const { defaultView } = scene;
  369. const { pickFramebuffer } = defaultView;
  370. const drawingBufferRectangle = scratchRectangle;
  371. pickBegin(scene, windowPosition, drawingBufferRectangle, width, height);
  372. const pickedObjects = pickFramebuffer.end(drawingBufferRectangle, limit); // Object[]
  373. pickEnd(scene);
  374. return pickedObjects;
  375. };
  376. /**
  377. * Returns an object with information about the voxel sample rendered at
  378. * a particular window coordinate. Returns <code>undefined</code> if there is no
  379. * voxel at that position.
  380. *
  381. * @param {Scene} scene
  382. * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
  383. * @param {number} [width=3] Width of the pick rectangle.
  384. * @param {number} [height=3] Height of the pick rectangle.
  385. * @returns {object|undefined} Object containing the picked primitive.
  386. */
  387. Picking.prototype.pickVoxelCoordinate = function (
  388. scene,
  389. windowPosition,
  390. width,
  391. height,
  392. ) {
  393. //>>includeStart('debug', pragmas.debug);
  394. Check.defined("windowPosition", windowPosition);
  395. //>>includeEnd('debug');
  396. const { context, frameState, defaultView } = scene;
  397. const { viewport, pickFramebuffer } = defaultView;
  398. scene.view = defaultView;
  399. viewport.x = 0;
  400. viewport.y = 0;
  401. viewport.width = context.drawingBufferWidth;
  402. viewport.height = context.drawingBufferHeight;
  403. let passState = defaultView.passState;
  404. passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
  405. const drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(
  406. scene,
  407. windowPosition,
  408. scratchPosition,
  409. );
  410. const drawingBufferRectangle = computePickingDrawingBufferRectangle(
  411. context.drawingBufferHeight,
  412. drawingBufferPosition,
  413. width,
  414. height,
  415. scratchRectangle,
  416. );
  417. scene.jobScheduler.disableThisFrame();
  418. scene.updateFrameState();
  419. frameState.cullingVolume = getPickCullingVolume(
  420. scene,
  421. drawingBufferPosition,
  422. drawingBufferRectangle.width,
  423. drawingBufferRectangle.height,
  424. viewport,
  425. );
  426. frameState.invertClassification = false;
  427. frameState.passes.pickVoxel = true;
  428. frameState.tilesetPassState = pickTilesetPassState;
  429. context.uniformState.update(frameState);
  430. scene.updateEnvironment();
  431. passState = pickFramebuffer.begin(drawingBufferRectangle, viewport);
  432. scene.updateAndExecuteCommands(passState, scratchColorZero);
  433. scene.resolveFramebuffers(passState);
  434. const voxelInfo = pickFramebuffer.readCenterPixel(drawingBufferRectangle);
  435. context.endFrame();
  436. return voxelInfo;
  437. };
  438. /**
  439. * Pick a metadata value at the given window position.
  440. *
  441. * The given `pickedMetadataInfo` defines the metadata value that is
  442. * supposed to be picked.
  443. *
  444. * The return type will depend on the type of the metadata property
  445. * that is picked. Given the current limitations of the types that
  446. * are supported for metadata picking, the return type will be one
  447. * of the following:
  448. *
  449. * - For `SCALAR`, the return type will be a `number`
  450. * - For `SCALAR` arrays, the return type will be a `number[]`
  451. * - For `VEC2`, the return type will be a `Cartesian2`
  452. * - For `VEC3`, the return type will be a `Cartesian3`
  453. * - For `VEC4`, the return type will be a `Cartesian4`
  454. *
  455. * Future implementations may additionally return `string`- or
  456. * `boolean` types, and `MATn` values as `MatrixN` objects,
  457. * and arrays of the respective types.
  458. *
  459. * @param {Cartesian2} windowPosition Window coordinates to perform picking on.
  460. * @param {PickedMetadataInfo} pickedMetadataInfo Information about the picked metadata.
  461. * @returns {MetadataValue|undefined} The metadata value, or `undefined`
  462. * when no matching metadata value could be picked at the given position
  463. *
  464. * @private
  465. */
  466. Picking.prototype.pickMetadata = function (
  467. scene,
  468. windowPosition,
  469. pickedMetadataInfo,
  470. ) {
  471. //>>includeStart('debug', pragmas.debug);
  472. Check.typeOf.object("windowPosition", windowPosition);
  473. Check.typeOf.object("pickedMetadataInfo", pickedMetadataInfo);
  474. //>>includeEnd('debug');
  475. const { context, frameState, defaultView } = scene;
  476. const { viewport, pickFramebuffer } = defaultView;
  477. scene.view = defaultView;
  478. viewport.x = 0;
  479. viewport.y = 0;
  480. viewport.width = context.drawingBufferWidth;
  481. viewport.height = context.drawingBufferHeight;
  482. let passState = defaultView.passState;
  483. passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
  484. const drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(
  485. scene,
  486. windowPosition,
  487. scratchPosition,
  488. );
  489. const drawingBufferRectangle = computePickingDrawingBufferRectangle(
  490. context.drawingBufferHeight,
  491. drawingBufferPosition,
  492. 1.0,
  493. 1.0,
  494. scratchRectangle,
  495. );
  496. scene.jobScheduler.disableThisFrame();
  497. scene.updateFrameState();
  498. frameState.cullingVolume = getPickCullingVolume(
  499. scene,
  500. drawingBufferPosition,
  501. drawingBufferRectangle.width,
  502. drawingBufferRectangle.height,
  503. viewport,
  504. );
  505. frameState.invertClassification = false;
  506. frameState.passes.pick = true;
  507. frameState.tilesetPassState = pickTilesetPassState;
  508. // Insert the information about the picked metadata property
  509. // into the frame state, so that the `Scene.updateDerivedCommands`
  510. // call can detect any changes in the picked metadata description,
  511. // and update the derived commands for the new picked metadata
  512. // property
  513. frameState.pickingMetadata = true;
  514. frameState.pickedMetadataInfo = pickedMetadataInfo;
  515. context.uniformState.update(frameState);
  516. scene.updateEnvironment();
  517. passState = pickFramebuffer.begin(drawingBufferRectangle, viewport);
  518. scene.updateAndExecuteCommands(passState, scratchColorZero);
  519. // When OIT is enabled, then the resolveFrameBuffers function
  520. // will juggle around several frame buffers, and eventually use
  521. // the "environmentState.originalFramebuffer" instead of the
  522. // picking frame buffer. Skipping a million questions, just
  523. // switch OIT off here:
  524. const oldOIT = scene._environmentState.useOIT;
  525. scene._environmentState.useOIT = false;
  526. scene.resolveFramebuffers(passState);
  527. scene._environmentState.useOIT = oldOIT;
  528. const rawMetadataPixel = pickFramebuffer.readCenterPixel(
  529. drawingBufferRectangle,
  530. );
  531. context.endFrame();
  532. frameState.pickingMetadata = false;
  533. const metadataValue = MetadataPicking.decodeMetadataValues(
  534. pickedMetadataInfo.classProperty,
  535. pickedMetadataInfo.metadataProperty,
  536. rawMetadataPixel,
  537. );
  538. return metadataValue;
  539. };
  540. /**
  541. * @typedef {object} PickedMetadataInfo
  542. *
  543. * Information about metadata that is supposed to be picked
  544. *
  545. * @property {string|undefined} schemaId The optional ID of the metadata schema
  546. * @property {string} className The name of the metadata class
  547. * @property {string} propertyName The name of the metadata property
  548. * @property {MetadataClassProperty} classProperty The metadata class property
  549. */
  550. function renderTranslucentDepthForPick(scene, drawingBufferPosition) {
  551. // PERFORMANCE_IDEA: render translucent only and merge with the previous frame
  552. const { defaultView, context, frameState, environmentState } = scene;
  553. const { viewport, pickDepthFramebuffer } = defaultView;
  554. scene.view = defaultView;
  555. viewport.x = 0;
  556. viewport.y = 0;
  557. viewport.width = context.drawingBufferWidth;
  558. viewport.height = context.drawingBufferHeight;
  559. let passState = defaultView.passState;
  560. passState.viewport = BoundingRectangle.clone(viewport, passState.viewport);
  561. scene.clearPasses(frameState.passes);
  562. frameState.passes.pick = true;
  563. frameState.passes.depth = true;
  564. frameState.cullingVolume = getPickCullingVolume(
  565. scene,
  566. drawingBufferPosition,
  567. 1,
  568. 1,
  569. viewport,
  570. );
  571. frameState.tilesetPassState = pickTilesetPassState;
  572. scene.updateEnvironment();
  573. environmentState.renderTranslucentDepthForPick = true;
  574. passState = pickDepthFramebuffer.update(
  575. context,
  576. drawingBufferPosition,
  577. viewport,
  578. );
  579. scene.updateAndExecuteCommands(passState, scratchColorZero);
  580. scene.resolveFramebuffers(passState);
  581. context.endFrame();
  582. }
  583. const scratchPerspectiveFrustum = new PerspectiveFrustum();
  584. const scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum();
  585. const scratchOrthographicFrustum = new OrthographicFrustum();
  586. const scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum();
  587. Picking.prototype.pickPositionWorldCoordinates = function (
  588. scene,
  589. windowPosition,
  590. result,
  591. ) {
  592. if (!scene.useDepthPicking) {
  593. return undefined;
  594. }
  595. //>>includeStart('debug', pragmas.debug);
  596. Check.defined("windowPosition", windowPosition);
  597. if (!scene.context.depthTexture) {
  598. throw new DeveloperError(
  599. "Picking from the depth buffer is not supported. Check pickPositionSupported.",
  600. );
  601. }
  602. //>>includeEnd('debug');
  603. const cacheKey = windowPosition.toString();
  604. if (this._pickPositionCacheDirty) {
  605. this._pickPositionCache = {};
  606. this._pickPositionCacheDirty = false;
  607. } else if (this._pickPositionCache.hasOwnProperty(cacheKey)) {
  608. return Cartesian3.clone(this._pickPositionCache[cacheKey], result);
  609. }
  610. const { context, frameState, camera, defaultView } = scene;
  611. const { uniformState } = context;
  612. scene.view = defaultView;
  613. const drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(
  614. scene,
  615. windowPosition,
  616. scratchPosition,
  617. );
  618. if (scene.pickTranslucentDepth) {
  619. renderTranslucentDepthForPick(scene, drawingBufferPosition);
  620. } else {
  621. scene.updateFrameState();
  622. uniformState.update(frameState);
  623. scene.updateEnvironment();
  624. }
  625. drawingBufferPosition.y = scene.drawingBufferHeight - drawingBufferPosition.y;
  626. // Create a working frustum from the original camera frustum.
  627. let frustum;
  628. if (defined(camera.frustum.fov)) {
  629. frustum = camera.frustum.clone(scratchPerspectiveFrustum);
  630. } else if (defined(camera.frustum.infiniteProjectionMatrix)) {
  631. frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum);
  632. } else if (defined(camera.frustum.width)) {
  633. frustum = camera.frustum.clone(scratchOrthographicFrustum);
  634. } else {
  635. frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum);
  636. }
  637. const { frustumCommandsList } = defaultView;
  638. const numFrustums = frustumCommandsList.length;
  639. for (let i = 0; i < numFrustums; ++i) {
  640. const pickDepth = this.getPickDepth(scene, i);
  641. const depth = pickDepth.getDepth(
  642. context,
  643. drawingBufferPosition.x,
  644. drawingBufferPosition.y,
  645. );
  646. if (!defined(depth)) {
  647. continue;
  648. }
  649. if (depth > 0.0 && depth < 1.0) {
  650. const renderedFrustum = frustumCommandsList[i];
  651. let height2D;
  652. if (scene.mode === SceneMode.SCENE2D) {
  653. height2D = camera.position.z;
  654. camera.position.z = height2D - renderedFrustum.near + 1.0;
  655. frustum.far = Math.max(1.0, renderedFrustum.far - renderedFrustum.near);
  656. frustum.near = 1.0;
  657. uniformState.update(frameState);
  658. uniformState.updateFrustum(frustum);
  659. } else {
  660. frustum.near =
  661. renderedFrustum.near *
  662. (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0);
  663. frustum.far = renderedFrustum.far;
  664. uniformState.updateFrustum(frustum);
  665. }
  666. result = SceneTransforms.drawingBufferToWorldCoordinates(
  667. scene,
  668. drawingBufferPosition,
  669. depth,
  670. result,
  671. );
  672. if (scene.mode === SceneMode.SCENE2D) {
  673. camera.position.z = height2D;
  674. uniformState.update(frameState);
  675. }
  676. this._pickPositionCache[cacheKey] = Cartesian3.clone(result);
  677. return result;
  678. }
  679. }
  680. this._pickPositionCache[cacheKey] = undefined;
  681. return undefined;
  682. };
  683. const scratchPickPositionCartographic = new Cartographic();
  684. Picking.prototype.pickPosition = function (scene, windowPosition, result) {
  685. result = this.pickPositionWorldCoordinates(scene, windowPosition, result);
  686. if (defined(result) && scene.mode !== SceneMode.SCENE3D) {
  687. Cartesian3.fromElements(result.y, result.z, result.x, result);
  688. const projection = scene.mapProjection;
  689. const ellipsoid = projection.ellipsoid;
  690. const cart = projection.unproject(result, scratchPickPositionCartographic);
  691. ellipsoid.cartographicToCartesian(cart, result);
  692. }
  693. return result;
  694. };
  695. /**
  696. * @param {object[]} pickedResults the results from the pickCallback
  697. * @param {number} limit If supplied, stop drilling after collecting this many picks.
  698. * @param {object[]} results
  699. * @param {object[]} pickedPrimitives
  700. * @param {object[]} pickedAttributes
  701. * @param {object[]} pickedFeatures
  702. * @returns {boolean} whether picking should end
  703. */
  704. function addDrillPickedResults(
  705. pickedResults,
  706. limit,
  707. results,
  708. pickedPrimitives,
  709. pickedAttributes,
  710. pickedFeatures,
  711. ) {
  712. for (const pickedResult of pickedResults) {
  713. const object = pickedResult.object;
  714. const position = pickedResult.position;
  715. const exclude = pickedResult.exclude;
  716. if (defined(position) && !defined(object)) {
  717. results.push(pickedResult);
  718. return true;
  719. }
  720. if (!defined(object) || !defined(object.primitive)) {
  721. return true;
  722. }
  723. if (!exclude) {
  724. results.push(pickedResult);
  725. if (results.length >= limit) {
  726. return true;
  727. }
  728. }
  729. const primitive = object.primitive;
  730. let hasShowAttribute = false;
  731. // If the picked object has a show attribute, use it.
  732. if (typeof primitive.getGeometryInstanceAttributes === "function") {
  733. if (defined(object.id)) {
  734. const attributes = primitive.getGeometryInstanceAttributes(object.id);
  735. if (defined(attributes) && defined(attributes.show)) {
  736. hasShowAttribute = true;
  737. attributes.show = ShowGeometryInstanceAttribute.toValue(
  738. false,
  739. attributes.show,
  740. );
  741. pickedAttributes.push(attributes);
  742. }
  743. }
  744. }
  745. if (object instanceof Cesium3DTileFeature) {
  746. hasShowAttribute = true;
  747. object.show = false;
  748. pickedFeatures.push(object);
  749. }
  750. // Otherwise, hide the entire primitive
  751. if (!hasShowAttribute) {
  752. primitive.show = false;
  753. pickedPrimitives.push(primitive);
  754. }
  755. }
  756. }
  757. /**
  758. * Drill pick by repeatedly calling a given `pickCallback`, each time stripping away the previously picked objects.
  759. * @param {function(number): object[]} pickCallback Pick callback to execute each iteration
  760. * @param {number} [limit=Number.MAX_VALUE] If supplied, stop drilling after collecting this many picks
  761. * @returns {object[]} List of picked results
  762. */
  763. function drillPick(pickCallback, limit) {
  764. // PERFORMANCE_IDEA: This function calls each primitive's update for each pass. Instead
  765. // we could update the primitive once, and then just execute their commands for each pass,
  766. // and cull commands for picked primitives. e.g., base on the command's owner.
  767. const results = [];
  768. const pickedPrimitives = [];
  769. const pickedAttributes = [];
  770. const pickedFeatures = [];
  771. if (!defined(limit)) {
  772. limit = Number.MAX_VALUE;
  773. }
  774. let pickedResults = pickCallback(limit);
  775. while (defined(pickedResults) && pickedResults.length > 0) {
  776. const complete = addDrillPickedResults(
  777. pickedResults,
  778. limit,
  779. results,
  780. pickedPrimitives,
  781. pickedAttributes,
  782. pickedFeatures,
  783. );
  784. if (complete) {
  785. break;
  786. }
  787. pickedResults = pickCallback(limit - results.length);
  788. }
  789. // Unhide everything we hid while drill picking
  790. for (let i = 0; i < pickedPrimitives.length; ++i) {
  791. pickedPrimitives[i].show = true;
  792. }
  793. for (let i = 0; i < pickedAttributes.length; ++i) {
  794. const attributes = pickedAttributes[i];
  795. attributes.show = ShowGeometryInstanceAttribute.toValue(
  796. true,
  797. attributes.show,
  798. );
  799. }
  800. for (let i = 0; i < pickedFeatures.length; ++i) {
  801. pickedFeatures[i].show = true;
  802. }
  803. return results;
  804. }
  805. Picking.prototype.drillPick = function (
  806. scene,
  807. windowPosition,
  808. limit,
  809. width,
  810. height,
  811. ) {
  812. const pickCallback = (limit) => {
  813. const pickedObjects = this.pick(
  814. scene,
  815. windowPosition,
  816. width,
  817. height,
  818. limit,
  819. );
  820. return pickedObjects.map((object) => ({
  821. object: object,
  822. position: undefined,
  823. exclude: false,
  824. }));
  825. };
  826. const objects = drillPick(pickCallback, limit);
  827. return objects.map((element) => element.object);
  828. };
  829. const scratchRight = new Cartesian3();
  830. const scratchUp = new Cartesian3();
  831. function MostDetailedRayPick(ray, width, tilesets) {
  832. this.ray = ray;
  833. this.width = width;
  834. this.tilesets = tilesets;
  835. this.ready = false;
  836. const pick = this;
  837. this.promise = new Promise((resolve) => {
  838. pick._completePick = () => {
  839. resolve();
  840. };
  841. });
  842. }
  843. function updateOffscreenCameraFromRay(picking, ray, width, camera) {
  844. const direction = ray.direction;
  845. const orthogonalAxis = Cartesian3.mostOrthogonalAxis(direction, scratchRight);
  846. const right = Cartesian3.cross(direction, orthogonalAxis, scratchRight);
  847. const up = Cartesian3.cross(direction, right, scratchUp);
  848. camera.position = ray.origin;
  849. camera.direction = direction;
  850. camera.up = up;
  851. camera.right = right;
  852. camera.frustum.width = width ?? offscreenDefaultWidth;
  853. return camera.frustum.computeCullingVolume(
  854. camera.positionWC,
  855. camera.directionWC,
  856. camera.upWC,
  857. );
  858. }
  859. function updateMostDetailedRayPick(picking, scene, rayPick) {
  860. const frameState = scene.frameState;
  861. const { ray, width, tilesets } = rayPick;
  862. const camera = picking._pickOffscreenView.camera;
  863. const cullingVolume = updateOffscreenCameraFromRay(
  864. picking,
  865. ray,
  866. width,
  867. camera,
  868. );
  869. const tilesetPassState = mostDetailedPreloadTilesetPassState;
  870. tilesetPassState.camera = camera;
  871. tilesetPassState.cullingVolume = cullingVolume;
  872. let ready = true;
  873. const tilesetsLength = tilesets.length;
  874. for (let i = 0; i < tilesetsLength; ++i) {
  875. const tileset = tilesets[i];
  876. if (tileset.show && scene.primitives.contains(tileset)) {
  877. // Only update tilesets that are still contained in the scene's primitive collection and are still visible
  878. // Update tilesets continually until all tilesets are ready. This way tiles are never removed from the cache.
  879. tileset.updateForPass(frameState, tilesetPassState);
  880. ready = ready && tilesetPassState.ready;
  881. }
  882. }
  883. if (ready) {
  884. rayPick._completePick();
  885. }
  886. return ready;
  887. }
  888. Picking.prototype.updateMostDetailedRayPicks = function (scene) {
  889. // Modifies array during iteration
  890. const rayPicks = this._mostDetailedRayPicks;
  891. for (let i = 0; i < rayPicks.length; ++i) {
  892. if (updateMostDetailedRayPick(this, scene, rayPicks[i])) {
  893. rayPicks.splice(i--, 1);
  894. }
  895. }
  896. };
  897. function getTilesets(primitives, objectsToExclude, tilesets) {
  898. for (let i = 0; i < primitives.length; ++i) {
  899. const primitive = primitives.get(i);
  900. if (primitive.show) {
  901. if (defined(primitive.isCesium3DTileset)) {
  902. if (
  903. !defined(objectsToExclude) ||
  904. objectsToExclude.indexOf(primitive) === -1
  905. ) {
  906. tilesets.push(primitive);
  907. }
  908. } else if (primitive instanceof PrimitiveCollection) {
  909. getTilesets(primitive, objectsToExclude, tilesets);
  910. }
  911. }
  912. }
  913. }
  914. /**
  915. * @private
  916. * @param {Picking} picking
  917. * @param {Scene} scene
  918. * @param {Ray} ray
  919. * @param {object[] | undefined} objectsToExclude
  920. * @param {number | undefined} width
  921. * @param {Function} callback
  922. * @returns {Promise<Cartesian3 | undefined>}
  923. */
  924. function launchMostDetailedRayPick(
  925. picking,
  926. scene,
  927. ray,
  928. objectsToExclude,
  929. width,
  930. callback,
  931. ) {
  932. const tilesets = [];
  933. getTilesets(scene.primitives, objectsToExclude, tilesets);
  934. if (tilesets.length === 0) {
  935. return Promise.resolve(callback());
  936. }
  937. const rayPick = new MostDetailedRayPick(ray, width, tilesets);
  938. picking._mostDetailedRayPicks.push(rayPick);
  939. return rayPick.promise.then(function () {
  940. return callback();
  941. });
  942. }
  943. function isExcluded(object, objectsToExclude) {
  944. if (
  945. !defined(object) ||
  946. !defined(objectsToExclude) ||
  947. objectsToExclude.length === 0
  948. ) {
  949. return false;
  950. }
  951. return (
  952. objectsToExclude.indexOf(object) > -1 ||
  953. objectsToExclude.indexOf(object.primitive) > -1 ||
  954. objectsToExclude.indexOf(object.id) > -1
  955. );
  956. }
  957. function getRayIntersection(
  958. picking,
  959. scene,
  960. ray,
  961. objectsToExclude,
  962. width,
  963. requirePosition,
  964. mostDetailed,
  965. ) {
  966. const { context, frameState } = scene;
  967. const uniformState = context.uniformState;
  968. const view = picking._pickOffscreenView;
  969. scene.view = view;
  970. updateOffscreenCameraFromRay(picking, ray, width, view.camera);
  971. const drawingBufferRectangle = BoundingRectangle.clone(
  972. view.viewport,
  973. scratchRectangle,
  974. );
  975. const passState = view.pickFramebuffer.begin(
  976. drawingBufferRectangle,
  977. view.viewport,
  978. );
  979. scene.jobScheduler.disableThisFrame();
  980. scene.updateFrameState();
  981. frameState.invertClassification = false;
  982. frameState.passes.pick = true;
  983. frameState.passes.offscreen = true;
  984. if (mostDetailed) {
  985. frameState.tilesetPassState = mostDetailedPickTilesetPassState;
  986. } else {
  987. frameState.tilesetPassState = pickTilesetPassState;
  988. }
  989. uniformState.update(frameState);
  990. scene.updateEnvironment();
  991. scene.updateAndExecuteCommands(passState, scratchColorZero);
  992. scene.resolveFramebuffers(passState);
  993. let position;
  994. // Picking one object, result is either [object] or []
  995. const object = view.pickFramebuffer.end(drawingBufferRectangle, 1)[0];
  996. if (scene.context.depthTexture) {
  997. const { frustumCommandsList } = view;
  998. const numFrustums = frustumCommandsList.length;
  999. for (let i = 0; i < numFrustums; ++i) {
  1000. const pickDepth = picking.getPickDepth(scene, i);
  1001. const depth = pickDepth.getDepth(context, 0, 0);
  1002. if (!defined(depth)) {
  1003. continue;
  1004. }
  1005. if (depth > 0.0 && depth < 1.0) {
  1006. const renderedFrustum = frustumCommandsList[i];
  1007. const near =
  1008. renderedFrustum.near *
  1009. (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0);
  1010. const far = renderedFrustum.far;
  1011. const distance = near + depth * (far - near);
  1012. position = Ray.getPoint(ray, distance);
  1013. break;
  1014. }
  1015. }
  1016. }
  1017. scene.view = scene.defaultView;
  1018. context.endFrame();
  1019. if (defined(object) || defined(position)) {
  1020. return {
  1021. object: object,
  1022. position: position,
  1023. exclude:
  1024. (!defined(position) && requirePosition) ||
  1025. isExcluded(object, objectsToExclude),
  1026. };
  1027. }
  1028. }
  1029. function drillPickFromRay(
  1030. picking,
  1031. scene,
  1032. ray,
  1033. limit,
  1034. objectsToExclude,
  1035. width,
  1036. requirePosition,
  1037. mostDetailed,
  1038. ) {
  1039. const pickCallback = function () {
  1040. const pickResult = getRayIntersection(
  1041. picking,
  1042. scene,
  1043. ray,
  1044. objectsToExclude,
  1045. width,
  1046. requirePosition,
  1047. mostDetailed,
  1048. );
  1049. return pickResult ? [pickResult] : undefined;
  1050. };
  1051. return drillPick(pickCallback, limit);
  1052. }
  1053. function pickFromRay(
  1054. picking,
  1055. scene,
  1056. ray,
  1057. objectsToExclude,
  1058. width,
  1059. requirePosition,
  1060. mostDetailed,
  1061. ) {
  1062. // Use drillPickFromRay rather than getRayIntersection directly to select the first non-excluded object
  1063. const results = drillPickFromRay(
  1064. picking,
  1065. scene,
  1066. ray,
  1067. 1,
  1068. objectsToExclude,
  1069. width,
  1070. requirePosition,
  1071. mostDetailed,
  1072. );
  1073. if (results.length > 0) {
  1074. return results[0];
  1075. }
  1076. }
  1077. function deferPromiseUntilPostRender(scene, promise) {
  1078. // Resolve promise after scene's postRender in case entities are created when the promise resolves.
  1079. // Entities can't be created between viewer._onTick and viewer._postRender.
  1080. return new Promise((resolve, reject) => {
  1081. promise
  1082. .then(function (result) {
  1083. const removeCallback = scene.postRender.addEventListener(function () {
  1084. removeCallback();
  1085. resolve(result);
  1086. });
  1087. scene.requestRender();
  1088. })
  1089. .catch(function (error) {
  1090. reject(error);
  1091. });
  1092. });
  1093. }
  1094. Picking.prototype.pickFromRay = function (scene, ray, objectsToExclude, width) {
  1095. //>>includeStart('debug', pragmas.debug);
  1096. Check.defined("ray", ray);
  1097. if (scene.mode !== SceneMode.SCENE3D) {
  1098. throw new DeveloperError(
  1099. "Ray intersections are only supported in 3D mode.",
  1100. );
  1101. }
  1102. //>>includeEnd('debug');
  1103. return pickFromRay(this, scene, ray, objectsToExclude, width, false, false);
  1104. };
  1105. Picking.prototype.drillPickFromRay = function (
  1106. scene,
  1107. ray,
  1108. limit,
  1109. objectsToExclude,
  1110. width,
  1111. ) {
  1112. //>>includeStart('debug', pragmas.debug);
  1113. Check.defined("ray", ray);
  1114. if (scene.mode !== SceneMode.SCENE3D) {
  1115. throw new DeveloperError(
  1116. "Ray intersections are only supported in 3D mode.",
  1117. );
  1118. }
  1119. //>>includeEnd('debug');
  1120. return drillPickFromRay(
  1121. this,
  1122. scene,
  1123. ray,
  1124. limit,
  1125. objectsToExclude,
  1126. width,
  1127. false,
  1128. false,
  1129. );
  1130. };
  1131. Picking.prototype.pickFromRayMostDetailed = function (
  1132. scene,
  1133. ray,
  1134. objectsToExclude,
  1135. width,
  1136. ) {
  1137. //>>includeStart('debug', pragmas.debug);
  1138. Check.defined("ray", ray);
  1139. if (scene.mode !== SceneMode.SCENE3D) {
  1140. throw new DeveloperError(
  1141. "Ray intersections are only supported in 3D mode.",
  1142. );
  1143. }
  1144. //>>includeEnd('debug');
  1145. const that = this;
  1146. ray = Ray.clone(ray);
  1147. objectsToExclude = defined(objectsToExclude)
  1148. ? objectsToExclude.slice()
  1149. : objectsToExclude;
  1150. return deferPromiseUntilPostRender(
  1151. scene,
  1152. launchMostDetailedRayPick(
  1153. that,
  1154. scene,
  1155. ray,
  1156. objectsToExclude,
  1157. width,
  1158. function () {
  1159. return pickFromRay(
  1160. that,
  1161. scene,
  1162. ray,
  1163. objectsToExclude,
  1164. width,
  1165. false,
  1166. true,
  1167. );
  1168. },
  1169. ),
  1170. );
  1171. };
  1172. Picking.prototype.drillPickFromRayMostDetailed = function (
  1173. scene,
  1174. ray,
  1175. limit,
  1176. objectsToExclude,
  1177. width,
  1178. ) {
  1179. //>>includeStart('debug', pragmas.debug);
  1180. Check.defined("ray", ray);
  1181. if (scene.mode !== SceneMode.SCENE3D) {
  1182. throw new DeveloperError(
  1183. "Ray intersections are only supported in 3D mode.",
  1184. );
  1185. }
  1186. //>>includeEnd('debug');
  1187. const that = this;
  1188. ray = Ray.clone(ray);
  1189. objectsToExclude = defined(objectsToExclude)
  1190. ? objectsToExclude.slice()
  1191. : objectsToExclude;
  1192. return deferPromiseUntilPostRender(
  1193. scene,
  1194. launchMostDetailedRayPick(
  1195. that,
  1196. scene,
  1197. ray,
  1198. objectsToExclude,
  1199. width,
  1200. function () {
  1201. return drillPickFromRay(
  1202. that,
  1203. scene,
  1204. ray,
  1205. limit,
  1206. objectsToExclude,
  1207. width,
  1208. false,
  1209. true,
  1210. );
  1211. },
  1212. ),
  1213. );
  1214. };
  1215. const scratchSurfacePosition = new Cartesian3();
  1216. const scratchSurfaceNormal = new Cartesian3();
  1217. const scratchSurfaceRay = new Ray();
  1218. const scratchCartographic = new Cartographic();
  1219. /**
  1220. * @private
  1221. * @param {Scene} scene
  1222. * @param {Cartographic} cartographic
  1223. * @returns {Ray}
  1224. */
  1225. function getRayForSampleHeight(scene, cartographic) {
  1226. const ellipsoid = scene.ellipsoid;
  1227. const height = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  1228. const surfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic(
  1229. cartographic,
  1230. scratchSurfaceNormal,
  1231. );
  1232. const surfacePosition = Cartographic.toCartesian(
  1233. cartographic,
  1234. ellipsoid,
  1235. scratchSurfacePosition,
  1236. );
  1237. const surfaceRay = scratchSurfaceRay;
  1238. surfaceRay.origin = surfacePosition;
  1239. surfaceRay.direction = surfaceNormal;
  1240. const ray = new Ray();
  1241. Ray.getPoint(surfaceRay, height, ray.origin);
  1242. Cartesian3.negate(surfaceNormal, ray.direction);
  1243. return ray;
  1244. }
  1245. /**
  1246. * @private
  1247. * @param {Scene} scene
  1248. * @param {Cartesian3} cartesian
  1249. * @returns {Ray}
  1250. */
  1251. function getRayForClampToHeight(scene, cartesian) {
  1252. const ellipsoid = scene.ellipsoid;
  1253. const cartographic = Cartographic.fromCartesian(
  1254. cartesian,
  1255. ellipsoid,
  1256. scratchCartographic,
  1257. );
  1258. return getRayForSampleHeight(scene, cartographic);
  1259. }
  1260. function getHeightFromCartesian(scene, cartesian) {
  1261. const ellipsoid = scene.ellipsoid;
  1262. const cartographic = Cartographic.fromCartesian(
  1263. cartesian,
  1264. ellipsoid,
  1265. scratchCartographic,
  1266. );
  1267. return cartographic.height;
  1268. }
  1269. function sampleHeightMostDetailed(
  1270. picking,
  1271. scene,
  1272. cartographic,
  1273. objectsToExclude,
  1274. width,
  1275. ) {
  1276. const ray = getRayForSampleHeight(scene, cartographic);
  1277. return launchMostDetailedRayPick(
  1278. picking,
  1279. scene,
  1280. ray,
  1281. objectsToExclude,
  1282. width,
  1283. function () {
  1284. const pickResult = pickFromRay(
  1285. picking,
  1286. scene,
  1287. ray,
  1288. objectsToExclude,
  1289. width,
  1290. true,
  1291. true,
  1292. );
  1293. if (defined(pickResult)) {
  1294. return getHeightFromCartesian(scene, pickResult.position);
  1295. }
  1296. },
  1297. );
  1298. }
  1299. /**
  1300. * @private
  1301. * @param {Picking} picking
  1302. * @param {Scene} scene
  1303. * @param {Cartesian3} cartesian
  1304. * @param {object[]} [objectsToExclude]
  1305. * @param {number} [width]
  1306. * @param {Cartesian3} [result]
  1307. * @returns {Promise<Cartesian3 | undefined>}
  1308. */
  1309. function clampToHeightMostDetailed(
  1310. picking,
  1311. scene,
  1312. cartesian,
  1313. objectsToExclude,
  1314. width,
  1315. result,
  1316. ) {
  1317. const ray = getRayForClampToHeight(scene, cartesian);
  1318. return launchMostDetailedRayPick(
  1319. picking,
  1320. scene,
  1321. ray,
  1322. objectsToExclude,
  1323. width,
  1324. function () {
  1325. const pickResult = pickFromRay(
  1326. picking,
  1327. scene,
  1328. ray,
  1329. objectsToExclude,
  1330. width,
  1331. true,
  1332. true,
  1333. );
  1334. if (defined(pickResult)) {
  1335. return Cartesian3.clone(pickResult.position, result);
  1336. }
  1337. },
  1338. );
  1339. }
  1340. Picking.prototype.sampleHeight = function (
  1341. scene,
  1342. position,
  1343. objectsToExclude,
  1344. width,
  1345. ) {
  1346. //>>includeStart('debug', pragmas.debug);
  1347. Check.defined("position", position);
  1348. if (scene.mode !== SceneMode.SCENE3D) {
  1349. throw new DeveloperError("sampleHeight is only supported in 3D mode.");
  1350. }
  1351. if (!scene.sampleHeightSupported) {
  1352. throw new DeveloperError(
  1353. "sampleHeight requires depth texture support. Check sampleHeightSupported.",
  1354. );
  1355. }
  1356. //>>includeEnd('debug');
  1357. const ray = getRayForSampleHeight(scene, position);
  1358. const pickResult = pickFromRay(
  1359. this,
  1360. scene,
  1361. ray,
  1362. objectsToExclude,
  1363. width,
  1364. true,
  1365. false,
  1366. );
  1367. if (defined(pickResult)) {
  1368. return getHeightFromCartesian(scene, pickResult.position);
  1369. }
  1370. };
  1371. Picking.prototype.clampToHeight = function (
  1372. scene,
  1373. cartesian,
  1374. objectsToExclude,
  1375. width,
  1376. result,
  1377. ) {
  1378. //>>includeStart('debug', pragmas.debug);
  1379. Check.defined("cartesian", cartesian);
  1380. if (scene.mode !== SceneMode.SCENE3D) {
  1381. throw new DeveloperError("clampToHeight is only supported in 3D mode.");
  1382. }
  1383. if (!scene.clampToHeightSupported) {
  1384. throw new DeveloperError(
  1385. "clampToHeight requires depth texture support. Check clampToHeightSupported.",
  1386. );
  1387. }
  1388. //>>includeEnd('debug');
  1389. const ray = getRayForClampToHeight(scene, cartesian);
  1390. const pickResult = pickFromRay(
  1391. this,
  1392. scene,
  1393. ray,
  1394. objectsToExclude,
  1395. width,
  1396. true,
  1397. false,
  1398. );
  1399. if (defined(pickResult)) {
  1400. return Cartesian3.clone(pickResult.position, result);
  1401. }
  1402. };
  1403. Picking.prototype.sampleHeightMostDetailed = function (
  1404. scene,
  1405. positions,
  1406. objectsToExclude,
  1407. width,
  1408. ) {
  1409. //>>includeStart('debug', pragmas.debug);
  1410. Check.defined("positions", positions);
  1411. if (scene.mode !== SceneMode.SCENE3D) {
  1412. throw new DeveloperError(
  1413. "sampleHeightMostDetailed is only supported in 3D mode.",
  1414. );
  1415. }
  1416. if (!scene.sampleHeightSupported) {
  1417. throw new DeveloperError(
  1418. "sampleHeightMostDetailed requires depth texture support. Check sampleHeightSupported.",
  1419. );
  1420. }
  1421. //>>includeEnd('debug');
  1422. objectsToExclude = defined(objectsToExclude)
  1423. ? objectsToExclude.slice()
  1424. : objectsToExclude;
  1425. const length = positions.length;
  1426. const promises = new Array(length);
  1427. for (let i = 0; i < length; ++i) {
  1428. promises[i] = sampleHeightMostDetailed(
  1429. this,
  1430. scene,
  1431. positions[i],
  1432. objectsToExclude,
  1433. width,
  1434. );
  1435. }
  1436. return deferPromiseUntilPostRender(
  1437. scene,
  1438. Promise.all(promises).then(function (heights) {
  1439. const length = heights.length;
  1440. for (let i = 0; i < length; ++i) {
  1441. positions[i].height = heights[i];
  1442. }
  1443. return positions;
  1444. }),
  1445. );
  1446. };
  1447. /**
  1448. * @private
  1449. * @param {Scene} scene
  1450. * @param {Cartesian3[]} cartesians
  1451. * @param {object[]} [objectsToExclude]
  1452. * @param {number} [width]
  1453. * @returns {Promise<Array<Cartesian3 | undefined>>}
  1454. */
  1455. Picking.prototype.clampToHeightMostDetailed = function (
  1456. scene,
  1457. cartesians,
  1458. objectsToExclude,
  1459. width,
  1460. ) {
  1461. //>>includeStart('debug', pragmas.debug);
  1462. Check.defined("cartesians", cartesians);
  1463. if (scene.mode !== SceneMode.SCENE3D) {
  1464. throw new DeveloperError(
  1465. "clampToHeightMostDetailed is only supported in 3D mode.",
  1466. );
  1467. }
  1468. if (!scene.clampToHeightSupported) {
  1469. throw new DeveloperError(
  1470. "clampToHeightMostDetailed requires depth texture support. Check clampToHeightSupported.",
  1471. );
  1472. }
  1473. //>>includeEnd('debug');
  1474. objectsToExclude = defined(objectsToExclude)
  1475. ? objectsToExclude.slice()
  1476. : objectsToExclude;
  1477. const length = cartesians.length;
  1478. const promises = new Array(length);
  1479. for (let i = 0; i < length; ++i) {
  1480. promises[i] = clampToHeightMostDetailed(
  1481. this,
  1482. scene,
  1483. cartesians[i],
  1484. objectsToExclude,
  1485. width,
  1486. cartesians[i],
  1487. );
  1488. }
  1489. return deferPromiseUntilPostRender(
  1490. scene,
  1491. Promise.all(promises).then(function (clampedCartesians) {
  1492. const length = clampedCartesians.length;
  1493. for (let i = 0; i < length; ++i) {
  1494. cartesians[i] = clampedCartesians[i];
  1495. }
  1496. return cartesians;
  1497. }),
  1498. );
  1499. };
  1500. Picking.prototype.destroy = function () {
  1501. this._pickOffscreenView =
  1502. this._pickOffscreenView && this._pickOffscreenView.destroy();
  1503. };
  1504. export default Picking;