PathUtils.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
  3. recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
  4. DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
  5. This software is provided 'as-is', without any express or implied
  6. warranty. In no event will the authors be held liable for any damages
  7. arising from the use of this software.
  8. Permission is granted to anyone to use this software for any purpose,
  9. including commercial applications, and to alter it and redistribute it
  10. freely, subject to the following restrictions:
  11. 1. The origin of this software must not be misrepresented; you must not
  12. claim that you wrote the original software. If you use this software
  13. in a product, an acknowledgment in the product documentation would be
  14. appreciated but is not required.
  15. 2. Altered source versions must be plainly marked as such, and must not be
  16. misrepresented as being the original software.
  17. 3. This notice may not be removed or altered from any source distribution.
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using DotRecast.Core;
  22. namespace DotRecast.Detour
  23. {
  24. public static class PathUtils
  25. {
  26. private const int MAX_STEER_POINTS = 3;
  27. public static bool GetSteerTarget(DtNavMeshQuery navQuery, RcVec3f startPos, RcVec3f endPos,
  28. float minTargetDist,
  29. List<long> path,
  30. out RcVec3f steerPos, out int steerPosFlag, out long steerPosRef)
  31. {
  32. steerPos = RcVec3f.Zero;
  33. steerPosFlag = 0;
  34. steerPosRef = 0;
  35. // Find steer target.
  36. var straightPath = new List<StraightPathItem>(MAX_STEER_POINTS);
  37. var result = navQuery.FindStraightPath(startPos, endPos, path, ref straightPath, MAX_STEER_POINTS, 0);
  38. if (result.Failed())
  39. {
  40. return false;
  41. }
  42. // Find vertex far enough to steer to.
  43. int ns = 0;
  44. while (ns < straightPath.Count)
  45. {
  46. // Stop at Off-Mesh link or when point is further than slop away.
  47. if (((straightPath[ns].flags & DtNavMeshQuery.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
  48. || !InRange(straightPath[ns].pos, startPos, minTargetDist, 1000.0f))
  49. break;
  50. ns++;
  51. }
  52. // Failed to find good point to steer to.
  53. if (ns >= straightPath.Count)
  54. return false;
  55. steerPos = straightPath[ns].pos;
  56. steerPos.y = startPos.y;
  57. steerPosFlag = straightPath[ns].flags;
  58. steerPosRef = straightPath[ns].refs;
  59. return true;
  60. }
  61. public static bool InRange(RcVec3f v1, RcVec3f v2, float r, float h)
  62. {
  63. float dx = v2.x - v1.x;
  64. float dy = v2.y - v1.y;
  65. float dz = v2.z - v1.z;
  66. return (dx * dx + dz * dz) < r * r && Math.Abs(dy) < h;
  67. }
  68. // This function checks if the path has a small U-turn, that is,
  69. // a polygon further in the path is adjacent to the first polygon
  70. // in the path. If that happens, a shortcut is taken.
  71. // This can happen if the target (T) location is at tile boundary,
  72. // and we're (S) approaching it parallel to the tile edge.
  73. // The choice at the vertex can be arbitrary,
  74. // +---+---+
  75. // |:::|:::|
  76. // +-S-+-T-+
  77. // |:::| | <-- the step can end up in here, resulting U-turn path.
  78. // +---+---+
  79. public static List<long> FixupShortcuts(List<long> path, DtNavMeshQuery navQuery)
  80. {
  81. if (path.Count < 3)
  82. {
  83. return path;
  84. }
  85. // Get connected polygons
  86. List<long> neis = new List<long>();
  87. var status = navQuery.GetAttachedNavMesh().GetTileAndPolyByRef(path[0], out var tile, out var poly);
  88. if (status.Failed())
  89. {
  90. return path;
  91. }
  92. for (int k = tile.polyLinks[poly.index]; k != DtNavMesh.DT_NULL_LINK; k = tile.links[k].next)
  93. {
  94. DtLink link = tile.links[k];
  95. if (link.refs != 0)
  96. {
  97. neis.Add(link.refs);
  98. }
  99. }
  100. // If any of the neighbour polygons is within the next few polygons
  101. // in the path, short cut to that polygon directly.
  102. const int maxLookAhead = 6;
  103. int cut = 0;
  104. for (int i = Math.Min(maxLookAhead, path.Count) - 1; i > 1 && cut == 0; i--)
  105. {
  106. for (int j = 0; j < neis.Count; j++)
  107. {
  108. if (path[i] == neis[j])
  109. {
  110. cut = i;
  111. break;
  112. }
  113. }
  114. }
  115. if (cut > 1)
  116. {
  117. List<long> shortcut = new List<long>();
  118. shortcut.Add(path[0]);
  119. shortcut.AddRange(path.GetRange(cut, path.Count - cut));
  120. return shortcut;
  121. }
  122. return path;
  123. }
  124. public static List<long> MergeCorridorStartMoved(List<long> path, List<long> visited)
  125. {
  126. int furthestPath = -1;
  127. int furthestVisited = -1;
  128. // Find furthest common polygon.
  129. for (int i = path.Count - 1; i >= 0; --i)
  130. {
  131. bool found = false;
  132. for (int j = visited.Count - 1; j >= 0; --j)
  133. {
  134. if (path[i] == visited[j])
  135. {
  136. furthestPath = i;
  137. furthestVisited = j;
  138. found = true;
  139. }
  140. }
  141. if (found)
  142. {
  143. break;
  144. }
  145. }
  146. // If no intersection found just return current path.
  147. if (furthestPath == -1 || furthestVisited == -1)
  148. {
  149. return path;
  150. }
  151. // Concatenate paths.
  152. // Adjust beginning of the buffer to include the visited.
  153. List<long> result = new List<long>();
  154. // Store visited
  155. for (int i = visited.Count - 1; i > furthestVisited; --i)
  156. {
  157. result.Add(visited[i]);
  158. }
  159. result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath));
  160. return result;
  161. }
  162. public static List<long> MergeCorridorEndMoved(List<long> path, List<long> visited)
  163. {
  164. int furthestPath = -1;
  165. int furthestVisited = -1;
  166. // Find furthest common polygon.
  167. for (int i = 0; i < path.Count; ++i)
  168. {
  169. bool found = false;
  170. for (int j = visited.Count - 1; j >= 0; --j)
  171. {
  172. if (path[i] == visited[j])
  173. {
  174. furthestPath = i;
  175. furthestVisited = j;
  176. found = true;
  177. }
  178. }
  179. if (found)
  180. {
  181. break;
  182. }
  183. }
  184. // If no intersection found just return current path.
  185. if (furthestPath == -1 || furthestVisited == -1)
  186. {
  187. return path;
  188. }
  189. // Concatenate paths.
  190. List<long> result = path.GetRange(0, furthestPath);
  191. result.AddRange(visited.GetRange(furthestVisited, visited.Count - furthestVisited));
  192. return result;
  193. }
  194. public static List<long> MergeCorridorStartShortcut(List<long> path, List<long> visited)
  195. {
  196. int furthestPath = -1;
  197. int furthestVisited = -1;
  198. // Find furthest common polygon.
  199. for (int i = path.Count - 1; i >= 0; --i)
  200. {
  201. bool found = false;
  202. for (int j = visited.Count - 1; j >= 0; --j)
  203. {
  204. if (path[i] == visited[j])
  205. {
  206. furthestPath = i;
  207. furthestVisited = j;
  208. found = true;
  209. }
  210. }
  211. if (found)
  212. {
  213. break;
  214. }
  215. }
  216. // If no intersection found just return current path.
  217. if (furthestPath == -1 || furthestVisited <= 0)
  218. {
  219. return path;
  220. }
  221. // Concatenate paths.
  222. // Adjust beginning of the buffer to include the visited.
  223. List<long> result = visited.GetRange(0, furthestVisited);
  224. result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath));
  225. return result;
  226. }
  227. }
  228. }