ActiveMapsListControl.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*===============================================================================
  2. Copyright (C) 2022 Immersal - Part of Hexagon. All Rights Reserved.
  3. This file is part of the Immersal SDK.
  4. The Immersal SDK cannot be copied, distributed, or made available to
  5. third-parties for commercial purposes without written permission of Immersal Ltd.
  6. Contact sdk@immersal.com for licensing requests.
  7. ===============================================================================*/
  8. using UnityEngine;
  9. using System.Collections.Generic;
  10. using System.Threading.Tasks;
  11. using UnityEngine.UI;
  12. using Immersal.REST;
  13. using Immersal.AR;
  14. namespace Immersal.Samples.Mapping.ActiveMapsList
  15. {
  16. [RequireComponent(typeof(ScrollRect))]
  17. public class ActiveMapsListControl : MonoBehaviour
  18. {
  19. [SerializeField]
  20. private VisualizeManager m_VisualizeManager = null;
  21. [SerializeField]
  22. private MapperSettings mapperSettings = null;
  23. [SerializeField]
  24. private GameObject itemTemplate = null;
  25. [SerializeField]
  26. private Transform contentParent = null;
  27. private ScrollRect scrollRect;
  28. private List<GameObject> items = new List<GameObject>();
  29. private List<ARMap> m_ActiveMaps = new List<ARMap>();
  30. private ToggleGroup m_toggleGroup = null;
  31. public int rootMapId = -1;
  32. public void GenerateItems()
  33. {
  34. DestroyItems();
  35. m_ActiveMaps.Clear();
  36. items.Clear();
  37. foreach(KeyValuePair<int, ARMap> entry in ARSpace.mapIdToMap)
  38. {
  39. m_ActiveMaps.Add(entry.Value);
  40. GameObject item = Instantiate(itemTemplate);
  41. items.Add(item);
  42. item.SetActive(true);
  43. item.transform.SetParent(contentParent, false);
  44. ActiveMapsListItem activeMapsListItem = item.GetComponent<ActiveMapsListItem>();
  45. activeMapsListItem.SetMapId(entry.Value.mapId);
  46. activeMapsListItem.SetName(entry.Value.mapName);
  47. activeMapsListItem.SetToggleGroup(m_toggleGroup);
  48. activeMapsListItem.SetListController(this);
  49. }
  50. if (items.Count > 0)
  51. {
  52. items[0].GetComponent<ActiveMapsListItem>().SetStateManually(true);
  53. }
  54. }
  55. public void DestroyItems()
  56. {
  57. foreach (GameObject item in items) {
  58. Destroy(item);
  59. }
  60. items.Clear();
  61. }
  62. private void ScrollToTop()
  63. {
  64. if (scrollRect != null)
  65. scrollRect.normalizedPosition = new Vector2(0, 1);
  66. }
  67. private void ScrollToBottom()
  68. {
  69. if (scrollRect != null)
  70. scrollRect.normalizedPosition = new Vector2(0, 0);
  71. }
  72. private void OnEnable()
  73. {
  74. if(m_toggleGroup == null)
  75. {
  76. m_toggleGroup = GetComponent<ToggleGroup>();
  77. }
  78. GenerateItems();
  79. scrollRect = GetComponent<ScrollRect>();
  80. ScrollToTop();
  81. }
  82. public async void SubmitAlignment()
  83. {
  84. if(mapperSettings == null)
  85. {
  86. Debug.Log("ActiveMapListControl: MapperSettings not found");
  87. return;
  88. }
  89. bool transformRootToOrigin = mapperSettings.transformRootToOrigin;
  90. // Debug.Log(string.Format("clicked, root id: {0}, map count {1}", rootMapId, m_ActiveMaps.Count));
  91. if(rootMapId > 0 && m_ActiveMaps.Count > 1)
  92. {
  93. Transform root = ARSpace.mapIdToMap[rootMapId].transform;
  94. Matrix4x4 worldSpaceRoot = root.localToWorldMatrix;
  95. foreach(KeyValuePair<int, ARMap> entry in ARSpace.mapIdToMap)
  96. {
  97. if(entry.Value.mapId != rootMapId)
  98. {
  99. // Debug.Log(string.Format("looping... {0}", entry.Value.mapId));
  100. Transform xf = entry.Value.transform;
  101. Matrix4x4 worldSpaceTransform = xf.localToWorldMatrix;
  102. if(transformRootToOrigin)
  103. {
  104. Matrix4x4 offset = worldSpaceRoot.inverse * worldSpaceTransform;
  105. await MapAlignmentSave(entry.Value.mapId, offset);
  106. }
  107. else
  108. {
  109. // TODO: implement ECEF/double support
  110. Vector3 rootPosMetadata = new Vector3((float)ARSpace.mapIdToMap[rootMapId].mapAlignment.tx, (float)ARSpace.mapIdToMap[rootMapId].mapAlignment.ty, (float)ARSpace.mapIdToMap[rootMapId].mapAlignment.tz);
  111. Quaternion rootRotMetadata = new Quaternion((float)ARSpace.mapIdToMap[rootMapId].mapAlignment.qx, (float)ARSpace.mapIdToMap[rootMapId].mapAlignment.qy, (float)ARSpace.mapIdToMap[rootMapId].mapAlignment.qz, (float)ARSpace.mapIdToMap[rootMapId].mapAlignment.qw);
  112. // IMPORTANT
  113. // Switch coordinate system handedness back from Immersal Cloud Service's default right-handed system to Unity's left-handed system
  114. Matrix4x4 b = Matrix4x4.TRS(rootPosMetadata, rootRotMetadata, Vector3.one);
  115. Matrix4x4 a = ARHelper.SwitchHandedness(b);
  116. Matrix4x4 offset = a * worldSpaceRoot.inverse * worldSpaceTransform;
  117. await MapAlignmentSave(entry.Value.mapId, offset);
  118. }
  119. }
  120. else
  121. {
  122. if(transformRootToOrigin)
  123. {
  124. // set root to origin
  125. Matrix4x4 identity = Matrix4x4.identity;
  126. await MapAlignmentSave(entry.Value.mapId, identity);
  127. }
  128. else
  129. {
  130. // or keep the root transform
  131. // await MapAlignmentSave(entry.Value.mapId, worldSpaceRoot);
  132. }
  133. }
  134. }
  135. m_VisualizeManager?.DefaultView();
  136. Immersal.Samples.Mapping.NotificationManager.Instance.GenerateSuccess("Map Alignments Saved");
  137. }
  138. }
  139. private async Task MapAlignmentSave(int mapId, Matrix4x4 m)
  140. {
  141. //
  142. // Updates map metadata to the Cloud Service and reloads to keep local files in sync
  143. //
  144. Vector3 pos = m.GetColumn(3);
  145. Quaternion rot = m.rotation;
  146. float scl = (m.lossyScale.x + m.lossyScale.y + m.lossyScale.z) / 3f; // Only uniform scale metadata is supported
  147. // IMPORTANT
  148. // Switching coordinate system handedness from Unity's left-handed system to Immersal Cloud Service's default right-handed system
  149. Matrix4x4 b = Matrix4x4.TRS(pos, rot, transform.localScale);
  150. Matrix4x4 a = ARHelper.SwitchHandedness(b);
  151. pos = a.GetColumn(3);
  152. rot = a.rotation;
  153. // Update map alignment metadata to Immersal Cloud Service
  154. JobMapAlignmentSetAsync j = new JobMapAlignmentSetAsync();
  155. j.id = mapId;
  156. j.tx = pos.x;
  157. j.ty = pos.y;
  158. j.tz = pos.z;
  159. j.qx = rot.x;
  160. j.qy = rot.y;
  161. j.qz = rot.z;
  162. j.qw = rot.w;
  163. j.scale = scl;
  164. j.OnResult += (SDKMapAlignmentSetResult result) =>
  165. {
  166. Debug.Log(string.Format("Alignment for map {0} saved", mapId));
  167. };
  168. j.OnError += (e) =>
  169. {
  170. Immersal.Samples.Mapping.NotificationManager.Instance.GenerateError("Network Error");
  171. Debug.Log(string.Format("Failed to save alignment for map id {0}\n{1}", mapId, e));
  172. };
  173. await j.RunJobAsync();
  174. }
  175. }
  176. }