SceneTransitioner.js 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import Cartographic from "../Core/Cartographic.js";
  3. import Check from "../Core/Check.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import EasingFunction from "../Core/EasingFunction.js";
  7. import CesiumMath from "../Core/Math.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  10. import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";
  11. import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";
  12. import Ray from "../Core/Ray.js";
  13. import ScreenSpaceEventHandler from "../Core/ScreenSpaceEventHandler.js";
  14. import ScreenSpaceEventType from "../Core/ScreenSpaceEventType.js";
  15. import Transforms from "../Core/Transforms.js";
  16. import Camera from "./Camera.js";
  17. import SceneMode from "./SceneMode.js";
  18. /**
  19. * @private
  20. */
  21. function SceneTransitioner(scene) {
  22. //>>includeStart('debug', pragmas.debug);
  23. Check.typeOf.object("scene", scene);
  24. //>>includeEnd('debug');
  25. this._scene = scene;
  26. this._currentTweens = [];
  27. this._morphHandler = undefined;
  28. this._morphCancelled = false;
  29. this._completeMorph = undefined;
  30. this._morphToOrthographic = false;
  31. }
  32. SceneTransitioner.prototype.completeMorph = function () {
  33. if (defined(this._completeMorph)) {
  34. this._completeMorph();
  35. }
  36. };
  37. SceneTransitioner.prototype.morphTo2D = function (duration, ellipsoid) {
  38. if (defined(this._completeMorph)) {
  39. this._completeMorph();
  40. }
  41. const scene = this._scene;
  42. this._previousMode = scene.mode;
  43. this._morphToOrthographic =
  44. scene.camera.frustum instanceof OrthographicFrustum;
  45. if (
  46. this._previousMode === SceneMode.SCENE2D ||
  47. this._previousMode === SceneMode.MORPHING
  48. ) {
  49. return;
  50. }
  51. this._scene.morphStart.raiseEvent(
  52. this,
  53. this._previousMode,
  54. SceneMode.SCENE2D,
  55. true
  56. );
  57. scene._mode = SceneMode.MORPHING;
  58. scene.camera._setTransform(Matrix4.IDENTITY);
  59. if (this._previousMode === SceneMode.COLUMBUS_VIEW) {
  60. morphFromColumbusViewTo2D(this, duration);
  61. } else {
  62. morphFrom3DTo2D(this, duration, ellipsoid);
  63. }
  64. if (duration === 0.0 && defined(this._completeMorph)) {
  65. this._completeMorph();
  66. }
  67. };
  68. const scratchToCVPosition = new Cartesian3();
  69. const scratchToCVDirection = new Cartesian3();
  70. const scratchToCVUp = new Cartesian3();
  71. const scratchToCVPosition2D = new Cartesian3();
  72. const scratchToCVDirection2D = new Cartesian3();
  73. const scratchToCVUp2D = new Cartesian3();
  74. const scratchToCVSurfacePosition = new Cartesian3();
  75. const scratchToCVCartographic = new Cartographic();
  76. const scratchToCVToENU = new Matrix4();
  77. const scratchToCVFrustumPerspective = new PerspectiveFrustum();
  78. const scratchToCVFrustumOrthographic = new OrthographicFrustum();
  79. const scratchToCVCamera = {
  80. position: undefined,
  81. direction: undefined,
  82. up: undefined,
  83. position2D: undefined,
  84. direction2D: undefined,
  85. up2D: undefined,
  86. frustum: undefined,
  87. };
  88. SceneTransitioner.prototype.morphToColumbusView = function (
  89. duration,
  90. ellipsoid
  91. ) {
  92. if (defined(this._completeMorph)) {
  93. this._completeMorph();
  94. }
  95. const scene = this._scene;
  96. this._previousMode = scene.mode;
  97. if (
  98. this._previousMode === SceneMode.COLUMBUS_VIEW ||
  99. this._previousMode === SceneMode.MORPHING
  100. ) {
  101. return;
  102. }
  103. this._scene.morphStart.raiseEvent(
  104. this,
  105. this._previousMode,
  106. SceneMode.COLUMBUS_VIEW,
  107. true
  108. );
  109. scene.camera._setTransform(Matrix4.IDENTITY);
  110. let position = scratchToCVPosition;
  111. const direction = scratchToCVDirection;
  112. const up = scratchToCVUp;
  113. if (duration > 0.0) {
  114. position.x = 0.0;
  115. position.y = -1.0;
  116. position.z = 1.0;
  117. position = Cartesian3.multiplyByScalar(
  118. Cartesian3.normalize(position, position),
  119. 5.0 * ellipsoid.maximumRadius,
  120. position
  121. );
  122. Cartesian3.negate(Cartesian3.normalize(position, direction), direction);
  123. Cartesian3.cross(Cartesian3.UNIT_X, direction, up);
  124. } else {
  125. const camera = scene.camera;
  126. if (this._previousMode === SceneMode.SCENE2D) {
  127. Cartesian3.clone(camera.position, position);
  128. position.z = camera.frustum.right - camera.frustum.left;
  129. Cartesian3.negate(Cartesian3.UNIT_Z, direction);
  130. Cartesian3.clone(Cartesian3.UNIT_Y, up);
  131. } else {
  132. Cartesian3.clone(camera.positionWC, position);
  133. Cartesian3.clone(camera.directionWC, direction);
  134. Cartesian3.clone(camera.upWC, up);
  135. const surfacePoint = ellipsoid.scaleToGeodeticSurface(
  136. position,
  137. scratchToCVSurfacePosition
  138. );
  139. const toENU = Transforms.eastNorthUpToFixedFrame(
  140. surfacePoint,
  141. ellipsoid,
  142. scratchToCVToENU
  143. );
  144. Matrix4.inverseTransformation(toENU, toENU);
  145. scene.mapProjection.project(
  146. ellipsoid.cartesianToCartographic(position, scratchToCVCartographic),
  147. position
  148. );
  149. Matrix4.multiplyByPointAsVector(toENU, direction, direction);
  150. Matrix4.multiplyByPointAsVector(toENU, up, up);
  151. }
  152. }
  153. let frustum;
  154. if (this._morphToOrthographic) {
  155. frustum = scratchToCVFrustumOrthographic;
  156. frustum.width = scene.camera.frustum.right - scene.camera.frustum.left;
  157. frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
  158. } else {
  159. frustum = scratchToCVFrustumPerspective;
  160. frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
  161. frustum.fov = CesiumMath.toRadians(60.0);
  162. }
  163. const cameraCV = scratchToCVCamera;
  164. cameraCV.position = position;
  165. cameraCV.direction = direction;
  166. cameraCV.up = up;
  167. cameraCV.frustum = frustum;
  168. const complete = completeColumbusViewCallback(cameraCV);
  169. createMorphHandler(this, complete);
  170. if (this._previousMode === SceneMode.SCENE2D) {
  171. morphFrom2DToColumbusView(this, duration, cameraCV, complete);
  172. } else {
  173. cameraCV.position2D = Matrix4.multiplyByPoint(
  174. Camera.TRANSFORM_2D,
  175. position,
  176. scratchToCVPosition2D
  177. );
  178. cameraCV.direction2D = Matrix4.multiplyByPointAsVector(
  179. Camera.TRANSFORM_2D,
  180. direction,
  181. scratchToCVDirection2D
  182. );
  183. cameraCV.up2D = Matrix4.multiplyByPointAsVector(
  184. Camera.TRANSFORM_2D,
  185. up,
  186. scratchToCVUp2D
  187. );
  188. scene._mode = SceneMode.MORPHING;
  189. morphFrom3DToColumbusView(this, duration, cameraCV, complete);
  190. }
  191. if (duration === 0.0 && defined(this._completeMorph)) {
  192. this._completeMorph();
  193. }
  194. };
  195. const scratchCVTo3DCamera = {
  196. position: new Cartesian3(),
  197. direction: new Cartesian3(),
  198. up: new Cartesian3(),
  199. frustum: undefined,
  200. };
  201. const scratch2DTo3DFrustumPersp = new PerspectiveFrustum();
  202. SceneTransitioner.prototype.morphTo3D = function (duration, ellipsoid) {
  203. if (defined(this._completeMorph)) {
  204. this._completeMorph();
  205. }
  206. const scene = this._scene;
  207. this._previousMode = scene.mode;
  208. if (
  209. this._previousMode === SceneMode.SCENE3D ||
  210. this._previousMode === SceneMode.MORPHING
  211. ) {
  212. return;
  213. }
  214. this._scene.morphStart.raiseEvent(
  215. this,
  216. this._previousMode,
  217. SceneMode.SCENE3D,
  218. true
  219. );
  220. scene._mode = SceneMode.MORPHING;
  221. scene.camera._setTransform(Matrix4.IDENTITY);
  222. if (this._previousMode === SceneMode.SCENE2D) {
  223. morphFrom2DTo3D(this, duration, ellipsoid);
  224. } else {
  225. let camera3D;
  226. if (duration > 0.0) {
  227. camera3D = scratchCVTo3DCamera;
  228. Cartesian3.fromDegrees(
  229. 0.0,
  230. 0.0,
  231. 5.0 * ellipsoid.maximumRadius,
  232. ellipsoid,
  233. camera3D.position
  234. );
  235. Cartesian3.negate(camera3D.position, camera3D.direction);
  236. Cartesian3.normalize(camera3D.direction, camera3D.direction);
  237. Cartesian3.clone(Cartesian3.UNIT_Z, camera3D.up);
  238. } else {
  239. camera3D = getColumbusViewTo3DCamera(this, ellipsoid);
  240. }
  241. let frustum;
  242. const camera = scene.camera;
  243. if (camera.frustum instanceof OrthographicFrustum) {
  244. frustum = camera.frustum.clone();
  245. } else {
  246. frustum = scratch2DTo3DFrustumPersp;
  247. frustum.aspectRatio =
  248. scene.drawingBufferWidth / scene.drawingBufferHeight;
  249. frustum.fov = CesiumMath.toRadians(60.0);
  250. }
  251. camera3D.frustum = frustum;
  252. const complete = complete3DCallback(camera3D);
  253. createMorphHandler(this, complete);
  254. morphFromColumbusViewTo3D(this, duration, camera3D, complete);
  255. }
  256. if (duration === 0.0 && defined(this._completeMorph)) {
  257. this._completeMorph();
  258. }
  259. };
  260. /**
  261. * Returns true if this object was destroyed; otherwise, false.
  262. * <br /><br />
  263. * If this object was destroyed, it should not be used; calling any function other than
  264. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  265. *
  266. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  267. */
  268. SceneTransitioner.prototype.isDestroyed = function () {
  269. return false;
  270. };
  271. /**
  272. * Once an object is destroyed, it should not be used; calling any function other than
  273. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  274. * assign the return value (<code>undefined</code>) to the object as done in the example.
  275. *
  276. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  277. *
  278. * @example
  279. * transitioner = transitioner && transitioner.destroy();
  280. */
  281. SceneTransitioner.prototype.destroy = function () {
  282. destroyMorphHandler(this);
  283. return destroyObject(this);
  284. };
  285. function createMorphHandler(transitioner, completeMorphFunction) {
  286. if (transitioner._scene.completeMorphOnUserInput) {
  287. transitioner._morphHandler = new ScreenSpaceEventHandler(
  288. transitioner._scene.canvas
  289. );
  290. const completeMorph = function () {
  291. transitioner._morphCancelled = true;
  292. transitioner._scene.camera.cancelFlight();
  293. completeMorphFunction(transitioner);
  294. };
  295. transitioner._completeMorph = completeMorph;
  296. transitioner._morphHandler.setInputAction(
  297. completeMorph,
  298. ScreenSpaceEventType.LEFT_DOWN
  299. );
  300. transitioner._morphHandler.setInputAction(
  301. completeMorph,
  302. ScreenSpaceEventType.MIDDLE_DOWN
  303. );
  304. transitioner._morphHandler.setInputAction(
  305. completeMorph,
  306. ScreenSpaceEventType.RIGHT_DOWN
  307. );
  308. transitioner._morphHandler.setInputAction(
  309. completeMorph,
  310. ScreenSpaceEventType.WHEEL
  311. );
  312. }
  313. }
  314. function destroyMorphHandler(transitioner) {
  315. const tweens = transitioner._currentTweens;
  316. for (let i = 0; i < tweens.length; ++i) {
  317. tweens[i].cancelTween();
  318. }
  319. transitioner._currentTweens.length = 0;
  320. transitioner._morphHandler =
  321. transitioner._morphHandler && transitioner._morphHandler.destroy();
  322. }
  323. const scratchCVTo3DCartographic = new Cartographic();
  324. const scratchCVTo3DSurfacePoint = new Cartesian3();
  325. const scratchCVTo3DFromENU = new Matrix4();
  326. function getColumbusViewTo3DCamera(transitioner, ellipsoid) {
  327. const scene = transitioner._scene;
  328. const camera = scene.camera;
  329. const camera3D = scratchCVTo3DCamera;
  330. const position = camera3D.position;
  331. const direction = camera3D.direction;
  332. const up = camera3D.up;
  333. const positionCarto = scene.mapProjection.unproject(
  334. camera.position,
  335. scratchCVTo3DCartographic
  336. );
  337. ellipsoid.cartographicToCartesian(positionCarto, position);
  338. const surfacePoint = ellipsoid.scaleToGeodeticSurface(
  339. position,
  340. scratchCVTo3DSurfacePoint
  341. );
  342. const fromENU = Transforms.eastNorthUpToFixedFrame(
  343. surfacePoint,
  344. ellipsoid,
  345. scratchCVTo3DFromENU
  346. );
  347. Matrix4.multiplyByPointAsVector(fromENU, camera.direction, direction);
  348. Matrix4.multiplyByPointAsVector(fromENU, camera.up, up);
  349. return camera3D;
  350. }
  351. const scratchCVTo3DStartPos = new Cartesian3();
  352. const scratchCVTo3DStartDir = new Cartesian3();
  353. const scratchCVTo3DStartUp = new Cartesian3();
  354. const scratchCVTo3DEndPos = new Cartesian3();
  355. const scratchCVTo3DEndDir = new Cartesian3();
  356. const scratchCVTo3DEndUp = new Cartesian3();
  357. function morphFromColumbusViewTo3D(
  358. transitioner,
  359. duration,
  360. endCamera,
  361. complete
  362. ) {
  363. duration *= 0.5;
  364. const scene = transitioner._scene;
  365. const camera = scene.camera;
  366. const startPos = Cartesian3.clone(camera.position, scratchCVTo3DStartPos);
  367. const startDir = Cartesian3.clone(camera.direction, scratchCVTo3DStartDir);
  368. const startUp = Cartesian3.clone(camera.up, scratchCVTo3DStartUp);
  369. const endPos = Matrix4.multiplyByPoint(
  370. Camera.TRANSFORM_2D_INVERSE,
  371. endCamera.position,
  372. scratchCVTo3DEndPos
  373. );
  374. const endDir = Matrix4.multiplyByPointAsVector(
  375. Camera.TRANSFORM_2D_INVERSE,
  376. endCamera.direction,
  377. scratchCVTo3DEndDir
  378. );
  379. const endUp = Matrix4.multiplyByPointAsVector(
  380. Camera.TRANSFORM_2D_INVERSE,
  381. endCamera.up,
  382. scratchCVTo3DEndUp
  383. );
  384. function update(value) {
  385. columbusViewMorph(startPos, endPos, value.time, camera.position);
  386. columbusViewMorph(startDir, endDir, value.time, camera.direction);
  387. columbusViewMorph(startUp, endUp, value.time, camera.up);
  388. Cartesian3.cross(camera.direction, camera.up, camera.right);
  389. Cartesian3.normalize(camera.right, camera.right);
  390. }
  391. const tween = scene.tweens.add({
  392. duration: duration,
  393. easingFunction: EasingFunction.QUARTIC_OUT,
  394. startObject: {
  395. time: 0.0,
  396. },
  397. stopObject: {
  398. time: 1.0,
  399. },
  400. update: update,
  401. complete: function () {
  402. addMorphTimeAnimations(transitioner, scene, 0.0, 1.0, duration, complete);
  403. },
  404. });
  405. transitioner._currentTweens.push(tween);
  406. }
  407. const scratch2DTo3DFrustumOrtho = new OrthographicFrustum();
  408. const scratch3DToCVStartPos = new Cartesian3();
  409. const scratch3DToCVStartDir = new Cartesian3();
  410. const scratch3DToCVStartUp = new Cartesian3();
  411. const scratch3DToCVEndPos = new Cartesian3();
  412. const scratch3DToCVEndDir = new Cartesian3();
  413. const scratch3DToCVEndUp = new Cartesian3();
  414. function morphFrom2DTo3D(transitioner, duration, ellipsoid) {
  415. duration /= 3.0;
  416. const scene = transitioner._scene;
  417. const camera = scene.camera;
  418. let camera3D;
  419. if (duration > 0.0) {
  420. camera3D = scratchCVTo3DCamera;
  421. Cartesian3.fromDegrees(
  422. 0.0,
  423. 0.0,
  424. 5.0 * ellipsoid.maximumRadius,
  425. ellipsoid,
  426. camera3D.position
  427. );
  428. Cartesian3.negate(camera3D.position, camera3D.direction);
  429. Cartesian3.normalize(camera3D.direction, camera3D.direction);
  430. Cartesian3.clone(Cartesian3.UNIT_Z, camera3D.up);
  431. } else {
  432. camera.position.z = camera.frustum.right - camera.frustum.left;
  433. camera3D = getColumbusViewTo3DCamera(transitioner, ellipsoid);
  434. }
  435. let frustum;
  436. if (transitioner._morphToOrthographic) {
  437. frustum = scratch2DTo3DFrustumOrtho;
  438. frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
  439. frustum.width = camera.frustum.right - camera.frustum.left;
  440. } else {
  441. frustum = scratch2DTo3DFrustumPersp;
  442. frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
  443. frustum.fov = CesiumMath.toRadians(60.0);
  444. }
  445. camera3D.frustum = frustum;
  446. const complete = complete3DCallback(camera3D);
  447. createMorphHandler(transitioner, complete);
  448. let morph;
  449. if (transitioner._morphToOrthographic) {
  450. morph = function () {
  451. morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
  452. };
  453. } else {
  454. morph = function () {
  455. morphOrthographicToPerspective(
  456. transitioner,
  457. duration,
  458. camera3D,
  459. function () {
  460. morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
  461. }
  462. );
  463. };
  464. }
  465. if (duration > 0.0) {
  466. scene._mode = SceneMode.SCENE2D;
  467. camera.flyTo({
  468. duration: duration,
  469. destination: Cartesian3.fromDegrees(
  470. 0.0,
  471. 0.0,
  472. 5.0 * ellipsoid.maximumRadius,
  473. ellipsoid,
  474. scratch3DToCVEndPos
  475. ),
  476. complete: function () {
  477. scene._mode = SceneMode.MORPHING;
  478. morph();
  479. },
  480. });
  481. } else {
  482. morph();
  483. }
  484. }
  485. function columbusViewMorph(startPosition, endPosition, time, result) {
  486. // Just linear for now.
  487. return Cartesian3.lerp(startPosition, endPosition, time, result);
  488. }
  489. function morphPerspectiveToOrthographic(
  490. transitioner,
  491. duration,
  492. endCamera,
  493. updateHeight,
  494. complete
  495. ) {
  496. const scene = transitioner._scene;
  497. const camera = scene.camera;
  498. if (camera.frustum instanceof OrthographicFrustum) {
  499. return;
  500. }
  501. const startFOV = camera.frustum.fov;
  502. const endFOV = CesiumMath.RADIANS_PER_DEGREE * 0.5;
  503. const d = endCamera.position.z * Math.tan(startFOV * 0.5);
  504. camera.frustum.far = d / Math.tan(endFOV * 0.5) + 10000000.0;
  505. function update(value) {
  506. camera.frustum.fov = CesiumMath.lerp(startFOV, endFOV, value.time);
  507. const height = d / Math.tan(camera.frustum.fov * 0.5);
  508. updateHeight(camera, height);
  509. }
  510. const tween = scene.tweens.add({
  511. duration: duration,
  512. easingFunction: EasingFunction.QUARTIC_OUT,
  513. startObject: {
  514. time: 0.0,
  515. },
  516. stopObject: {
  517. time: 1.0,
  518. },
  519. update: update,
  520. complete: function () {
  521. camera.frustum = endCamera.frustum.clone();
  522. complete(transitioner);
  523. },
  524. });
  525. transitioner._currentTweens.push(tween);
  526. }
  527. const scratchCVTo2DStartPos = new Cartesian3();
  528. const scratchCVTo2DStartDir = new Cartesian3();
  529. const scratchCVTo2DStartUp = new Cartesian3();
  530. const scratchCVTo2DEndPos = new Cartesian3();
  531. const scratchCVTo2DEndDir = new Cartesian3();
  532. const scratchCVTo2DEndUp = new Cartesian3();
  533. const scratchCVTo2DFrustum = new OrthographicOffCenterFrustum();
  534. const scratchCVTo2DRay = new Ray();
  535. const scratchCVTo2DPickPos = new Cartesian3();
  536. const scratchCVTo2DCamera = {
  537. position: undefined,
  538. direction: undefined,
  539. up: undefined,
  540. frustum: undefined,
  541. };
  542. function morphFromColumbusViewTo2D(transitioner, duration) {
  543. duration *= 0.5;
  544. const scene = transitioner._scene;
  545. const camera = scene.camera;
  546. const startPos = Cartesian3.clone(camera.position, scratchCVTo2DStartPos);
  547. const startDir = Cartesian3.clone(camera.direction, scratchCVTo2DStartDir);
  548. const startUp = Cartesian3.clone(camera.up, scratchCVTo2DStartUp);
  549. const endDir = Cartesian3.negate(Cartesian3.UNIT_Z, scratchCVTo2DEndDir);
  550. const endUp = Cartesian3.clone(Cartesian3.UNIT_Y, scratchCVTo2DEndUp);
  551. const endPos = scratchCVTo2DEndPos;
  552. if (duration > 0.0) {
  553. Cartesian3.clone(Cartesian3.ZERO, scratchCVTo2DEndPos);
  554. endPos.z = 5.0 * scene.mapProjection.ellipsoid.maximumRadius;
  555. } else {
  556. Cartesian3.clone(startPos, scratchCVTo2DEndPos);
  557. const ray = scratchCVTo2DRay;
  558. Matrix4.multiplyByPoint(Camera.TRANSFORM_2D, startPos, ray.origin);
  559. Matrix4.multiplyByPointAsVector(
  560. Camera.TRANSFORM_2D,
  561. startDir,
  562. ray.direction
  563. );
  564. const globe = scene.globe;
  565. if (defined(globe)) {
  566. const pickPos = globe.pickWorldCoordinates(
  567. ray,
  568. scene,
  569. true,
  570. scratchCVTo2DPickPos
  571. );
  572. if (defined(pickPos)) {
  573. Matrix4.multiplyByPoint(Camera.TRANSFORM_2D_INVERSE, pickPos, endPos);
  574. endPos.z += Cartesian3.distance(startPos, endPos);
  575. }
  576. }
  577. }
  578. const frustum = scratchCVTo2DFrustum;
  579. frustum.right = endPos.z * 0.5;
  580. frustum.left = -frustum.right;
  581. frustum.top =
  582. frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth);
  583. frustum.bottom = -frustum.top;
  584. const camera2D = scratchCVTo2DCamera;
  585. camera2D.position = endPos;
  586. camera2D.direction = endDir;
  587. camera2D.up = endUp;
  588. camera2D.frustum = frustum;
  589. const complete = complete2DCallback(camera2D);
  590. createMorphHandler(transitioner, complete);
  591. function updateCV(value) {
  592. columbusViewMorph(startPos, endPos, value.time, camera.position);
  593. columbusViewMorph(startDir, endDir, value.time, camera.direction);
  594. columbusViewMorph(startUp, endUp, value.time, camera.up);
  595. Cartesian3.cross(camera.direction, camera.up, camera.right);
  596. Cartesian3.normalize(camera.right, camera.right);
  597. camera._adjustOrthographicFrustum(true);
  598. }
  599. function updateHeight(camera, height) {
  600. camera.position.z = height;
  601. }
  602. const tween = scene.tweens.add({
  603. duration: duration,
  604. easingFunction: EasingFunction.QUARTIC_OUT,
  605. startObject: {
  606. time: 0.0,
  607. },
  608. stopObject: {
  609. time: 1.0,
  610. },
  611. update: updateCV,
  612. complete: function () {
  613. morphPerspectiveToOrthographic(
  614. transitioner,
  615. duration,
  616. camera2D,
  617. updateHeight,
  618. complete
  619. );
  620. },
  621. });
  622. transitioner._currentTweens.push(tween);
  623. }
  624. const scratch3DTo2DCartographic = new Cartographic();
  625. const scratch3DTo2DCamera = {
  626. position: new Cartesian3(),
  627. direction: new Cartesian3(),
  628. up: new Cartesian3(),
  629. position2D: new Cartesian3(),
  630. direction2D: new Cartesian3(),
  631. up2D: new Cartesian3(),
  632. frustum: new OrthographicOffCenterFrustum(),
  633. };
  634. const scratch3DTo2DEndCamera = {
  635. position: new Cartesian3(),
  636. direction: new Cartesian3(),
  637. up: new Cartesian3(),
  638. frustum: undefined,
  639. };
  640. const scratch3DTo2DPickPosition = new Cartesian3();
  641. const scratch3DTo2DRay = new Ray();
  642. const scratch3DTo2DToENU = new Matrix4();
  643. const scratch3DTo2DSurfacePoint = new Cartesian3();
  644. function morphFrom3DTo2D(transitioner, duration, ellipsoid) {
  645. duration *= 0.5;
  646. const scene = transitioner._scene;
  647. const camera = scene.camera;
  648. const camera2D = scratch3DTo2DCamera;
  649. if (duration > 0.0) {
  650. Cartesian3.clone(Cartesian3.ZERO, camera2D.position);
  651. camera2D.position.z = 5.0 * ellipsoid.maximumRadius;
  652. Cartesian3.negate(Cartesian3.UNIT_Z, camera2D.direction);
  653. Cartesian3.clone(Cartesian3.UNIT_Y, camera2D.up);
  654. } else {
  655. ellipsoid.cartesianToCartographic(
  656. camera.positionWC,
  657. scratch3DTo2DCartographic
  658. );
  659. scene.mapProjection.project(scratch3DTo2DCartographic, camera2D.position);
  660. Cartesian3.negate(Cartesian3.UNIT_Z, camera2D.direction);
  661. Cartesian3.clone(Cartesian3.UNIT_Y, camera2D.up);
  662. const ray = scratch3DTo2DRay;
  663. Cartesian3.clone(camera2D.position2D, ray.origin);
  664. const rayDirection = Cartesian3.clone(camera.directionWC, ray.direction);
  665. const surfacePoint = ellipsoid.scaleToGeodeticSurface(
  666. camera.positionWC,
  667. scratch3DTo2DSurfacePoint
  668. );
  669. const toENU = Transforms.eastNorthUpToFixedFrame(
  670. surfacePoint,
  671. ellipsoid,
  672. scratch3DTo2DToENU
  673. );
  674. Matrix4.inverseTransformation(toENU, toENU);
  675. Matrix4.multiplyByPointAsVector(toENU, rayDirection, rayDirection);
  676. Matrix4.multiplyByPointAsVector(
  677. Camera.TRANSFORM_2D,
  678. rayDirection,
  679. rayDirection
  680. );
  681. const globe = scene.globe;
  682. if (defined(globe)) {
  683. const pickedPos = globe.pickWorldCoordinates(
  684. ray,
  685. scene,
  686. true,
  687. scratch3DTo2DPickPosition
  688. );
  689. if (defined(pickedPos)) {
  690. const height = Cartesian3.distance(camera2D.position2D, pickedPos);
  691. pickedPos.x += height;
  692. Cartesian3.clone(pickedPos, camera2D.position2D);
  693. }
  694. }
  695. }
  696. function updateHeight(camera, height) {
  697. camera.position.x = height;
  698. }
  699. Matrix4.multiplyByPoint(
  700. Camera.TRANSFORM_2D,
  701. camera2D.position,
  702. camera2D.position2D
  703. );
  704. Matrix4.multiplyByPointAsVector(
  705. Camera.TRANSFORM_2D,
  706. camera2D.direction,
  707. camera2D.direction2D
  708. );
  709. Matrix4.multiplyByPointAsVector(
  710. Camera.TRANSFORM_2D,
  711. camera2D.up,
  712. camera2D.up2D
  713. );
  714. const frustum = camera2D.frustum;
  715. frustum.right = camera2D.position.z * 0.5;
  716. frustum.left = -frustum.right;
  717. frustum.top =
  718. frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth);
  719. frustum.bottom = -frustum.top;
  720. const endCamera = scratch3DTo2DEndCamera;
  721. Matrix4.multiplyByPoint(
  722. Camera.TRANSFORM_2D_INVERSE,
  723. camera2D.position2D,
  724. endCamera.position
  725. );
  726. Cartesian3.clone(camera2D.direction, endCamera.direction);
  727. Cartesian3.clone(camera2D.up, endCamera.up);
  728. endCamera.frustum = frustum;
  729. const complete = complete2DCallback(endCamera);
  730. createMorphHandler(transitioner, complete);
  731. function completeCallback() {
  732. morphPerspectiveToOrthographic(
  733. transitioner,
  734. duration,
  735. camera2D,
  736. updateHeight,
  737. complete
  738. );
  739. }
  740. morphFrom3DToColumbusView(transitioner, duration, camera2D, completeCallback);
  741. }
  742. function morphOrthographicToPerspective(
  743. transitioner,
  744. duration,
  745. cameraCV,
  746. complete
  747. ) {
  748. const scene = transitioner._scene;
  749. const camera = scene.camera;
  750. const height = camera.frustum.right - camera.frustum.left;
  751. camera.frustum = cameraCV.frustum.clone();
  752. const endFOV = camera.frustum.fov;
  753. const startFOV = CesiumMath.RADIANS_PER_DEGREE * 0.5;
  754. const d = height * Math.tan(endFOV * 0.5);
  755. camera.frustum.far = d / Math.tan(startFOV * 0.5) + 10000000.0;
  756. camera.frustum.fov = startFOV;
  757. function update(value) {
  758. camera.frustum.fov = CesiumMath.lerp(startFOV, endFOV, value.time);
  759. camera.position.z = d / Math.tan(camera.frustum.fov * 0.5);
  760. }
  761. const tween = scene.tweens.add({
  762. duration: duration,
  763. easingFunction: EasingFunction.QUARTIC_OUT,
  764. startObject: {
  765. time: 0.0,
  766. },
  767. stopObject: {
  768. time: 1.0,
  769. },
  770. update: update,
  771. complete: function () {
  772. complete(transitioner);
  773. },
  774. });
  775. transitioner._currentTweens.push(tween);
  776. }
  777. function morphFrom2DToColumbusView(transitioner, duration, cameraCV, complete) {
  778. duration *= 0.5;
  779. const scene = transitioner._scene;
  780. const camera = scene.camera;
  781. const endPos = Cartesian3.clone(cameraCV.position, scratch3DToCVEndPos);
  782. const endDir = Cartesian3.clone(cameraCV.direction, scratch3DToCVEndDir);
  783. const endUp = Cartesian3.clone(cameraCV.up, scratch3DToCVEndUp);
  784. scene._mode = SceneMode.MORPHING;
  785. function morph() {
  786. camera.frustum = cameraCV.frustum.clone();
  787. const startPos = Cartesian3.clone(camera.position, scratch3DToCVStartPos);
  788. const startDir = Cartesian3.clone(camera.direction, scratch3DToCVStartDir);
  789. const startUp = Cartesian3.clone(camera.up, scratch3DToCVStartUp);
  790. startPos.z = endPos.z;
  791. function update(value) {
  792. columbusViewMorph(startPos, endPos, value.time, camera.position);
  793. columbusViewMorph(startDir, endDir, value.time, camera.direction);
  794. columbusViewMorph(startUp, endUp, value.time, camera.up);
  795. Cartesian3.cross(camera.direction, camera.up, camera.right);
  796. Cartesian3.normalize(camera.right, camera.right);
  797. }
  798. const tween = scene.tweens.add({
  799. duration: duration,
  800. easingFunction: EasingFunction.QUARTIC_OUT,
  801. startObject: {
  802. time: 0.0,
  803. },
  804. stopObject: {
  805. time: 1.0,
  806. },
  807. update: update,
  808. complete: function () {
  809. complete(transitioner);
  810. },
  811. });
  812. transitioner._currentTweens.push(tween);
  813. }
  814. if (transitioner._morphToOrthographic) {
  815. morph();
  816. } else {
  817. morphOrthographicToPerspective(transitioner, 0.0, cameraCV, morph);
  818. }
  819. }
  820. function morphFrom3DToColumbusView(
  821. transitioner,
  822. duration,
  823. endCamera,
  824. complete
  825. ) {
  826. const scene = transitioner._scene;
  827. const camera = scene.camera;
  828. const startPos = Cartesian3.clone(camera.position, scratch3DToCVStartPos);
  829. const startDir = Cartesian3.clone(camera.direction, scratch3DToCVStartDir);
  830. const startUp = Cartesian3.clone(camera.up, scratch3DToCVStartUp);
  831. const endPos = Cartesian3.clone(endCamera.position2D, scratch3DToCVEndPos);
  832. const endDir = Cartesian3.clone(endCamera.direction2D, scratch3DToCVEndDir);
  833. const endUp = Cartesian3.clone(endCamera.up2D, scratch3DToCVEndUp);
  834. function update(value) {
  835. columbusViewMorph(startPos, endPos, value.time, camera.position);
  836. columbusViewMorph(startDir, endDir, value.time, camera.direction);
  837. columbusViewMorph(startUp, endUp, value.time, camera.up);
  838. Cartesian3.cross(camera.direction, camera.up, camera.right);
  839. Cartesian3.normalize(camera.right, camera.right);
  840. camera._adjustOrthographicFrustum(true);
  841. }
  842. const tween = scene.tweens.add({
  843. duration: duration,
  844. easingFunction: EasingFunction.QUARTIC_OUT,
  845. startObject: {
  846. time: 0.0,
  847. },
  848. stopObject: {
  849. time: 1.0,
  850. },
  851. update: update,
  852. complete: function () {
  853. addMorphTimeAnimations(transitioner, scene, 1.0, 0.0, duration, complete);
  854. },
  855. });
  856. transitioner._currentTweens.push(tween);
  857. }
  858. function addMorphTimeAnimations(
  859. transitioner,
  860. scene,
  861. start,
  862. stop,
  863. duration,
  864. complete
  865. ) {
  866. // Later, this will be linear and each object will adjust, if desired, in its vertex shader.
  867. const options = {
  868. object: scene,
  869. property: "morphTime",
  870. startValue: start,
  871. stopValue: stop,
  872. duration: duration,
  873. easingFunction: EasingFunction.QUARTIC_OUT,
  874. };
  875. if (defined(complete)) {
  876. options.complete = function () {
  877. complete(transitioner);
  878. };
  879. }
  880. const tween = scene.tweens.addProperty(options);
  881. transitioner._currentTweens.push(tween);
  882. }
  883. function complete3DCallback(camera3D) {
  884. return function (transitioner) {
  885. const scene = transitioner._scene;
  886. scene._mode = SceneMode.SCENE3D;
  887. scene.morphTime = SceneMode.getMorphTime(SceneMode.SCENE3D);
  888. destroyMorphHandler(transitioner);
  889. const camera = scene.camera;
  890. if (
  891. transitioner._previousMode !== SceneMode.MORPHING ||
  892. transitioner._morphCancelled
  893. ) {
  894. transitioner._morphCancelled = false;
  895. Cartesian3.clone(camera3D.position, camera.position);
  896. Cartesian3.clone(camera3D.direction, camera.direction);
  897. Cartesian3.clone(camera3D.up, camera.up);
  898. Cartesian3.cross(camera.direction, camera.up, camera.right);
  899. Cartesian3.normalize(camera.right, camera.right);
  900. camera.frustum = camera3D.frustum.clone();
  901. }
  902. const frustum = camera.frustum;
  903. if (scene.frameState.useLogDepth) {
  904. frustum.near = 0.1;
  905. frustum.far = 10000000000.0;
  906. }
  907. const wasMorphing = defined(transitioner._completeMorph);
  908. transitioner._completeMorph = undefined;
  909. scene.camera.update(scene.mode);
  910. transitioner._scene.morphComplete.raiseEvent(
  911. transitioner,
  912. transitioner._previousMode,
  913. SceneMode.SCENE3D,
  914. wasMorphing
  915. );
  916. };
  917. }
  918. function complete2DCallback(camera2D) {
  919. return function (transitioner) {
  920. const scene = transitioner._scene;
  921. scene._mode = SceneMode.SCENE2D;
  922. scene.morphTime = SceneMode.getMorphTime(SceneMode.SCENE2D);
  923. destroyMorphHandler(transitioner);
  924. const camera = scene.camera;
  925. Cartesian3.clone(camera2D.position, camera.position);
  926. camera.position.z = scene.mapProjection.ellipsoid.maximumRadius * 2.0;
  927. Cartesian3.clone(camera2D.direction, camera.direction);
  928. Cartesian3.clone(camera2D.up, camera.up);
  929. Cartesian3.cross(camera.direction, camera.up, camera.right);
  930. Cartesian3.normalize(camera.right, camera.right);
  931. camera.frustum = camera2D.frustum.clone();
  932. const wasMorphing = defined(transitioner._completeMorph);
  933. transitioner._completeMorph = undefined;
  934. scene.camera.update(scene.mode);
  935. transitioner._scene.morphComplete.raiseEvent(
  936. transitioner,
  937. transitioner._previousMode,
  938. SceneMode.SCENE2D,
  939. wasMorphing
  940. );
  941. };
  942. }
  943. function completeColumbusViewCallback(cameraCV) {
  944. return function (transitioner) {
  945. const scene = transitioner._scene;
  946. scene._mode = SceneMode.COLUMBUS_VIEW;
  947. scene.morphTime = SceneMode.getMorphTime(SceneMode.COLUMBUS_VIEW);
  948. destroyMorphHandler(transitioner);
  949. const camera = scene.camera;
  950. if (
  951. transitioner._previousModeMode !== SceneMode.MORPHING ||
  952. transitioner._morphCancelled
  953. ) {
  954. transitioner._morphCancelled = false;
  955. Cartesian3.clone(cameraCV.position, camera.position);
  956. Cartesian3.clone(cameraCV.direction, camera.direction);
  957. Cartesian3.clone(cameraCV.up, camera.up);
  958. Cartesian3.cross(camera.direction, camera.up, camera.right);
  959. Cartesian3.normalize(camera.right, camera.right);
  960. }
  961. const frustum = camera.frustum;
  962. if (scene.frameState.useLogDepth) {
  963. frustum.near = 0.1;
  964. frustum.far = 10000000000.0;
  965. }
  966. const wasMorphing = defined(transitioner._completeMorph);
  967. transitioner._completeMorph = undefined;
  968. scene.camera.update(scene.mode);
  969. transitioner._scene.morphComplete.raiseEvent(
  970. transitioner,
  971. transitioner._previousMode,
  972. SceneMode.COLUMBUS_VIEW,
  973. wasMorphing
  974. );
  975. };
  976. }
  977. export default SceneTransitioner;