NavigationManager.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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 System.Collections.Generic;
  9. using UnityEngine;
  10. using UnityEngine.Events;
  11. using UnityEngine.UI;
  12. using UnityEngine.AI;
  13. using Immersal.AR;
  14. using TMPro;
  15. namespace Immersal.Samples.Navigation
  16. {
  17. [System.Serializable]
  18. public class NavigationEvent : UnityEvent<Transform>
  19. {
  20. }
  21. public class NavigationManager : MonoBehaviour
  22. {
  23. // Navigation Visualization references
  24. [Header("Visualization")]
  25. [SerializeField]
  26. private GameObject m_navigationPathPrefab = null;
  27. /*
  28. [SerializeField]
  29. private GameObject m_navigationArrowPrefab = null;
  30. */
  31. // UI Object references
  32. [Header("UI Objects")]
  33. [SerializeField]
  34. private GameObject m_TargetsList = null;
  35. [SerializeField]
  36. private Sprite m_ShowListIcon = null;
  37. [SerializeField]
  38. private Sprite m_SelectTargetIcon = null;
  39. [SerializeField]
  40. private Image m_TargetsListIcon = null;
  41. [SerializeField]
  42. private TextMeshProUGUI m_TargetsListText = null;
  43. [SerializeField]
  44. private GameObject m_StopNavigationButton = null;
  45. // Navigation Settings
  46. private enum NavigationMode { NavMesh, Graph};
  47. [Header("Settings")]
  48. [SerializeField]
  49. private NavigationMode m_navigationMode = NavigationMode.NavMesh;
  50. public bool inEditMode = false;
  51. /*
  52. [SerializeField]
  53. private bool m_showArrow = true;
  54. */
  55. [SerializeField]
  56. private float m_ArrivedDistanceThreshold = 1.0f;
  57. [SerializeField]
  58. private float m_pathWidth = 0.3f;
  59. [SerializeField]
  60. private float m_heightOffset = 0.5f;
  61. // Navigation State Events
  62. [Header("Events")]
  63. [SerializeField]
  64. private NavigationEvent onTargetFound = new NavigationEvent();
  65. [SerializeField]
  66. private NavigationEvent onTargetNotFound = new NavigationEvent();
  67. private ARSpace m_arSpace = null;
  68. private bool m_managerInitialized = false;
  69. private bool m_navigationActive = false;
  70. private Transform m_targetTransform = null;
  71. private IsNavigationTarget m_NavigationTarget = null;
  72. private Transform m_playerTransform = null;
  73. private GameObject m_navigationPathObject = null;
  74. private NavigationPath m_navigationPath = null;
  75. [SerializeField]
  76. private NavigationGraphManager m_NavigationGraphManager = null;
  77. private enum NavigationState { NotNavigating, Navigating};
  78. private NavigationState m_navigationState = NavigationState.NotNavigating;
  79. private static NavigationManager instance = null;
  80. public static NavigationManager Instance
  81. {
  82. get
  83. {
  84. #if UNITY_EDITOR
  85. if (instance == null && !Application.isPlaying)
  86. {
  87. instance = FindObjectOfType<NavigationManager>();
  88. }
  89. #endif
  90. if (instance == null)
  91. {
  92. Debug.LogError("No NavigationManager instance found. Ensure one exists in the scene.");
  93. }
  94. return instance;
  95. }
  96. }
  97. public bool navigationActive
  98. {
  99. get { return m_navigationActive; }
  100. }
  101. void Awake()
  102. {
  103. if (instance == null)
  104. {
  105. instance = this;
  106. }
  107. if (instance != this)
  108. {
  109. Debug.LogError("NavigationManager: There must be only one NavigationManager in a scene.");
  110. UnityEngine.Object.DestroyImmediate(this);
  111. return;
  112. }
  113. }
  114. private void Start()
  115. {
  116. InitializeNavigationManager();
  117. if (m_managerInitialized)
  118. {
  119. m_TargetsListIcon.sprite = m_ShowListIcon;
  120. m_TargetsListText.text = "Show Navigation Targets";
  121. }
  122. }
  123. private void Update()
  124. {
  125. if(m_managerInitialized && m_navigationState == NavigationState.Navigating)
  126. {
  127. TryToFindPath(m_NavigationTarget);
  128. }
  129. }
  130. public void InitializeNavigation(NavigationTargetListButton button)
  131. {
  132. if (!m_managerInitialized)
  133. {
  134. Debug.LogWarning("NavigationManager: Navigation Manager not properly initialized.");
  135. return;
  136. }
  137. m_targetTransform = button.targetObject.transform;
  138. m_NavigationTarget = button.targetObject.GetComponent<IsNavigationTarget>();
  139. TryToFindPath(m_NavigationTarget);
  140. }
  141. public void TryToFindPath(IsNavigationTarget navigationTarget)
  142. {
  143. List<Vector3> corners;
  144. // Convert to Unity's world space coordinates to use NavMesh
  145. Vector3 startPosition = m_playerTransform.position;
  146. Vector3 targetPosition = navigationTarget.position;
  147. Vector3 delta = targetPosition - startPosition;
  148. float distanceToTarget = new Vector3(delta.x, delta.y, delta.z).magnitude;
  149. if (distanceToTarget < m_ArrivedDistanceThreshold)
  150. {
  151. m_navigationActive = false;
  152. m_navigationState = NavigationState.NotNavigating;
  153. UpdateNavigationUI(m_navigationState);
  154. DisplayArrivedNotification();
  155. return;
  156. }
  157. switch (m_navigationMode)
  158. {
  159. case NavigationMode.NavMesh:
  160. startPosition = ARSpaceToUnity(m_arSpace.transform, m_arSpace.initialOffset, startPosition);
  161. targetPosition = ARSpaceToUnity(m_arSpace.transform, m_arSpace.initialOffset, targetPosition);
  162. corners = FindPathNavMesh(startPosition, targetPosition);
  163. if (corners.Count >= 2)
  164. {
  165. m_navigationActive = true;
  166. m_navigationState = NavigationState.Navigating;
  167. UpdateNavigationUI(m_navigationState);
  168. m_navigationPath.GeneratePath(corners, m_arSpace.transform.up);
  169. m_navigationPath.pathWidth = m_pathWidth;
  170. }
  171. else
  172. {
  173. Mapping.NotificationManager.Instance.GenerateNotification("Path to target not found.");
  174. UpdateNavigationUI(m_navigationState);
  175. }
  176. break;
  177. case NavigationMode.Graph:
  178. corners = m_NavigationGraphManager.FindPath(startPosition, targetPosition);
  179. if (corners.Count >= 2)
  180. {
  181. m_navigationActive = true;
  182. m_navigationState = NavigationState.Navigating;
  183. UpdateNavigationUI(m_navigationState);
  184. m_navigationPath.GeneratePath(corners, m_arSpace.transform.up);
  185. m_navigationPath.pathWidth = m_pathWidth;
  186. }
  187. else
  188. {
  189. Mapping.NotificationManager.Instance.GenerateNotification("Path to target not found.");
  190. UpdateNavigationUI(m_navigationState);
  191. }
  192. break;
  193. }
  194. }
  195. private List<Vector3> FindPathNavMesh(Vector3 startPosition, Vector3 targetPosition)
  196. {
  197. NavMeshPath path = new NavMeshPath();
  198. List<Vector3> collapsedCorners = new List<Vector3>();
  199. if (NavMesh.CalculatePath(startPosition, targetPosition, NavMesh.AllAreas, path))
  200. {
  201. List<Vector3> corners = new List<Vector3>(path.corners);
  202. for (int i = 0; i < corners.Count; i++)
  203. {
  204. corners[i] = corners[i] + new Vector3(0f, m_heightOffset, 0f);
  205. corners[i] = UnityToARSpace(m_arSpace.transform, m_arSpace.initialOffset, corners[i]);
  206. }
  207. for (int i = 0; i < corners.Count - 1; i++)
  208. {
  209. Vector3 currentPoint = corners[i];
  210. Vector3 nextPoint = corners[i + 1];
  211. float threshold = 0.75f;
  212. if (Vector3.Distance(currentPoint, nextPoint) > threshold)
  213. {
  214. collapsedCorners.Add(currentPoint);
  215. }
  216. }
  217. collapsedCorners.Add(corners[corners.Count - 1]);
  218. }
  219. return collapsedCorners;
  220. }
  221. public void ToggleTargetsList()
  222. {
  223. if (!m_managerInitialized)
  224. {
  225. Debug.LogWarning("NavigationManager: Navigation Manager not properly initialized.");
  226. return;
  227. }
  228. if (m_TargetsList.activeInHierarchy)
  229. {
  230. m_TargetsList.SetActive(false);
  231. if (m_ShowListIcon != null && m_TargetsListIcon != null)
  232. {
  233. m_TargetsListIcon.sprite = m_ShowListIcon;
  234. }
  235. if (m_TargetsListText != null)
  236. {
  237. m_TargetsListText.text = "Show Navigation Targets";
  238. }
  239. }
  240. else
  241. {
  242. m_TargetsList.SetActive(true);
  243. m_TargetsList.GetComponent<NavigationTargetListControl>().GenerateButtons();
  244. if (m_SelectTargetIcon != null && m_TargetsListIcon != null)
  245. {
  246. m_TargetsListIcon.sprite = m_SelectTargetIcon;
  247. }
  248. if (m_TargetsListText != null)
  249. {
  250. m_TargetsListText.text = "Select Navigation Target";
  251. }
  252. }
  253. }
  254. public void ToggleEditMode()
  255. {
  256. inEditMode = !inEditMode;
  257. }
  258. public void DisplayPathNotFoundNotification()
  259. {
  260. #if !(UNITY_STANDALONE || PLATFORM_LUMIN)
  261. Handheld.Vibrate();
  262. #endif
  263. Mapping.NotificationManager.Instance.GenerateNotification("Path to target could not be found.");
  264. onTargetNotFound.Invoke(m_targetTransform);
  265. }
  266. public void DisplayArrivedNotification()
  267. {
  268. #if !(UNITY_STANDALONE || PLATFORM_LUMIN)
  269. Handheld.Vibrate();
  270. #endif
  271. Mapping.NotificationManager.Instance.GenerateNotification("You have arrived!");
  272. onTargetFound.Invoke(m_targetTransform);
  273. }
  274. public void StopNavigation()
  275. {
  276. m_navigationActive = false;
  277. m_navigationState = NavigationState.NotNavigating;
  278. UpdateNavigationUI(m_navigationState);
  279. Mapping.NotificationManager.Instance.GenerateNotification("Navigation stopped.");
  280. }
  281. private void UpdateNavigationUI(NavigationState navigationState)
  282. {
  283. switch(navigationState)
  284. {
  285. case NavigationState.NotNavigating:
  286. m_StopNavigationButton.SetActive(false);
  287. m_navigationPathObject.SetActive(false);
  288. break;
  289. case NavigationState.Navigating:
  290. m_StopNavigationButton.SetActive(true);
  291. m_navigationPathObject.SetActive(true);
  292. break;
  293. }
  294. }
  295. private void InitializeNavigationManager()
  296. {
  297. if (m_arSpace == null)
  298. {
  299. m_arSpace = FindObjectOfType<ARSpace>();
  300. if (m_arSpace == null)
  301. {
  302. Debug.LogWarning("NavigationManager: No AR Space found in scene, ensure one exists.");
  303. return;
  304. }
  305. }
  306. m_NavigationGraphManager = GetComponent<NavigationGraphManager>();
  307. if (m_NavigationGraphManager == null)
  308. {
  309. Debug.LogWarning("NavigationManager: Missing Navigation Graph Manager component.");
  310. return;
  311. }
  312. m_playerTransform = Camera.main.transform;
  313. if (m_playerTransform == null)
  314. {
  315. Debug.LogWarning("NavigationManager: Could not find the main camera. Do you have the MainCamera tag applied?");
  316. return;
  317. }
  318. if (m_navigationPathPrefab == null)
  319. {
  320. Debug.LogWarning("NavigationManager: Missing navigation path object reference.");
  321. return;
  322. }
  323. if(m_navigationPathPrefab != null)
  324. {
  325. if (m_navigationPathObject == null)
  326. {
  327. m_navigationPathObject = Instantiate(m_navigationPathPrefab);
  328. m_navigationPathObject.SetActive(false);
  329. m_navigationPath = m_navigationPathObject.GetComponent<NavigationPath>();
  330. }
  331. if(m_navigationPath == null)
  332. {
  333. Debug.LogWarning("NavigationManager: NavigationPath component in Navigation path is missing.");
  334. return;
  335. }
  336. }
  337. if (m_TargetsList == null)
  338. {
  339. Debug.LogWarning("NavigationManager: Navigation Targets List reference is missing.");
  340. return;
  341. }
  342. if (m_ShowListIcon == null)
  343. {
  344. Debug.LogWarning("NavigationManager: \"Show List\" icon is missing.");
  345. return;
  346. }
  347. if (m_SelectTargetIcon == null)
  348. {
  349. Debug.LogWarning("NavigationManager: \"Select Target\" icon is missing.");
  350. return;
  351. }
  352. if (m_TargetsListIcon == null)
  353. {
  354. Debug.LogWarning("NavigationManager: \"Targets List\" icon reference is missing.");
  355. return;
  356. }
  357. if (m_TargetsListText == null)
  358. {
  359. Debug.LogWarning("NavigationManager: \"Targets List\" text reference is missing.");
  360. return;
  361. }
  362. if (m_StopNavigationButton == null)
  363. {
  364. Debug.LogWarning("NavigationManager: Stop Navigation Button reference is missing.");
  365. return;
  366. }
  367. m_managerInitialized = true;
  368. }
  369. private Vector3 ARSpaceToUnity(Transform arspace, Matrix4x4 arspaceoffset, Vector3 pos)
  370. {
  371. Matrix4x4 m = arspace.worldToLocalMatrix;
  372. pos = m.MultiplyPoint(pos);
  373. pos = arspaceoffset.MultiplyPoint(pos);
  374. return pos;
  375. }
  376. private Vector3 ARSpaceToUnity(Transform arspace, Vector3 pos)
  377. {
  378. pos = ARSpaceToUnity(arspace, Matrix4x4.identity, pos);
  379. return pos;
  380. }
  381. private Vector3 UnityToARSpace(Transform arspace, Matrix4x4 arspaceOffset, Vector3 pos)
  382. {
  383. pos = arspaceOffset.inverse.MultiplyPoint(pos);
  384. Matrix4x4 m = arspace.localToWorldMatrix;
  385. pos = m.MultiplyPoint(pos);
  386. return pos;
  387. }
  388. private Vector3 UnityToARSpace(Transform arspace, Vector3 pos)
  389. {
  390. pos = UnityToARSpace(arspace, Matrix4x4.identity, pos);
  391. return pos;
  392. }
  393. }
  394. }