NavigationGraphManager.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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;
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11. namespace Immersal.Samples.Navigation
  12. {
  13. public class Node
  14. {
  15. public Vector3 position;
  16. public List<Node> neighbours = new List<Node>();
  17. public string nodeName;
  18. public Node(Vector3 position)
  19. {
  20. this.position = position;
  21. }
  22. public Node()
  23. {
  24. }
  25. public Node(Vector3 position, List<Node> neighbours)
  26. {
  27. this.position = position;
  28. this.neighbours = neighbours;
  29. }
  30. public float Cost
  31. {
  32. get; set;
  33. }
  34. public Node Parent
  35. {
  36. get; set;
  37. }
  38. }
  39. public class NavigationGraphManager : MonoBehaviour
  40. {
  41. public class LineSegment
  42. {
  43. public Vector3 startPosition;
  44. public Vector3 endPosition;
  45. public LineSegment(Vector3 start, Vector3 end)
  46. {
  47. this.startPosition = start;
  48. this.endPosition = end;
  49. }
  50. }
  51. public class Edge
  52. {
  53. public Node start;
  54. public Node end;
  55. public Edge(Node start, Node end)
  56. {
  57. this.start = start;
  58. this.end = end;
  59. }
  60. }
  61. [SerializeField]
  62. private float startYOffset = -0.25f;
  63. private List<Waypoint> m_Waypoints = new List<Waypoint>();
  64. private List<IsNavigationTarget> m_NavigationTargets = new List<IsNavigationTarget>();
  65. private Mesh m_Mesh;
  66. private MeshFilter m_MeshFilter;
  67. private MeshRenderer m_MeshRenderer;
  68. private Immersal.AR.ARSpace m_ArSpace = null;
  69. #region singleton pattern
  70. private static NavigationGraphManager instance = null;
  71. public static NavigationGraphManager Instance
  72. {
  73. get
  74. {
  75. #if UNITY_EDITOR
  76. if (instance == null && !Application.isPlaying)
  77. {
  78. instance = FindObjectOfType<NavigationGraphManager>();
  79. }
  80. #endif
  81. if (instance == null)
  82. {
  83. Debug.LogError("No NavigationGraphManager instance found. Ensure one exists in the scene.");
  84. }
  85. return instance;
  86. }
  87. }
  88. void Awake()
  89. {
  90. if (instance == null)
  91. {
  92. instance = this;
  93. }
  94. if (instance != this)
  95. {
  96. Debug.LogError("There must be only one NavigationGraphManager in a scene.");
  97. UnityEngine.Object.DestroyImmediate(this);
  98. return;
  99. }
  100. }
  101. #endregion
  102. void Start()
  103. {
  104. InitializeMeshRenderer();
  105. m_ArSpace = FindObjectOfType<Immersal.AR.ARSpace>();
  106. }
  107. void Update()
  108. {
  109. if (Immersal.Samples.Navigation.NavigationManager.Instance.inEditMode)
  110. {
  111. m_MeshRenderer.enabled = true;
  112. DrawConnections(0.075f, m_Mesh);
  113. }
  114. else
  115. {
  116. m_MeshRenderer.enabled = false;
  117. }
  118. }
  119. private void InitializeMeshRenderer()
  120. {
  121. if (m_Mesh == null)
  122. m_Mesh = new Mesh();
  123. m_MeshFilter = GetComponent<MeshFilter>();
  124. m_MeshRenderer = GetComponent<MeshRenderer>();
  125. if (m_MeshFilter == null)
  126. m_MeshFilter = gameObject.AddComponent<MeshFilter>();
  127. if (m_MeshRenderer == null)
  128. m_MeshRenderer = gameObject.AddComponent<MeshRenderer>();
  129. m_MeshFilter.mesh = m_Mesh;
  130. }
  131. private void DrawConnections(float LineSegmentWidth, Mesh mesh)
  132. {
  133. List<LineSegment> lineSegments = new List<LineSegment>();
  134. foreach (Waypoint wp in m_Waypoints)
  135. {
  136. foreach (Waypoint neighbour in wp.neighbours)
  137. {
  138. if (neighbour != null)
  139. {
  140. LineSegment ls = new LineSegment(wp.position, neighbour.position);
  141. lineSegments.Add(ls);
  142. }
  143. }
  144. }
  145. if (lineSegments.Count < 1)
  146. {
  147. mesh.Clear();
  148. return;
  149. }
  150. int segmentCount = lineSegments.Count;
  151. int offset = 0;
  152. Vector3[] vertices = new Vector3[segmentCount * 4];
  153. Vector2[] uvs = new Vector2[segmentCount * 4];
  154. int[] triangleIndices = new int[segmentCount * 6];
  155. foreach (LineSegment ls in lineSegments)
  156. {
  157. {
  158. Vector3 startPosition = transform.worldToLocalMatrix.MultiplyPoint(ls.startPosition);
  159. Vector3 endPosition = transform.worldToLocalMatrix.MultiplyPoint(ls.endPosition);
  160. Vector3 billboardUp = Camera.main.transform.forward;
  161. //billboardUp = new Vector3(billboardUp.x, 0f, billboardUp.z);
  162. Quaternion edgeOrientation = Quaternion.LookRotation(endPosition - startPosition, billboardUp);
  163. Matrix4x4 startTransform = Matrix4x4.TRS(startPosition, edgeOrientation, Vector3.one);
  164. Matrix4x4 endTransform = Matrix4x4.TRS(endPosition, edgeOrientation, Vector3.one);
  165. Vector3[] shape = new Vector3[] { new Vector3(-0.5f, 0f, 0f), new Vector3(0.5f, 0f, 0f) };
  166. float[] shapeU = new float[] { 0f, 1f };
  167. int verticesOffset = offset * 4;
  168. int triangleIndicesOffset = offset * 6;
  169. vertices[verticesOffset + 0] = startTransform.MultiplyPoint(shape[0] * LineSegmentWidth);
  170. vertices[verticesOffset + 1] = startTransform.MultiplyPoint(shape[1] * LineSegmentWidth);
  171. vertices[verticesOffset + 2] = endTransform.MultiplyPoint(shape[1] * LineSegmentWidth);
  172. vertices[verticesOffset + 3] = endTransform.MultiplyPoint(shape[0] * LineSegmentWidth);
  173. uvs[verticesOffset + 0] = new Vector2(0f, 1f);
  174. uvs[verticesOffset + 1] = new Vector2(0f, 0f);
  175. uvs[verticesOffset + 2] = new Vector2(1f, 0f);
  176. uvs[verticesOffset + 3] = new Vector2(1f, 1f);
  177. triangleIndices[triangleIndicesOffset + 0] = verticesOffset + 0;
  178. triangleIndices[triangleIndicesOffset + 1] = verticesOffset + 1;
  179. triangleIndices[triangleIndicesOffset + 2] = verticesOffset + 2;
  180. triangleIndices[triangleIndicesOffset + 3] = verticesOffset + 0;
  181. triangleIndices[triangleIndicesOffset + 4] = verticesOffset + 2;
  182. triangleIndices[triangleIndicesOffset + 5] = verticesOffset + 3;
  183. offset++;
  184. }
  185. mesh.Clear();
  186. mesh.vertices = vertices;
  187. mesh.uv = uvs;
  188. mesh.triangles = triangleIndices;
  189. }
  190. }
  191. public void AddWaypoint(Waypoint wp)
  192. {
  193. if (!m_Waypoints.Contains(wp))
  194. {
  195. m_Waypoints.Add(wp);
  196. }
  197. }
  198. public void RemoveWaypoint(Waypoint wp)
  199. {
  200. if (m_Waypoints.Contains(wp))
  201. {
  202. m_Waypoints.Remove(wp);
  203. }
  204. }
  205. public void DeleteAllWaypoints()
  206. {
  207. foreach (Waypoint wp in m_Waypoints)
  208. {
  209. Destroy(wp.gameObject);
  210. }
  211. m_Waypoints.Clear();
  212. }
  213. public void AddTarget(IsNavigationTarget target)
  214. {
  215. if (!m_NavigationTargets.Contains(target))
  216. {
  217. m_NavigationTargets.Add(target);
  218. }
  219. }
  220. public void RemoveTarget(IsNavigationTarget target)
  221. {
  222. if (m_NavigationTargets.Contains(target))
  223. {
  224. m_NavigationTargets.Remove(target);
  225. }
  226. }
  227. public void DeleteAllNavigationTargets()
  228. {
  229. foreach (IsNavigationTarget target in m_NavigationTargets)
  230. {
  231. Destroy(target.gameObject);
  232. }
  233. m_NavigationTargets.Clear();
  234. }
  235. public List<Vector3> FindPath(Vector3 startPosition, Vector3 endPosition)
  236. {
  237. List<Vector3> pathPositions = new List<Vector3>();
  238. if(m_Waypoints.Count < 1)
  239. {
  240. return pathPositions;
  241. }
  242. //
  243. // Convert a list of Waypoints to a dictionary of Nodes and neighbours and build the graph.
  244. //
  245. Dictionary<Waypoint, Node> link = new Dictionary<Waypoint, Node>();
  246. Dictionary<Node, List<Node>> graph = new Dictionary<Node, List<Node>>();
  247. foreach (Waypoint wp in m_Waypoints)
  248. {
  249. Node n = new Node(wp.position);
  250. n.nodeName = wp.name;
  251. link[wp] = n;
  252. }
  253. foreach (Waypoint wp in link.Keys)
  254. {
  255. List<Node> nodes = new List<Node>();
  256. foreach (Waypoint neighbour in wp.neighbours)
  257. {
  258. nodes.Add(link[neighbour]);
  259. }
  260. graph[link[wp]] = nodes;
  261. }
  262. //
  263. // Add in and out Nodes for Main Camera and Navigation Target (start position and end position)
  264. //
  265. Node startNode = new Node(startPosition + new Vector3(0f, startYOffset, 0f));
  266. startNode.nodeName = "GRAPH - START NODE";
  267. Node inNode = new Node();
  268. inNode.nodeName = "GRAPH - IN NODE";
  269. EdgeToNearestPointInGraph(ref startNode, ref inNode, ref graph);
  270. Node endNode = new Node(endPosition);
  271. endNode.nodeName = "GRAPH - END NODE";
  272. Node outNode = new Node();
  273. outNode.nodeName = "GRAPH - OUT NODE";
  274. EdgeToNearestPointInGraph(ref endNode, ref outNode, ref graph);
  275. pathPositions = GetPathPositions(startNode, endNode, graph);
  276. if (pathPositions.Count < 2)
  277. {
  278. Debug.LogWarning("NAVIGATION GRAPH MANAGER: Path could not be found");
  279. return pathPositions;
  280. }
  281. return pathPositions;
  282. }
  283. private void GetClosestNode(ref Node searchNode, ref Dictionary<Node, List<Node>> graph)
  284. {
  285. float min = Mathf.Infinity;
  286. Node nearestNode = null;
  287. foreach (Node node in graph.Keys)
  288. {
  289. float dist = (node.position - searchNode.position).magnitude;
  290. if (dist < min)
  291. {
  292. min = dist;
  293. nearestNode = node;
  294. }
  295. }
  296. searchNode.neighbours.Add(nearestNode);
  297. graph[nearestNode].Add(searchNode);
  298. }
  299. private void EdgeToNearestPointInGraph(ref Node searchNode, ref Node insertNode, ref Dictionary<Node, List<Node>> graph)
  300. {
  301. Vector3 nodePos = Vector3.zero;
  302. float min = Mathf.Infinity;
  303. List<Edge> edges = new List<Edge>();
  304. foreach (KeyValuePair<Node, List<Node>> k in graph)
  305. {
  306. foreach (Node neighbour in k.Value)
  307. {
  308. Edge edge = new Edge(k.Key, neighbour);
  309. edges.Add(edge);
  310. }
  311. }
  312. Edge nearestEdge = null;
  313. foreach (Edge edge in edges)
  314. {
  315. Vector3 pos;
  316. Vector3 p = searchNode.position;
  317. Vector3 a = edge.start.position;
  318. Vector3 b = edge.end.position;
  319. Vector3 ap = new Vector3(p.x - a.x, p.y - a.y, p.z - a.z);
  320. Vector3 ab = new Vector3(b.x - a.x, b.y - a.y, b.z - a.z);
  321. float d2 = Vector3.SqrMagnitude(ab);
  322. float t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y) + (p.z - a.z) * (b.z - a.z)) / d2;
  323. if (t < 0f || t > 1f)
  324. {
  325. if (Vector3.SqrMagnitude(a - p) < Vector3.SqrMagnitude(b - p))
  326. {
  327. pos = a;
  328. }
  329. else
  330. {
  331. pos = b;
  332. }
  333. }
  334. else
  335. {
  336. pos = a + Vector3.Dot(ap, ab) / Vector3.Dot(ab, ab) * ab;
  337. }
  338. float currentDistance = Vector3.SqrMagnitude(pos - p);
  339. if (currentDistance < min)
  340. {
  341. min = currentDistance;
  342. nodePos = pos;
  343. nearestEdge = edge;
  344. }
  345. }
  346. insertNode.position = nodePos;
  347. insertNode.neighbours.Add(nearestEdge.start);
  348. insertNode.neighbours.Add(nearestEdge.end);
  349. graph[nearestEdge.start].Add(insertNode);
  350. graph[nearestEdge.end].Add(insertNode);
  351. graph[nearestEdge.start].Remove(nearestEdge.end);
  352. graph[nearestEdge.end].Remove(nearestEdge.start);
  353. searchNode.neighbours.Add(insertNode);
  354. insertNode.neighbours.Add(searchNode);
  355. graph[insertNode] = insertNode.neighbours;
  356. graph[searchNode] = searchNode.neighbours;
  357. }
  358. public List<Vector3> GetPathPositions(Node startNode, Node endNode, Dictionary<Node, List<Node>> graph)
  359. {
  360. List<Vector3> pathPositions = new List<Vector3>();
  361. List<Node> openList = new List<Node>();
  362. List<Node> closedList = new List<Node>();
  363. openList.Add(startNode);
  364. while (openList.Count > 0)
  365. {
  366. Node currNode = openList[0];
  367. for (int i = 1; i < openList.Count; i++)
  368. {
  369. if (openList[i].Cost < currNode.Cost)
  370. {
  371. currNode = openList[i];
  372. }
  373. }
  374. openList.Remove(currNode);
  375. closedList.Add(currNode);
  376. if (currNode == endNode)
  377. {
  378. pathPositions = NodesToPathPositions(startNode, endNode);
  379. return pathPositions;
  380. }
  381. foreach (Node n in graph[currNode])
  382. {
  383. if (!closedList.Contains(n))
  384. {
  385. float totalCost = currNode.Cost + n.Cost;
  386. if (totalCost < n.Cost || !openList.Contains(n))
  387. {
  388. n.Cost = totalCost;
  389. n.Parent = currNode;
  390. if (!openList.Contains(n))
  391. {
  392. openList.Add(n);
  393. }
  394. }
  395. }
  396. }
  397. }
  398. return pathPositions;
  399. }
  400. private List<Vector3> NodesToPathPositions(Node startNode, Node endNode)
  401. {
  402. List<Vector3> pathPositions = new List<Vector3>();
  403. Node currNode = endNode;
  404. while (currNode != startNode)
  405. {
  406. pathPositions.Add(currNode.position);
  407. currNode = currNode.Parent;
  408. }
  409. pathPositions.Add(startNode.position);
  410. pathPositions.Reverse();
  411. return pathPositions;
  412. }
  413. }
  414. }