EntityView.js 14 KB

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