EntityView.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import Check from "../Core/Check.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defined from "../Core/defined.js";
  5. import Ellipsoid from "../Core/Ellipsoid.js";
  6. import HeadingPitchRange from "../Core/HeadingPitchRange.js";
  7. import JulianDate from "../Core/JulianDate.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import Matrix3 from "../Core/Matrix3.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import Transforms from "../Core/Transforms.js";
  12. import SceneMode from "../Scene/SceneMode.js";
  13. const updateTransformMatrix3Scratch1 = new Matrix3();
  14. const updateTransformMatrix3Scratch2 = new Matrix3();
  15. const updateTransformMatrix3Scratch3 = new Matrix3();
  16. const updateTransformMatrix4Scratch = new Matrix4();
  17. const updateTransformCartesian3Scratch1 = new Cartesian3();
  18. const updateTransformCartesian3Scratch2 = new Cartesian3();
  19. const updateTransformCartesian3Scratch3 = new Cartesian3();
  20. const updateTransformCartesian3Scratch4 = new Cartesian3();
  21. const updateTransformCartesian3Scratch5 = new Cartesian3();
  22. const updateTransformCartesian3Scratch6 = new Cartesian3();
  23. const deltaTime = new JulianDate();
  24. const northUpAxisFactor = 1.25; // times ellipsoid's maximum radius
  25. function updateTransform(
  26. that,
  27. camera,
  28. updateLookAt,
  29. saveCamera,
  30. positionProperty,
  31. time,
  32. ellipsoid
  33. ) {
  34. const mode = that.scene.mode;
  35. let cartesian = positionProperty.getValue(time, that._lastCartesian);
  36. if (defined(cartesian)) {
  37. let hasBasis = false;
  38. let invertVelocity = false;
  39. let xBasis;
  40. let yBasis;
  41. let zBasis;
  42. if (mode === SceneMode.SCENE3D) {
  43. // The time delta was determined based on how fast satellites move compared to vehicles near the surface.
  44. // Slower moving vehicles will most likely default to east-north-up, while faster ones will be VVLH.
  45. JulianDate.addSeconds(time, 0.001, deltaTime);
  46. let deltaCartesian = positionProperty.getValue(
  47. deltaTime,
  48. updateTransformCartesian3Scratch1
  49. );
  50. // If no valid position at (time + 0.001), sample at (time - 0.001) and invert the vector
  51. if (!defined(deltaCartesian)) {
  52. JulianDate.addSeconds(time, -0.001, deltaTime);
  53. deltaCartesian = positionProperty.getValue(
  54. deltaTime,
  55. updateTransformCartesian3Scratch1
  56. );
  57. invertVelocity = true;
  58. }
  59. if (defined(deltaCartesian)) {
  60. let toInertial = Transforms.computeFixedToIcrfMatrix(
  61. time,
  62. updateTransformMatrix3Scratch1
  63. );
  64. let toInertialDelta = Transforms.computeFixedToIcrfMatrix(
  65. deltaTime,
  66. updateTransformMatrix3Scratch2
  67. );
  68. let toFixed;
  69. if (!defined(toInertial) || !defined(toInertialDelta)) {
  70. toFixed = Transforms.computeTemeToPseudoFixedMatrix(
  71. time,
  72. updateTransformMatrix3Scratch3
  73. );
  74. toInertial = Matrix3.transpose(
  75. toFixed,
  76. updateTransformMatrix3Scratch1
  77. );
  78. toInertialDelta = Transforms.computeTemeToPseudoFixedMatrix(
  79. deltaTime,
  80. updateTransformMatrix3Scratch2
  81. );
  82. Matrix3.transpose(toInertialDelta, toInertialDelta);
  83. } else {
  84. toFixed = Matrix3.transpose(
  85. toInertial,
  86. updateTransformMatrix3Scratch3
  87. );
  88. }
  89. const inertialCartesian = Matrix3.multiplyByVector(
  90. toInertial,
  91. cartesian,
  92. updateTransformCartesian3Scratch5
  93. );
  94. const inertialDeltaCartesian = Matrix3.multiplyByVector(
  95. toInertialDelta,
  96. deltaCartesian,
  97. updateTransformCartesian3Scratch6
  98. );
  99. Cartesian3.subtract(
  100. inertialCartesian,
  101. inertialDeltaCartesian,
  102. updateTransformCartesian3Scratch4
  103. );
  104. const inertialVelocity =
  105. Cartesian3.magnitude(updateTransformCartesian3Scratch4) * 1000.0; // meters/sec
  106. const mu = CesiumMath.GRAVITATIONALPARAMETER; // m^3 / sec^2
  107. const semiMajorAxis =
  108. -mu /
  109. (inertialVelocity * inertialVelocity -
  110. (2 * mu) / Cartesian3.magnitude(inertialCartesian));
  111. if (
  112. semiMajorAxis < 0 ||
  113. semiMajorAxis > northUpAxisFactor * ellipsoid.maximumRadius
  114. ) {
  115. // North-up viewing from deep space.
  116. // X along the nadir
  117. xBasis = updateTransformCartesian3Scratch2;
  118. Cartesian3.normalize(cartesian, xBasis);
  119. Cartesian3.negate(xBasis, xBasis);
  120. // Z is North
  121. zBasis = Cartesian3.clone(
  122. Cartesian3.UNIT_Z,
  123. updateTransformCartesian3Scratch3
  124. );
  125. // Y is along the cross of z and x (right handed basis / in the direction of motion)
  126. yBasis = Cartesian3.cross(
  127. zBasis,
  128. xBasis,
  129. updateTransformCartesian3Scratch1
  130. );
  131. if (Cartesian3.magnitude(yBasis) > CesiumMath.EPSILON7) {
  132. Cartesian3.normalize(xBasis, xBasis);
  133. Cartesian3.normalize(yBasis, yBasis);
  134. zBasis = Cartesian3.cross(
  135. xBasis,
  136. yBasis,
  137. updateTransformCartesian3Scratch3
  138. );
  139. Cartesian3.normalize(zBasis, zBasis);
  140. hasBasis = true;
  141. }
  142. } else if (
  143. !Cartesian3.equalsEpsilon(
  144. cartesian,
  145. deltaCartesian,
  146. CesiumMath.EPSILON7
  147. )
  148. ) {
  149. // Approximation of VVLH (Vehicle Velocity Local Horizontal) with the Z-axis flipped.
  150. // Z along the position
  151. zBasis = updateTransformCartesian3Scratch2;
  152. Cartesian3.normalize(inertialCartesian, zBasis);
  153. Cartesian3.normalize(inertialDeltaCartesian, inertialDeltaCartesian);
  154. // Y is along the angular momentum vector (e.g. "orbit normal")
  155. yBasis = Cartesian3.cross(
  156. zBasis,
  157. inertialDeltaCartesian,
  158. updateTransformCartesian3Scratch3
  159. );
  160. if (invertVelocity) {
  161. yBasis = Cartesian3.multiplyByScalar(yBasis, -1, yBasis);
  162. }
  163. if (
  164. !Cartesian3.equalsEpsilon(
  165. yBasis,
  166. Cartesian3.ZERO,
  167. CesiumMath.EPSILON7
  168. )
  169. ) {
  170. // X is along the cross of y and z (right handed basis / in the direction of motion)
  171. xBasis = Cartesian3.cross(
  172. yBasis,
  173. zBasis,
  174. updateTransformCartesian3Scratch1
  175. );
  176. Matrix3.multiplyByVector(toFixed, xBasis, xBasis);
  177. Matrix3.multiplyByVector(toFixed, yBasis, yBasis);
  178. Matrix3.multiplyByVector(toFixed, zBasis, zBasis);
  179. Cartesian3.normalize(xBasis, xBasis);
  180. Cartesian3.normalize(yBasis, yBasis);
  181. Cartesian3.normalize(zBasis, zBasis);
  182. hasBasis = true;
  183. }
  184. }
  185. }
  186. }
  187. if (defined(that.boundingSphere)) {
  188. cartesian = that.boundingSphere.center;
  189. }
  190. let position;
  191. let direction;
  192. let up;
  193. if (saveCamera) {
  194. position = Cartesian3.clone(
  195. camera.position,
  196. updateTransformCartesian3Scratch4
  197. );
  198. direction = Cartesian3.clone(
  199. camera.direction,
  200. updateTransformCartesian3Scratch5
  201. );
  202. up = Cartesian3.clone(camera.up, updateTransformCartesian3Scratch6);
  203. }
  204. const transform = updateTransformMatrix4Scratch;
  205. if (hasBasis) {
  206. transform[0] = xBasis.x;
  207. transform[1] = xBasis.y;
  208. transform[2] = xBasis.z;
  209. transform[3] = 0.0;
  210. transform[4] = yBasis.x;
  211. transform[5] = yBasis.y;
  212. transform[6] = yBasis.z;
  213. transform[7] = 0.0;
  214. transform[8] = zBasis.x;
  215. transform[9] = zBasis.y;
  216. transform[10] = zBasis.z;
  217. transform[11] = 0.0;
  218. transform[12] = cartesian.x;
  219. transform[13] = cartesian.y;
  220. transform[14] = cartesian.z;
  221. transform[15] = 0.0;
  222. } else {
  223. // Stationary or slow-moving, low-altitude objects use East-North-Up.
  224. Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform);
  225. }
  226. camera._setTransform(transform);
  227. if (saveCamera) {
  228. Cartesian3.clone(position, camera.position);
  229. Cartesian3.clone(direction, camera.direction);
  230. Cartesian3.clone(up, camera.up);
  231. Cartesian3.cross(direction, up, camera.right);
  232. }
  233. }
  234. if (updateLookAt) {
  235. const offset =
  236. mode === SceneMode.SCENE2D ||
  237. Cartesian3.equals(that._offset3D, Cartesian3.ZERO)
  238. ? undefined
  239. : that._offset3D;
  240. camera.lookAtTransform(camera.transform, offset);
  241. }
  242. }
  243. /**
  244. * A utility object for tracking an entity with the camera.
  245. * @alias EntityView
  246. * @constructor
  247. *
  248. * @param {Entity} entity The entity to track with the camera.
  249. * @param {Scene} scene The scene to use.
  250. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use for orienting the camera.
  251. */
  252. function EntityView(entity, scene, ellipsoid) {
  253. //>>includeStart('debug', pragmas.debug);
  254. Check.defined("entity", entity);
  255. Check.defined("scene", scene);
  256. //>>includeEnd('debug');
  257. /**
  258. * The entity to track with the camera.
  259. * @type {Entity}
  260. */
  261. this.entity = entity;
  262. /**
  263. * The scene in which to track the object.
  264. * @type {Scene}
  265. */
  266. this.scene = scene;
  267. /**
  268. * The ellipsoid to use for orienting the camera.
  269. * @type {Ellipsoid}
  270. */
  271. this.ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  272. /**
  273. * The bounding sphere of the object.
  274. * @type {BoundingSphere}
  275. */
  276. this.boundingSphere = undefined;
  277. // Shadow copies of the objects so we can detect changes.
  278. this._lastEntity = undefined;
  279. this._mode = undefined;
  280. this._lastCartesian = new Cartesian3();
  281. this._defaultOffset3D = undefined;
  282. this._offset3D = new Cartesian3();
  283. }
  284. // STATIC properties defined here, not per-instance.
  285. Object.defineProperties(EntityView, {
  286. /**
  287. * Gets or sets a camera offset that will be used to
  288. * initialize subsequent EntityViews.
  289. * @memberof EntityView
  290. * @type {Cartesian3}
  291. */
  292. defaultOffset3D: {
  293. get: function () {
  294. return this._defaultOffset3D;
  295. },
  296. set: function (vector) {
  297. this._defaultOffset3D = Cartesian3.clone(vector, new Cartesian3());
  298. },
  299. },
  300. });
  301. // Initialize the static property.
  302. EntityView.defaultOffset3D = new Cartesian3(-14000, 3500, 3500);
  303. const scratchHeadingPitchRange = new HeadingPitchRange();
  304. const scratchCartesian = new Cartesian3();
  305. /**
  306. * Should be called each animation frame to update the camera
  307. * to the latest settings.
  308. * @param {JulianDate} time The current animation time.
  309. * @param {BoundingSphere} [boundingSphere] bounding sphere of the object.
  310. */
  311. EntityView.prototype.update = function (time, boundingSphere) {
  312. //>>includeStart('debug', pragmas.debug);
  313. Check.defined("time", time);
  314. //>>includeEnd('debug');
  315. const scene = this.scene;
  316. const ellipsoid = this.ellipsoid;
  317. const sceneMode = scene.mode;
  318. if (sceneMode === SceneMode.MORPHING) {
  319. return;
  320. }
  321. const entity = this.entity;
  322. const positionProperty = entity.position;
  323. if (!defined(positionProperty)) {
  324. return;
  325. }
  326. const objectChanged = entity !== this._lastEntity;
  327. const sceneModeChanged = sceneMode !== this._mode;
  328. const camera = scene.camera;
  329. let updateLookAt = objectChanged || sceneModeChanged;
  330. let saveCamera = true;
  331. if (objectChanged) {
  332. const viewFromProperty = entity.viewFrom;
  333. const hasViewFrom = defined(viewFromProperty);
  334. if (!hasViewFrom && defined(boundingSphere)) {
  335. // The default HPR is not ideal for high altitude objects so
  336. // we scale the pitch as we get further from the earth for a more
  337. // downward view.
  338. scratchHeadingPitchRange.pitch = -CesiumMath.PI_OVER_FOUR;
  339. scratchHeadingPitchRange.range = 0;
  340. const position = positionProperty.getValue(time, scratchCartesian);
  341. if (defined(position)) {
  342. const factor =
  343. 2 -
  344. 1 /
  345. Math.max(
  346. 1,
  347. Cartesian3.magnitude(position) / ellipsoid.maximumRadius
  348. );
  349. scratchHeadingPitchRange.pitch *= factor;
  350. }
  351. camera.viewBoundingSphere(boundingSphere, scratchHeadingPitchRange);
  352. this.boundingSphere = boundingSphere;
  353. updateLookAt = false;
  354. saveCamera = false;
  355. } else if (
  356. !hasViewFrom ||
  357. !defined(viewFromProperty.getValue(time, this._offset3D))
  358. ) {
  359. Cartesian3.clone(EntityView._defaultOffset3D, this._offset3D);
  360. }
  361. } else if (!sceneModeChanged && this._mode !== SceneMode.SCENE2D) {
  362. Cartesian3.clone(camera.position, this._offset3D);
  363. }
  364. this._lastEntity = entity;
  365. this._mode = sceneMode;
  366. updateTransform(
  367. this,
  368. camera,
  369. updateLookAt,
  370. saveCamera,
  371. positionProperty,
  372. time,
  373. ellipsoid
  374. );
  375. };
  376. export default EntityView;