SceneTransforms.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. import BoundingRectangle from "../Core/BoundingRectangle.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartesian4 from "../Core/Cartesian4.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import defined from "../Core/defined.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import Matrix4 from "../Core/Matrix4.js";
  10. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  11. import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";
  12. import Transforms from "../Core/Transforms.js";
  13. import SceneMode from "./SceneMode.js";
  14. /**
  15. * Functions that do scene-dependent transforms between rendering-related coordinate systems.
  16. *
  17. * @namespace SceneTransforms
  18. */
  19. var SceneTransforms = {};
  20. var actualPositionScratch = new Cartesian4(0, 0, 0, 1);
  21. var positionCC = new Cartesian4();
  22. var scratchViewport = new BoundingRectangle();
  23. var scratchWindowCoord0 = new Cartesian2();
  24. var scratchWindowCoord1 = new Cartesian2();
  25. /**
  26. * Transforms a position in WGS84 coordinates to window coordinates. This is commonly used to place an
  27. * HTML element at the same screen position as an object in the scene.
  28. *
  29. * @param {Scene} scene The scene.
  30. * @param {Cartesian3} position The position in WGS84 (world) coordinates.
  31. * @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates.
  32. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. This may be <code>undefined</code> if the input position is near the center of the ellipsoid.
  33. *
  34. * @example
  35. * // Output the window position of longitude/latitude (0, 0) every time the mouse moves.
  36. * var scene = widget.scene;
  37. * var ellipsoid = scene.globe.ellipsoid;
  38. * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  39. * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  40. * handler.setInputAction(function(movement) {
  41. * console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position));
  42. * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  43. */
  44. SceneTransforms.wgs84ToWindowCoordinates = function (scene, position, result) {
  45. return SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates(
  46. scene,
  47. position,
  48. Cartesian3.ZERO,
  49. result
  50. );
  51. };
  52. var scratchCartesian4 = new Cartesian4();
  53. var scratchEyeOffset = new Cartesian3();
  54. function worldToClip(position, eyeOffset, camera, result) {
  55. var viewMatrix = camera.viewMatrix;
  56. var positionEC = Matrix4.multiplyByVector(
  57. viewMatrix,
  58. Cartesian4.fromElements(
  59. position.x,
  60. position.y,
  61. position.z,
  62. 1,
  63. scratchCartesian4
  64. ),
  65. scratchCartesian4
  66. );
  67. var zEyeOffset = Cartesian3.multiplyComponents(
  68. eyeOffset,
  69. Cartesian3.normalize(positionEC, scratchEyeOffset),
  70. scratchEyeOffset
  71. );
  72. positionEC.x += eyeOffset.x + zEyeOffset.x;
  73. positionEC.y += eyeOffset.y + zEyeOffset.y;
  74. positionEC.z += zEyeOffset.z;
  75. return Matrix4.multiplyByVector(
  76. camera.frustum.projectionMatrix,
  77. positionEC,
  78. result
  79. );
  80. }
  81. var scratchMaxCartographic = new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO);
  82. var scratchProjectedCartesian = new Cartesian3();
  83. var scratchCameraPosition = new Cartesian3();
  84. /**
  85. * @private
  86. */
  87. SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates = function (
  88. scene,
  89. position,
  90. eyeOffset,
  91. result
  92. ) {
  93. //>>includeStart('debug', pragmas.debug);
  94. if (!defined(scene)) {
  95. throw new DeveloperError("scene is required.");
  96. }
  97. if (!defined(position)) {
  98. throw new DeveloperError("position is required.");
  99. }
  100. //>>includeEnd('debug');
  101. // Transform for 3D, 2D, or Columbus view
  102. var frameState = scene.frameState;
  103. var actualPosition = SceneTransforms.computeActualWgs84Position(
  104. frameState,
  105. position,
  106. actualPositionScratch
  107. );
  108. if (!defined(actualPosition)) {
  109. return undefined;
  110. }
  111. // Assuming viewport takes up the entire canvas...
  112. var canvas = scene.canvas;
  113. var viewport = scratchViewport;
  114. viewport.x = 0;
  115. viewport.y = 0;
  116. viewport.width = canvas.clientWidth;
  117. viewport.height = canvas.clientHeight;
  118. var camera = scene.camera;
  119. var cameraCentered = false;
  120. if (frameState.mode === SceneMode.SCENE2D) {
  121. var projection = scene.mapProjection;
  122. var maxCartographic = scratchMaxCartographic;
  123. var maxCoord = projection.project(
  124. maxCartographic,
  125. scratchProjectedCartesian
  126. );
  127. var cameraPosition = Cartesian3.clone(
  128. camera.position,
  129. scratchCameraPosition
  130. );
  131. var frustum = camera.frustum.clone();
  132. var viewportTransformation = Matrix4.computeViewportTransformation(
  133. viewport,
  134. 0.0,
  135. 1.0,
  136. new Matrix4()
  137. );
  138. var projectionMatrix = camera.frustum.projectionMatrix;
  139. var x = camera.positionWC.y;
  140. var eyePoint = Cartesian3.fromElements(
  141. CesiumMath.sign(x) * maxCoord.x - x,
  142. 0.0,
  143. -camera.positionWC.x
  144. );
  145. var windowCoordinates = Transforms.pointToGLWindowCoordinates(
  146. projectionMatrix,
  147. viewportTransformation,
  148. eyePoint
  149. );
  150. if (
  151. x === 0.0 ||
  152. windowCoordinates.x <= 0.0 ||
  153. windowCoordinates.x >= canvas.clientWidth
  154. ) {
  155. cameraCentered = true;
  156. } else {
  157. if (windowCoordinates.x > canvas.clientWidth * 0.5) {
  158. viewport.width = windowCoordinates.x;
  159. camera.frustum.right = maxCoord.x - x;
  160. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  161. SceneTransforms.clipToGLWindowCoordinates(
  162. viewport,
  163. positionCC,
  164. scratchWindowCoord0
  165. );
  166. viewport.x += windowCoordinates.x;
  167. camera.position.x = -camera.position.x;
  168. var right = camera.frustum.right;
  169. camera.frustum.right = -camera.frustum.left;
  170. camera.frustum.left = -right;
  171. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  172. SceneTransforms.clipToGLWindowCoordinates(
  173. viewport,
  174. positionCC,
  175. scratchWindowCoord1
  176. );
  177. } else {
  178. viewport.x += windowCoordinates.x;
  179. viewport.width -= windowCoordinates.x;
  180. camera.frustum.left = -maxCoord.x - x;
  181. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  182. SceneTransforms.clipToGLWindowCoordinates(
  183. viewport,
  184. positionCC,
  185. scratchWindowCoord0
  186. );
  187. viewport.x = viewport.x - viewport.width;
  188. camera.position.x = -camera.position.x;
  189. var left = camera.frustum.left;
  190. camera.frustum.left = -camera.frustum.right;
  191. camera.frustum.right = -left;
  192. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  193. SceneTransforms.clipToGLWindowCoordinates(
  194. viewport,
  195. positionCC,
  196. scratchWindowCoord1
  197. );
  198. }
  199. Cartesian3.clone(cameraPosition, camera.position);
  200. camera.frustum = frustum.clone();
  201. result = Cartesian2.clone(scratchWindowCoord0, result);
  202. if (result.x < 0.0 || result.x > canvas.clientWidth) {
  203. result.x = scratchWindowCoord1.x;
  204. }
  205. }
  206. }
  207. if (frameState.mode !== SceneMode.SCENE2D || cameraCentered) {
  208. // View-projection matrix to transform from world coordinates to clip coordinates
  209. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  210. if (
  211. positionCC.z < 0 &&
  212. !(camera.frustum instanceof OrthographicFrustum) &&
  213. !(camera.frustum instanceof OrthographicOffCenterFrustum)
  214. ) {
  215. return undefined;
  216. }
  217. result = SceneTransforms.clipToGLWindowCoordinates(
  218. viewport,
  219. positionCC,
  220. result
  221. );
  222. }
  223. result.y = canvas.clientHeight - result.y;
  224. return result;
  225. };
  226. /**
  227. * Transforms a position in WGS84 coordinates to drawing buffer coordinates. This may produce different
  228. * results from SceneTransforms.wgs84ToWindowCoordinates when the browser zoom is not 100%, or on high-DPI displays.
  229. *
  230. * @param {Scene} scene The scene.
  231. * @param {Cartesian3} position The position in WGS84 (world) coordinates.
  232. * @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates.
  233. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. This may be <code>undefined</code> if the input position is near the center of the ellipsoid.
  234. *
  235. * @example
  236. * // Output the window position of longitude/latitude (0, 0) every time the mouse moves.
  237. * var scene = widget.scene;
  238. * var ellipsoid = scene.globe.ellipsoid;
  239. * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  240. * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  241. * handler.setInputAction(function(movement) {
  242. * console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position));
  243. * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  244. */
  245. SceneTransforms.wgs84ToDrawingBufferCoordinates = function (
  246. scene,
  247. position,
  248. result
  249. ) {
  250. result = SceneTransforms.wgs84ToWindowCoordinates(scene, position, result);
  251. if (!defined(result)) {
  252. return undefined;
  253. }
  254. return SceneTransforms.transformWindowToDrawingBuffer(scene, result, result);
  255. };
  256. var projectedPosition = new Cartesian3();
  257. var positionInCartographic = new Cartographic();
  258. /**
  259. * @private
  260. */
  261. SceneTransforms.computeActualWgs84Position = function (
  262. frameState,
  263. position,
  264. result
  265. ) {
  266. var mode = frameState.mode;
  267. if (mode === SceneMode.SCENE3D) {
  268. return Cartesian3.clone(position, result);
  269. }
  270. var projection = frameState.mapProjection;
  271. var cartographic = projection.ellipsoid.cartesianToCartographic(
  272. position,
  273. positionInCartographic
  274. );
  275. if (!defined(cartographic)) {
  276. return undefined;
  277. }
  278. projection.project(cartographic, projectedPosition);
  279. if (mode === SceneMode.COLUMBUS_VIEW) {
  280. return Cartesian3.fromElements(
  281. projectedPosition.z,
  282. projectedPosition.x,
  283. projectedPosition.y,
  284. result
  285. );
  286. }
  287. if (mode === SceneMode.SCENE2D) {
  288. return Cartesian3.fromElements(
  289. 0.0,
  290. projectedPosition.x,
  291. projectedPosition.y,
  292. result
  293. );
  294. }
  295. // mode === SceneMode.MORPHING
  296. var morphTime = frameState.morphTime;
  297. return Cartesian3.fromElements(
  298. CesiumMath.lerp(projectedPosition.z, position.x, morphTime),
  299. CesiumMath.lerp(projectedPosition.x, position.y, morphTime),
  300. CesiumMath.lerp(projectedPosition.y, position.z, morphTime),
  301. result
  302. );
  303. };
  304. var positionNDC = new Cartesian3();
  305. var positionWC = new Cartesian3();
  306. var viewportTransform = new Matrix4();
  307. /**
  308. * @private
  309. */
  310. SceneTransforms.clipToGLWindowCoordinates = function (
  311. viewport,
  312. position,
  313. result
  314. ) {
  315. // Perspective divide to transform from clip coordinates to normalized device coordinates
  316. Cartesian3.divideByScalar(position, position.w, positionNDC);
  317. // Viewport transform to transform from clip coordinates to window coordinates
  318. Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform);
  319. Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC);
  320. return Cartesian2.fromCartesian3(positionWC, result);
  321. };
  322. /**
  323. * @private
  324. */
  325. SceneTransforms.transformWindowToDrawingBuffer = function (
  326. scene,
  327. windowPosition,
  328. result
  329. ) {
  330. var canvas = scene.canvas;
  331. var xScale = scene.drawingBufferWidth / canvas.clientWidth;
  332. var yScale = scene.drawingBufferHeight / canvas.clientHeight;
  333. return Cartesian2.fromElements(
  334. windowPosition.x * xScale,
  335. windowPosition.y * yScale,
  336. result
  337. );
  338. };
  339. var scratchNDC = new Cartesian4();
  340. var scratchWorldCoords = new Cartesian4();
  341. /**
  342. * @private
  343. */
  344. SceneTransforms.drawingBufferToWgs84Coordinates = function (
  345. scene,
  346. drawingBufferPosition,
  347. depth,
  348. result
  349. ) {
  350. var context = scene.context;
  351. var uniformState = context.uniformState;
  352. var currentFrustum = uniformState.currentFrustum;
  353. var near = currentFrustum.x;
  354. var far = currentFrustum.y;
  355. if (scene.frameState.useLogDepth) {
  356. // transforming logarithmic depth of form
  357. // log2(z + 1) / log2( far + 1);
  358. // to perspective form
  359. // (far - far * near / z) / (far - near)
  360. var log2Depth = depth * uniformState.log2FarDepthFromNearPlusOne;
  361. var depthFromNear = Math.pow(2.0, log2Depth) - 1.0;
  362. depth = (far * (1.0 - near / (depthFromNear + near))) / (far - near);
  363. }
  364. var viewport = scene.view.passState.viewport;
  365. var ndc = Cartesian4.clone(Cartesian4.UNIT_W, scratchNDC);
  366. ndc.x = ((drawingBufferPosition.x - viewport.x) / viewport.width) * 2.0 - 1.0;
  367. ndc.y =
  368. ((drawingBufferPosition.y - viewport.y) / viewport.height) * 2.0 - 1.0;
  369. ndc.z = depth * 2.0 - 1.0;
  370. ndc.w = 1.0;
  371. var worldCoords;
  372. var frustum = scene.camera.frustum;
  373. if (!defined(frustum.fovy)) {
  374. if (defined(frustum._offCenterFrustum)) {
  375. frustum = frustum._offCenterFrustum;
  376. }
  377. worldCoords = scratchWorldCoords;
  378. worldCoords.x =
  379. (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) *
  380. 0.5;
  381. worldCoords.y =
  382. (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) *
  383. 0.5;
  384. worldCoords.z = (ndc.z * (near - far) - near - far) * 0.5;
  385. worldCoords.w = 1.0;
  386. worldCoords = Matrix4.multiplyByVector(
  387. uniformState.inverseView,
  388. worldCoords,
  389. worldCoords
  390. );
  391. } else {
  392. worldCoords = Matrix4.multiplyByVector(
  393. uniformState.inverseViewProjection,
  394. ndc,
  395. scratchWorldCoords
  396. );
  397. // Reverse perspective divide
  398. var w = 1.0 / worldCoords.w;
  399. Cartesian3.multiplyByScalar(worldCoords, w, worldCoords);
  400. }
  401. return Cartesian3.fromCartesian4(worldCoords, result);
  402. };
  403. export default SceneTransforms;