SceneTransforms.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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. * @exports 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(scene, position, Cartesian3.ZERO, result);
  46. };
  47. var scratchCartesian4 = new Cartesian4();
  48. var scratchEyeOffset = new Cartesian3();
  49. function worldToClip(position, eyeOffset, camera, result) {
  50. var viewMatrix = camera.viewMatrix;
  51. var positionEC = Matrix4.multiplyByVector(viewMatrix, Cartesian4.fromElements(position.x, position.y, position.z, 1, scratchCartesian4), scratchCartesian4);
  52. var zEyeOffset = Cartesian3.multiplyComponents(eyeOffset, Cartesian3.normalize(positionEC, scratchEyeOffset), scratchEyeOffset);
  53. positionEC.x += eyeOffset.x + zEyeOffset.x;
  54. positionEC.y += eyeOffset.y + zEyeOffset.y;
  55. positionEC.z += zEyeOffset.z;
  56. return Matrix4.multiplyByVector(camera.frustum.projectionMatrix, positionEC, result);
  57. }
  58. var scratchMaxCartographic = new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO);
  59. var scratchProjectedCartesian = new Cartesian3();
  60. var scratchCameraPosition = new Cartesian3();
  61. /**
  62. * @private
  63. */
  64. SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates = function(scene, position, eyeOffset, result) {
  65. //>>includeStart('debug', pragmas.debug);
  66. if (!defined(scene)) {
  67. throw new DeveloperError('scene is required.');
  68. }
  69. if (!defined(position)) {
  70. throw new DeveloperError('position is required.');
  71. }
  72. //>>includeEnd('debug');
  73. // Transform for 3D, 2D, or Columbus view
  74. var frameState = scene.frameState;
  75. var actualPosition = SceneTransforms.computeActualWgs84Position(frameState, position, actualPositionScratch);
  76. if (!defined(actualPosition)) {
  77. return undefined;
  78. }
  79. // Assuming viewport takes up the entire canvas...
  80. var canvas = scene.canvas;
  81. var viewport = scratchViewport;
  82. viewport.x = 0;
  83. viewport.y = 0;
  84. viewport.width = canvas.clientWidth;
  85. viewport.height = canvas.clientHeight;
  86. var camera = scene.camera;
  87. var cameraCentered = false;
  88. if (frameState.mode === SceneMode.SCENE2D) {
  89. var projection = scene.mapProjection;
  90. var maxCartographic = scratchMaxCartographic;
  91. var maxCoord = projection.project(maxCartographic, scratchProjectedCartesian);
  92. var cameraPosition = Cartesian3.clone(camera.position, scratchCameraPosition);
  93. var frustum = camera.frustum.clone();
  94. var viewportTransformation = Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, new Matrix4());
  95. var projectionMatrix = camera.frustum.projectionMatrix;
  96. var x = camera.positionWC.y;
  97. var eyePoint = Cartesian3.fromElements(CesiumMath.sign(x) * maxCoord.x - x, 0.0, -camera.positionWC.x);
  98. var windowCoordinates = Transforms.pointToGLWindowCoordinates(projectionMatrix, viewportTransformation, eyePoint);
  99. if (x === 0.0 || windowCoordinates.x <= 0.0 || windowCoordinates.x >= canvas.clientWidth) {
  100. cameraCentered = true;
  101. } else {
  102. if (windowCoordinates.x > canvas.clientWidth * 0.5) {
  103. viewport.width = windowCoordinates.x;
  104. camera.frustum.right = maxCoord.x - x;
  105. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  106. SceneTransforms.clipToGLWindowCoordinates(viewport, positionCC, scratchWindowCoord0);
  107. viewport.x += windowCoordinates.x;
  108. camera.position.x = -camera.position.x;
  109. var right = camera.frustum.right;
  110. camera.frustum.right = -camera.frustum.left;
  111. camera.frustum.left = -right;
  112. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  113. SceneTransforms.clipToGLWindowCoordinates(viewport, positionCC, scratchWindowCoord1);
  114. } else {
  115. viewport.x += windowCoordinates.x;
  116. viewport.width -= windowCoordinates.x;
  117. camera.frustum.left = -maxCoord.x - x;
  118. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  119. SceneTransforms.clipToGLWindowCoordinates(viewport, positionCC, scratchWindowCoord0);
  120. viewport.x = viewport.x - viewport.width;
  121. camera.position.x = -camera.position.x;
  122. var left = camera.frustum.left;
  123. camera.frustum.left = -camera.frustum.right;
  124. camera.frustum.right = -left;
  125. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  126. SceneTransforms.clipToGLWindowCoordinates(viewport, positionCC, scratchWindowCoord1);
  127. }
  128. Cartesian3.clone(cameraPosition, camera.position);
  129. camera.frustum = frustum.clone();
  130. result = Cartesian2.clone(scratchWindowCoord0, result);
  131. if (result.x < 0.0 || result.x > canvas.clientWidth) {
  132. result.x = scratchWindowCoord1.x;
  133. }
  134. }
  135. }
  136. if (frameState.mode !== SceneMode.SCENE2D || cameraCentered) {
  137. // View-projection matrix to transform from world coordinates to clip coordinates
  138. positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
  139. if (positionCC.z < 0 && !(camera.frustum instanceof OrthographicFrustum) && !(camera.frustum instanceof OrthographicOffCenterFrustum)) {
  140. return undefined;
  141. }
  142. result = SceneTransforms.clipToGLWindowCoordinates(viewport, positionCC, result);
  143. }
  144. result.y = canvas.clientHeight - result.y;
  145. return result;
  146. };
  147. /**
  148. * Transforms a position in WGS84 coordinates to drawing buffer coordinates. This may produce different
  149. * results from SceneTransforms.wgs84ToWindowCoordinates when the browser zoom is not 100%, or on high-DPI displays.
  150. *
  151. * @param {Scene} scene The scene.
  152. * @param {Cartesian3} position The position in WGS84 (world) coordinates.
  153. * @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates.
  154. * @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.
  155. *
  156. * @example
  157. * // Output the window position of longitude/latitude (0, 0) every time the mouse moves.
  158. * var scene = widget.scene;
  159. * var ellipsoid = scene.globe.ellipsoid;
  160. * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  161. * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  162. * handler.setInputAction(function(movement) {
  163. * console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position));
  164. * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  165. */
  166. SceneTransforms.wgs84ToDrawingBufferCoordinates = function(scene, position, result) {
  167. result = SceneTransforms.wgs84ToWindowCoordinates(scene, position, result);
  168. if (!defined(result)) {
  169. return undefined;
  170. }
  171. return SceneTransforms.transformWindowToDrawingBuffer(scene, result, result);
  172. };
  173. var projectedPosition = new Cartesian3();
  174. var positionInCartographic = new Cartographic();
  175. /**
  176. * @private
  177. */
  178. SceneTransforms.computeActualWgs84Position = function(frameState, position, result) {
  179. var mode = frameState.mode;
  180. if (mode === SceneMode.SCENE3D) {
  181. return Cartesian3.clone(position, result);
  182. }
  183. var projection = frameState.mapProjection;
  184. var cartographic = projection.ellipsoid.cartesianToCartographic(position, positionInCartographic);
  185. if (!defined(cartographic)) {
  186. return undefined;
  187. }
  188. projection.project(cartographic, projectedPosition);
  189. if (mode === SceneMode.COLUMBUS_VIEW) {
  190. return Cartesian3.fromElements(projectedPosition.z, projectedPosition.x, projectedPosition.y, result);
  191. }
  192. if (mode === SceneMode.SCENE2D) {
  193. return Cartesian3.fromElements(0.0, projectedPosition.x, projectedPosition.y, result);
  194. }
  195. // mode === SceneMode.MORPHING
  196. var morphTime = frameState.morphTime;
  197. return Cartesian3.fromElements(
  198. CesiumMath.lerp(projectedPosition.z, position.x, morphTime),
  199. CesiumMath.lerp(projectedPosition.x, position.y, morphTime),
  200. CesiumMath.lerp(projectedPosition.y, position.z, morphTime),
  201. result);
  202. };
  203. var positionNDC = new Cartesian3();
  204. var positionWC = new Cartesian3();
  205. var viewportTransform = new Matrix4();
  206. /**
  207. * @private
  208. */
  209. SceneTransforms.clipToGLWindowCoordinates = function(viewport, position, result) {
  210. // Perspective divide to transform from clip coordinates to normalized device coordinates
  211. Cartesian3.divideByScalar(position, position.w, positionNDC);
  212. // Viewport transform to transform from clip coordinates to window coordinates
  213. Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform);
  214. Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC);
  215. return Cartesian2.fromCartesian3(positionWC, result);
  216. };
  217. /**
  218. * @private
  219. */
  220. SceneTransforms.transformWindowToDrawingBuffer = function(scene, windowPosition, result) {
  221. var canvas = scene.canvas;
  222. var xScale = scene.drawingBufferWidth / canvas.clientWidth;
  223. var yScale = scene.drawingBufferHeight / canvas.clientHeight;
  224. return Cartesian2.fromElements(windowPosition.x * xScale, windowPosition.y * yScale, result);
  225. };
  226. var scratchNDC = new Cartesian4();
  227. var scratchWorldCoords = new Cartesian4();
  228. /**
  229. * @private
  230. */
  231. SceneTransforms.drawingBufferToWgs84Coordinates = function(scene, drawingBufferPosition, depth, result) {
  232. var context = scene.context;
  233. var uniformState = context.uniformState;
  234. var currentFrustum = uniformState.currentFrustum;
  235. var near = currentFrustum.x;
  236. var far = currentFrustum.y;
  237. if (scene.frameState.useLogDepth) {
  238. // transforming logarithmic depth of form
  239. // log2(z + 1) / log2( far + 1);
  240. // to perspective form
  241. // (far - far * near / z) / (far - near)
  242. depth = Math.pow(2.0, depth * CesiumMath.log2(far + 1.0)) - 1.0;
  243. depth = far * (1.0 - near / depth) / (far - near);
  244. }
  245. var viewport = scene.view.passState.viewport;
  246. var ndc = Cartesian4.clone(Cartesian4.UNIT_W, scratchNDC);
  247. ndc.x = (drawingBufferPosition.x - viewport.x) / viewport.width * 2.0 - 1.0;
  248. ndc.y = (drawingBufferPosition.y - viewport.y) / viewport.height * 2.0 - 1.0;
  249. ndc.z = (depth * 2.0) - 1.0;
  250. ndc.w = 1.0;
  251. var worldCoords;
  252. var frustum = scene.camera.frustum;
  253. if (!defined(frustum.fovy)) {
  254. if (defined(frustum._offCenterFrustum)) {
  255. frustum = frustum._offCenterFrustum;
  256. }
  257. worldCoords = scratchWorldCoords;
  258. worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5;
  259. worldCoords.y = (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5;
  260. worldCoords.z = (ndc.z * (near - far) - near - far) * 0.5;
  261. worldCoords.w = 1.0;
  262. worldCoords = Matrix4.multiplyByVector(uniformState.inverseView, worldCoords, worldCoords);
  263. } else {
  264. worldCoords = Matrix4.multiplyByVector(uniformState.inverseViewProjection, ndc, scratchWorldCoords);
  265. // Reverse perspective divide
  266. var w = 1.0 / worldCoords.w;
  267. Cartesian3.multiplyByScalar(worldCoords, w, worldCoords);
  268. }
  269. return Cartesian3.fromCartesian4(worldCoords, result);
  270. };
  271. export default SceneTransforms;