Cesium for Unity 1.15.2
Loading...
Searching...
No Matches
CesiumGlobeAnchor.cs
Go to the documentation of this file.
1using Reinterop;
2using System;
3using System.Collections;
4using UnityEngine;
5using Unity.Mathematics;
6
7#if UNITY_EDITOR
8using UnityEditor;
9#endif
10
11namespace CesiumForUnity
12{
43 [ExecuteInEditMode]
44 [ReinteropNativeImplementation("CesiumForUnityNative::CesiumGlobeAnchorImpl", "CesiumGlobeAnchorImpl.h", staticOnly: true)]
45 [AddComponentMenu("Cesium/Cesium Globe Anchor")]
46 [IconAttribute("Packages/com.cesium.unity/Editor/Resources/Cesium-24x24.png")]
47 public partial class CesiumGlobeAnchor : MonoBehaviour, ICesiumRestartable
48 {
49 #region Fields
50
51 // These fields are marked internal so that they can be accessed from C++.
52 // See https://github.com/CesiumGS/cesium-unity/issues/210
53
54 [SerializeField]
55 internal bool _adjustOrientationForGlobeWhenMoving = true;
56
57 [SerializeField]
58 internal bool _detectTransformChanges = true;
59
60 [SerializeField]
61 internal double4x4 _localToGlobeFixedMatrix = double4x4.identity;
62
63 // True if _localToGlobeFixedMatrix has a valid value.
64 // False if it has not yet been initialized from the Transform.
65 [SerializeField]
66 internal bool _localToGlobeFixedMatrixIsValid = false;
67
68 // The last known local Transform, used to detect changes in the Transform so that
69 // the precise globe coordinates can be recomputed from it. These are invalid before OnEnable.
70 [NonSerialized]
71 internal bool _lastLocalsAreValid = false;
72
73 [NonSerialized]
74 internal Vector3 _lastLocalPosition;
75
76 [NonSerialized]
77 internal Quaternion _lastLocalRotation;
78
79 [NonSerialized]
80 internal Vector3 _lastLocalScale;
81
82 // The resolved georeference containing this globe anchor. This is just a cache
83 // of `GetComponentInParent<CesiumGeoreference>()`.
84 [NonSerialized]
85 internal CesiumGeoreference _georeference;
86
87 [NonSerialized]
88 internal double3? _lastEllipsoidRadii;
89
90 #endregion
91
92 #region User-editable properties
93
119 {
120 get => this._adjustOrientationForGlobeWhenMoving;
121 set => this._adjustOrientationForGlobeWhenMoving = value;
122 }
123
142 {
143 get => this._detectTransformChanges;
144 set
145 {
146 this._detectTransformChanges = value;
147 this.StartOrStopDetectingTransformChanges();
148 }
149 }
150
161 public double4x4 localToGlobeFixedMatrix
162 {
163 get
164 {
165 this.InitializeEcefIfNeeded();
166 return this._localToGlobeFixedMatrix;
167 }
168 set
169 {
170 this.InitializeEcefIfNeeded();
171 this.UpdateEcef(value);
172 }
173 }
174
187 {
188 get
189 {
190 this.UpdateGeoreferenceIfNecessary();
191
192 if (this._georeference == null || this._georeference.ellipsoid == null)
193 {
194 // Cannot get property if there is no georeference, or the georeference has no ellipsoid.
195 return new double3(0.0, 0.0, 0.0);
196 }
197
199 }
200 set
201 {
202 this.UpdateGeoreferenceIfNecessary();
203
204 if (this._georeference == null || this._georeference.ellipsoid == null)
205 {
206 // Cannot set property if there is no georeference, or the georeference has no ellipsoid.
207 return;
208 }
209
210 this.positionGlobeFixed = this._georeference.ellipsoid.LongitudeLatitudeHeightToCenteredFixed(value);
211 }
212 }
213
220 public double3 positionGlobeFixed
221 {
222 get => this.localToGlobeFixedMatrix.c3.xyz;
223 set
224 {
225 double4x4 newModelToEcef = this.localToGlobeFixedMatrix;
226 newModelToEcef.c3 = new double4(value.x, value.y, value.z, 1.0);
227 this.localToGlobeFixedMatrix = newModelToEcef;
228 }
229 }
230
245 public quaternion rotationGlobeFixed
246 {
247 get
248 {
249 double3 translation;
250 quaternion rotation;
251 double3 scale;
252
253 Helpers.MatrixToTranslationRotationAndScale(this.localToGlobeFixedMatrix, out translation, out rotation, out scale);
254
255 return rotation;
256 }
257 set
258 {
259 double3 translation;
260 quaternion rotation;
261 double3 scale;
262
263 Helpers.MatrixToTranslationRotationAndScale(this.localToGlobeFixedMatrix, out translation, out rotation, out scale);
264 this.localToGlobeFixedMatrix = Helpers.TranslationRotationAndScaleToMatrix(translation, value, scale);
265 }
266 }
267
278 public quaternion rotationEastUpNorth
279 {
280 get
281 {
282 this.InitializeEcefIfNeeded();
283 return this.GetLocalToEastUpNorthRotation();
284 }
285 set
286 {
287 this.InitializeEcefIfNeeded();
288 this.SetLocalToEastUpNorthRotation(value);
289 }
290 }
291
300 public double3 scaleGlobeFixed
301 {
302 get
303 {
304 double3 translation;
305 quaternion rotation;
306 double3 scale;
307
308 Helpers.MatrixToTranslationRotationAndScale(this.localToGlobeFixedMatrix, out translation, out rotation, out scale);
309
310 return scale;
311 }
312 set
313 {
314 double3 translation;
315 quaternion rotation;
316 double3 scale;
317
318 Helpers.MatrixToTranslationRotationAndScale(this.localToGlobeFixedMatrix, out translation, out rotation, out scale);
319 this.localToGlobeFixedMatrix = Helpers.TranslationRotationAndScaleToMatrix(translation, rotation, value);
320 }
321 }
322
333 public double3 scaleEastUpNorth
334 {
335 get => -this.scaleGlobeFixed;
336 set => this.scaleGlobeFixed = -value;
337 }
338
339 #endregion
340
341 #region Deprecated Functionality
342
343 [Obsolete("Use positionGlobeFixed.x instead.")]
344 public double ecefX
345 {
346 get => this.positionGlobeFixed.x;
347 set
348 {
349 double3 position = this.positionGlobeFixed;
350 position.x = value;
351 this.positionGlobeFixed = position;
352 }
353 }
354
355 [Obsolete("Use positionGlobeFixed.y instead.")]
356 public double ecefY
357 {
358 get => this.positionGlobeFixed.y;
359 set
360 {
361 double3 position = this.positionGlobeFixed;
362 position.y = value;
363 this.positionGlobeFixed = position;
364 }
365 }
366
367 [Obsolete("Use positionGlobeFixed.z instead.")]
368 public double ecefZ
369 {
370 get => this.positionGlobeFixed.z;
371 set
372 {
373 double3 position = this.positionGlobeFixed;
374 position.z = value;
375 this.positionGlobeFixed = position;
376 }
377 }
378
379 [Obsolete("Use longitudeLatitudeHeight.x instead.")]
380 public double longitude
381 {
382 get => this.longitudeLatitudeHeight.x;
383 set
384 {
385 double3 position = this.longitudeLatitudeHeight;
386 position.x = value;
387 this.longitudeLatitudeHeight = position;
388 }
389 }
390
391 [Obsolete("Use longitudeLatitudeHeight.y instead.")]
392 public double latitude
393 {
394 get => this.longitudeLatitudeHeight.y;
395 set
396 {
397 double3 position = this.longitudeLatitudeHeight;
398 position.y = value;
399 this.longitudeLatitudeHeight = position;
400 }
401 }
402
403 [Obsolete("Use longitudeLatitudeHeight.z instead.")]
404 public double height
405 {
406 get => this.longitudeLatitudeHeight.z;
407 set
408 {
409 double3 position = this.longitudeLatitudeHeight;
410 position.z = value;
411 this.longitudeLatitudeHeight = position;
412 }
413 }
414
415 [Obsolete("Set the longitudeLatitudeHeight property instead.")]
417 {
418 this.longitudeLatitudeHeight = new double3(longitude, latitude, height);
419 }
420
421
422 [Obsolete("Set the positionGlobeFixed property instead.")]
423 public void SetPositionEarthCenteredEarthFixed(double x, double y, double z)
424 {
425 this.positionGlobeFixed = new double3(x, y, z);
426 }
427
428 #endregion
429
430 #region Public Methods
431
467 public void Sync()
468 {
469 // If the ellipsoid changed since last sync, we need to update from transform since our ECEF mapping
470 // is going to be invalid.
471 bool isEllipsoidChanged = this._lastEllipsoidRadii.HasValue && this._georeference != null ?
472 (this._lastEllipsoidRadii.Value.x != this._georeference.ellipsoid.radii.x ||
473 this._lastEllipsoidRadii.Value.y != this._georeference.ellipsoid.radii.y ||
474 this._lastEllipsoidRadii.Value.z != this._georeference.ellipsoid.radii.z) : true;
475
476 // If we don't have a local -> globe fixed matrix yet, we must update from the Transform
477 bool updateFromTransform = !this._localToGlobeFixedMatrixIsValid;
478 if (!isEllipsoidChanged && !updateFromTransform && this._lastLocalsAreValid)
479 {
480 // We may also need to update from the Transform if it has changed
481 // since the last time we computed the local -> globe fixed matrix.
482 updateFromTransform =
483 this._lastLocalPosition != this.transform.localPosition ||
484 this._lastLocalRotation != this.transform.localRotation ||
485 this._lastLocalScale != this.transform.localScale;
486 }
487
488 if (isEllipsoidChanged || updateFromTransform)
489 this.UpdateEcefFromTransform();
490 else
491 this.UpdateEcef(this._localToGlobeFixedMatrix);
492 }
493
499 public void Restart()
500 {
501 this.UpdateGeoreference();
502
503 if (this._georeference == null)
504 Debug.LogWarning($"{this.gameObject.name} is not nested inside a game object with a CesiumGeoreference. Most of its CesiumGlobeAnchor functionality will not work.");
505
506 this.Sync();
507 this.StartOrStopDetectingTransformChanges();
508 }
509
510 #endregion
511
512 #region Unity Messages
513
514 private void UpdateGeoreference()
515 {
516 if (this._georeference != null)
517 this._georeference.RemoveGlobeAnchor(this);
518
519 this._georeference = this.gameObject.GetComponentInParent<CesiumGeoreference>();
520
521 if (this._georeference != null)
522 {
523 this._georeference.Initialize();
524 if (this.isActiveAndEnabled)
525 this._georeference.AddGlobeAnchor(this);
526 }
527 }
528
529 internal void UpdateGeoreferenceIfNecessary()
530 {
531 if (this._georeference == null)
532 this.UpdateGeoreference();
533 }
534
535 private void OnEnable()
536 {
537 this.Restart();
538 }
539
545 private void Reset()
546 {
547 this.Restart();
548 }
549
550 private void OnDisable()
551 {
552 if (this._georeference != null)
553 this._georeference.RemoveGlobeAnchor(this);
554 this._georeference = null;
555 }
556
557 private void OnTransformParentChanged()
558 {
559 this.UpdateGeoreference();
560 this.Sync();
561 }
562
563 #endregion
564
565 #region Coroutines
566
567 private void StartOrStopDetectingTransformChanges()
568 {
569 this.StopCoroutine("DetectTransformChanges");
570
571 bool start = this._detectTransformChanges;
572
573#if UNITY_EDITOR
574 // Always detect changes in Edit mode.
575 if (!EditorApplication.isPlaying)
576 start = true;
577#endif
578
579 // Can't start a coroutine on an inactive game object
580 if (!this.isActiveAndEnabled)
581 start = false;
582
583 if (start)
584 this.StartCoroutine("DetectTransformChanges");
585 }
586
587 private IEnumerator DetectTransformChanges()
588 {
589 // Detect changes in the Transform component.
590 // We don't use Transform.hasChanged because we can't control when it is reset to false.
591 WaitUntil waitForChanges = new WaitUntil(() => this._lastLocalsAreValid && (
592 this.transform.localPosition != this._lastLocalPosition ||
593 this.transform.localRotation != this._lastLocalRotation ||
594 this.transform.localScale != this._lastLocalScale));
595
596 while (true)
597 {
598 yield return waitForChanges;
599 this.UpdateEcefFromTransform();
600 }
601 }
602
603 #endregion
604
605 #region Updaters
606
607 private void InitializeEcefIfNeeded()
608 {
609 if (!this._localToGlobeFixedMatrixIsValid)
610 this.UpdateEcefFromTransform();
611 }
612
613 private void UpdateEcef(double4x4 newModelToEcef)
614 {
615 this.UpdateGeoreferenceIfNecessary();
616 this.SetNewLocalToGlobeFixedMatrix(newModelToEcef);
617 }
618
619 private void UpdateEcefFromTransform()
620 {
621 this.UpdateGeoreferenceIfNecessary();
622 if (this._georeference != null)
623 this.SetNewLocalToGlobeFixedMatrixFromTransform();
624#if UNITY_EDITOR
625 PrefabUtility.RecordPrefabInstancePropertyModifications(this);
626#endif
627 }
628
636 private partial void SetNewLocalToGlobeFixedMatrix(double4x4 newLocalToGlobeFixedMatrix);
637
644 private partial void SetNewLocalToGlobeFixedMatrixFromTransform();
645
652 private partial quaternion GetLocalToEastUpNorthRotation();
653
660 private partial void SetLocalToEastUpNorthRotation(quaternion newRotation);
661
662 #endregion
663 }
664}
partial double3 LongitudeLatitudeHeightToCenteredFixed(double3 longitudeLatitudeHeight)
Convert longitude, latitude, and height to Ellipsoid-Centered, Ellipsoid-Fixed (ECEF) coordinates.
partial double3 CenteredFixedToLongitudeLatitudeHeight(double3 ellipsoidCenteredEllipsoidFixed)
Convert Ellipsoid-Centered, Ellipsoid-Fixed (ECEF) coordinates to longitude, latitude,...
Controls how global geospatial coordinates are mapped to coordinates in the Unity scene.
void AddGlobeAnchor(CesiumGlobeAnchor globeAnchor)
Register a globe anchor with this georeference.
void Initialize()
Initializes this georeference so that other objects may use it to locate the globe in the world.
CesiumEllipsoid ellipsoid
The CesiumEllipsoid that is referenced.
void RemoveGlobeAnchor(CesiumGlobeAnchor globeAnchor)
Deregisters a globe anchor with this georeference, so the globe anchor will no longer be updated when...
Anchors this game object to the globe.
void SetPositionEarthCenteredEarthFixed(double x, double y, double z)
double3 longitudeLatitudeHeight
Gets or sets the longitude, latitude, and height of this object.
quaternion rotationGlobeFixed
Gets or sets the rotation from the game object's coordinate system to the Earth-Centered,...
double4x4 localToGlobeFixedMatrix
Gets or sets the 4x4 transformation matrix from this game object's local coordinate system to the Ear...
bool detectTransformChanges
Gets or sets whether to automatically detect changes in the game object's.
bool adjustOrientationForGlobeWhenMoving
Gets or sets whether to adjust the game object's orientation based on globe curvature as the game obj...
quaternion rotationEastUpNorth
Gets or sets the rotation from the game object's coordinate system to a local coordinate system cente...
void SetPositionLongitudeLatitudeHeight(double longitude, double latitude, double height)
double3 scaleGlobeFixed
Gets the scale from the game object's coordinate system to the Earth-Centered, Earth-Fixed coordinate...
double3 positionGlobeFixed
Gets or sets the game object's position in the Earth-Centered, Earth-Fixed (ECEF) coordinates in mete...
void Sync()
Synchronizes the properties of this CesiumGlobeAnchor.
double3 scaleEastUpNorth
Gets or sets the scale from the game object's coordinate system to a local coordinate system centered...
void Restart()
Completely re-initializes the state of this object from its serialized properties.
An interface for an object with a Restart method, allowing the state of the object to be reinitialize...