game_machine 0.0.11 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -1
  3. data/Gemfile.lock +32 -47
  4. data/Rakefile +0 -27
  5. data/bin/bundle_run.sh +1 -0
  6. data/bin/game_machine +29 -27
  7. data/config/cluster.conf +6 -5
  8. data/config/default.conf +164 -0
  9. data/config/game_machine.sql +33 -0
  10. data/config/game_messages.proto +87 -25
  11. data/config/gamecloud.conf +140 -0
  12. data/config/messages.proto +46 -53
  13. data/config/test.conf +149 -0
  14. data/game_machine.gemspec +10 -5
  15. data/games/boot.rb +3 -0
  16. data/games/example/data/game_data.yml +4 -4
  17. data/games/example/lib/aggressive_npc.rb +1 -1
  18. data/games/example/lib/game.rb +1 -2
  19. data/games/example/lib/player_register.rb +1 -1
  20. data/games/routes.rb +9 -0
  21. data/games/tutorial/boot.rb +12 -0
  22. data/games/tutorial/item_manager.rb +256 -0
  23. data/games/tutorial/object_store.rb +55 -0
  24. data/games/tutorial/seed.rb +52 -0
  25. data/games/tutorial/sql_store.rb +30 -0
  26. data/java/project/build.gradle +134 -0
  27. data/java/project/component.erb +719 -0
  28. data/java/{gradle.properties → project/gradle.properties} +1 -1
  29. data/java/project/gradle/wrapper/gradle-wrapper.jar +0 -0
  30. data/java/{gradle → project/gradle}/wrapper/gradle-wrapper.properties +2 -2
  31. data/java/{gradlew → project/gradlew} +0 -0
  32. data/java/{gradlew.bat → project/gradlew.bat} +0 -0
  33. data/java/project/local_lib/AdminUi.jar +0 -0
  34. data/java/{local_lib/protostuff-compiler-1.0.7-jarjar.jar → project/local_lib/protostuff-compiler-1.0.8-jarjar.jar} +0 -0
  35. data/java/project/local_lib/sigar/libsigar-amd64-freebsd-6.so +0 -0
  36. data/java/project/local_lib/sigar/libsigar-amd64-linux.so +0 -0
  37. data/java/project/local_lib/sigar/libsigar-amd64-solaris.so +0 -0
  38. data/java/project/local_lib/sigar/libsigar-ia64-hpux-11.sl +0 -0
  39. data/java/project/local_lib/sigar/libsigar-ia64-linux.so +0 -0
  40. data/java/project/local_lib/sigar/libsigar-pa-hpux-11.sl +0 -0
  41. data/java/project/local_lib/sigar/libsigar-ppc-aix-5.so +0 -0
  42. data/java/project/local_lib/sigar/libsigar-ppc-linux.so +0 -0
  43. data/java/project/local_lib/sigar/libsigar-ppc64-aix-5.so +0 -0
  44. data/java/project/local_lib/sigar/libsigar-ppc64-linux.so +0 -0
  45. data/java/project/local_lib/sigar/libsigar-s390x-linux.so +0 -0
  46. data/java/project/local_lib/sigar/libsigar-sparc-solaris.so +0 -0
  47. data/java/project/local_lib/sigar/libsigar-sparc64-solaris.so +0 -0
  48. data/java/project/local_lib/sigar/libsigar-universal-macosx.dylib +0 -0
  49. data/java/project/local_lib/sigar/libsigar-universal64-macosx.dylib +0 -0
  50. data/java/project/local_lib/sigar/libsigar-x86-freebsd-5.so +0 -0
  51. data/java/project/local_lib/sigar/libsigar-x86-freebsd-6.so +0 -0
  52. data/java/project/local_lib/sigar/libsigar-x86-linux.so +0 -0
  53. data/java/project/local_lib/sigar/libsigar-x86-solaris.so +0 -0
  54. data/java/project/local_lib/sigar/sigar-amd64-winnt.dll +0 -0
  55. data/java/project/local_lib/sigar/sigar-x86-winnt.dll +0 -0
  56. data/java/project/local_lib/sigar/sigar-x86-winnt.lib +0 -0
  57. data/java/project/model.erb +99 -0
  58. data/java/{settings.gradle → project/settings.gradle} +0 -0
  59. data/java/project/src/main/java/com/game_machine/authentication/DefaultAuthenticator.java +28 -0
  60. data/java/project/src/main/java/com/game_machine/authentication/PlayerAuthenticator.java +6 -0
  61. data/java/project/src/main/java/com/game_machine/authentication/PublicAuthenticator.java +20 -0
  62. data/java/{src → project/src}/main/java/com/game_machine/core/ActorFactory.java +0 -0
  63. data/java/{src → project/src}/main/java/com/game_machine/core/ActorUtil.java +13 -0
  64. data/java/project/src/main/java/com/game_machine/core/AuthorizedPlayers.java +23 -0
  65. data/java/project/src/main/java/com/game_machine/core/ClientMessageDecoder.java +36 -0
  66. data/java/project/src/main/java/com/game_machine/core/ClientMessageEncoder.java +19 -0
  67. data/java/project/src/main/java/com/game_machine/core/CloudClient.java +298 -0
  68. data/java/{src → project/src}/main/java/com/game_machine/core/CommandProxy.java +0 -0
  69. data/java/project/src/main/java/com/game_machine/core/Commands.java +20 -0
  70. data/java/project/src/main/java/com/game_machine/core/DatastoreCommands.java +43 -0
  71. data/java/project/src/main/java/com/game_machine/core/DbConnectionPool.java +72 -0
  72. data/java/project/src/main/java/com/game_machine/core/DefaultMovementVerifier.java +56 -0
  73. data/java/{src → project/src}/main/java/com/game_machine/core/EntitySerializer.java +0 -0
  74. data/java/project/src/main/java/com/game_machine/core/EntityTracking.java +119 -0
  75. data/java/{src → project/src}/main/java/com/game_machine/core/EventStreamHandler.java +1 -1
  76. data/java/project/src/main/java/com/game_machine/core/GameActor.java +73 -0
  77. data/java/project/src/main/java/com/game_machine/core/GameMachineLoader.java +43 -0
  78. data/java/project/src/main/java/com/game_machine/core/GameMessageActor.java +44 -0
  79. data/java/project/src/main/java/com/game_machine/core/Grid.java +255 -0
  80. data/java/{src → project/src}/main/java/com/game_machine/core/GridValue.java +0 -0
  81. data/java/project/src/main/java/com/game_machine/core/Hashring.java +66 -0
  82. data/java/{src → project/src}/main/java/com/game_machine/core/IActorFactory.java +0 -0
  83. data/java/project/src/main/java/com/game_machine/core/LocalLinkedBuffer.java +20 -0
  84. data/java/project/src/main/java/com/game_machine/core/MessageGateway.java +120 -0
  85. data/java/project/src/main/java/com/game_machine/core/MessagePersister.java +26 -0
  86. data/java/project/src/main/java/com/game_machine/core/MonoProxy.java +39 -0
  87. data/java/project/src/main/java/com/game_machine/core/MovementVerifier.java +7 -0
  88. data/java/{src → project/src}/main/java/com/game_machine/core/NetMessage.java +10 -6
  89. data/java/project/src/main/java/com/game_machine/core/PersistentMessage.java +9 -0
  90. data/java/project/src/main/java/com/game_machine/core/PlayerCommands.java +31 -0
  91. data/java/project/src/main/java/com/game_machine/core/TcpServer.java +100 -0
  92. data/java/project/src/main/java/com/game_machine/core/TcpServerHandler.java +54 -0
  93. data/java/project/src/main/java/com/game_machine/core/TcpServerInitializer.java +32 -0
  94. data/java/project/src/main/java/com/game_machine/core/UdpClient.java +86 -0
  95. data/java/{src → project/src}/main/java/com/game_machine/core/UdpServer.java +18 -27
  96. data/java/{src → project/src}/main/java/com/game_machine/core/UdpServerHandler.java +23 -26
  97. data/java/project/src/main/java/com/game_machine/core/Vector3.java +159 -0
  98. data/java/project/src/main/java/com/game_machine/orm/models/PlayerItem.java +118 -0
  99. data/java/project/src/main/java/com/game_machine/orm/models/TestObject.java +110 -0
  100. data/java/project/src/main/java/com/game_machine/tutorial/LootGenerator.java +26 -0
  101. data/java/{src → project/src}/main/resources/game_machine.java.stg +3 -1
  102. data/java/project/src/main/resources/logback.properties +13 -0
  103. data/java/project/src/main/resources/logback.xml +76 -0
  104. data/java/{src → project/src}/main/resources/protostuff.properties +0 -0
  105. data/java/src/main/java/game/MyGameActor.java +26 -0
  106. data/lib/game_machine.rb +17 -16
  107. data/lib/game_machine/actor.rb +1 -1
  108. data/lib/game_machine/actor/base.rb +8 -31
  109. data/lib/game_machine/actor/builder.rb +5 -6
  110. data/lib/game_machine/actor/game_actor.rb +55 -0
  111. data/lib/game_machine/actor/reloadable.rb +6 -1
  112. data/lib/game_machine/akka.rb +26 -32
  113. data/lib/game_machine/app_config.rb +39 -26
  114. data/lib/game_machine/application.rb +56 -62
  115. data/lib/game_machine/client_manager.rb +14 -8
  116. data/lib/game_machine/cloud_updater.rb +51 -0
  117. data/lib/game_machine/cluster_monitor.rb +3 -3
  118. data/lib/game_machine/commands.rb +1 -1
  119. data/lib/game_machine/commands/misc_commands.rb +4 -8
  120. data/lib/game_machine/commands/player_commands.rb +8 -0
  121. data/lib/game_machine/console.rb +1 -0
  122. data/lib/game_machine/console/build.rb +57 -24
  123. data/lib/game_machine/console/bundle.rb +95 -0
  124. data/lib/game_machine/console/deploy.rb +30 -0
  125. data/lib/game_machine/console/install.rb +70 -36
  126. data/lib/game_machine/console/server.rb +2 -69
  127. data/lib/game_machine/data_store.rb +111 -15
  128. data/lib/game_machine/data_stores/couchbase.rb +8 -3
  129. data/lib/game_machine/data_stores/gamecloud.rb +93 -0
  130. data/lib/game_machine/data_stores/jdbc.rb +98 -0
  131. data/lib/game_machine/default_handlers.rb +2 -0
  132. data/lib/game_machine/default_handlers/team_handler.rb +51 -0
  133. data/lib/game_machine/default_handlers/zone_manager.rb +30 -0
  134. data/lib/game_machine/endpoints.rb +0 -4
  135. data/lib/game_machine/endpoints/udp_incoming.rb +13 -5
  136. data/lib/game_machine/endpoints/udp_outgoing.rb +15 -9
  137. data/lib/game_machine/game_systems.rb +0 -2
  138. data/lib/game_machine/game_systems/agents/controller.rb +2 -2
  139. data/lib/game_machine/game_systems/entity_tracking.rb +0 -3
  140. data/lib/game_machine/game_systems/region_manager.rb +3 -2
  141. data/lib/game_machine/game_systems/region_service.rb +2 -2
  142. data/lib/game_machine/game_systems/remote_echo.rb +10 -0
  143. data/lib/game_machine/game_systems/team_manager.rb +2 -11
  144. data/lib/game_machine/grid.rb +5 -18
  145. data/lib/game_machine/handlers/authentication.rb +1 -9
  146. data/lib/game_machine/handlers/game.rb +27 -2
  147. data/lib/game_machine/handlers/player_authentication.rb +87 -0
  148. data/lib/game_machine/handlers/request.rb +9 -11
  149. data/lib/game_machine/hocon_config.rb +81 -0
  150. data/lib/game_machine/java_lib.rb +14 -1
  151. data/lib/game_machine/logger.rb +10 -23
  152. data/lib/game_machine/models.rb +1 -0
  153. data/lib/game_machine/mono_server.rb +6 -1
  154. data/lib/game_machine/object_db.rb +12 -6
  155. data/lib/game_machine/protobuf.rb +1 -1
  156. data/lib/game_machine/protobuf/game_messages.rb +13 -3
  157. data/lib/game_machine/protobuf/generate.rb +107 -5
  158. data/lib/game_machine/restart_watcher.rb +1 -1
  159. data/lib/game_machine/routes.rb +23 -0
  160. data/lib/game_machine/scheduler.rb +1 -1
  161. data/lib/game_machine/securerandom.rb +2 -0
  162. data/lib/game_machine/system_stats.rb +28 -7
  163. data/lib/game_machine/version.rb +1 -1
  164. data/lib/game_machine/wavefront_ext.rb +47 -0
  165. data/lib/game_machine/write_behind_cache.rb +24 -9
  166. data/mono/server/Makefile +1 -1
  167. data/mono/server/Newtonsoft.Json.dll +0 -0
  168. data/mono/server/build.bat +1 -1
  169. data/mono/server/callable.cs +9 -0
  170. data/mono/server/echo.cs +17 -0
  171. data/mono/server/message_router.cs +16 -23
  172. data/mono/server/messages.cs +1792 -417
  173. data/mono/server/protobuf-net.dll +0 -0
  174. data/mono/server/server.cs +120 -0
  175. data/mono/server/server.exe +0 -0
  176. data/pathfinding/astar.cpp +149 -0
  177. data/pathfinding/build.sh +6 -0
  178. data/pathfinding/build.txt +16 -0
  179. data/pathfinding/crowd.cpp +194 -0
  180. data/pathfinding/include/astar.h +49 -0
  181. data/pathfinding/include/common.h +5 -0
  182. data/pathfinding/include/crowd.h +43 -0
  183. data/pathfinding/include/micropather.h +511 -0
  184. data/pathfinding/include/navmesh.h +114 -0
  185. data/pathfinding/include/pathfinder.h +24 -0
  186. data/pathfinding/main.cpp +108 -17
  187. data/pathfinding/micropather.cpp +1062 -0
  188. data/pathfinding/navmesh.cpp +408 -0
  189. data/pathfinding/overrides/DetourCrowd.cpp +1446 -0
  190. data/pathfinding/overrides/DetourNavMeshQuery.cpp +3551 -0
  191. data/pathfinding/overrides/DetourNavMeshQuery.h +538 -0
  192. data/pathfinding/pathfinder.cpp +117 -0
  193. data/pathfinding/{bin → premake}/premake4 +0 -0
  194. data/pathfinding/premake/premake4.exe +0 -0
  195. data/pathfinding/premake4.lua +12 -3
  196. data/spec/actor/actor_spec.rb +0 -7
  197. data/spec/client_manager_spec.rb +1 -1
  198. data/spec/couchproxy_spec.rb +38 -0
  199. data/spec/entity_persistence_spec.rb +129 -0
  200. data/spec/game_systems/team_manager_spec.rb +2 -2
  201. data/spec/hashring_spec.rb +17 -39
  202. data/spec/java_grid_spec.rb +0 -2
  203. data/spec/misc_spec.rb +111 -0
  204. data/spec/mono_spec.rb +50 -3
  205. data/spec/reliable_message_spec.rb +38 -0
  206. data/spec/spec_helper.rb +4 -4
  207. data/spec/spec_helper_minimal.rb +10 -0
  208. data/web/app.rb +108 -86
  209. data/web/config/trinidad.yml +1 -0
  210. data/web/views/add_player.erb +25 -0
  211. data/web/views/index.erb +0 -0
  212. data/web/views/layout.erb +48 -0
  213. data/web/views/login.erb +25 -0
  214. data/web/views/players.erb +24 -0
  215. metadata +209 -94
  216. data/config/config.example.yml +0 -100
  217. data/config/regions.example.yml +0 -9
  218. data/games/example/lib/authentication_handler.rb +0 -69
  219. data/games/models.rb +0 -3
  220. data/games/models/clan_member.rb +0 -8
  221. data/games/models/clan_profile.rb +0 -9
  222. data/games/models/player.rb +0 -7
  223. data/games/plugins.rb +0 -1
  224. data/games/plugins/team_handler.rb +0 -49
  225. data/games/preload.rb +0 -13
  226. data/java/.gitignore +0 -1
  227. data/java/build.gradle +0 -95
  228. data/java/component.erb +0 -396
  229. data/java/gradle/wrapper/gradle-wrapper.jar +0 -0
  230. data/java/src/main/java/com/game_machine/core/GameMachineLoader.java +0 -25
  231. data/java/src/main/java/com/game_machine/core/Grid.java +0 -195
  232. data/java/src/main/resources/logback.xml +0 -14
  233. data/java/src/main/resources/logging.properties +0 -3
  234. data/lib/game_machine/actor/mono_actor.rb +0 -89
  235. data/lib/game_machine/auth_handlers/base.rb +0 -21
  236. data/lib/game_machine/auth_handlers/public.rb +0 -34
  237. data/lib/game_machine/endpoints/mono_gateway.rb +0 -87
  238. data/lib/game_machine/endpoints/tcp.rb +0 -51
  239. data/lib/game_machine/endpoints/tcp_handler.rb +0 -75
  240. data/lib/game_machine/endpoints/udp.rb +0 -88
  241. data/lib/game_machine/game_loader.rb +0 -46
  242. data/lib/game_machine/game_systems/region_settings.rb +0 -13
  243. data/lib/game_machine/hashring.rb +0 -48
  244. data/lib/game_machine/settings.rb +0 -11
  245. data/mono/server/actor.cs +0 -37
  246. data/mono/server/iactor.cs +0 -11
  247. data/mono/server/message_util.cs +0 -29
  248. data/mono/server/proxy_client.cs +0 -73
  249. data/mono/server/proxy_server.cs +0 -30
  250. data/mono/server/test_actor.cs +0 -33
  251. data/pathfinding/include/pathfind.h +0 -167
  252. data/pathfinding/pathfind.cpp +0 -174
  253. data/pathfinding/pathfinder.cs +0 -66
  254. data/script/server.sh +0 -109
  255. data/script/watch.sh +0 -11
  256. data/spec/commands/navigation_commands_spec.rb +0 -51
  257. data/spec/game_systems/entity_tracking_spec.rb +0 -64
  258. data/spec/navigation/detour_navmesh_spec.rb +0 -34
  259. data/spec/navigation/detour_path_spec.rb +0 -25
  260. data/spec/udp_server_spec.rb +0 -10
  261. data/web/controllers/auth_controller.rb +0 -19
  262. data/web/controllers/base_controller.rb +0 -16
  263. data/web/controllers/index_controller.rb +0 -7
  264. data/web/controllers/log_controller.rb +0 -47
  265. data/web/controllers/messages_controller.rb +0 -59
  266. data/web/controllers/player_register_controller.rb +0 -15
  267. data/web/views/game_messages.haml +0 -45
  268. data/web/views/index.haml +0 -6
  269. data/web/views/layout.haml +0 -41
  270. data/web/views/logs.haml +0 -32
  271. data/web/views/player_register.haml +0 -22
  272. data/web/views/player_registered.haml +0 -2
  273. data/web/views/register_layout.haml +0 -22
  274. data/web/views/restart.haml +0 -35
@@ -0,0 +1,3551 @@
1
+ //
2
+ // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
3
+ //
4
+ // This software is provided 'as-is', without any express or implied
5
+ // warranty. In no event will the authors be held liable for any damages
6
+ // arising from the use of this software.
7
+ // Permission is granted to anyone to use this software for any purpose,
8
+ // including commercial applications, and to alter it and redistribute it
9
+ // freely, subject to the following restrictions:
10
+ // 1. The origin of this software must not be misrepresented; you must not
11
+ // claim that you wrote the original software. If you use this software
12
+ // in a product, an acknowledgment in the product documentation would be
13
+ // appreciated but is not required.
14
+ // 2. Altered source versions must be plainly marked as such, and must not be
15
+ // misrepresented as being the original software.
16
+ // 3. This notice may not be removed or altered from any source distribution.
17
+ //
18
+ #include <cstdlib>
19
+ #include <stdio.h>
20
+ #include <float.h>
21
+ #include <string.h>
22
+ #include "DetourNavMeshQuery.h"
23
+ #include "DetourNavMesh.h"
24
+ #include "DetourNode.h"
25
+ #include "DetourCommon.h"
26
+ #include "DetourMath.h"
27
+ #include "DetourAlloc.h"
28
+ #include "DetourAssert.h"
29
+ #include <new>
30
+
31
+ /// @class dtQueryFilter
32
+ ///
33
+ /// <b>The Default Implementation</b>
34
+ ///
35
+ /// At construction: All area costs default to 1.0. All flags are included
36
+ /// and none are excluded.
37
+ ///
38
+ /// If a polygon has both an include and an exclude flag, it will be excluded.
39
+ ///
40
+ /// The way filtering works, a navigation mesh polygon must have at least one flag
41
+ /// set to ever be considered by a query. So a polygon with no flags will never
42
+ /// be considered.
43
+ ///
44
+ /// Setting the include flags to 0 will result in all polygons being excluded.
45
+ ///
46
+ /// <b>Custom Implementations</b>
47
+ ///
48
+ /// DT_VIRTUAL_QUERYFILTER must be defined in order to extend this class.
49
+ ///
50
+ /// Implement a custom query filter by overriding the virtual passFilter()
51
+ /// and getCost() functions. If this is done, both functions should be as
52
+ /// fast as possible. Use cached local copies of data rather than accessing
53
+ /// your own objects where possible.
54
+ ///
55
+ /// Custom implementations do not need to adhere to the flags or cost logic
56
+ /// used by the default implementation.
57
+ ///
58
+ /// In order for A* searches to work properly, the cost should be proportional to
59
+ /// the travel distance. Implementing a cost modifier less than 1.0 is likely
60
+ /// to lead to problems during pathfinding.
61
+ ///
62
+ /// @see dtNavMeshQuery
63
+
64
+ dtQueryFilter::dtQueryFilter() :
65
+ m_includeFlags(0xffff),
66
+ m_excludeFlags(0)
67
+ {
68
+ for (int i = 0; i < DT_MAX_AREAS; ++i)
69
+ m_areaCost[i] = 1.0f;
70
+ }
71
+
72
+ #ifdef DT_VIRTUAL_QUERYFILTER
73
+ bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
74
+ const dtMeshTile* /*tile*/,
75
+ const dtPoly* poly) const
76
+ {
77
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
78
+ }
79
+
80
+ float dtQueryFilter::getCost(const float* pa, const float* pb,
81
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
82
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
83
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
84
+ {
85
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
86
+ }
87
+
88
+ bool dtQueryFilter::isPassable(const float* pa, const float* pb) const
89
+ {
90
+ return true;
91
+ }
92
+ #else
93
+ inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
94
+ const dtMeshTile* /*tile*/,
95
+ const dtPoly* poly) const
96
+ {
97
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
98
+ }
99
+
100
+ inline float dtQueryFilter::getCost(const float* pa, const float* pb,
101
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
102
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
103
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
104
+ {
105
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
106
+ }
107
+ #endif
108
+
109
+ static const float H_SCALE = 0.999f; // Search heuristic scale.
110
+
111
+
112
+ dtNavMeshQuery* dtAllocNavMeshQuery()
113
+ {
114
+ void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM);
115
+ if (!mem) return 0;
116
+ return new(mem) dtNavMeshQuery;
117
+ }
118
+
119
+ void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
120
+ {
121
+ if (!navmesh) return;
122
+ navmesh->~dtNavMeshQuery();
123
+ dtFree(navmesh);
124
+ }
125
+
126
+ //////////////////////////////////////////////////////////////////////////////////////////
127
+
128
+ /// @class dtNavMeshQuery
129
+ ///
130
+ /// For methods that support undersized buffers, if the buffer is too small
131
+ /// to hold the entire result set the return status of the method will include
132
+ /// the #DT_BUFFER_TOO_SMALL flag.
133
+ ///
134
+ /// Constant member functions can be used by multiple clients without side
135
+ /// effects. (E.g. No change to the closed list. No impact on an in-progress
136
+ /// sliced path query. Etc.)
137
+ ///
138
+ /// Walls and portals: A @e wall is a polygon segment that is
139
+ /// considered impassable. A @e portal is a passable segment between polygons.
140
+ /// A portal may be treated as a wall based on the dtQueryFilter used for a query.
141
+ ///
142
+ /// @see dtNavMesh, dtQueryFilter, #dtAllocNavMeshQuery(), #dtAllocNavMeshQuery()
143
+
144
+ dtNavMeshQuery::dtNavMeshQuery() :
145
+ m_nav(0),
146
+ m_tinyNodePool(0),
147
+ m_nodePool(0),
148
+ m_openList(0)
149
+ {
150
+ memset(&m_query, 0, sizeof(dtQueryData));
151
+ }
152
+
153
+ dtNavMeshQuery::~dtNavMeshQuery()
154
+ {
155
+ if (m_tinyNodePool)
156
+ m_tinyNodePool->~dtNodePool();
157
+ if (m_nodePool)
158
+ m_nodePool->~dtNodePool();
159
+ if (m_openList)
160
+ m_openList->~dtNodeQueue();
161
+ dtFree(m_tinyNodePool);
162
+ dtFree(m_nodePool);
163
+ dtFree(m_openList);
164
+ }
165
+
166
+ /// @par
167
+ ///
168
+ /// Must be the first function called after construction, before other
169
+ /// functions are used.
170
+ ///
171
+ /// This function can be used multiple times.
172
+ dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
173
+ {
174
+ m_nav = nav;
175
+
176
+ if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
177
+ {
178
+ if (m_nodePool)
179
+ {
180
+ m_nodePool->~dtNodePool();
181
+ dtFree(m_nodePool);
182
+ m_nodePool = 0;
183
+ }
184
+ m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
185
+ if (!m_nodePool)
186
+ return DT_FAILURE | DT_OUT_OF_MEMORY;
187
+ }
188
+ else
189
+ {
190
+ m_nodePool->clear();
191
+ }
192
+
193
+ if (!m_tinyNodePool)
194
+ {
195
+ m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32);
196
+ if (!m_tinyNodePool)
197
+ return DT_FAILURE | DT_OUT_OF_MEMORY;
198
+ }
199
+ else
200
+ {
201
+ m_tinyNodePool->clear();
202
+ }
203
+
204
+ // TODO: check the open list size too.
205
+ if (!m_openList || m_openList->getCapacity() < maxNodes)
206
+ {
207
+ if (m_openList)
208
+ {
209
+ m_openList->~dtNodeQueue();
210
+ dtFree(m_openList);
211
+ m_openList = 0;
212
+ }
213
+ m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes);
214
+ if (!m_openList)
215
+ return DT_FAILURE | DT_OUT_OF_MEMORY;
216
+ }
217
+ else
218
+ {
219
+ m_openList->clear();
220
+ }
221
+
222
+ return DT_SUCCESS;
223
+ }
224
+
225
+ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
226
+ dtPolyRef* randomRef, float* randomPt) const
227
+ {
228
+ dtAssert(m_nav);
229
+
230
+ // Randomly pick one tile. Assume that all tiles cover roughly the same area.
231
+ const dtMeshTile* tile = 0;
232
+ float tsum = 0.0f;
233
+ for (int i = 0; i < m_nav->getMaxTiles(); i++)
234
+ {
235
+ const dtMeshTile* t = m_nav->getTile(i);
236
+ if (!t || !t->header) continue;
237
+
238
+ // Choose random tile using reservoi sampling.
239
+ const float area = 1.0f; // Could be tile area too.
240
+ tsum += area;
241
+ const float u = frand();
242
+ if (u*tsum <= area)
243
+ tile = t;
244
+ }
245
+ if (!tile)
246
+ return DT_FAILURE;
247
+
248
+ // Randomly pick one polygon weighted by polygon area.
249
+ const dtPoly* poly = 0;
250
+ dtPolyRef polyRef = 0;
251
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
252
+
253
+ float areaSum = 0.0f;
254
+ for (int i = 0; i < tile->header->polyCount; ++i)
255
+ {
256
+ const dtPoly* p = &tile->polys[i];
257
+ // Do not return off-mesh connection polygons.
258
+ if (p->getType() != DT_POLYTYPE_GROUND)
259
+ continue;
260
+ // Must pass filter
261
+ const dtPolyRef ref = base | (dtPolyRef)i;
262
+ if (!filter->passFilter(ref, tile, p))
263
+ continue;
264
+
265
+ // Calc area of the polygon.
266
+ float polyArea = 0.0f;
267
+ for (int j = 2; j < p->vertCount; ++j)
268
+ {
269
+ const float* va = &tile->verts[p->verts[0]*3];
270
+ const float* vb = &tile->verts[p->verts[j-1]*3];
271
+ const float* vc = &tile->verts[p->verts[j]*3];
272
+ polyArea += dtTriArea2D(va,vb,vc);
273
+ }
274
+
275
+ // Choose random polygon weighted by area, using reservoi sampling.
276
+ areaSum += polyArea;
277
+ const float u = frand();
278
+ if (u*areaSum <= polyArea)
279
+ {
280
+ poly = p;
281
+ polyRef = ref;
282
+ }
283
+ }
284
+
285
+ if (!poly)
286
+ return DT_FAILURE;
287
+
288
+ // Randomly pick point on polygon.
289
+ const float* v = &tile->verts[poly->verts[0]*3];
290
+ float verts[3*DT_VERTS_PER_POLYGON];
291
+ float areas[DT_VERTS_PER_POLYGON];
292
+ dtVcopy(&verts[0*3],v);
293
+ for (int j = 1; j < poly->vertCount; ++j)
294
+ {
295
+ v = &tile->verts[poly->verts[j]*3];
296
+ dtVcopy(&verts[j*3],v);
297
+ }
298
+
299
+ const float s = frand();
300
+ const float t = frand();
301
+
302
+ float pt[3];
303
+ dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
304
+
305
+ float h = 0.0f;
306
+ dtStatus status = getPolyHeight(polyRef, pt, &h);
307
+ if (dtStatusFailed(status))
308
+ return status;
309
+ pt[1] = h;
310
+
311
+ dtVcopy(randomPt, pt);
312
+ *randomRef = polyRef;
313
+
314
+ return DT_SUCCESS;
315
+ }
316
+
317
+ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
318
+ const dtQueryFilter* filter, float (*frand)(),
319
+ dtPolyRef* randomRef, float* randomPt) const
320
+ {
321
+ dtAssert(m_nav);
322
+ dtAssert(m_nodePool);
323
+ dtAssert(m_openList);
324
+
325
+ // Validate input
326
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
327
+ return DT_FAILURE | DT_INVALID_PARAM;
328
+
329
+ const dtMeshTile* startTile = 0;
330
+ const dtPoly* startPoly = 0;
331
+ m_nav->getTileAndPolyByRefUnsafe(startRef, &startTile, &startPoly);
332
+ if (!filter->passFilter(startRef, startTile, startPoly))
333
+ return DT_FAILURE | DT_INVALID_PARAM;
334
+
335
+ m_nodePool->clear();
336
+ m_openList->clear();
337
+
338
+ dtNode* startNode = m_nodePool->getNode(startRef);
339
+ dtVcopy(startNode->pos, centerPos);
340
+ startNode->pidx = 0;
341
+ startNode->cost = 0;
342
+ startNode->total = 0;
343
+ startNode->id = startRef;
344
+ startNode->flags = DT_NODE_OPEN;
345
+ m_openList->push(startNode);
346
+
347
+ dtStatus status = DT_SUCCESS;
348
+
349
+ const float radiusSqr = dtSqr(radius);
350
+ float areaSum = 0.0f;
351
+
352
+ const dtMeshTile* randomTile = 0;
353
+ const dtPoly* randomPoly = 0;
354
+ dtPolyRef randomPolyRef = 0;
355
+
356
+ while (!m_openList->empty())
357
+ {
358
+ dtNode* bestNode = m_openList->pop();
359
+ bestNode->flags &= ~DT_NODE_OPEN;
360
+ bestNode->flags |= DT_NODE_CLOSED;
361
+
362
+ // Get poly and tile.
363
+ // The API input has been cheked already, skip checking internal data.
364
+ const dtPolyRef bestRef = bestNode->id;
365
+ const dtMeshTile* bestTile = 0;
366
+ const dtPoly* bestPoly = 0;
367
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
368
+
369
+ // Place random locations on on ground.
370
+ if (bestPoly->getType() == DT_POLYTYPE_GROUND)
371
+ {
372
+ // Calc area of the polygon.
373
+ float polyArea = 0.0f;
374
+ for (int j = 2; j < bestPoly->vertCount; ++j)
375
+ {
376
+ const float* va = &bestTile->verts[bestPoly->verts[0]*3];
377
+ const float* vb = &bestTile->verts[bestPoly->verts[j-1]*3];
378
+ const float* vc = &bestTile->verts[bestPoly->verts[j]*3];
379
+ polyArea += dtTriArea2D(va,vb,vc);
380
+ }
381
+ // Choose random polygon weighted by area, using reservoi sampling.
382
+ areaSum += polyArea;
383
+ const float u = frand();
384
+ if (u*areaSum <= polyArea)
385
+ {
386
+ randomTile = bestTile;
387
+ randomPoly = bestPoly;
388
+ randomPolyRef = bestRef;
389
+ }
390
+ }
391
+
392
+
393
+ // Get parent poly and tile.
394
+ dtPolyRef parentRef = 0;
395
+ const dtMeshTile* parentTile = 0;
396
+ const dtPoly* parentPoly = 0;
397
+ if (bestNode->pidx)
398
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
399
+ if (parentRef)
400
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
401
+
402
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
403
+ {
404
+ const dtLink* link = &bestTile->links[i];
405
+ dtPolyRef neighbourRef = link->ref;
406
+ // Skip invalid neighbours and do not follow back to parent.
407
+ if (!neighbourRef || neighbourRef == parentRef)
408
+ continue;
409
+
410
+ // Expand to neighbour
411
+ const dtMeshTile* neighbourTile = 0;
412
+ const dtPoly* neighbourPoly = 0;
413
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
414
+
415
+ // Do not advance if the polygon is excluded by the filter.
416
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
417
+ continue;
418
+
419
+ // Find edge and calc distance to the edge.
420
+ float va[3], vb[3];
421
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
422
+ continue;
423
+
424
+ // If the circle is not touching the next polygon, skip it.
425
+ float tseg;
426
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
427
+ if (distSqr > radiusSqr)
428
+ continue;
429
+
430
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
431
+ if (!neighbourNode)
432
+ {
433
+ status |= DT_OUT_OF_NODES;
434
+ continue;
435
+ }
436
+
437
+ if (neighbourNode->flags & DT_NODE_CLOSED)
438
+ continue;
439
+
440
+ // Cost
441
+ if (neighbourNode->flags == 0)
442
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
443
+
444
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
445
+
446
+ // The node is already in open list and the new result is worse, skip.
447
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
448
+ continue;
449
+
450
+ neighbourNode->id = neighbourRef;
451
+ neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
452
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
453
+ neighbourNode->total = total;
454
+
455
+ if (neighbourNode->flags & DT_NODE_OPEN)
456
+ {
457
+ m_openList->modify(neighbourNode);
458
+ }
459
+ else
460
+ {
461
+ neighbourNode->flags = DT_NODE_OPEN;
462
+ m_openList->push(neighbourNode);
463
+ }
464
+ }
465
+ }
466
+
467
+ if (!randomPoly)
468
+ return DT_FAILURE;
469
+
470
+ // Randomly pick point on polygon.
471
+ const float* v = &randomTile->verts[randomPoly->verts[0]*3];
472
+ float verts[3*DT_VERTS_PER_POLYGON];
473
+ float areas[DT_VERTS_PER_POLYGON];
474
+ dtVcopy(&verts[0*3],v);
475
+ for (int j = 1; j < randomPoly->vertCount; ++j)
476
+ {
477
+ v = &randomTile->verts[randomPoly->verts[j]*3];
478
+ dtVcopy(&verts[j*3],v);
479
+ }
480
+
481
+ const float s = frand();
482
+ const float t = frand();
483
+
484
+ float pt[3];
485
+ dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
486
+
487
+ float h = 0.0f;
488
+ dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
489
+ if (dtStatusFailed(status))
490
+ return stat;
491
+ pt[1] = h;
492
+
493
+ dtVcopy(randomPt, pt);
494
+ *randomRef = randomPolyRef;
495
+
496
+ return DT_SUCCESS;
497
+ }
498
+
499
+
500
+ //////////////////////////////////////////////////////////////////////////////////////////
501
+
502
+ /// @par
503
+ ///
504
+ /// Uses the detail polygons to find the surface height. (Most accurate.)
505
+ ///
506
+ /// @p pos does not have to be within the bounds of the polygon or navigation mesh.
507
+ ///
508
+ /// See closestPointOnPolyBoundary() for a limited but faster option.
509
+ ///
510
+ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
511
+ {
512
+ dtAssert(m_nav);
513
+ const dtMeshTile* tile = 0;
514
+ const dtPoly* poly = 0;
515
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
516
+ return DT_FAILURE | DT_INVALID_PARAM;
517
+ if (!tile)
518
+ return DT_FAILURE | DT_INVALID_PARAM;
519
+
520
+ // Off-mesh connections don't have detail polygons.
521
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
522
+ {
523
+ const float* v0 = &tile->verts[poly->verts[0]*3];
524
+ const float* v1 = &tile->verts[poly->verts[1]*3];
525
+ const float d0 = dtVdist(pos, v0);
526
+ const float d1 = dtVdist(pos, v1);
527
+ const float u = d0 / (d0+d1);
528
+ dtVlerp(closest, v0, v1, u);
529
+ if (posOverPoly)
530
+ *posOverPoly = false;
531
+ return DT_SUCCESS;
532
+ }
533
+
534
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
535
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
536
+
537
+ // Clamp point to be inside the polygon.
538
+ float verts[DT_VERTS_PER_POLYGON*3];
539
+ float edged[DT_VERTS_PER_POLYGON];
540
+ float edget[DT_VERTS_PER_POLYGON];
541
+ const int nv = poly->vertCount;
542
+ for (int i = 0; i < nv; ++i)
543
+ dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
544
+
545
+ dtVcopy(closest, pos);
546
+ if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
547
+ {
548
+ // Point is outside the polygon, dtClamp to nearest edge.
549
+ float dmin = FLT_MAX;
550
+ int imin = -1;
551
+ for (int i = 0; i < nv; ++i)
552
+ {
553
+ if (edged[i] < dmin)
554
+ {
555
+ dmin = edged[i];
556
+ imin = i;
557
+ }
558
+ }
559
+ const float* va = &verts[imin*3];
560
+ const float* vb = &verts[((imin+1)%nv)*3];
561
+ dtVlerp(closest, va, vb, edget[imin]);
562
+
563
+ if (posOverPoly)
564
+ *posOverPoly = false;
565
+ }
566
+ else
567
+ {
568
+ if (posOverPoly)
569
+ *posOverPoly = true;
570
+ }
571
+
572
+ // Find height at the location.
573
+ for (int j = 0; j < pd->triCount; ++j)
574
+ {
575
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
576
+ const float* v[3];
577
+ for (int k = 0; k < 3; ++k)
578
+ {
579
+ if (t[k] < poly->vertCount)
580
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
581
+ else
582
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
583
+ }
584
+ float h;
585
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
586
+ {
587
+ closest[1] = h;
588
+ break;
589
+ }
590
+ }
591
+
592
+ return DT_SUCCESS;
593
+ }
594
+
595
+ /// @par
596
+ ///
597
+ /// Much faster than closestPointOnPoly().
598
+ ///
599
+ /// If the provided position lies within the polygon's xz-bounds (above or below),
600
+ /// then @p pos and @p closest will be equal.
601
+ ///
602
+ /// The height of @p closest will be the polygon boundary. The height detail is not used.
603
+ ///
604
+ /// @p pos does not have to be within the bounds of the polybon or the navigation mesh.
605
+ ///
606
+ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const
607
+ {
608
+ dtAssert(m_nav);
609
+
610
+ const dtMeshTile* tile = 0;
611
+ const dtPoly* poly = 0;
612
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
613
+ return DT_FAILURE | DT_INVALID_PARAM;
614
+
615
+ // Collect vertices.
616
+ float verts[DT_VERTS_PER_POLYGON*3];
617
+ float edged[DT_VERTS_PER_POLYGON];
618
+ float edget[DT_VERTS_PER_POLYGON];
619
+ int nv = 0;
620
+ for (int i = 0; i < (int)poly->vertCount; ++i)
621
+ {
622
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
623
+ nv++;
624
+ }
625
+
626
+ bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget);
627
+ if (inside)
628
+ {
629
+ // Point is inside the polygon, return the point.
630
+ dtVcopy(closest, pos);
631
+ }
632
+ else
633
+ {
634
+ // Point is outside the polygon, dtClamp to nearest edge.
635
+ float dmin = FLT_MAX;
636
+ int imin = -1;
637
+ for (int i = 0; i < nv; ++i)
638
+ {
639
+ if (edged[i] < dmin)
640
+ {
641
+ dmin = edged[i];
642
+ imin = i;
643
+ }
644
+ }
645
+ const float* va = &verts[imin*3];
646
+ const float* vb = &verts[((imin+1)%nv)*3];
647
+ dtVlerp(closest, va, vb, edget[imin]);
648
+ }
649
+
650
+ return DT_SUCCESS;
651
+ }
652
+
653
+ /// @par
654
+ ///
655
+ /// Will return #DT_FAILURE if the provided position is outside the xz-bounds
656
+ /// of the polygon.
657
+ ///
658
+ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
659
+ {
660
+ dtAssert(m_nav);
661
+
662
+ const dtMeshTile* tile = 0;
663
+ const dtPoly* poly = 0;
664
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
665
+ return DT_FAILURE | DT_INVALID_PARAM;
666
+
667
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
668
+ {
669
+ const float* v0 = &tile->verts[poly->verts[0]*3];
670
+ const float* v1 = &tile->verts[poly->verts[1]*3];
671
+ const float d0 = dtVdist2D(pos, v0);
672
+ const float d1 = dtVdist2D(pos, v1);
673
+ const float u = d0 / (d0+d1);
674
+ if (height)
675
+ *height = v0[1] + (v1[1] - v0[1]) * u;
676
+ return DT_SUCCESS;
677
+ }
678
+ else
679
+ {
680
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
681
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
682
+ for (int j = 0; j < pd->triCount; ++j)
683
+ {
684
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
685
+ const float* v[3];
686
+ for (int k = 0; k < 3; ++k)
687
+ {
688
+ if (t[k] < poly->vertCount)
689
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
690
+ else
691
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
692
+ }
693
+ float h;
694
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
695
+ {
696
+ if (height)
697
+ *height = h;
698
+ return DT_SUCCESS;
699
+ }
700
+ }
701
+ }
702
+
703
+ return DT_FAILURE | DT_INVALID_PARAM;
704
+ }
705
+
706
+ /// @par
707
+ ///
708
+ /// @note If the search box does not intersect any polygons the search will
709
+ /// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check
710
+ /// @p nearestRef before using @p nearestPt.
711
+ ///
712
+ /// @warning This function is not suitable for large area searches. If the search
713
+ /// extents overlaps more than 128 polygons it may return an invalid result.
714
+ ///
715
+ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
716
+ const dtQueryFilter* filter,
717
+ dtPolyRef* nearestRef, float* nearestPt) const
718
+ {
719
+ dtAssert(m_nav);
720
+
721
+ *nearestRef = 0;
722
+
723
+ // Get nearby polygons from proximity grid.
724
+ dtPolyRef polys[128];
725
+ int polyCount = 0;
726
+ if (dtStatusFailed(queryPolygons(center, extents, filter, polys, &polyCount, 128)))
727
+ return DT_FAILURE | DT_INVALID_PARAM;
728
+
729
+ // Find nearest polygon amongst the nearby polygons.
730
+ dtPolyRef nearest = 0;
731
+ float nearestDistanceSqr = FLT_MAX;
732
+ for (int i = 0; i < polyCount; ++i)
733
+ {
734
+ dtPolyRef ref = polys[i];
735
+ float closestPtPoly[3];
736
+ float diff[3];
737
+ bool posOverPoly = false;
738
+ float d = 0;
739
+ closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly);
740
+
741
+ // If a point is directly over a polygon and closer than
742
+ // climb height, favor that instead of straight line nearest point.
743
+ dtVsub(diff, center, closestPtPoly);
744
+ if (posOverPoly)
745
+ {
746
+ const dtMeshTile* tile = 0;
747
+ const dtPoly* poly = 0;
748
+ m_nav->getTileAndPolyByRefUnsafe(polys[i], &tile, &poly);
749
+ d = dtAbs(diff[1]) - tile->header->walkableClimb;
750
+ d = d > 0 ? d*d : 0;
751
+ }
752
+ else
753
+ {
754
+ d = dtVlenSqr(diff);
755
+ }
756
+
757
+ if (d < nearestDistanceSqr)
758
+ {
759
+ if (nearestPt)
760
+ dtVcopy(nearestPt, closestPtPoly);
761
+ nearestDistanceSqr = d;
762
+ nearest = ref;
763
+ }
764
+ }
765
+
766
+ if (nearestRef)
767
+ *nearestRef = nearest;
768
+
769
+ return DT_SUCCESS;
770
+ }
771
+
772
+ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
773
+ const dtQueryFilter* filter,
774
+ dtPolyRef* polys, const int maxPolys) const
775
+ {
776
+ dtAssert(m_nav);
777
+
778
+ if (tile->bvTree)
779
+ {
780
+ const dtBVNode* node = &tile->bvTree[0];
781
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
782
+ const float* tbmin = tile->header->bmin;
783
+ const float* tbmax = tile->header->bmax;
784
+ const float qfac = tile->header->bvQuantFactor;
785
+
786
+ // Calculate quantized box
787
+ unsigned short bmin[3], bmax[3];
788
+ // dtClamp query box to world box.
789
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
790
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
791
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
792
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
793
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
794
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
795
+ // Quantize
796
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
797
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
798
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
799
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
800
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
801
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
802
+
803
+ // Traverse tree
804
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
805
+ int n = 0;
806
+ while (node < end)
807
+ {
808
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
809
+ const bool isLeafNode = node->i >= 0;
810
+
811
+ if (isLeafNode && overlap)
812
+ {
813
+ dtPolyRef ref = base | (dtPolyRef)node->i;
814
+ if (filter->passFilter(ref, tile, &tile->polys[node->i]))
815
+ {
816
+ if (n < maxPolys)
817
+ polys[n++] = ref;
818
+ }
819
+ }
820
+
821
+ if (overlap || isLeafNode)
822
+ node++;
823
+ else
824
+ {
825
+ const int escapeIndex = -node->i;
826
+ node += escapeIndex;
827
+ }
828
+ }
829
+
830
+ return n;
831
+ }
832
+ else
833
+ {
834
+ float bmin[3], bmax[3];
835
+ int n = 0;
836
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
837
+ for (int i = 0; i < tile->header->polyCount; ++i)
838
+ {
839
+ const dtPoly* p = &tile->polys[i];
840
+ // Do not return off-mesh connection polygons.
841
+ if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
842
+ continue;
843
+ // Must pass filter
844
+ const dtPolyRef ref = base | (dtPolyRef)i;
845
+ if (!filter->passFilter(ref, tile, p))
846
+ continue;
847
+ // Calc polygon bounds.
848
+ const float* v = &tile->verts[p->verts[0]*3];
849
+ dtVcopy(bmin, v);
850
+ dtVcopy(bmax, v);
851
+ for (int j = 1; j < p->vertCount; ++j)
852
+ {
853
+ v = &tile->verts[p->verts[j]*3];
854
+ dtVmin(bmin, v);
855
+ dtVmax(bmax, v);
856
+ }
857
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
858
+ {
859
+ if (n < maxPolys)
860
+ polys[n++] = ref;
861
+ }
862
+ }
863
+ return n;
864
+ }
865
+ }
866
+
867
+ /// @par
868
+ ///
869
+ /// If no polygons are found, the function will return #DT_SUCCESS with a
870
+ /// @p polyCount of zero.
871
+ ///
872
+ /// If @p polys is too small to hold the entire result set, then the array will
873
+ /// be filled to capacity. The method of choosing which polygons from the
874
+ /// full set are included in the partial result set is undefined.
875
+ ///
876
+ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
877
+ const dtQueryFilter* filter,
878
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const
879
+ {
880
+ dtAssert(m_nav);
881
+
882
+ float bmin[3], bmax[3];
883
+ dtVsub(bmin, center, extents);
884
+ dtVadd(bmax, center, extents);
885
+
886
+ // Find tiles the query touches.
887
+ int minx, miny, maxx, maxy;
888
+ m_nav->calcTileLoc(bmin, &minx, &miny);
889
+ m_nav->calcTileLoc(bmax, &maxx, &maxy);
890
+
891
+ static const int MAX_NEIS = 32;
892
+ const dtMeshTile* neis[MAX_NEIS];
893
+
894
+ int n = 0;
895
+ for (int y = miny; y <= maxy; ++y)
896
+ {
897
+ for (int x = minx; x <= maxx; ++x)
898
+ {
899
+ const int nneis = m_nav->getTilesAt(x,y,neis,MAX_NEIS);
900
+ for (int j = 0; j < nneis; ++j)
901
+ {
902
+ n += queryPolygonsInTile(neis[j], bmin, bmax, filter, polys+n, maxPolys-n);
903
+ if (n >= maxPolys)
904
+ {
905
+ *polyCount = n;
906
+ return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
907
+ }
908
+ }
909
+ }
910
+ }
911
+ *polyCount = n;
912
+
913
+ return DT_SUCCESS;
914
+ }
915
+
916
+ /// @par
917
+ ///
918
+ /// If the end polygon cannot be reached through the navigation graph,
919
+ /// the last polygon in the path will be the nearest the end polygon.
920
+ ///
921
+ /// If the path array is to small to hold the full result, it will be filled as
922
+ /// far as possible from the start polygon toward the end polygon.
923
+ ///
924
+ /// The start and end positions are used to calculate traversal costs.
925
+ /// (The y-values impact the result.)
926
+ ///
927
+ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
928
+ const float* startPos, const float* endPos,
929
+ const dtQueryFilter* filter,
930
+ dtPolyRef* path, int* pathCount, const int maxPath) const
931
+ {
932
+ dtAssert(m_nav);
933
+ dtAssert(m_nodePool);
934
+ dtAssert(m_openList);
935
+
936
+ *pathCount = 0;
937
+
938
+ if (!startRef || !endRef)
939
+ return DT_FAILURE | DT_INVALID_PARAM;
940
+
941
+ if (!maxPath)
942
+ return DT_FAILURE | DT_INVALID_PARAM;
943
+
944
+ // Validate input
945
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
946
+ return DT_FAILURE | DT_INVALID_PARAM;
947
+
948
+ if (startRef == endRef)
949
+ {
950
+ path[0] = startRef;
951
+ *pathCount = 1;
952
+ return DT_SUCCESS;
953
+ }
954
+
955
+ m_nodePool->clear();
956
+ m_openList->clear();
957
+
958
+ dtNode* startNode = m_nodePool->getNode(startRef);
959
+ dtVcopy(startNode->pos, startPos);
960
+ startNode->pidx = 0;
961
+ startNode->cost = 0;
962
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
963
+ startNode->id = startRef;
964
+ startNode->flags = DT_NODE_OPEN;
965
+ m_openList->push(startNode);
966
+
967
+ dtNode* lastBestNode = startNode;
968
+ float lastBestNodeCost = startNode->total;
969
+
970
+ dtStatus status = DT_SUCCESS;
971
+
972
+ while (!m_openList->empty())
973
+ {
974
+ // Remove node from open list and put it in closed list.
975
+ dtNode* bestNode = m_openList->pop();
976
+ bestNode->flags &= ~DT_NODE_OPEN;
977
+ bestNode->flags |= DT_NODE_CLOSED;
978
+
979
+ // Reached the goal, stop searching.
980
+ if (bestNode->id == endRef)
981
+ {
982
+ lastBestNode = bestNode;
983
+ break;
984
+ }
985
+
986
+ // Get current poly and tile.
987
+ // The API input has been cheked already, skip checking internal data.
988
+ const dtPolyRef bestRef = bestNode->id;
989
+ const dtMeshTile* bestTile = 0;
990
+ const dtPoly* bestPoly = 0;
991
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
992
+
993
+ // Get parent poly and tile.
994
+ dtPolyRef parentRef = 0;
995
+ const dtMeshTile* parentTile = 0;
996
+ const dtPoly* parentPoly = 0;
997
+ if (bestNode->pidx)
998
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
999
+ if (parentRef)
1000
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
1001
+
1002
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
1003
+ {
1004
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
1005
+
1006
+ // Skip invalid ids and do not expand back to where we came from.
1007
+ if (!neighbourRef || neighbourRef == parentRef)
1008
+ continue;
1009
+
1010
+ // Get neighbour poly and tile.
1011
+ // The API input has been cheked already, skip checking internal data.
1012
+ const dtMeshTile* neighbourTile = 0;
1013
+ const dtPoly* neighbourPoly = 0;
1014
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
1015
+
1016
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
1017
+ continue;
1018
+
1019
+ // deal explicitly with crossing tile boundaries
1020
+ unsigned char crossSide = 0;
1021
+ if (bestTile->links[i].side != 0xff)
1022
+ crossSide = bestTile->links[i].side >> 1;
1023
+
1024
+ // get the node
1025
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
1026
+ if (!neighbourNode)
1027
+ {
1028
+ status |= DT_OUT_OF_NODES;
1029
+ continue;
1030
+ }
1031
+
1032
+ // If the node is visited the first time, calculate node position.
1033
+ if (neighbourNode->flags == 0)
1034
+ {
1035
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
1036
+ neighbourRef, neighbourPoly, neighbourTile,
1037
+ neighbourNode->pos);
1038
+ }
1039
+
1040
+ const bool passable = filter->isPassable(bestNode->pos, neighbourNode->pos);
1041
+ if (!passable)
1042
+ continue;
1043
+
1044
+ // Calculate cost and heuristic.
1045
+ float cost = 0;
1046
+ float heuristic = 0;
1047
+
1048
+ // Special case for last node.
1049
+ if (neighbourRef == endRef)
1050
+ {
1051
+ // Cost
1052
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
1053
+ parentRef, parentTile, parentPoly,
1054
+ bestRef, bestTile, bestPoly,
1055
+ neighbourRef, neighbourTile, neighbourPoly);
1056
+ const float endCost = filter->getCost(neighbourNode->pos, endPos,
1057
+ bestRef, bestTile, bestPoly,
1058
+ neighbourRef, neighbourTile, neighbourPoly,
1059
+ 0, 0, 0);
1060
+
1061
+ cost = bestNode->cost + curCost + endCost;
1062
+ heuristic = 0;
1063
+ }
1064
+ else
1065
+ {
1066
+ // Cost
1067
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
1068
+ parentRef, parentTile, parentPoly,
1069
+ bestRef, bestTile, bestPoly,
1070
+ neighbourRef, neighbourTile, neighbourPoly);
1071
+ cost = bestNode->cost + curCost;
1072
+ heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE;
1073
+ }
1074
+
1075
+ const float total = cost + heuristic;
1076
+
1077
+ // The node is already in open list and the new result is worse, skip.
1078
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
1079
+ continue;
1080
+ // The node is already visited and process, and the new result is worse, skip.
1081
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
1082
+ continue;
1083
+
1084
+ // Add or update the node.
1085
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
1086
+ neighbourNode->id = neighbourRef;
1087
+ neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
1088
+ neighbourNode->cost = cost;
1089
+ neighbourNode->total = total;
1090
+
1091
+ if (neighbourNode->flags & DT_NODE_OPEN)
1092
+ {
1093
+ // Already in open, update node location.
1094
+ m_openList->modify(neighbourNode);
1095
+ }
1096
+ else
1097
+ {
1098
+ // Put the node in open list.
1099
+ neighbourNode->flags |= DT_NODE_OPEN;
1100
+ m_openList->push(neighbourNode);
1101
+ }
1102
+
1103
+ // Update nearest node to target so far.
1104
+ if (heuristic < lastBestNodeCost)
1105
+ {
1106
+ lastBestNodeCost = heuristic;
1107
+ lastBestNode = neighbourNode;
1108
+ }
1109
+ }
1110
+ }
1111
+
1112
+ if (lastBestNode->id != endRef)
1113
+ status |= DT_PARTIAL_RESULT;
1114
+
1115
+ // Reverse the path.
1116
+ dtNode* prev = 0;
1117
+ dtNode* node = lastBestNode;
1118
+ do
1119
+ {
1120
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
1121
+ node->pidx = m_nodePool->getNodeIdx(prev);
1122
+ prev = node;
1123
+ node = next;
1124
+ }
1125
+ while (node);
1126
+
1127
+ // Store path
1128
+ node = prev;
1129
+ int n = 0;
1130
+ do
1131
+ {
1132
+ path[n++] = node->id;
1133
+ if (n >= maxPath)
1134
+ {
1135
+ status |= DT_BUFFER_TOO_SMALL;
1136
+ break;
1137
+ }
1138
+ node = m_nodePool->getNodeAtIdx(node->pidx);
1139
+ }
1140
+ while (node);
1141
+
1142
+ *pathCount = n;
1143
+
1144
+ return status;
1145
+ }
1146
+
1147
+ /// @par
1148
+ ///
1149
+ /// @warning Calling any non-slice methods before calling finalizeSlicedFindPath()
1150
+ /// or finalizeSlicedFindPathPartial() may result in corrupted data!
1151
+ ///
1152
+ /// The @p filter pointer is stored and used for the duration of the sliced
1153
+ /// path query.
1154
+ ///
1155
+ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
1156
+ const float* startPos, const float* endPos,
1157
+ const dtQueryFilter* filter, const unsigned int options)
1158
+ {
1159
+ dtAssert(m_nav);
1160
+ dtAssert(m_nodePool);
1161
+ dtAssert(m_openList);
1162
+
1163
+ // Init path state.
1164
+ memset(&m_query, 0, sizeof(dtQueryData));
1165
+ m_query.status = DT_FAILURE;
1166
+ m_query.startRef = startRef;
1167
+ m_query.endRef = endRef;
1168
+ dtVcopy(m_query.startPos, startPos);
1169
+ dtVcopy(m_query.endPos, endPos);
1170
+ m_query.filter = filter;
1171
+ m_query.options = options;
1172
+ m_query.raycastLimitSqr = FLT_MAX;
1173
+
1174
+ if (!startRef || !endRef)
1175
+ return DT_FAILURE | DT_INVALID_PARAM;
1176
+
1177
+ // Validate input
1178
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
1179
+ return DT_FAILURE | DT_INVALID_PARAM;
1180
+
1181
+ // trade quality with performance?
1182
+ if (options & DT_FINDPATH_ANY_ANGLE)
1183
+ {
1184
+ // limiting to several times the character radius yields nice results. It is not sensitive
1185
+ // so it is enough to compute it from the first tile.
1186
+ const dtMeshTile* tile = m_nav->getTileByRef(startRef);
1187
+ float agentRadius = tile->header->walkableRadius;
1188
+ m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS);
1189
+ }
1190
+
1191
+ if (startRef == endRef)
1192
+ {
1193
+ m_query.status = DT_SUCCESS;
1194
+ return DT_SUCCESS;
1195
+ }
1196
+
1197
+ m_nodePool->clear();
1198
+ m_openList->clear();
1199
+
1200
+ dtNode* startNode = m_nodePool->getNode(startRef);
1201
+ dtVcopy(startNode->pos, startPos);
1202
+ startNode->pidx = 0;
1203
+ startNode->cost = 0;
1204
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
1205
+ startNode->id = startRef;
1206
+ startNode->flags = DT_NODE_OPEN;
1207
+ m_openList->push(startNode);
1208
+
1209
+ m_query.status = DT_IN_PROGRESS;
1210
+ m_query.lastBestNode = startNode;
1211
+ m_query.lastBestNodeCost = startNode->total;
1212
+
1213
+ return m_query.status;
1214
+ }
1215
+
1216
+ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
1217
+ {
1218
+ if (!dtStatusInProgress(m_query.status))
1219
+ return m_query.status;
1220
+
1221
+ // Make sure the request is still valid.
1222
+ if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef))
1223
+ {
1224
+ m_query.status = DT_FAILURE;
1225
+ return DT_FAILURE;
1226
+ }
1227
+
1228
+ dtRaycastHit rayHit;
1229
+ rayHit.maxPath = 0;
1230
+
1231
+ int iter = 0;
1232
+ while (iter < maxIter && !m_openList->empty())
1233
+ {
1234
+ iter++;
1235
+
1236
+ // Remove node from open list and put it in closed list.
1237
+ dtNode* bestNode = m_openList->pop();
1238
+ bestNode->flags &= ~DT_NODE_OPEN;
1239
+ bestNode->flags |= DT_NODE_CLOSED;
1240
+
1241
+ // Reached the goal, stop searching.
1242
+ if (bestNode->id == m_query.endRef)
1243
+ {
1244
+ m_query.lastBestNode = bestNode;
1245
+ const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
1246
+ m_query.status = DT_SUCCESS | details;
1247
+ if (doneIters)
1248
+ *doneIters = iter;
1249
+ return m_query.status;
1250
+ }
1251
+
1252
+ // Get current poly and tile.
1253
+ // The API input has been cheked already, skip checking internal data.
1254
+ const dtPolyRef bestRef = bestNode->id;
1255
+ const dtMeshTile* bestTile = 0;
1256
+ const dtPoly* bestPoly = 0;
1257
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly)))
1258
+ {
1259
+ // The polygon has disappeared during the sliced query, fail.
1260
+ m_query.status = DT_FAILURE;
1261
+ if (doneIters)
1262
+ *doneIters = iter;
1263
+ return m_query.status;
1264
+ }
1265
+
1266
+ // Get parent and grand parent poly and tile.
1267
+ dtPolyRef parentRef = 0, grandpaRef = 0;
1268
+ const dtMeshTile* parentTile = 0;
1269
+ const dtPoly* parentPoly = 0;
1270
+ dtNode* parentNode = 0;
1271
+ if (bestNode->pidx)
1272
+ {
1273
+ parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx);
1274
+ parentRef = parentNode->id;
1275
+ if (parentNode->pidx)
1276
+ grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id;
1277
+ }
1278
+ if (parentRef)
1279
+ {
1280
+ bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly));
1281
+ if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) )
1282
+ {
1283
+ // The polygon has disappeared during the sliced query, fail.
1284
+ m_query.status = DT_FAILURE;
1285
+ if (doneIters)
1286
+ *doneIters = iter;
1287
+ return m_query.status;
1288
+ }
1289
+ }
1290
+
1291
+ // decide whether to test raycast to previous nodes
1292
+ bool tryLOS = false;
1293
+ if (m_query.options & DT_FINDPATH_ANY_ANGLE)
1294
+ {
1295
+ if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr))
1296
+ tryLOS = true;
1297
+ }
1298
+
1299
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
1300
+ {
1301
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
1302
+
1303
+ // Skip invalid ids and do not expand back to where we came from.
1304
+ if (!neighbourRef || neighbourRef == parentRef)
1305
+ continue;
1306
+
1307
+ // Get neighbour poly and tile.
1308
+ // The API input has been cheked already, skip checking internal data.
1309
+ const dtMeshTile* neighbourTile = 0;
1310
+ const dtPoly* neighbourPoly = 0;
1311
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
1312
+
1313
+ if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
1314
+ continue;
1315
+
1316
+ // get the neighbor node
1317
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, 0);
1318
+ if (!neighbourNode)
1319
+ {
1320
+ m_query.status |= DT_OUT_OF_NODES;
1321
+ continue;
1322
+ }
1323
+
1324
+ // do not expand to nodes that were already visited from the same parent
1325
+ if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx)
1326
+ continue;
1327
+
1328
+ // If the node is visited the first time, calculate node position.
1329
+ if (neighbourNode->flags == 0)
1330
+ {
1331
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
1332
+ neighbourRef, neighbourPoly, neighbourTile,
1333
+ neighbourNode->pos);
1334
+ }
1335
+
1336
+ // Calculate cost and heuristic.
1337
+ float cost = 0;
1338
+ float heuristic = 0;
1339
+
1340
+ // raycast parent
1341
+ bool foundShortCut = false;
1342
+ rayHit.pathCost = rayHit.t = 0;
1343
+ if (tryLOS)
1344
+ {
1345
+ raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef);
1346
+ foundShortCut = rayHit.t >= 1.0f;
1347
+ }
1348
+
1349
+ // update move cost
1350
+ if (foundShortCut)
1351
+ {
1352
+ // shortcut found using raycast. Using shorter cost instead
1353
+ cost = parentNode->cost + rayHit.pathCost;
1354
+ }
1355
+ else
1356
+ {
1357
+ // No shortcut found.
1358
+ const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
1359
+ parentRef, parentTile, parentPoly,
1360
+ bestRef, bestTile, bestPoly,
1361
+ neighbourRef, neighbourTile, neighbourPoly);
1362
+ cost = bestNode->cost + curCost;
1363
+ }
1364
+
1365
+ // Special case for last node.
1366
+ if (neighbourRef == m_query.endRef)
1367
+ {
1368
+ const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
1369
+ bestRef, bestTile, bestPoly,
1370
+ neighbourRef, neighbourTile, neighbourPoly,
1371
+ 0, 0, 0);
1372
+
1373
+ cost = cost + endCost;
1374
+ heuristic = 0;
1375
+ }
1376
+ else
1377
+ {
1378
+ heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
1379
+ }
1380
+
1381
+ const float total = cost + heuristic;
1382
+
1383
+ // The node is already in open list and the new result is worse, skip.
1384
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
1385
+ continue;
1386
+ // The node is already visited and process, and the new result is worse, skip.
1387
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
1388
+ continue;
1389
+
1390
+ // Add or update the node.
1391
+ neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode);
1392
+ neighbourNode->id = neighbourRef;
1393
+ neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED));
1394
+ neighbourNode->cost = cost;
1395
+ neighbourNode->total = total;
1396
+ if (foundShortCut)
1397
+ neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED);
1398
+
1399
+ if (neighbourNode->flags & DT_NODE_OPEN)
1400
+ {
1401
+ // Already in open, update node location.
1402
+ m_openList->modify(neighbourNode);
1403
+ }
1404
+ else
1405
+ {
1406
+ // Put the node in open list.
1407
+ neighbourNode->flags |= DT_NODE_OPEN;
1408
+ m_openList->push(neighbourNode);
1409
+ }
1410
+
1411
+ // Update nearest node to target so far.
1412
+ if (heuristic < m_query.lastBestNodeCost)
1413
+ {
1414
+ m_query.lastBestNodeCost = heuristic;
1415
+ m_query.lastBestNode = neighbourNode;
1416
+ }
1417
+ }
1418
+ }
1419
+
1420
+ // Exhausted all nodes, but could not find path.
1421
+ if (m_openList->empty())
1422
+ {
1423
+ const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
1424
+ m_query.status = DT_SUCCESS | details;
1425
+ }
1426
+
1427
+ if (doneIters)
1428
+ *doneIters = iter;
1429
+
1430
+ return m_query.status;
1431
+ }
1432
+
1433
+ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
1434
+ {
1435
+ *pathCount = 0;
1436
+
1437
+ if (dtStatusFailed(m_query.status))
1438
+ {
1439
+ // Reset query.
1440
+ memset(&m_query, 0, sizeof(dtQueryData));
1441
+ return DT_FAILURE;
1442
+ }
1443
+
1444
+ int n = 0;
1445
+
1446
+ if (m_query.startRef == m_query.endRef)
1447
+ {
1448
+ // Special case: the search starts and ends at same poly.
1449
+ path[n++] = m_query.startRef;
1450
+ }
1451
+ else
1452
+ {
1453
+ // Reverse the path.
1454
+ dtAssert(m_query.lastBestNode);
1455
+
1456
+ if (m_query.lastBestNode->id != m_query.endRef)
1457
+ m_query.status |= DT_PARTIAL_RESULT;
1458
+
1459
+ dtNode* prev = 0;
1460
+ dtNode* node = m_query.lastBestNode;
1461
+ int prevRay = 0;
1462
+ do
1463
+ {
1464
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
1465
+ node->pidx = m_nodePool->getNodeIdx(prev);
1466
+ prev = node;
1467
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
1468
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
1469
+ prevRay = nextRay;
1470
+ node = next;
1471
+ }
1472
+ while (node);
1473
+
1474
+ // Store path
1475
+ node = prev;
1476
+ do
1477
+ {
1478
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
1479
+ dtStatus status = 0;
1480
+ if (node->flags & DT_NODE_PARENT_DETACHED)
1481
+ {
1482
+ float t, normal[3];
1483
+ int m;
1484
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
1485
+ n += m;
1486
+ // raycast ends on poly boundary and the path might include the next poly boundary.
1487
+ if (path[n-1] == next->id)
1488
+ n--; // remove to avoid duplicates
1489
+ }
1490
+ else
1491
+ {
1492
+ path[n++] = node->id;
1493
+ if (n >= maxPath)
1494
+ status = DT_BUFFER_TOO_SMALL;
1495
+ }
1496
+
1497
+ if (status & DT_STATUS_DETAIL_MASK)
1498
+ {
1499
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
1500
+ break;
1501
+ }
1502
+ node = next;
1503
+ }
1504
+ while (node);
1505
+ }
1506
+
1507
+ const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
1508
+
1509
+ // Reset query.
1510
+ memset(&m_query, 0, sizeof(dtQueryData));
1511
+
1512
+ *pathCount = n;
1513
+
1514
+ return DT_SUCCESS | details;
1515
+ }
1516
+
1517
+ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
1518
+ dtPolyRef* path, int* pathCount, const int maxPath)
1519
+ {
1520
+ *pathCount = 0;
1521
+
1522
+ if (existingSize == 0)
1523
+ {
1524
+ return DT_FAILURE;
1525
+ }
1526
+
1527
+ if (dtStatusFailed(m_query.status))
1528
+ {
1529
+ // Reset query.
1530
+ memset(&m_query, 0, sizeof(dtQueryData));
1531
+ return DT_FAILURE;
1532
+ }
1533
+
1534
+ int n = 0;
1535
+
1536
+ if (m_query.startRef == m_query.endRef)
1537
+ {
1538
+ // Special case: the search starts and ends at same poly.
1539
+ path[n++] = m_query.startRef;
1540
+ }
1541
+ else
1542
+ {
1543
+ // Find furthest existing node that was visited.
1544
+ dtNode* prev = 0;
1545
+ dtNode* node = 0;
1546
+ for (int i = existingSize-1; i >= 0; --i)
1547
+ {
1548
+ m_nodePool->findNodes(existing[i], &node, 1);
1549
+ if (node)
1550
+ break;
1551
+ }
1552
+
1553
+ if (!node)
1554
+ {
1555
+ m_query.status |= DT_PARTIAL_RESULT;
1556
+ dtAssert(m_query.lastBestNode);
1557
+ node = m_query.lastBestNode;
1558
+ }
1559
+
1560
+ // Reverse the path.
1561
+ int prevRay = 0;
1562
+ do
1563
+ {
1564
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
1565
+ node->pidx = m_nodePool->getNodeIdx(prev);
1566
+ prev = node;
1567
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
1568
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
1569
+ prevRay = nextRay;
1570
+ node = next;
1571
+ }
1572
+ while (node);
1573
+
1574
+ // Store path
1575
+ node = prev;
1576
+ do
1577
+ {
1578
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
1579
+ dtStatus status = 0;
1580
+ if (node->flags & DT_NODE_PARENT_DETACHED)
1581
+ {
1582
+ float t, normal[3];
1583
+ int m;
1584
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
1585
+ n += m;
1586
+ // raycast ends on poly boundary and the path might include the next poly boundary.
1587
+ if (path[n-1] == next->id)
1588
+ n--; // remove to avoid duplicates
1589
+ }
1590
+ else
1591
+ {
1592
+ path[n++] = node->id;
1593
+ if (n >= maxPath)
1594
+ status = DT_BUFFER_TOO_SMALL;
1595
+ }
1596
+
1597
+ if (status & DT_STATUS_DETAIL_MASK)
1598
+ {
1599
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
1600
+ break;
1601
+ }
1602
+ node = next;
1603
+ }
1604
+ while (node);
1605
+ }
1606
+
1607
+ const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
1608
+
1609
+ // Reset query.
1610
+ memset(&m_query, 0, sizeof(dtQueryData));
1611
+
1612
+ *pathCount = n;
1613
+
1614
+ return DT_SUCCESS | details;
1615
+ }
1616
+
1617
+
1618
+ dtStatus dtNavMeshQuery::appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref,
1619
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
1620
+ int* straightPathCount, const int maxStraightPath) const
1621
+ {
1622
+ if ((*straightPathCount) > 0 && dtVequal(&straightPath[((*straightPathCount)-1)*3], pos))
1623
+ {
1624
+ // The vertices are equal, update flags and poly.
1625
+ if (straightPathFlags)
1626
+ straightPathFlags[(*straightPathCount)-1] = flags;
1627
+ if (straightPathRefs)
1628
+ straightPathRefs[(*straightPathCount)-1] = ref;
1629
+ }
1630
+ else
1631
+ {
1632
+ // Append new vertex.
1633
+ dtVcopy(&straightPath[(*straightPathCount)*3], pos);
1634
+ if (straightPathFlags)
1635
+ straightPathFlags[(*straightPathCount)] = flags;
1636
+ if (straightPathRefs)
1637
+ straightPathRefs[(*straightPathCount)] = ref;
1638
+ (*straightPathCount)++;
1639
+ // If reached end of path or there is no space to append more vertices, return.
1640
+ if (flags == DT_STRAIGHTPATH_END || (*straightPathCount) >= maxStraightPath)
1641
+ {
1642
+ return DT_SUCCESS | (((*straightPathCount) >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
1643
+ }
1644
+ }
1645
+ return DT_IN_PROGRESS;
1646
+ }
1647
+
1648
+ dtStatus dtNavMeshQuery::appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
1649
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
1650
+ int* straightPathCount, const int maxStraightPath, const int options) const
1651
+ {
1652
+ const float* startPos = &straightPath[(*straightPathCount-1)*3];
1653
+ // Append or update last vertex
1654
+ dtStatus stat = 0;
1655
+ for (int i = startIdx; i < endIdx; i++)
1656
+ {
1657
+ // Calculate portal
1658
+ const dtPolyRef from = path[i];
1659
+ const dtMeshTile* fromTile = 0;
1660
+ const dtPoly* fromPoly = 0;
1661
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly)))
1662
+ return DT_FAILURE | DT_INVALID_PARAM;
1663
+
1664
+ const dtPolyRef to = path[i+1];
1665
+ const dtMeshTile* toTile = 0;
1666
+ const dtPoly* toPoly = 0;
1667
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly)))
1668
+ return DT_FAILURE | DT_INVALID_PARAM;
1669
+
1670
+ float left[3], right[3];
1671
+ if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right)))
1672
+ break;
1673
+
1674
+ if (options & DT_STRAIGHTPATH_AREA_CROSSINGS)
1675
+ {
1676
+ // Skip intersection if only area crossings are requested.
1677
+ if (fromPoly->getArea() == toPoly->getArea())
1678
+ continue;
1679
+ }
1680
+
1681
+ // Append intersection
1682
+ float s,t;
1683
+ if (dtIntersectSegSeg2D(startPos, endPos, left, right, s, t))
1684
+ {
1685
+ float pt[3];
1686
+ dtVlerp(pt, left,right, t);
1687
+
1688
+ stat = appendVertex(pt, 0, path[i+1],
1689
+ straightPath, straightPathFlags, straightPathRefs,
1690
+ straightPathCount, maxStraightPath);
1691
+ if (stat != DT_IN_PROGRESS)
1692
+ return stat;
1693
+ }
1694
+ }
1695
+ return DT_IN_PROGRESS;
1696
+ }
1697
+
1698
+ /// @par
1699
+ ///
1700
+ /// This method peforms what is often called 'string pulling'.
1701
+ ///
1702
+ /// The start position is clamped to the first polygon in the path, and the
1703
+ /// end position is clamped to the last. So the start and end positions should
1704
+ /// normally be within or very near the first and last polygons respectively.
1705
+ ///
1706
+ /// The returned polygon references represent the reference id of the polygon
1707
+ /// that is entered at the associated path position. The reference id associated
1708
+ /// with the end point will always be zero. This allows, for example, matching
1709
+ /// off-mesh link points to their representative polygons.
1710
+ ///
1711
+ /// If the provided result buffers are too small for the entire result set,
1712
+ /// they will be filled as far as possible from the start toward the end
1713
+ /// position.
1714
+ ///
1715
+ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos,
1716
+ const dtPolyRef* path, const int pathSize,
1717
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
1718
+ int* straightPathCount, const int maxStraightPath, const int options) const
1719
+ {
1720
+ dtAssert(m_nav);
1721
+
1722
+ *straightPathCount = 0;
1723
+
1724
+ if (!maxStraightPath)
1725
+ return DT_FAILURE | DT_INVALID_PARAM;
1726
+
1727
+ if (!path[0])
1728
+ return DT_FAILURE | DT_INVALID_PARAM;
1729
+
1730
+ dtStatus stat = 0;
1731
+
1732
+ // TODO: Should this be callers responsibility?
1733
+ float closestStartPos[3];
1734
+ if (dtStatusFailed(closestPointOnPolyBoundary(path[0], startPos, closestStartPos)))
1735
+ return DT_FAILURE | DT_INVALID_PARAM;
1736
+
1737
+ float closestEndPos[3];
1738
+ if (dtStatusFailed(closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos)))
1739
+ return DT_FAILURE | DT_INVALID_PARAM;
1740
+
1741
+ // Add start point.
1742
+ stat = appendVertex(closestStartPos, DT_STRAIGHTPATH_START, path[0],
1743
+ straightPath, straightPathFlags, straightPathRefs,
1744
+ straightPathCount, maxStraightPath);
1745
+ if (stat != DT_IN_PROGRESS)
1746
+ return stat;
1747
+
1748
+ if (pathSize > 1)
1749
+ {
1750
+ float portalApex[3], portalLeft[3], portalRight[3];
1751
+ dtVcopy(portalApex, closestStartPos);
1752
+ dtVcopy(portalLeft, portalApex);
1753
+ dtVcopy(portalRight, portalApex);
1754
+ int apexIndex = 0;
1755
+ int leftIndex = 0;
1756
+ int rightIndex = 0;
1757
+
1758
+ unsigned char leftPolyType = 0;
1759
+ unsigned char rightPolyType = 0;
1760
+
1761
+ dtPolyRef leftPolyRef = path[0];
1762
+ dtPolyRef rightPolyRef = path[0];
1763
+
1764
+ for (int i = 0; i < pathSize; ++i)
1765
+ {
1766
+ float left[3], right[3];
1767
+ unsigned char fromType, toType;
1768
+
1769
+ if (i+1 < pathSize)
1770
+ {
1771
+ // Next portal.
1772
+ if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
1773
+ {
1774
+ // Failed to get portal points, in practice this means that path[i+1] is invalid polygon.
1775
+ // Clamp the end point to path[i], and return the path so far.
1776
+
1777
+ if (dtStatusFailed(closestPointOnPolyBoundary(path[i], endPos, closestEndPos)))
1778
+ {
1779
+ // This should only happen when the first polygon is invalid.
1780
+ return DT_FAILURE | DT_INVALID_PARAM;
1781
+ }
1782
+
1783
+ // Apeend portals along the current straight path segment.
1784
+ if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
1785
+ {
1786
+ stat = appendPortals(apexIndex, i, closestEndPos, path,
1787
+ straightPath, straightPathFlags, straightPathRefs,
1788
+ straightPathCount, maxStraightPath, options);
1789
+ }
1790
+
1791
+ stat = appendVertex(closestEndPos, 0, path[i],
1792
+ straightPath, straightPathFlags, straightPathRefs,
1793
+ straightPathCount, maxStraightPath);
1794
+
1795
+ return DT_SUCCESS | DT_PARTIAL_RESULT | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
1796
+ }
1797
+
1798
+ // If starting really close the portal, advance.
1799
+ if (i == 0)
1800
+ {
1801
+ float t;
1802
+ if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f))
1803
+ continue;
1804
+ }
1805
+ }
1806
+ else
1807
+ {
1808
+ // End of the path.
1809
+ dtVcopy(left, closestEndPos);
1810
+ dtVcopy(right, closestEndPos);
1811
+
1812
+ fromType = toType = DT_POLYTYPE_GROUND;
1813
+ }
1814
+
1815
+ // Right vertex.
1816
+ if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
1817
+ {
1818
+ if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
1819
+ {
1820
+ dtVcopy(portalRight, right);
1821
+ rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
1822
+ rightPolyType = toType;
1823
+ rightIndex = i;
1824
+ }
1825
+ else
1826
+ {
1827
+ // Append portals along the current straight path segment.
1828
+ if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
1829
+ {
1830
+ stat = appendPortals(apexIndex, leftIndex, portalLeft, path,
1831
+ straightPath, straightPathFlags, straightPathRefs,
1832
+ straightPathCount, maxStraightPath, options);
1833
+ if (stat != DT_IN_PROGRESS)
1834
+ return stat;
1835
+ }
1836
+
1837
+ dtVcopy(portalApex, portalLeft);
1838
+ apexIndex = leftIndex;
1839
+
1840
+ unsigned char flags = 0;
1841
+ if (!leftPolyRef)
1842
+ flags = DT_STRAIGHTPATH_END;
1843
+ else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
1844
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
1845
+ dtPolyRef ref = leftPolyRef;
1846
+
1847
+ // Append or update vertex
1848
+ stat = appendVertex(portalApex, flags, ref,
1849
+ straightPath, straightPathFlags, straightPathRefs,
1850
+ straightPathCount, maxStraightPath);
1851
+ if (stat != DT_IN_PROGRESS)
1852
+ return stat;
1853
+
1854
+ dtVcopy(portalLeft, portalApex);
1855
+ dtVcopy(portalRight, portalApex);
1856
+ leftIndex = apexIndex;
1857
+ rightIndex = apexIndex;
1858
+
1859
+ // Restart
1860
+ i = apexIndex;
1861
+
1862
+ continue;
1863
+ }
1864
+ }
1865
+
1866
+ // Left vertex.
1867
+ if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
1868
+ {
1869
+ if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
1870
+ {
1871
+ dtVcopy(portalLeft, left);
1872
+ leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
1873
+ leftPolyType = toType;
1874
+ leftIndex = i;
1875
+ }
1876
+ else
1877
+ {
1878
+ // Append portals along the current straight path segment.
1879
+ if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
1880
+ {
1881
+ stat = appendPortals(apexIndex, rightIndex, portalRight, path,
1882
+ straightPath, straightPathFlags, straightPathRefs,
1883
+ straightPathCount, maxStraightPath, options);
1884
+ if (stat != DT_IN_PROGRESS)
1885
+ return stat;
1886
+ }
1887
+
1888
+ dtVcopy(portalApex, portalRight);
1889
+ apexIndex = rightIndex;
1890
+
1891
+ unsigned char flags = 0;
1892
+ if (!rightPolyRef)
1893
+ flags = DT_STRAIGHTPATH_END;
1894
+ else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
1895
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
1896
+ dtPolyRef ref = rightPolyRef;
1897
+
1898
+ // Append or update vertex
1899
+ stat = appendVertex(portalApex, flags, ref,
1900
+ straightPath, straightPathFlags, straightPathRefs,
1901
+ straightPathCount, maxStraightPath);
1902
+ if (stat != DT_IN_PROGRESS)
1903
+ return stat;
1904
+
1905
+ dtVcopy(portalLeft, portalApex);
1906
+ dtVcopy(portalRight, portalApex);
1907
+ leftIndex = apexIndex;
1908
+ rightIndex = apexIndex;
1909
+
1910
+ // Restart
1911
+ i = apexIndex;
1912
+
1913
+ continue;
1914
+ }
1915
+ }
1916
+ }
1917
+
1918
+ // Append portals along the current straight path segment.
1919
+ if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
1920
+ {
1921
+ stat = appendPortals(apexIndex, pathSize-1, closestEndPos, path,
1922
+ straightPath, straightPathFlags, straightPathRefs,
1923
+ straightPathCount, maxStraightPath, options);
1924
+ if (stat != DT_IN_PROGRESS)
1925
+ return stat;
1926
+ }
1927
+ }
1928
+
1929
+ stat = appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,
1930
+ straightPath, straightPathFlags, straightPathRefs,
1931
+ straightPathCount, maxStraightPath);
1932
+
1933
+ return DT_SUCCESS | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
1934
+ }
1935
+
1936
+ /// @par
1937
+ ///
1938
+ /// This method is optimized for small delta movement and a small number of
1939
+ /// polygons. If used for too great a distance, the result set will form an
1940
+ /// incomplete path.
1941
+ ///
1942
+ /// @p resultPos will equal the @p endPos if the end is reached.
1943
+ /// Otherwise the closest reachable position will be returned.
1944
+ ///
1945
+ /// @p resultPos is not projected onto the surface of the navigation
1946
+ /// mesh. Use #getPolyHeight if this is needed.
1947
+ ///
1948
+ /// This method treats the end position in the same manner as
1949
+ /// the #raycast method. (As a 2D point.) See that method's documentation
1950
+ /// for details.
1951
+ ///
1952
+ /// If the @p visited array is too small to hold the entire result set, it will
1953
+ /// be filled as far as possible from the start position toward the end
1954
+ /// position.
1955
+ ///
1956
+ dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
1957
+ const dtQueryFilter* filter,
1958
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const
1959
+ {
1960
+ dtAssert(m_nav);
1961
+ dtAssert(m_tinyNodePool);
1962
+
1963
+ *visitedCount = 0;
1964
+
1965
+ // Validate input
1966
+ if (!startRef)
1967
+ return DT_FAILURE | DT_INVALID_PARAM;
1968
+ if (!m_nav->isValidPolyRef(startRef))
1969
+ return DT_FAILURE | DT_INVALID_PARAM;
1970
+
1971
+ dtStatus status = DT_SUCCESS;
1972
+
1973
+ static const int MAX_STACK = 48;
1974
+ dtNode* stack[MAX_STACK];
1975
+ int nstack = 0;
1976
+
1977
+ m_tinyNodePool->clear();
1978
+
1979
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
1980
+ startNode->pidx = 0;
1981
+ startNode->cost = 0;
1982
+ startNode->total = 0;
1983
+ startNode->id = startRef;
1984
+ startNode->flags = DT_NODE_CLOSED;
1985
+ stack[nstack++] = startNode;
1986
+
1987
+ float bestPos[3];
1988
+ float bestDist = FLT_MAX;
1989
+ dtNode* bestNode = 0;
1990
+ dtVcopy(bestPos, startPos);
1991
+
1992
+ // Search constraints
1993
+ float searchPos[3], searchRadSqr;
1994
+ dtVlerp(searchPos, startPos, endPos, 0.5f);
1995
+ searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f);
1996
+
1997
+ float verts[DT_VERTS_PER_POLYGON*3];
1998
+
1999
+ while (nstack)
2000
+ {
2001
+ // Pop front.
2002
+ dtNode* curNode = stack[0];
2003
+ for (int i = 0; i < nstack-1; ++i)
2004
+ stack[i] = stack[i+1];
2005
+ nstack--;
2006
+
2007
+ // Get poly and tile.
2008
+ // The API input has been cheked already, skip checking internal data.
2009
+ const dtPolyRef curRef = curNode->id;
2010
+ const dtMeshTile* curTile = 0;
2011
+ const dtPoly* curPoly = 0;
2012
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
2013
+
2014
+ // Collect vertices.
2015
+ const int nverts = curPoly->vertCount;
2016
+ for (int i = 0; i < nverts; ++i)
2017
+ dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]);
2018
+
2019
+ // If target is inside the poly, stop search.
2020
+ if (dtPointInPolygon(endPos, verts, nverts))
2021
+ {
2022
+ bestNode = curNode;
2023
+ dtVcopy(bestPos, endPos);
2024
+ break;
2025
+ }
2026
+
2027
+ // Find wall edges and find nearest point inside the walls.
2028
+ for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++)
2029
+ {
2030
+ // Find links to neighbours.
2031
+ static const int MAX_NEIS = 8;
2032
+ int nneis = 0;
2033
+ dtPolyRef neis[MAX_NEIS];
2034
+
2035
+ if (curPoly->neis[j] & DT_EXT_LINK)
2036
+ {
2037
+ // Tile border.
2038
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
2039
+ {
2040
+ const dtLink* link = &curTile->links[k];
2041
+ if (link->edge == j)
2042
+ {
2043
+ if (link->ref != 0)
2044
+ {
2045
+ const dtMeshTile* neiTile = 0;
2046
+ const dtPoly* neiPoly = 0;
2047
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
2048
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
2049
+ {
2050
+ if (nneis < MAX_NEIS)
2051
+ neis[nneis++] = link->ref;
2052
+ }
2053
+ }
2054
+ }
2055
+ }
2056
+ }
2057
+ else if (curPoly->neis[j])
2058
+ {
2059
+ const unsigned int idx = (unsigned int)(curPoly->neis[j]-1);
2060
+ const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx;
2061
+ if (filter->passFilter(ref, curTile, &curTile->polys[idx]))
2062
+ {
2063
+ // Internal edge, encode id.
2064
+ neis[nneis++] = ref;
2065
+ }
2066
+ }
2067
+
2068
+ if (!nneis)
2069
+ {
2070
+ // Wall edge, calc distance.
2071
+ const float* vj = &verts[j*3];
2072
+ const float* vi = &verts[i*3];
2073
+ float tseg;
2074
+ const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg);
2075
+ if (distSqr < bestDist)
2076
+ {
2077
+ // Update nearest distance.
2078
+ dtVlerp(bestPos, vj,vi, tseg);
2079
+ bestDist = distSqr;
2080
+ bestNode = curNode;
2081
+ }
2082
+ }
2083
+ else
2084
+ {
2085
+ for (int k = 0; k < nneis; ++k)
2086
+ {
2087
+ // Skip if no node can be allocated.
2088
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]);
2089
+ if (!neighbourNode)
2090
+ continue;
2091
+ // Skip if already visited.
2092
+ if (neighbourNode->flags & DT_NODE_CLOSED)
2093
+ continue;
2094
+
2095
+ // Skip the link if it is too far from search constraint.
2096
+ // TODO: Maybe should use getPortalPoints(), but this one is way faster.
2097
+ const float* vj = &verts[j*3];
2098
+ const float* vi = &verts[i*3];
2099
+ float tseg;
2100
+ float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg);
2101
+ if (distSqr > searchRadSqr)
2102
+ continue;
2103
+
2104
+ // Mark as the node as visited and push to queue.
2105
+ if (nstack < MAX_STACK)
2106
+ {
2107
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
2108
+ neighbourNode->flags |= DT_NODE_CLOSED;
2109
+ stack[nstack++] = neighbourNode;
2110
+ }
2111
+ }
2112
+ }
2113
+ }
2114
+ }
2115
+
2116
+ int n = 0;
2117
+ if (bestNode)
2118
+ {
2119
+ // Reverse the path.
2120
+ dtNode* prev = 0;
2121
+ dtNode* node = bestNode;
2122
+ do
2123
+ {
2124
+ dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx);
2125
+ node->pidx = m_tinyNodePool->getNodeIdx(prev);
2126
+ prev = node;
2127
+ node = next;
2128
+ }
2129
+ while (node);
2130
+
2131
+ // Store result
2132
+ node = prev;
2133
+ do
2134
+ {
2135
+ visited[n++] = node->id;
2136
+ if (n >= maxVisitedSize)
2137
+ {
2138
+ status |= DT_BUFFER_TOO_SMALL;
2139
+ break;
2140
+ }
2141
+ node = m_tinyNodePool->getNodeAtIdx(node->pidx);
2142
+ }
2143
+ while (node);
2144
+ }
2145
+
2146
+ dtVcopy(resultPos, bestPos);
2147
+
2148
+ *visitedCount = n;
2149
+
2150
+ return status;
2151
+ }
2152
+
2153
+
2154
+ dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
2155
+ unsigned char& fromType, unsigned char& toType) const
2156
+ {
2157
+ dtAssert(m_nav);
2158
+
2159
+ const dtMeshTile* fromTile = 0;
2160
+ const dtPoly* fromPoly = 0;
2161
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly)))
2162
+ return DT_FAILURE | DT_INVALID_PARAM;
2163
+ fromType = fromPoly->getType();
2164
+
2165
+ const dtMeshTile* toTile = 0;
2166
+ const dtPoly* toPoly = 0;
2167
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly)))
2168
+ return DT_FAILURE | DT_INVALID_PARAM;
2169
+ toType = toPoly->getType();
2170
+
2171
+ return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right);
2172
+ }
2173
+
2174
+ // Returns portal points between two polygons.
2175
+ dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
2176
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
2177
+ float* left, float* right) const
2178
+ {
2179
+ // Find the link that points to the 'to' polygon.
2180
+ const dtLink* link = 0;
2181
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
2182
+ {
2183
+ if (fromTile->links[i].ref == to)
2184
+ {
2185
+ link = &fromTile->links[i];
2186
+ break;
2187
+ }
2188
+ }
2189
+ if (!link)
2190
+ return DT_FAILURE | DT_INVALID_PARAM;
2191
+
2192
+ // Handle off-mesh connections.
2193
+ if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
2194
+ {
2195
+ // Find link that points to first vertex.
2196
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
2197
+ {
2198
+ if (fromTile->links[i].ref == to)
2199
+ {
2200
+ const int v = fromTile->links[i].edge;
2201
+ dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]);
2202
+ dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]);
2203
+ return DT_SUCCESS;
2204
+ }
2205
+ }
2206
+ return DT_FAILURE | DT_INVALID_PARAM;
2207
+ }
2208
+
2209
+ if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
2210
+ {
2211
+ for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next)
2212
+ {
2213
+ if (toTile->links[i].ref == from)
2214
+ {
2215
+ const int v = toTile->links[i].edge;
2216
+ dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]);
2217
+ dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]);
2218
+ return DT_SUCCESS;
2219
+ }
2220
+ }
2221
+ return DT_FAILURE | DT_INVALID_PARAM;
2222
+ }
2223
+
2224
+ // Find portal vertices.
2225
+ const int v0 = fromPoly->verts[link->edge];
2226
+ const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount];
2227
+ dtVcopy(left, &fromTile->verts[v0*3]);
2228
+ dtVcopy(right, &fromTile->verts[v1*3]);
2229
+
2230
+ // If the link is at tile boundary, dtClamp the vertices to
2231
+ // the link width.
2232
+ if (link->side != 0xff)
2233
+ {
2234
+ // Unpack portal limits.
2235
+ if (link->bmin != 0 || link->bmax != 255)
2236
+ {
2237
+ const float s = 1.0f/255.0f;
2238
+ const float tmin = link->bmin*s;
2239
+ const float tmax = link->bmax*s;
2240
+ dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin);
2241
+ dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax);
2242
+ }
2243
+ }
2244
+
2245
+ return DT_SUCCESS;
2246
+ }
2247
+
2248
+ // Returns edge mid point between two polygons.
2249
+ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const
2250
+ {
2251
+ float left[3], right[3];
2252
+ unsigned char fromType, toType;
2253
+ if (dtStatusFailed(getPortalPoints(from, to, left,right, fromType, toType)))
2254
+ return DT_FAILURE | DT_INVALID_PARAM;
2255
+ mid[0] = (left[0]+right[0])*0.5f;
2256
+ mid[1] = (left[1]+right[1])*0.5f;
2257
+ mid[2] = (left[2]+right[2])*0.5f;
2258
+ return DT_SUCCESS;
2259
+ }
2260
+
2261
+ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
2262
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
2263
+ float* mid) const
2264
+ {
2265
+ float left[3], right[3];
2266
+ if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right)))
2267
+ return DT_FAILURE | DT_INVALID_PARAM;
2268
+ mid[0] = (left[0]+right[0])*0.5f;
2269
+ mid[1] = (left[1]+right[1])*0.5f;
2270
+ mid[2] = (left[2]+right[2])*0.5f;
2271
+ return DT_SUCCESS;
2272
+ }
2273
+
2274
+
2275
+
2276
+ /// @par
2277
+ ///
2278
+ /// This method is meant to be used for quick, short distance checks.
2279
+ ///
2280
+ /// If the path array is too small to hold the result, it will be filled as
2281
+ /// far as possible from the start postion toward the end position.
2282
+ ///
2283
+ /// <b>Using the Hit Parameter (t)</b>
2284
+ ///
2285
+ /// If the hit parameter is a very high value (FLT_MAX), then the ray has hit
2286
+ /// the end position. In this case the path represents a valid corridor to the
2287
+ /// end position and the value of @p hitNormal is undefined.
2288
+ ///
2289
+ /// If the hit parameter is zero, then the start position is on the wall that
2290
+ /// was hit and the value of @p hitNormal is undefined.
2291
+ ///
2292
+ /// If 0 < t < 1.0 then the following applies:
2293
+ ///
2294
+ /// @code
2295
+ /// distanceToHitBorder = distanceToEndPosition * t
2296
+ /// hitPoint = startPos + (endPos - startPos) * t
2297
+ /// @endcode
2298
+ ///
2299
+ /// <b>Use Case Restriction</b>
2300
+ ///
2301
+ /// The raycast ignores the y-value of the end position. (2D check.) This
2302
+ /// places significant limits on how it can be used. For example:
2303
+ ///
2304
+ /// Consider a scene where there is a main floor with a second floor balcony
2305
+ /// that hangs over the main floor. So the first floor mesh extends below the
2306
+ /// balcony mesh. The start position is somewhere on the first floor. The end
2307
+ /// position is on the balcony.
2308
+ ///
2309
+ /// The raycast will search toward the end position along the first floor mesh.
2310
+ /// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
2311
+ /// (no wall hit), meaning it reached the end position. This is one example of why
2312
+ /// this method is meant for short distance checks.
2313
+ ///
2314
+ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
2315
+ const dtQueryFilter* filter,
2316
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
2317
+ {
2318
+ dtRaycastHit hit;
2319
+ hit.path = path;
2320
+ hit.maxPath = maxPath;
2321
+
2322
+ dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit);
2323
+
2324
+ *t = hit.t;
2325
+ if (hitNormal)
2326
+ dtVcopy(hitNormal, hit.hitNormal);
2327
+ if (pathCount)
2328
+ *pathCount = hit.pathCount;
2329
+
2330
+ return status;
2331
+ }
2332
+
2333
+
2334
+ /// @par
2335
+ ///
2336
+ /// This method is meant to be used for quick, short distance checks.
2337
+ ///
2338
+ /// If the path array is too small to hold the result, it will be filled as
2339
+ /// far as possible from the start postion toward the end position.
2340
+ ///
2341
+ /// <b>Using the Hit Parameter t of RaycastHit</b>
2342
+ ///
2343
+ /// If the hit parameter is a very high value (FLT_MAX), then the ray has hit
2344
+ /// the end position. In this case the path represents a valid corridor to the
2345
+ /// end position and the value of @p hitNormal is undefined.
2346
+ ///
2347
+ /// If the hit parameter is zero, then the start position is on the wall that
2348
+ /// was hit and the value of @p hitNormal is undefined.
2349
+ ///
2350
+ /// If 0 < t < 1.0 then the following applies:
2351
+ ///
2352
+ /// @code
2353
+ /// distanceToHitBorder = distanceToEndPosition * t
2354
+ /// hitPoint = startPos + (endPos - startPos) * t
2355
+ /// @endcode
2356
+ ///
2357
+ /// <b>Use Case Restriction</b>
2358
+ ///
2359
+ /// The raycast ignores the y-value of the end position. (2D check.) This
2360
+ /// places significant limits on how it can be used. For example:
2361
+ ///
2362
+ /// Consider a scene where there is a main floor with a second floor balcony
2363
+ /// that hangs over the main floor. So the first floor mesh extends below the
2364
+ /// balcony mesh. The start position is somewhere on the first floor. The end
2365
+ /// position is on the balcony.
2366
+ ///
2367
+ /// The raycast will search toward the end position along the first floor mesh.
2368
+ /// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
2369
+ /// (no wall hit), meaning it reached the end position. This is one example of why
2370
+ /// this method is meant for short distance checks.
2371
+ ///
2372
+ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
2373
+ const dtQueryFilter* filter, const unsigned int options,
2374
+ dtRaycastHit* hit, dtPolyRef prevRef) const
2375
+ {
2376
+ dtAssert(m_nav);
2377
+
2378
+ hit->t = 0;
2379
+ hit->pathCount = 0;
2380
+ hit->pathCost = 0;
2381
+
2382
+ // Validate input
2383
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
2384
+ return DT_FAILURE | DT_INVALID_PARAM;
2385
+ if (prevRef && !m_nav->isValidPolyRef(prevRef))
2386
+ return DT_FAILURE | DT_INVALID_PARAM;
2387
+
2388
+ float dir[3], curPos[3], lastPos[3];
2389
+ float verts[DT_VERTS_PER_POLYGON*3+3];
2390
+ int n = 0;
2391
+
2392
+ dtVcopy(curPos, startPos);
2393
+ dtVsub(dir, endPos, startPos);
2394
+ dtVset(hit->hitNormal, 0, 0, 0);
2395
+
2396
+ dtStatus status = DT_SUCCESS;
2397
+
2398
+ const dtMeshTile* prevTile, *tile, *nextTile;
2399
+ const dtPoly* prevPoly, *poly, *nextPoly;
2400
+ dtPolyRef curRef, nextRef;
2401
+
2402
+ // The API input has been checked already, skip checking internal data.
2403
+ nextRef = curRef = startRef;
2404
+ tile = 0;
2405
+ poly = 0;
2406
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
2407
+ nextTile = prevTile = tile;
2408
+ nextPoly = prevPoly = poly;
2409
+ if (prevRef)
2410
+ m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly);
2411
+
2412
+ while (curRef)
2413
+ {
2414
+ // Cast ray against current polygon.
2415
+
2416
+ // Collect vertices.
2417
+ int nv = 0;
2418
+ for (int i = 0; i < (int)poly->vertCount; ++i)
2419
+ {
2420
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
2421
+ nv++;
2422
+ }
2423
+
2424
+ float tmin, tmax;
2425
+ int segMin, segMax;
2426
+ if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
2427
+ {
2428
+ // Could not hit the polygon, keep the old t and report hit.
2429
+ hit->pathCount = n;
2430
+ return status;
2431
+ }
2432
+ // Keep track of furthest t so far.
2433
+ if (tmax > hit->t)
2434
+ hit->t = tmax;
2435
+
2436
+ // Store visited polygons.
2437
+ if (n < hit->maxPath)
2438
+ hit->path[n++] = curRef;
2439
+ else
2440
+ status |= DT_BUFFER_TOO_SMALL;
2441
+
2442
+ // Ray end is completely inside the polygon.
2443
+ if (segMax == -1)
2444
+ {
2445
+ hit->t = FLT_MAX;
2446
+ hit->pathCount = n;
2447
+
2448
+ // add the cost
2449
+ if (options & DT_RAYCAST_USE_COSTS)
2450
+ hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly);
2451
+ return status;
2452
+ }
2453
+
2454
+ // Follow neighbours.
2455
+ nextRef = 0;
2456
+
2457
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
2458
+ {
2459
+ const dtLink* link = &tile->links[i];
2460
+
2461
+ // Find link which contains this edge.
2462
+ if ((int)link->edge != segMax)
2463
+ continue;
2464
+
2465
+ // Get pointer to the next polygon.
2466
+ nextTile = 0;
2467
+ nextPoly = 0;
2468
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
2469
+
2470
+ // Skip off-mesh connections.
2471
+ if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
2472
+ continue;
2473
+
2474
+ // Skip links based on filter.
2475
+ if (!filter->passFilter(link->ref, nextTile, nextPoly))
2476
+ continue;
2477
+
2478
+ // If the link is internal, just return the ref.
2479
+ if (link->side == 0xff)
2480
+ {
2481
+ nextRef = link->ref;
2482
+ break;
2483
+ }
2484
+
2485
+ // If the link is at tile boundary,
2486
+
2487
+ // Check if the link spans the whole edge, and accept.
2488
+ if (link->bmin == 0 && link->bmax == 255)
2489
+ {
2490
+ nextRef = link->ref;
2491
+ break;
2492
+ }
2493
+
2494
+ // Check for partial edge links.
2495
+ const int v0 = poly->verts[link->edge];
2496
+ const int v1 = poly->verts[(link->edge+1) % poly->vertCount];
2497
+ const float* left = &tile->verts[v0*3];
2498
+ const float* right = &tile->verts[v1*3];
2499
+
2500
+ // Check that the intersection lies inside the link portal.
2501
+ if (link->side == 0 || link->side == 4)
2502
+ {
2503
+ // Calculate link size.
2504
+ const float s = 1.0f/255.0f;
2505
+ float lmin = left[2] + (right[2] - left[2])*(link->bmin*s);
2506
+ float lmax = left[2] + (right[2] - left[2])*(link->bmax*s);
2507
+ if (lmin > lmax) dtSwap(lmin, lmax);
2508
+
2509
+ // Find Z intersection.
2510
+ float z = startPos[2] + (endPos[2]-startPos[2])*tmax;
2511
+ if (z >= lmin && z <= lmax)
2512
+ {
2513
+ nextRef = link->ref;
2514
+ break;
2515
+ }
2516
+ }
2517
+ else if (link->side == 2 || link->side == 6)
2518
+ {
2519
+ // Calculate link size.
2520
+ const float s = 1.0f/255.0f;
2521
+ float lmin = left[0] + (right[0] - left[0])*(link->bmin*s);
2522
+ float lmax = left[0] + (right[0] - left[0])*(link->bmax*s);
2523
+ if (lmin > lmax) dtSwap(lmin, lmax);
2524
+
2525
+ // Find X intersection.
2526
+ float x = startPos[0] + (endPos[0]-startPos[0])*tmax;
2527
+ if (x >= lmin && x <= lmax)
2528
+ {
2529
+ nextRef = link->ref;
2530
+ break;
2531
+ }
2532
+ }
2533
+ }
2534
+
2535
+ // add the cost
2536
+ if (options & DT_RAYCAST_USE_COSTS)
2537
+ {
2538
+ // compute the intersection point at the furthest end of the polygon
2539
+ // and correct the height (since the raycast moves in 2d)
2540
+ dtVcopy(lastPos, curPos);
2541
+ dtVmad(curPos, startPos, dir, hit->t);
2542
+ float* e1 = &verts[segMax*3];
2543
+ float* e2 = &verts[((segMax+1)%nv)*3];
2544
+ float eDir[3], diff[3];
2545
+ dtVsub(eDir, e2, e1);
2546
+ dtVsub(diff, curPos, e1);
2547
+ float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2];
2548
+ curPos[1] = e1[1] + eDir[1] * s;
2549
+
2550
+ hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly);
2551
+ }
2552
+
2553
+ if (!nextRef)
2554
+ {
2555
+ // No neighbour, we hit a wall.
2556
+
2557
+ // Calculate hit normal.
2558
+ const int a = segMax;
2559
+ const int b = segMax+1 < nv ? segMax+1 : 0;
2560
+ const float* va = &verts[a*3];
2561
+ const float* vb = &verts[b*3];
2562
+ const float dx = vb[0] - va[0];
2563
+ const float dz = vb[2] - va[2];
2564
+ hit->hitNormal[0] = dz;
2565
+ hit->hitNormal[1] = 0;
2566
+ hit->hitNormal[2] = -dx;
2567
+ dtVnormalize(hit->hitNormal);
2568
+
2569
+ hit->pathCount = n;
2570
+ return status;
2571
+ }
2572
+
2573
+ // No hit, advance to neighbour polygon.
2574
+ prevRef = curRef;
2575
+ curRef = nextRef;
2576
+ prevTile = tile;
2577
+ tile = nextTile;
2578
+ prevPoly = poly;
2579
+ poly = nextPoly;
2580
+ }
2581
+
2582
+ hit->pathCount = n;
2583
+
2584
+ return status;
2585
+ }
2586
+
2587
+ /// @par
2588
+ ///
2589
+ /// At least one result array must be provided.
2590
+ ///
2591
+ /// The order of the result set is from least to highest cost to reach the polygon.
2592
+ ///
2593
+ /// A common use case for this method is to perform Dijkstra searches.
2594
+ /// Candidate polygons are found by searching the graph beginning at the start polygon.
2595
+ ///
2596
+ /// If a polygon is not found via the graph search, even if it intersects the
2597
+ /// search circle, it will not be included in the result set. For example:
2598
+ ///
2599
+ /// polyA is the start polygon.
2600
+ /// polyB shares an edge with polyA. (Is adjacent.)
2601
+ /// polyC shares an edge with polyB, but not with polyA
2602
+ /// Even if the search circle overlaps polyC, it will not be included in the
2603
+ /// result set unless polyB is also in the set.
2604
+ ///
2605
+ /// The value of the center point is used as the start position for cost
2606
+ /// calculations. It is not projected onto the surface of the mesh, so its
2607
+ /// y-value will effect the costs.
2608
+ ///
2609
+ /// Intersection tests occur in 2D. All polygons and the search circle are
2610
+ /// projected onto the xz-plane. So the y-value of the center point does not
2611
+ /// effect intersection tests.
2612
+ ///
2613
+ /// If the result arrays are to small to hold the entire result set, they will be
2614
+ /// filled to capacity.
2615
+ ///
2616
+ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
2617
+ const dtQueryFilter* filter,
2618
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
2619
+ int* resultCount, const int maxResult) const
2620
+ {
2621
+ dtAssert(m_nav);
2622
+ dtAssert(m_nodePool);
2623
+ dtAssert(m_openList);
2624
+
2625
+ *resultCount = 0;
2626
+
2627
+ // Validate input
2628
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
2629
+ return DT_FAILURE | DT_INVALID_PARAM;
2630
+
2631
+ m_nodePool->clear();
2632
+ m_openList->clear();
2633
+
2634
+ dtNode* startNode = m_nodePool->getNode(startRef);
2635
+ dtVcopy(startNode->pos, centerPos);
2636
+ startNode->pidx = 0;
2637
+ startNode->cost = 0;
2638
+ startNode->total = 0;
2639
+ startNode->id = startRef;
2640
+ startNode->flags = DT_NODE_OPEN;
2641
+ m_openList->push(startNode);
2642
+
2643
+ dtStatus status = DT_SUCCESS;
2644
+
2645
+ int n = 0;
2646
+ if (n < maxResult)
2647
+ {
2648
+ if (resultRef)
2649
+ resultRef[n] = startNode->id;
2650
+ if (resultParent)
2651
+ resultParent[n] = 0;
2652
+ if (resultCost)
2653
+ resultCost[n] = 0;
2654
+ ++n;
2655
+ }
2656
+ else
2657
+ {
2658
+ status |= DT_BUFFER_TOO_SMALL;
2659
+ }
2660
+
2661
+ const float radiusSqr = dtSqr(radius);
2662
+
2663
+ while (!m_openList->empty())
2664
+ {
2665
+ dtNode* bestNode = m_openList->pop();
2666
+ bestNode->flags &= ~DT_NODE_OPEN;
2667
+ bestNode->flags |= DT_NODE_CLOSED;
2668
+
2669
+ // Get poly and tile.
2670
+ // The API input has been cheked already, skip checking internal data.
2671
+ const dtPolyRef bestRef = bestNode->id;
2672
+ const dtMeshTile* bestTile = 0;
2673
+ const dtPoly* bestPoly = 0;
2674
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
2675
+
2676
+ // Get parent poly and tile.
2677
+ dtPolyRef parentRef = 0;
2678
+ const dtMeshTile* parentTile = 0;
2679
+ const dtPoly* parentPoly = 0;
2680
+ if (bestNode->pidx)
2681
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
2682
+ if (parentRef)
2683
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
2684
+
2685
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
2686
+ {
2687
+ const dtLink* link = &bestTile->links[i];
2688
+ dtPolyRef neighbourRef = link->ref;
2689
+ // Skip invalid neighbours and do not follow back to parent.
2690
+ if (!neighbourRef || neighbourRef == parentRef)
2691
+ continue;
2692
+
2693
+ // Expand to neighbour
2694
+ const dtMeshTile* neighbourTile = 0;
2695
+ const dtPoly* neighbourPoly = 0;
2696
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
2697
+
2698
+ // Do not advance if the polygon is excluded by the filter.
2699
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
2700
+ continue;
2701
+
2702
+ // Find edge and calc distance to the edge.
2703
+ float va[3], vb[3];
2704
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
2705
+ continue;
2706
+
2707
+ // If the circle is not touching the next polygon, skip it.
2708
+ float tseg;
2709
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
2710
+ if (distSqr > radiusSqr)
2711
+ continue;
2712
+
2713
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
2714
+ if (!neighbourNode)
2715
+ {
2716
+ status |= DT_OUT_OF_NODES;
2717
+ continue;
2718
+ }
2719
+
2720
+ if (neighbourNode->flags & DT_NODE_CLOSED)
2721
+ continue;
2722
+
2723
+ // Cost
2724
+ if (neighbourNode->flags == 0)
2725
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
2726
+
2727
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
2728
+
2729
+ // The node is already in open list and the new result is worse, skip.
2730
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
2731
+ continue;
2732
+
2733
+ neighbourNode->id = neighbourRef;
2734
+ neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
2735
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
2736
+ neighbourNode->total = total;
2737
+
2738
+ if (neighbourNode->flags & DT_NODE_OPEN)
2739
+ {
2740
+ m_openList->modify(neighbourNode);
2741
+ }
2742
+ else
2743
+ {
2744
+ if (n < maxResult)
2745
+ {
2746
+ if (resultRef)
2747
+ resultRef[n] = neighbourNode->id;
2748
+ if (resultParent)
2749
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
2750
+ if (resultCost)
2751
+ resultCost[n] = neighbourNode->total;
2752
+ ++n;
2753
+ }
2754
+ else
2755
+ {
2756
+ status |= DT_BUFFER_TOO_SMALL;
2757
+ }
2758
+ neighbourNode->flags = DT_NODE_OPEN;
2759
+ m_openList->push(neighbourNode);
2760
+ }
2761
+ }
2762
+ }
2763
+
2764
+ *resultCount = n;
2765
+
2766
+ return status;
2767
+ }
2768
+
2769
+ /// @par
2770
+ ///
2771
+ /// The order of the result set is from least to highest cost.
2772
+ ///
2773
+ /// At least one result array must be provided.
2774
+ ///
2775
+ /// A common use case for this method is to perform Dijkstra searches.
2776
+ /// Candidate polygons are found by searching the graph beginning at the start
2777
+ /// polygon.
2778
+ ///
2779
+ /// The same intersection test restrictions that apply to findPolysAroundCircle()
2780
+ /// method apply to this method.
2781
+ ///
2782
+ /// The 3D centroid of the search polygon is used as the start position for cost
2783
+ /// calculations.
2784
+ ///
2785
+ /// Intersection tests occur in 2D. All polygons are projected onto the
2786
+ /// xz-plane. So the y-values of the vertices do not effect intersection tests.
2787
+ ///
2788
+ /// If the result arrays are is too small to hold the entire result set, they will
2789
+ /// be filled to capacity.
2790
+ ///
2791
+ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
2792
+ const dtQueryFilter* filter,
2793
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
2794
+ int* resultCount, const int maxResult) const
2795
+ {
2796
+ dtAssert(m_nav);
2797
+ dtAssert(m_nodePool);
2798
+ dtAssert(m_openList);
2799
+
2800
+ *resultCount = 0;
2801
+
2802
+ // Validate input
2803
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
2804
+ return DT_FAILURE | DT_INVALID_PARAM;
2805
+
2806
+ m_nodePool->clear();
2807
+ m_openList->clear();
2808
+
2809
+ float centerPos[3] = {0,0,0};
2810
+ for (int i = 0; i < nverts; ++i)
2811
+ dtVadd(centerPos,centerPos,&verts[i*3]);
2812
+ dtVscale(centerPos,centerPos,1.0f/nverts);
2813
+
2814
+ dtNode* startNode = m_nodePool->getNode(startRef);
2815
+ dtVcopy(startNode->pos, centerPos);
2816
+ startNode->pidx = 0;
2817
+ startNode->cost = 0;
2818
+ startNode->total = 0;
2819
+ startNode->id = startRef;
2820
+ startNode->flags = DT_NODE_OPEN;
2821
+ m_openList->push(startNode);
2822
+
2823
+ dtStatus status = DT_SUCCESS;
2824
+
2825
+ int n = 0;
2826
+ if (n < maxResult)
2827
+ {
2828
+ if (resultRef)
2829
+ resultRef[n] = startNode->id;
2830
+ if (resultParent)
2831
+ resultParent[n] = 0;
2832
+ if (resultCost)
2833
+ resultCost[n] = 0;
2834
+ ++n;
2835
+ }
2836
+ else
2837
+ {
2838
+ status |= DT_BUFFER_TOO_SMALL;
2839
+ }
2840
+
2841
+ while (!m_openList->empty())
2842
+ {
2843
+ dtNode* bestNode = m_openList->pop();
2844
+ bestNode->flags &= ~DT_NODE_OPEN;
2845
+ bestNode->flags |= DT_NODE_CLOSED;
2846
+
2847
+ // Get poly and tile.
2848
+ // The API input has been cheked already, skip checking internal data.
2849
+ const dtPolyRef bestRef = bestNode->id;
2850
+ const dtMeshTile* bestTile = 0;
2851
+ const dtPoly* bestPoly = 0;
2852
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
2853
+
2854
+ // Get parent poly and tile.
2855
+ dtPolyRef parentRef = 0;
2856
+ const dtMeshTile* parentTile = 0;
2857
+ const dtPoly* parentPoly = 0;
2858
+ if (bestNode->pidx)
2859
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
2860
+ if (parentRef)
2861
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
2862
+
2863
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
2864
+ {
2865
+ const dtLink* link = &bestTile->links[i];
2866
+ dtPolyRef neighbourRef = link->ref;
2867
+ // Skip invalid neighbours and do not follow back to parent.
2868
+ if (!neighbourRef || neighbourRef == parentRef)
2869
+ continue;
2870
+
2871
+ // Expand to neighbour
2872
+ const dtMeshTile* neighbourTile = 0;
2873
+ const dtPoly* neighbourPoly = 0;
2874
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
2875
+
2876
+ // Do not advance if the polygon is excluded by the filter.
2877
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
2878
+ continue;
2879
+
2880
+ // Find edge and calc distance to the edge.
2881
+ float va[3], vb[3];
2882
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
2883
+ continue;
2884
+
2885
+ // If the poly is not touching the edge to the next polygon, skip the connection it.
2886
+ float tmin, tmax;
2887
+ int segMin, segMax;
2888
+ if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax))
2889
+ continue;
2890
+ if (tmin > 1.0f || tmax < 0.0f)
2891
+ continue;
2892
+
2893
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
2894
+ if (!neighbourNode)
2895
+ {
2896
+ status |= DT_OUT_OF_NODES;
2897
+ continue;
2898
+ }
2899
+
2900
+ if (neighbourNode->flags & DT_NODE_CLOSED)
2901
+ continue;
2902
+
2903
+ // Cost
2904
+ if (neighbourNode->flags == 0)
2905
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
2906
+
2907
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
2908
+
2909
+ // The node is already in open list and the new result is worse, skip.
2910
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
2911
+ continue;
2912
+
2913
+ neighbourNode->id = neighbourRef;
2914
+ neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
2915
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
2916
+ neighbourNode->total = total;
2917
+
2918
+ if (neighbourNode->flags & DT_NODE_OPEN)
2919
+ {
2920
+ m_openList->modify(neighbourNode);
2921
+ }
2922
+ else
2923
+ {
2924
+ if (n < maxResult)
2925
+ {
2926
+ if (resultRef)
2927
+ resultRef[n] = neighbourNode->id;
2928
+ if (resultParent)
2929
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
2930
+ if (resultCost)
2931
+ resultCost[n] = neighbourNode->total;
2932
+ ++n;
2933
+ }
2934
+ else
2935
+ {
2936
+ status |= DT_BUFFER_TOO_SMALL;
2937
+ }
2938
+ neighbourNode->flags = DT_NODE_OPEN;
2939
+ m_openList->push(neighbourNode);
2940
+ }
2941
+ }
2942
+ }
2943
+
2944
+ *resultCount = n;
2945
+
2946
+ return status;
2947
+ }
2948
+
2949
+ /// @par
2950
+ ///
2951
+ /// This method is optimized for a small search radius and small number of result
2952
+ /// polygons.
2953
+ ///
2954
+ /// Candidate polygons are found by searching the navigation graph beginning at
2955
+ /// the start polygon.
2956
+ ///
2957
+ /// The same intersection test restrictions that apply to the findPolysAroundCircle
2958
+ /// mehtod applies to this method.
2959
+ ///
2960
+ /// The value of the center point is used as the start point for cost calculations.
2961
+ /// It is not projected onto the surface of the mesh, so its y-value will effect
2962
+ /// the costs.
2963
+ ///
2964
+ /// Intersection tests occur in 2D. All polygons and the search circle are
2965
+ /// projected onto the xz-plane. So the y-value of the center point does not
2966
+ /// effect intersection tests.
2967
+ ///
2968
+ /// If the result arrays are is too small to hold the entire result set, they will
2969
+ /// be filled to capacity.
2970
+ ///
2971
+ dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
2972
+ const dtQueryFilter* filter,
2973
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
2974
+ int* resultCount, const int maxResult) const
2975
+ {
2976
+ dtAssert(m_nav);
2977
+ dtAssert(m_tinyNodePool);
2978
+
2979
+ *resultCount = 0;
2980
+
2981
+ // Validate input
2982
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
2983
+ return DT_FAILURE | DT_INVALID_PARAM;
2984
+
2985
+ static const int MAX_STACK = 48;
2986
+ dtNode* stack[MAX_STACK];
2987
+ int nstack = 0;
2988
+
2989
+ m_tinyNodePool->clear();
2990
+
2991
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
2992
+ startNode->pidx = 0;
2993
+ startNode->id = startRef;
2994
+ startNode->flags = DT_NODE_CLOSED;
2995
+ stack[nstack++] = startNode;
2996
+
2997
+ const float radiusSqr = dtSqr(radius);
2998
+
2999
+ float pa[DT_VERTS_PER_POLYGON*3];
3000
+ float pb[DT_VERTS_PER_POLYGON*3];
3001
+
3002
+ dtStatus status = DT_SUCCESS;
3003
+
3004
+ int n = 0;
3005
+ if (n < maxResult)
3006
+ {
3007
+ resultRef[n] = startNode->id;
3008
+ if (resultParent)
3009
+ resultParent[n] = 0;
3010
+ ++n;
3011
+ }
3012
+ else
3013
+ {
3014
+ status |= DT_BUFFER_TOO_SMALL;
3015
+ }
3016
+
3017
+ while (nstack)
3018
+ {
3019
+ // Pop front.
3020
+ dtNode* curNode = stack[0];
3021
+ for (int i = 0; i < nstack-1; ++i)
3022
+ stack[i] = stack[i+1];
3023
+ nstack--;
3024
+
3025
+ // Get poly and tile.
3026
+ // The API input has been cheked already, skip checking internal data.
3027
+ const dtPolyRef curRef = curNode->id;
3028
+ const dtMeshTile* curTile = 0;
3029
+ const dtPoly* curPoly = 0;
3030
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
3031
+
3032
+ for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next)
3033
+ {
3034
+ const dtLink* link = &curTile->links[i];
3035
+ dtPolyRef neighbourRef = link->ref;
3036
+ // Skip invalid neighbours.
3037
+ if (!neighbourRef)
3038
+ continue;
3039
+
3040
+ // Skip if cannot alloca more nodes.
3041
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef);
3042
+ if (!neighbourNode)
3043
+ continue;
3044
+ // Skip visited.
3045
+ if (neighbourNode->flags & DT_NODE_CLOSED)
3046
+ continue;
3047
+
3048
+ // Expand to neighbour
3049
+ const dtMeshTile* neighbourTile = 0;
3050
+ const dtPoly* neighbourPoly = 0;
3051
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
3052
+
3053
+ // Skip off-mesh connections.
3054
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
3055
+ continue;
3056
+
3057
+ // Do not advance if the polygon is excluded by the filter.
3058
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
3059
+ continue;
3060
+
3061
+ // Find edge and calc distance to the edge.
3062
+ float va[3], vb[3];
3063
+ if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
3064
+ continue;
3065
+
3066
+ // If the circle is not touching the next polygon, skip it.
3067
+ float tseg;
3068
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
3069
+ if (distSqr > radiusSqr)
3070
+ continue;
3071
+
3072
+ // Mark node visited, this is done before the overlap test so that
3073
+ // we will not visit the poly again if the test fails.
3074
+ neighbourNode->flags |= DT_NODE_CLOSED;
3075
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
3076
+
3077
+ // Check that the polygon does not collide with existing polygons.
3078
+
3079
+ // Collect vertices of the neighbour poly.
3080
+ const int npa = neighbourPoly->vertCount;
3081
+ for (int k = 0; k < npa; ++k)
3082
+ dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]);
3083
+
3084
+ bool overlap = false;
3085
+ for (int j = 0; j < n; ++j)
3086
+ {
3087
+ dtPolyRef pastRef = resultRef[j];
3088
+
3089
+ // Connected polys do not overlap.
3090
+ bool connected = false;
3091
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
3092
+ {
3093
+ if (curTile->links[k].ref == pastRef)
3094
+ {
3095
+ connected = true;
3096
+ break;
3097
+ }
3098
+ }
3099
+ if (connected)
3100
+ continue;
3101
+
3102
+ // Potentially overlapping.
3103
+ const dtMeshTile* pastTile = 0;
3104
+ const dtPoly* pastPoly = 0;
3105
+ m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly);
3106
+
3107
+ // Get vertices and test overlap
3108
+ const int npb = pastPoly->vertCount;
3109
+ for (int k = 0; k < npb; ++k)
3110
+ dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]);
3111
+
3112
+ if (dtOverlapPolyPoly2D(pa,npa, pb,npb))
3113
+ {
3114
+ overlap = true;
3115
+ break;
3116
+ }
3117
+ }
3118
+ if (overlap)
3119
+ continue;
3120
+
3121
+ // This poly is fine, store and advance to the poly.
3122
+ if (n < maxResult)
3123
+ {
3124
+ resultRef[n] = neighbourRef;
3125
+ if (resultParent)
3126
+ resultParent[n] = curRef;
3127
+ ++n;
3128
+ }
3129
+ else
3130
+ {
3131
+ status |= DT_BUFFER_TOO_SMALL;
3132
+ }
3133
+
3134
+ if (nstack < MAX_STACK)
3135
+ {
3136
+ stack[nstack++] = neighbourNode;
3137
+ }
3138
+ }
3139
+ }
3140
+
3141
+ *resultCount = n;
3142
+
3143
+ return status;
3144
+ }
3145
+
3146
+
3147
+ struct dtSegInterval
3148
+ {
3149
+ dtPolyRef ref;
3150
+ short tmin, tmax;
3151
+ };
3152
+
3153
+ static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts,
3154
+ const short tmin, const short tmax, const dtPolyRef ref)
3155
+ {
3156
+ if (nints+1 > maxInts) return;
3157
+ // Find insertion point.
3158
+ int idx = 0;
3159
+ while (idx < nints)
3160
+ {
3161
+ if (tmax <= ints[idx].tmin)
3162
+ break;
3163
+ idx++;
3164
+ }
3165
+ // Move current results.
3166
+ if (nints-idx)
3167
+ memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx));
3168
+ // Store
3169
+ ints[idx].ref = ref;
3170
+ ints[idx].tmin = tmin;
3171
+ ints[idx].tmax = tmax;
3172
+ nints++;
3173
+ }
3174
+
3175
+ /// @par
3176
+ ///
3177
+ /// If the @p segmentRefs parameter is provided, then all polygon segments will be returned.
3178
+ /// Otherwise only the wall segments are returned.
3179
+ ///
3180
+ /// A segment that is normally a portal will be included in the result set as a
3181
+ /// wall if the @p filter results in the neighbor polygon becoomming impassable.
3182
+ ///
3183
+ /// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the
3184
+ /// maximum segments per polygon of the source navigation mesh.
3185
+ ///
3186
+ dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
3187
+ float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount,
3188
+ const int maxSegments) const
3189
+ {
3190
+ dtAssert(m_nav);
3191
+
3192
+ *segmentCount = 0;
3193
+
3194
+ const dtMeshTile* tile = 0;
3195
+ const dtPoly* poly = 0;
3196
+ if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
3197
+ return DT_FAILURE | DT_INVALID_PARAM;
3198
+
3199
+ int n = 0;
3200
+ static const int MAX_INTERVAL = 16;
3201
+ dtSegInterval ints[MAX_INTERVAL];
3202
+ int nints;
3203
+
3204
+ const bool storePortals = segmentRefs != 0;
3205
+
3206
+ dtStatus status = DT_SUCCESS;
3207
+
3208
+ for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++)
3209
+ {
3210
+ // Skip non-solid edges.
3211
+ nints = 0;
3212
+ if (poly->neis[j] & DT_EXT_LINK)
3213
+ {
3214
+ // Tile border.
3215
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
3216
+ {
3217
+ const dtLink* link = &tile->links[k];
3218
+ if (link->edge == j)
3219
+ {
3220
+ if (link->ref != 0)
3221
+ {
3222
+ const dtMeshTile* neiTile = 0;
3223
+ const dtPoly* neiPoly = 0;
3224
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
3225
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
3226
+ {
3227
+ insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax, link->ref);
3228
+ }
3229
+ }
3230
+ }
3231
+ }
3232
+ }
3233
+ else
3234
+ {
3235
+ // Internal edge
3236
+ dtPolyRef neiRef = 0;
3237
+ if (poly->neis[j])
3238
+ {
3239
+ const unsigned int idx = (unsigned int)(poly->neis[j]-1);
3240
+ neiRef = m_nav->getPolyRefBase(tile) | idx;
3241
+ if (!filter->passFilter(neiRef, tile, &tile->polys[idx]))
3242
+ neiRef = 0;
3243
+ }
3244
+
3245
+ // If the edge leads to another polygon and portals are not stored, skip.
3246
+ if (neiRef != 0 && !storePortals)
3247
+ continue;
3248
+
3249
+ if (n < maxSegments)
3250
+ {
3251
+ const float* vj = &tile->verts[poly->verts[j]*3];
3252
+ const float* vi = &tile->verts[poly->verts[i]*3];
3253
+ float* seg = &segmentVerts[n*6];
3254
+ dtVcopy(seg+0, vj);
3255
+ dtVcopy(seg+3, vi);
3256
+ if (segmentRefs)
3257
+ segmentRefs[n] = neiRef;
3258
+ n++;
3259
+ }
3260
+ else
3261
+ {
3262
+ status |= DT_BUFFER_TOO_SMALL;
3263
+ }
3264
+
3265
+ continue;
3266
+ }
3267
+
3268
+ // Add sentinels
3269
+ insertInterval(ints, nints, MAX_INTERVAL, -1, 0, 0);
3270
+ insertInterval(ints, nints, MAX_INTERVAL, 255, 256, 0);
3271
+
3272
+ // Store segments.
3273
+ const float* vj = &tile->verts[poly->verts[j]*3];
3274
+ const float* vi = &tile->verts[poly->verts[i]*3];
3275
+ for (int k = 1; k < nints; ++k)
3276
+ {
3277
+ // Portal segment.
3278
+ if (storePortals && ints[k].ref)
3279
+ {
3280
+ const float tmin = ints[k].tmin/255.0f;
3281
+ const float tmax = ints[k].tmax/255.0f;
3282
+ if (n < maxSegments)
3283
+ {
3284
+ float* seg = &segmentVerts[n*6];
3285
+ dtVlerp(seg+0, vj,vi, tmin);
3286
+ dtVlerp(seg+3, vj,vi, tmax);
3287
+ if (segmentRefs)
3288
+ segmentRefs[n] = ints[k].ref;
3289
+ n++;
3290
+ }
3291
+ else
3292
+ {
3293
+ status |= DT_BUFFER_TOO_SMALL;
3294
+ }
3295
+ }
3296
+
3297
+ // Wall segment.
3298
+ const int imin = ints[k-1].tmax;
3299
+ const int imax = ints[k].tmin;
3300
+ if (imin != imax)
3301
+ {
3302
+ const float tmin = imin/255.0f;
3303
+ const float tmax = imax/255.0f;
3304
+ if (n < maxSegments)
3305
+ {
3306
+ float* seg = &segmentVerts[n*6];
3307
+ dtVlerp(seg+0, vj,vi, tmin);
3308
+ dtVlerp(seg+3, vj,vi, tmax);
3309
+ if (segmentRefs)
3310
+ segmentRefs[n] = 0;
3311
+ n++;
3312
+ }
3313
+ else
3314
+ {
3315
+ status |= DT_BUFFER_TOO_SMALL;
3316
+ }
3317
+ }
3318
+ }
3319
+ }
3320
+
3321
+ *segmentCount = n;
3322
+
3323
+ return status;
3324
+ }
3325
+
3326
+ /// @par
3327
+ ///
3328
+ /// @p hitPos is not adjusted using the height detail data.
3329
+ ///
3330
+ /// @p hitDist will equal the search radius if there is no wall within the
3331
+ /// radius. In this case the values of @p hitPos and @p hitNormal are
3332
+ /// undefined.
3333
+ ///
3334
+ /// The normal will become unpredicable if @p hitDist is a very small number.
3335
+ ///
3336
+ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
3337
+ const dtQueryFilter* filter,
3338
+ float* hitDist, float* hitPos, float* hitNormal) const
3339
+ {
3340
+ dtAssert(m_nav);
3341
+ dtAssert(m_nodePool);
3342
+ dtAssert(m_openList);
3343
+
3344
+ // Validate input
3345
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
3346
+ return DT_FAILURE | DT_INVALID_PARAM;
3347
+
3348
+ m_nodePool->clear();
3349
+ m_openList->clear();
3350
+
3351
+ dtNode* startNode = m_nodePool->getNode(startRef);
3352
+ dtVcopy(startNode->pos, centerPos);
3353
+ startNode->pidx = 0;
3354
+ startNode->cost = 0;
3355
+ startNode->total = 0;
3356
+ startNode->id = startRef;
3357
+ startNode->flags = DT_NODE_OPEN;
3358
+ m_openList->push(startNode);
3359
+
3360
+ float radiusSqr = dtSqr(maxRadius);
3361
+
3362
+ dtStatus status = DT_SUCCESS;
3363
+
3364
+ while (!m_openList->empty())
3365
+ {
3366
+ dtNode* bestNode = m_openList->pop();
3367
+ bestNode->flags &= ~DT_NODE_OPEN;
3368
+ bestNode->flags |= DT_NODE_CLOSED;
3369
+
3370
+ // Get poly and tile.
3371
+ // The API input has been cheked already, skip checking internal data.
3372
+ const dtPolyRef bestRef = bestNode->id;
3373
+ const dtMeshTile* bestTile = 0;
3374
+ const dtPoly* bestPoly = 0;
3375
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
3376
+
3377
+ // Get parent poly and tile.
3378
+ dtPolyRef parentRef = 0;
3379
+ const dtMeshTile* parentTile = 0;
3380
+ const dtPoly* parentPoly = 0;
3381
+ if (bestNode->pidx)
3382
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
3383
+ if (parentRef)
3384
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
3385
+
3386
+ // Hit test walls.
3387
+ for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++)
3388
+ {
3389
+ // Skip non-solid edges.
3390
+ if (bestPoly->neis[j] & DT_EXT_LINK)
3391
+ {
3392
+ // Tile border.
3393
+ bool solid = true;
3394
+ for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next)
3395
+ {
3396
+ const dtLink* link = &bestTile->links[k];
3397
+ if (link->edge == j)
3398
+ {
3399
+ if (link->ref != 0)
3400
+ {
3401
+ const dtMeshTile* neiTile = 0;
3402
+ const dtPoly* neiPoly = 0;
3403
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
3404
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
3405
+ solid = false;
3406
+ }
3407
+ break;
3408
+ }
3409
+ }
3410
+ if (!solid) continue;
3411
+ }
3412
+ else if (bestPoly->neis[j])
3413
+ {
3414
+ // Internal edge
3415
+ const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1);
3416
+ const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx;
3417
+ if (filter->passFilter(ref, bestTile, &bestTile->polys[idx]))
3418
+ continue;
3419
+ }
3420
+
3421
+ // Calc distance to the edge.
3422
+ const float* vj = &bestTile->verts[bestPoly->verts[j]*3];
3423
+ const float* vi = &bestTile->verts[bestPoly->verts[i]*3];
3424
+ float tseg;
3425
+ float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg);
3426
+
3427
+ // Edge is too far, skip.
3428
+ if (distSqr > radiusSqr)
3429
+ continue;
3430
+
3431
+ // Hit wall, update radius.
3432
+ radiusSqr = distSqr;
3433
+ // Calculate hit pos.
3434
+ hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg;
3435
+ hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg;
3436
+ hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg;
3437
+ }
3438
+
3439
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
3440
+ {
3441
+ const dtLink* link = &bestTile->links[i];
3442
+ dtPolyRef neighbourRef = link->ref;
3443
+ // Skip invalid neighbours and do not follow back to parent.
3444
+ if (!neighbourRef || neighbourRef == parentRef)
3445
+ continue;
3446
+
3447
+ // Expand to neighbour.
3448
+ const dtMeshTile* neighbourTile = 0;
3449
+ const dtPoly* neighbourPoly = 0;
3450
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
3451
+
3452
+ // Skip off-mesh connections.
3453
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
3454
+ continue;
3455
+
3456
+ // Calc distance to the edge.
3457
+ const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3];
3458
+ const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3];
3459
+ float tseg;
3460
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
3461
+
3462
+ // If the circle is not touching the next polygon, skip it.
3463
+ if (distSqr > radiusSqr)
3464
+ continue;
3465
+
3466
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
3467
+ continue;
3468
+
3469
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
3470
+ if (!neighbourNode)
3471
+ {
3472
+ status |= DT_OUT_OF_NODES;
3473
+ continue;
3474
+ }
3475
+
3476
+ if (neighbourNode->flags & DT_NODE_CLOSED)
3477
+ continue;
3478
+
3479
+ // Cost
3480
+ if (neighbourNode->flags == 0)
3481
+ {
3482
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
3483
+ neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos);
3484
+ }
3485
+
3486
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
3487
+
3488
+ // The node is already in open list and the new result is worse, skip.
3489
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
3490
+ continue;
3491
+
3492
+ neighbourNode->id = neighbourRef;
3493
+ neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
3494
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
3495
+ neighbourNode->total = total;
3496
+
3497
+ if (neighbourNode->flags & DT_NODE_OPEN)
3498
+ {
3499
+ m_openList->modify(neighbourNode);
3500
+ }
3501
+ else
3502
+ {
3503
+ neighbourNode->flags |= DT_NODE_OPEN;
3504
+ m_openList->push(neighbourNode);
3505
+ }
3506
+ }
3507
+ }
3508
+
3509
+ // Calc hit normal.
3510
+ dtVsub(hitNormal, centerPos, hitPos);
3511
+ dtVnormalize(hitNormal);
3512
+
3513
+ *hitDist = dtMathSqrtf(radiusSqr);
3514
+
3515
+ return status;
3516
+ }
3517
+
3518
+ bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const
3519
+ {
3520
+ const dtMeshTile* tile = 0;
3521
+ const dtPoly* poly = 0;
3522
+ dtStatus status = m_nav->getTileAndPolyByRef(ref, &tile, &poly);
3523
+ // If cannot get polygon, assume it does not exists and boundary is invalid.
3524
+ if (dtStatusFailed(status))
3525
+ return false;
3526
+ // If cannot pass filter, assume flags has changed and boundary is invalid.
3527
+ if (!filter->passFilter(ref, tile, poly))
3528
+ return false;
3529
+ return true;
3530
+ }
3531
+
3532
+ /// @par
3533
+ ///
3534
+ /// The closed list is the list of polygons that were fully evaluated during
3535
+ /// the last navigation graph search. (A* or Dijkstra)
3536
+ ///
3537
+ bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
3538
+ {
3539
+ if (!m_nodePool) return false;
3540
+
3541
+ dtNode* nodes[DT_MAX_STATES_PER_NODE];
3542
+ int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE);
3543
+
3544
+ for (int i=0; i<n; i++)
3545
+ {
3546
+ if (nodes[i]->flags & DT_NODE_CLOSED)
3547
+ return true;
3548
+ }
3549
+
3550
+ return false;
3551
+ }