game_machine 0.0.11 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,408 @@
1
+
2
+ #include "navmesh.h"
3
+ using namespace std;
4
+
5
+ myQueryFilter::myQueryFilter() :
6
+ m_includeFlags(0xffff),
7
+ m_excludeFlags(0)
8
+
9
+ {
10
+ for (int i = 0; i < DT_MAX_AREAS; ++i)
11
+ m_areaCost[i] = 1.0f;
12
+ }
13
+
14
+ myQueryFilter::~myQueryFilter() { }
15
+
16
+ void myQueryFilter::setNavmesh(Navmesh* n)
17
+ {
18
+ navmesh = n;
19
+ }
20
+
21
+ bool myQueryFilter::passFilter(const dtPolyRef /*ref*/,
22
+ const dtMeshTile* /*tile*/,
23
+ const dtPoly* poly) const
24
+ {
25
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
26
+ }
27
+
28
+ bool myQueryFilter::isPassable(const float* pa, const float* pb) const
29
+ {
30
+ int x = int(pb[0]);
31
+ int y = int(pb[2]);
32
+
33
+ int index = y*navmesh->gwidth+x;
34
+ int c = navmesh->gmap[ index ];
35
+
36
+ if (c > 0)
37
+ {
38
+ fprintf (stderr, "isPassable false %f %f %f -> %f %f %f\n", pa[0], pa[1], pa[2],pb[0], pb[1], pb[2]);
39
+ return false;
40
+ }
41
+
42
+ return true;
43
+ }
44
+
45
+ float myQueryFilter::getCost(const float* pa, const float* pb,
46
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
47
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
48
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
49
+ {
50
+
51
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
52
+ }
53
+
54
+
55
+ float const Navmesh::STEP_SIZE = 0.5f;
56
+ float const Navmesh::SLOP = 0.01f;
57
+
58
+ void Navmesh::gmapSetPassable(int a, int b, int radius)
59
+ {
60
+ for(int x = a - radius; x <= a + radius; x++)
61
+ {
62
+ for(int y = b - radius; y <= b + radius; y++)
63
+ {
64
+ gmap[ y*gwidth + x] = 0;
65
+ }
66
+ }
67
+ }
68
+
69
+ void Navmesh::gmapSetBlocked(int a, int b, int radius)
70
+ {
71
+ for(int x = a - radius; x <= a + radius; x++)
72
+ {
73
+ for(int y = b - radius; y <= b + radius; y++)
74
+ {
75
+ gmap[ y*gwidth + x] = radius;
76
+ }
77
+ }
78
+ }
79
+
80
+ Navmesh::Navmesh(int id, const char *file) :
81
+ meshId(id),
82
+ gmap(2000*2000+1,0)
83
+ {
84
+ gwidth = 2000;
85
+ gheight = 2000;
86
+ mesh = load_navmesh(file);
87
+ query = dtAllocNavMeshQuery();
88
+ query->init(mesh, 4096);
89
+ }
90
+
91
+ bool Navmesh::meshLoaded()
92
+ {
93
+ if (mesh == 0)
94
+ {
95
+ return false;
96
+ }
97
+ return true;
98
+ }
99
+
100
+ int Navmesh::findPath(float startx, float starty,
101
+ float startz, float endx, float endy, float endz, int find_straight_path,
102
+ float* resultPath) {
103
+
104
+ if (query == NULL) {
105
+ return P_QUERY_NOT_FOUND;
106
+ }
107
+
108
+ float m_spos[3] = {startx,starty,startz};
109
+ float m_epos[3] = {endx,endy,endz};
110
+
111
+
112
+ dtPolyRef m_polys[MAX_POLYS];
113
+
114
+ //dtQueryFilter m_filter;
115
+ myQueryFilter m_filter;
116
+ m_filter.setNavmesh(this);
117
+ float straight[MAX_POLYS*3];
118
+ int straightPathCount = 0;
119
+ float polyPickExt[3] = {2,4,2};
120
+ int m_npolys = 0;
121
+
122
+ float m_smoothPath[MAX_SMOOTH*3];
123
+ int m_nsmoothPath = 0;
124
+
125
+ m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
126
+ m_filter.setExcludeFlags(0);
127
+
128
+ dtPolyRef m_startRef, m_endRef;
129
+
130
+ dtStatus res;
131
+
132
+ res = query->findNearestPoly(m_spos, polyPickExt, &m_filter, &m_startRef, 0);
133
+ if (res == DT_SUCCESS) {
134
+ if (m_startRef == 0) {
135
+ return P_NO_START_POLY;
136
+ }
137
+ } else {
138
+ return P_NO_START_POLY;
139
+ }
140
+
141
+ res = query->findNearestPoly(m_epos, polyPickExt, &m_filter, &m_endRef, 0);
142
+ if (res == DT_SUCCESS) {
143
+ if (m_endRef == 0) {
144
+ return P_NO_END_POLY;
145
+ }
146
+ } else {
147
+ return P_NO_END_POLY;
148
+ }
149
+
150
+ res = query->findPath(m_startRef, m_endRef, m_spos, m_epos, &m_filter, m_polys, &m_npolys, MAX_POLYS);
151
+ if (res != DT_SUCCESS) {
152
+ return P_PATH_NOT_FOUND;
153
+ }
154
+
155
+
156
+ if (find_straight_path == 0) {
157
+ dtPolyRef polys[MAX_POLYS];
158
+ memcpy(polys, m_polys, sizeof(dtPolyRef)*m_npolys);
159
+ int npolys = m_npolys;
160
+
161
+ float iterPos[3], targetPos[3];
162
+ query->closestPointOnPoly(m_startRef, m_spos, iterPos,0);
163
+ query->closestPointOnPoly(polys[npolys-1], m_epos, targetPos,0);
164
+
165
+ m_nsmoothPath = 0;
166
+
167
+ dtVcopy(&m_smoothPath[m_nsmoothPath*3], iterPos);
168
+ m_nsmoothPath++;
169
+
170
+ while (npolys && m_nsmoothPath < MAX_SMOOTH) {
171
+ // Find location to steer towards.
172
+ float steerPos[3];
173
+ unsigned char steerPosFlag;
174
+ dtPolyRef steerPosRef;
175
+
176
+ if (!getSteerTarget(query, iterPos, targetPos, SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef)) {
177
+ break;
178
+ }
179
+
180
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) ? true : false;
181
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
182
+
183
+ // Find movement delta.
184
+ float delta[3], len;
185
+ dtVsub(delta, steerPos, iterPos);
186
+ len = dtSqrt(dtVdot(delta,delta));
187
+ // If the steer target is end of path or off-mesh link, do not move past the location.
188
+ if ((endOfPath || offMeshConnection) && len < STEP_SIZE) {
189
+ len = 1;
190
+ } else {
191
+ len = STEP_SIZE / len;
192
+ }
193
+
194
+ float moveTgt[3];
195
+ dtVmad(moveTgt, iterPos, delta, len);
196
+
197
+ // Move
198
+ float result[3];
199
+ dtPolyRef visited[16];
200
+ int nvisited = 0;
201
+ query->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter, result, visited, &nvisited, 16);
202
+
203
+ npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
204
+ npolys = fixupShortcuts(polys, npolys, query);
205
+
206
+ float h = 0;
207
+ query->getPolyHeight(polys[0], result, &h);
208
+ result[1] = h;
209
+ dtVcopy(iterPos, result);
210
+
211
+ if (endOfPath && inRange(iterPos, steerPos, SLOP, 1.0f)) {
212
+ // Reached end of path.
213
+ fprintf (stderr, "End of path reached\n");
214
+ dtVcopy(iterPos, targetPos);
215
+ if (m_nsmoothPath < MAX_SMOOTH) {
216
+ dtVcopy(&m_smoothPath[m_nsmoothPath*3], iterPos);
217
+ m_nsmoothPath++;
218
+ }
219
+ break;
220
+ }
221
+
222
+ if (m_nsmoothPath < MAX_SMOOTH) {
223
+ dtVcopy(&m_smoothPath[m_nsmoothPath*3], iterPos);
224
+ m_nsmoothPath++;
225
+ }
226
+ }
227
+
228
+ memcpy(resultPath, m_smoothPath, sizeof(float)*3*m_nsmoothPath);
229
+ return m_nsmoothPath;
230
+ }
231
+
232
+ query->findStraightPath(m_spos, m_epos, m_polys, m_npolys, straight, 0, 0, &straightPathCount, MAX_POLYS);
233
+ memcpy(resultPath, straight, sizeof(float)*3*straightPathCount);
234
+ return straightPathCount;
235
+ }
236
+
237
+ float* Navmesh::getPathPtr(int max_paths) {
238
+ float *path;
239
+ path = new float[max_paths*3];
240
+ return path;
241
+ }
242
+
243
+ void Navmesh::freePath(float* path) {
244
+ delete [] path;
245
+ }
246
+
247
+ dtNavMesh* Navmesh::getMesh() {
248
+ return mesh;
249
+ }
250
+
251
+ dtNavMeshQuery* Navmesh::getQuery() {
252
+ return query;
253
+ }
254
+
255
+ void Navmesh::freeQuery(dtNavMeshQuery* query) {
256
+ dtFreeNavMeshQuery(query);
257
+ }
258
+
259
+
260
+
261
+ inline bool Navmesh::inRange(const float* v1, const float* v2, const float r, const float h)
262
+ {
263
+ const float dx = v2[0] - v1[0];
264
+ const float dy = v2[1] - v1[1];
265
+ const float dz = v2[2] - v1[2];
266
+ return (dx*dx + dz*dz) < r*r && fabsf(dy) < h;
267
+ }
268
+
269
+ int Navmesh::fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
270
+ {
271
+ if (npath < 3)
272
+ return npath;
273
+
274
+ // Get connected polygons
275
+ static const int maxNeis = 16;
276
+ dtPolyRef neis[maxNeis];
277
+ int nneis = 0;
278
+
279
+ const dtMeshTile* tile = 0;
280
+ const dtPoly* poly = 0;
281
+ if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
282
+ return npath;
283
+
284
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
285
+ {
286
+ const dtLink* link = &tile->links[k];
287
+ if (link->ref != 0)
288
+ {
289
+ if (nneis < maxNeis)
290
+ neis[nneis++] = link->ref;
291
+ }
292
+ }
293
+
294
+ // If any of the neighbour polygons is within the next few polygons
295
+ // in the path, short cut to that polygon directly.
296
+ static const int maxLookAhead = 6;
297
+ int cut = 0;
298
+ for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
299
+ for (int j = 0; j < nneis; j++)
300
+ {
301
+ if (path[i] == neis[j]) {
302
+ cut = i;
303
+ break;
304
+ }
305
+ }
306
+ }
307
+ if (cut > 1)
308
+ {
309
+ int offset = cut-1;
310
+ npath -= offset;
311
+ for (int i = 1; i < npath; i++)
312
+ path[i] = path[i+offset];
313
+ }
314
+
315
+ return npath;
316
+ }
317
+
318
+ bool Navmesh::getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
319
+ const float minTargetDist,
320
+ const dtPolyRef* path, const int pathSize,
321
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef,
322
+ float* outPoints, int* outPointCount)
323
+ {
324
+ // Find steer target.
325
+ static const int MAX_STEER_POINTS = 3;
326
+ float steerPath[MAX_STEER_POINTS*3];
327
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
328
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
329
+ int nsteerPath = 0;
330
+ navQuery->findStraightPath(startPos, endPos, path, pathSize,
331
+ steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS);
332
+ if (!nsteerPath)
333
+ return false;
334
+
335
+ if (outPoints && outPointCount)
336
+ {
337
+ *outPointCount = nsteerPath;
338
+ for (int i = 0; i < nsteerPath; ++i)
339
+ dtVcopy(&outPoints[i*3], &steerPath[i*3]);
340
+ }
341
+
342
+
343
+ // Find vertex far enough to steer to.
344
+ int ns = 0;
345
+ while (ns < nsteerPath)
346
+ {
347
+ // Stop at Off-Mesh link or when point is further than slop away.
348
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
349
+ !inRange(&steerPath[ns*3], startPos, minTargetDist, 1000.0f))
350
+ break;
351
+ ns++;
352
+ }
353
+ // Failed to find good point to steer to.
354
+ if (ns >= nsteerPath)
355
+ return false;
356
+
357
+ dtVcopy(steerPos, &steerPath[ns*3]);
358
+ steerPos[1] = startPos[1];
359
+ steerPosFlag = steerPathFlags[ns];
360
+ steerPosRef = steerPathPolys[ns];
361
+
362
+ return true;
363
+ }
364
+
365
+ int Navmesh::fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
366
+ const dtPolyRef* visited, const int nvisited)
367
+ {
368
+ int furthestPath = -1;
369
+ int furthestVisited = -1;
370
+
371
+ // Find furthest common polygon.
372
+ for (int i = npath-1; i >= 0; --i)
373
+ {
374
+ bool found = false;
375
+ for (int j = nvisited-1; j >= 0; --j)
376
+ {
377
+ if (path[i] == visited[j])
378
+ {
379
+ furthestPath = i;
380
+ furthestVisited = j;
381
+ found = true;
382
+ }
383
+ }
384
+ if (found)
385
+ break;
386
+ }
387
+
388
+ // If no intersection found just return current path.
389
+ if (furthestPath == -1 || furthestVisited == -1)
390
+ return npath;
391
+
392
+ // Concatenate paths.
393
+
394
+ // Adjust beginning of the buffer to include the visited.
395
+ const int req = nvisited - furthestVisited;
396
+ const int orig = rcMin(furthestPath+1, npath);
397
+ int size = rcMax(0, npath-orig);
398
+ if (req+size > maxPath)
399
+ size = maxPath-req;
400
+ if (size)
401
+ memmove(path+req, path+orig, size*sizeof(dtPolyRef));
402
+
403
+ // Store visited
404
+ for (int i = 0; i < req; ++i)
405
+ path[i] = visited[(nvisited-1)-i];
406
+
407
+ return req+size;
408
+ }
@@ -0,0 +1,1446 @@
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
+
19
+ #define _USE_MATH_DEFINES
20
+ #include <string.h>
21
+ #include <float.h>
22
+ #include <stdlib.h>
23
+ #include <new>
24
+ #include "DetourCrowd.h"
25
+ #include "DetourNavMesh.h"
26
+ #include "DetourNavMeshQuery.h"
27
+ #include "DetourObstacleAvoidance.h"
28
+ #include "DetourCommon.h"
29
+ #include "DetourMath.h"
30
+ #include "DetourAssert.h"
31
+ #include "DetourAlloc.h"
32
+
33
+
34
+ dtCrowd* dtAllocCrowd()
35
+ {
36
+ void* mem = dtAlloc(sizeof(dtCrowd), DT_ALLOC_PERM);
37
+ if (!mem) return 0;
38
+ return new(mem) dtCrowd;
39
+ }
40
+
41
+ void dtFreeCrowd(dtCrowd* ptr)
42
+ {
43
+ if (!ptr) return;
44
+ ptr->~dtCrowd();
45
+ dtFree(ptr);
46
+ }
47
+
48
+
49
+ static const int MAX_ITERS_PER_UPDATE = 100;
50
+
51
+ static const int MAX_PATHQUEUE_NODES = 4096;
52
+ static const int MAX_COMMON_NODES = 512;
53
+
54
+ inline float tween(const float t, const float t0, const float t1)
55
+ {
56
+ return dtClamp((t-t0) / (t1-t0), 0.0f, 1.0f);
57
+ }
58
+
59
+ static void integrate(dtCrowdAgent* ag, const float dt)
60
+ {
61
+ // Fake dynamic constraint.
62
+ const float maxDelta = ag->params.maxAcceleration * dt;
63
+ float dv[3];
64
+ dtVsub(dv, ag->nvel, ag->vel);
65
+ float ds = dtVlen(dv);
66
+ if (ds > maxDelta)
67
+ dtVscale(dv, dv, maxDelta/ds);
68
+ dtVadd(ag->vel, ag->vel, dv);
69
+
70
+ // Integrate
71
+ if (dtVlen(ag->vel) > 0.0001f)
72
+ dtVmad(ag->npos, ag->npos, ag->vel, dt);
73
+ else
74
+ dtVset(ag->vel,0,0,0);
75
+ }
76
+
77
+ static bool overOffmeshConnection(const dtCrowdAgent* ag, const float radius)
78
+ {
79
+ if (!ag->ncorners)
80
+ return false;
81
+
82
+ const bool offMeshConnection = (ag->cornerFlags[ag->ncorners-1] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
83
+ if (offMeshConnection)
84
+ {
85
+ const float distSq = dtVdist2DSqr(ag->npos, &ag->cornerVerts[(ag->ncorners-1)*3]);
86
+ if (distSq < radius*radius)
87
+ return true;
88
+ }
89
+
90
+ return false;
91
+ }
92
+
93
+ static float getDistanceToGoal(const dtCrowdAgent* ag, const float range)
94
+ {
95
+ if (!ag->ncorners)
96
+ return range;
97
+
98
+ const bool endOfPath = (ag->cornerFlags[ag->ncorners-1] & DT_STRAIGHTPATH_END) ? true : false;
99
+ if (endOfPath)
100
+ return dtMin(dtVdist2D(ag->npos, &ag->cornerVerts[(ag->ncorners-1)*3]), range);
101
+
102
+ return range;
103
+ }
104
+
105
+ static void calcSmoothSteerDirection(const dtCrowdAgent* ag, float* dir)
106
+ {
107
+ if (!ag->ncorners)
108
+ {
109
+ dtVset(dir, 0,0,0);
110
+ return;
111
+ }
112
+
113
+ const int ip0 = 0;
114
+ const int ip1 = dtMin(1, ag->ncorners-1);
115
+ const float* p0 = &ag->cornerVerts[ip0*3];
116
+ const float* p1 = &ag->cornerVerts[ip1*3];
117
+
118
+ float dir0[3], dir1[3];
119
+ dtVsub(dir0, p0, ag->npos);
120
+ dtVsub(dir1, p1, ag->npos);
121
+ dir0[1] = 0;
122
+ dir1[1] = 0;
123
+
124
+ float len0 = dtVlen(dir0);
125
+ float len1 = dtVlen(dir1);
126
+ if (len1 > 0.001f)
127
+ dtVscale(dir1,dir1,1.0f/len1);
128
+
129
+ dir[0] = dir0[0] - dir1[0]*len0*0.5f;
130
+ dir[1] = 0;
131
+ dir[2] = dir0[2] - dir1[2]*len0*0.5f;
132
+
133
+ dtVnormalize(dir);
134
+ }
135
+
136
+ static void calcStraightSteerDirection(const dtCrowdAgent* ag, float* dir)
137
+ {
138
+ if (!ag->ncorners)
139
+ {
140
+ dtVset(dir, 0,0,0);
141
+ return;
142
+ }
143
+ dtVsub(dir, &ag->cornerVerts[0], ag->npos);
144
+ dir[1] = 0;
145
+ dtVnormalize(dir);
146
+ }
147
+
148
+ static int addNeighbour(const int idx, const float dist,
149
+ dtCrowdNeighbour* neis, const int nneis, const int maxNeis)
150
+ {
151
+ // Insert neighbour based on the distance.
152
+ dtCrowdNeighbour* nei = 0;
153
+ if (!nneis)
154
+ {
155
+ nei = &neis[nneis];
156
+ }
157
+ else if (dist >= neis[nneis-1].dist)
158
+ {
159
+ if (nneis >= maxNeis)
160
+ return nneis;
161
+ nei = &neis[nneis];
162
+ }
163
+ else
164
+ {
165
+ int i;
166
+ for (i = 0; i < nneis; ++i)
167
+ if (dist <= neis[i].dist)
168
+ break;
169
+
170
+ const int tgt = i+1;
171
+ const int n = dtMin(nneis-i, maxNeis-tgt);
172
+
173
+ dtAssert(tgt+n <= maxNeis);
174
+
175
+ if (n > 0)
176
+ memmove(&neis[tgt], &neis[i], sizeof(dtCrowdNeighbour)*n);
177
+ nei = &neis[i];
178
+ }
179
+
180
+ memset(nei, 0, sizeof(dtCrowdNeighbour));
181
+
182
+ nei->idx = idx;
183
+ nei->dist = dist;
184
+
185
+ return dtMin(nneis+1, maxNeis);
186
+ }
187
+
188
+ static int getNeighbours(const float* pos, const float height, const float range,
189
+ const dtCrowdAgent* skip, dtCrowdNeighbour* result, const int maxResult,
190
+ dtCrowdAgent** agents, const int /*nagents*/, dtProximityGrid* grid)
191
+ {
192
+ int n = 0;
193
+
194
+ static const int MAX_NEIS = 32;
195
+ unsigned short ids[MAX_NEIS];
196
+ int nids = grid->queryItems(pos[0]-range, pos[2]-range,
197
+ pos[0]+range, pos[2]+range,
198
+ ids, MAX_NEIS);
199
+
200
+ for (int i = 0; i < nids; ++i)
201
+ {
202
+ const dtCrowdAgent* ag = agents[ids[i]];
203
+
204
+ if (ag == skip) continue;
205
+
206
+ // Check for overlap.
207
+ float diff[3];
208
+ dtVsub(diff, pos, ag->npos);
209
+ if (dtMathFabs(diff[1]) >= (height+ag->params.height)/2.0f)
210
+ continue;
211
+ diff[1] = 0;
212
+ const float distSqr = dtVlenSqr(diff);
213
+ if (distSqr > dtSqr(range))
214
+ continue;
215
+
216
+ n = addNeighbour(ids[i], distSqr, result, n, maxResult);
217
+ }
218
+ return n;
219
+ }
220
+
221
+ static int addToOptQueue(dtCrowdAgent* newag, dtCrowdAgent** agents, const int nagents, const int maxAgents)
222
+ {
223
+ // Insert neighbour based on greatest time.
224
+ int slot = 0;
225
+ if (!nagents)
226
+ {
227
+ slot = nagents;
228
+ }
229
+ else if (newag->topologyOptTime <= agents[nagents-1]->topologyOptTime)
230
+ {
231
+ if (nagents >= maxAgents)
232
+ return nagents;
233
+ slot = nagents;
234
+ }
235
+ else
236
+ {
237
+ int i;
238
+ for (i = 0; i < nagents; ++i)
239
+ if (newag->topologyOptTime >= agents[i]->topologyOptTime)
240
+ break;
241
+
242
+ const int tgt = i+1;
243
+ const int n = dtMin(nagents-i, maxAgents-tgt);
244
+
245
+ dtAssert(tgt+n <= maxAgents);
246
+
247
+ if (n > 0)
248
+ memmove(&agents[tgt], &agents[i], sizeof(dtCrowdAgent*)*n);
249
+ slot = i;
250
+ }
251
+
252
+ agents[slot] = newag;
253
+
254
+ return dtMin(nagents+1, maxAgents);
255
+ }
256
+
257
+ static int addToPathQueue(dtCrowdAgent* newag, dtCrowdAgent** agents, const int nagents, const int maxAgents)
258
+ {
259
+ // Insert neighbour based on greatest time.
260
+ int slot = 0;
261
+ if (!nagents)
262
+ {
263
+ slot = nagents;
264
+ }
265
+ else if (newag->targetReplanTime <= agents[nagents-1]->targetReplanTime)
266
+ {
267
+ if (nagents >= maxAgents)
268
+ return nagents;
269
+ slot = nagents;
270
+ }
271
+ else
272
+ {
273
+ int i;
274
+ for (i = 0; i < nagents; ++i)
275
+ if (newag->targetReplanTime >= agents[i]->targetReplanTime)
276
+ break;
277
+
278
+ const int tgt = i+1;
279
+ const int n = dtMin(nagents-i, maxAgents-tgt);
280
+
281
+ dtAssert(tgt+n <= maxAgents);
282
+
283
+ if (n > 0)
284
+ memmove(&agents[tgt], &agents[i], sizeof(dtCrowdAgent*)*n);
285
+ slot = i;
286
+ }
287
+
288
+ agents[slot] = newag;
289
+
290
+ return dtMin(nagents+1, maxAgents);
291
+ }
292
+
293
+
294
+ /**
295
+ @class dtCrowd
296
+ @par
297
+
298
+ This is the core class of the @ref crowd module. See the @ref crowd documentation for a summary
299
+ of the crowd features.
300
+
301
+ A common method for setting up the crowd is as follows:
302
+
303
+ -# Allocate the crowd using #dtAllocCrowd.
304
+ -# Initialize the crowd using #init().
305
+ -# Set the avoidance configurations using #setObstacleAvoidanceParams().
306
+ -# Add agents using #addAgent() and make an initial movement request using #requestMoveTarget().
307
+
308
+ A common process for managing the crowd is as follows:
309
+
310
+ -# Call #update() to allow the crowd to manage its agents.
311
+ -# Retrieve agent information using #getActiveAgents().
312
+ -# Make movement requests using #requestMoveTarget() when movement goal changes.
313
+ -# Repeat every frame.
314
+
315
+ Some agent configuration settings can be updated using #updateAgentParameters(). But the crowd owns the
316
+ agent position. So it is not possible to update an active agent's position. If agent position
317
+ must be fed back into the crowd, the agent must be removed and re-added.
318
+
319
+ Notes:
320
+
321
+ - Path related information is available for newly added agents only after an #update() has been
322
+ performed.
323
+ - Agent objects are kept in a pool and re-used. So it is important when using agent objects to check the value of
324
+ #dtCrowdAgent::active to determine if the agent is actually in use or not.
325
+ - This class is meant to provide 'local' movement. There is a limit of 256 polygons in the path corridor.
326
+ So it is not meant to provide automatic pathfinding services over long distances.
327
+
328
+ @see dtAllocCrowd(), dtFreeCrowd(), init(), dtCrowdAgent
329
+
330
+ */
331
+
332
+ dtCrowd::dtCrowd() :
333
+ m_maxAgents(0),
334
+ m_agents(0),
335
+ m_activeAgents(0),
336
+ m_agentAnims(0),
337
+ m_obstacleQuery(0),
338
+ m_grid(0),
339
+ m_pathResult(0),
340
+ m_maxPathResult(0),
341
+ m_maxAgentRadius(0),
342
+ m_velocitySampleCount(0),
343
+ m_navquery(0)
344
+ {
345
+ }
346
+
347
+ dtCrowd::~dtCrowd()
348
+ {
349
+ purge();
350
+ }
351
+
352
+ void dtCrowd::purge()
353
+ {
354
+ for (int i = 0; i < m_maxAgents; ++i)
355
+ m_agents[i].~dtCrowdAgent();
356
+ dtFree(m_agents);
357
+ m_agents = 0;
358
+ m_maxAgents = 0;
359
+
360
+ dtFree(m_activeAgents);
361
+ m_activeAgents = 0;
362
+
363
+ dtFree(m_agentAnims);
364
+ m_agentAnims = 0;
365
+
366
+ dtFree(m_pathResult);
367
+ m_pathResult = 0;
368
+
369
+ dtFreeProximityGrid(m_grid);
370
+ m_grid = 0;
371
+
372
+ dtFreeObstacleAvoidanceQuery(m_obstacleQuery);
373
+ m_obstacleQuery = 0;
374
+
375
+ dtFreeNavMeshQuery(m_navquery);
376
+ m_navquery = 0;
377
+ }
378
+
379
+ /// @par
380
+ ///
381
+ /// May be called more than once to purge and re-initialize the crowd.
382
+ bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav)
383
+ {
384
+ purge();
385
+
386
+ m_maxAgents = maxAgents;
387
+ m_maxAgentRadius = maxAgentRadius;
388
+
389
+ dtVset(m_ext, m_maxAgentRadius*2.0f,m_maxAgentRadius*1.5f,m_maxAgentRadius*2.0f);
390
+
391
+ m_grid = dtAllocProximityGrid();
392
+ if (!m_grid)
393
+ return false;
394
+ if (!m_grid->init(m_maxAgents*4, maxAgentRadius*3))
395
+ return false;
396
+
397
+ m_obstacleQuery = dtAllocObstacleAvoidanceQuery();
398
+ if (!m_obstacleQuery)
399
+ return false;
400
+ if (!m_obstacleQuery->init(6, 8))
401
+ return false;
402
+
403
+ // Init obstacle query params.
404
+ memset(m_obstacleQueryParams, 0, sizeof(m_obstacleQueryParams));
405
+ for (int i = 0; i < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS; ++i)
406
+ {
407
+ dtObstacleAvoidanceParams* params = &m_obstacleQueryParams[i];
408
+ params->velBias = 0.4f;
409
+ params->weightDesVel = 2.0f;
410
+ params->weightCurVel = 0.75f;
411
+ params->weightSide = 0.75f;
412
+ params->weightToi = 2.5f;
413
+ params->horizTime = 2.5f;
414
+ params->gridSize = 33;
415
+ params->adaptiveDivs = 7;
416
+ params->adaptiveRings = 2;
417
+ params->adaptiveDepth = 5;
418
+ }
419
+
420
+ // Allocate temp buffer for merging paths.
421
+ m_maxPathResult = 256;
422
+ m_pathResult = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*m_maxPathResult, DT_ALLOC_PERM);
423
+ if (!m_pathResult)
424
+ return false;
425
+
426
+ if (!m_pathq.init(m_maxPathResult, MAX_PATHQUEUE_NODES, nav))
427
+ return false;
428
+
429
+ m_agents = (dtCrowdAgent*)dtAlloc(sizeof(dtCrowdAgent)*m_maxAgents, DT_ALLOC_PERM);
430
+ if (!m_agents)
431
+ return false;
432
+
433
+ m_activeAgents = (dtCrowdAgent**)dtAlloc(sizeof(dtCrowdAgent*)*m_maxAgents, DT_ALLOC_PERM);
434
+ if (!m_activeAgents)
435
+ return false;
436
+
437
+ m_agentAnims = (dtCrowdAgentAnimation*)dtAlloc(sizeof(dtCrowdAgentAnimation)*m_maxAgents, DT_ALLOC_PERM);
438
+ if (!m_agentAnims)
439
+ return false;
440
+
441
+ for (int i = 0; i < m_maxAgents; ++i)
442
+ {
443
+ new(&m_agents[i]) dtCrowdAgent();
444
+ m_agents[i].active = false;
445
+ if (!m_agents[i].corridor.init(m_maxPathResult))
446
+ return false;
447
+ }
448
+
449
+ for (int i = 0; i < m_maxAgents; ++i)
450
+ {
451
+ m_agentAnims[i].active = false;
452
+ }
453
+
454
+ // The navquery is mostly used for local searches, no need for large node pool.
455
+ m_navquery = dtAllocNavMeshQuery();
456
+ if (!m_navquery)
457
+ return false;
458
+ if (dtStatusFailed(m_navquery->init(nav, MAX_COMMON_NODES)))
459
+ return false;
460
+
461
+ return true;
462
+ }
463
+
464
+ void dtCrowd::setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params)
465
+ {
466
+ if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
467
+ memcpy(&m_obstacleQueryParams[idx], params, sizeof(dtObstacleAvoidanceParams));
468
+ }
469
+
470
+ const dtObstacleAvoidanceParams* dtCrowd::getObstacleAvoidanceParams(const int idx) const
471
+ {
472
+ if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
473
+ return &m_obstacleQueryParams[idx];
474
+ return 0;
475
+ }
476
+
477
+ int dtCrowd::getAgentCount() const
478
+ {
479
+ return m_maxAgents;
480
+ }
481
+
482
+ /// @par
483
+ ///
484
+ /// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
485
+ const dtCrowdAgent* dtCrowd::getAgent(const int idx)
486
+ {
487
+ if (idx < 0 || idx >= m_maxAgents)
488
+ return 0;
489
+ return &m_agents[idx];
490
+ }
491
+
492
+ ///
493
+ /// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
494
+ dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
495
+ {
496
+ if (idx < 0 || idx >= m_maxAgents)
497
+ return 0;
498
+ return &m_agents[idx];
499
+ }
500
+
501
+ void dtCrowd::updateAgentParameters(const int idx, const dtCrowdAgentParams* params)
502
+ {
503
+ if (idx < 0 || idx >= m_maxAgents)
504
+ return;
505
+ memcpy(&m_agents[idx].params, params, sizeof(dtCrowdAgentParams));
506
+ }
507
+
508
+ /// @par
509
+ ///
510
+ /// The agent's position will be constrained to the surface of the navigation mesh.
511
+ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
512
+ {
513
+ // Find empty slot.
514
+ int idx = -1;
515
+ for (int i = 0; i < m_maxAgents; ++i)
516
+ {
517
+ if (!m_agents[i].active)
518
+ {
519
+ idx = i;
520
+ break;
521
+ }
522
+ }
523
+ if (idx == -1)
524
+ return -1;
525
+
526
+ dtCrowdAgent* ag = &m_agents[idx];
527
+
528
+ updateAgentParameters(idx, params);
529
+
530
+ // Find nearest position on navmesh and place the agent there.
531
+ float nearest[3];
532
+ dtPolyRef ref = 0;
533
+ dtVcopy(nearest, pos);
534
+ dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
535
+ if (dtStatusFailed(status))
536
+ {
537
+ dtVcopy(nearest, pos);
538
+ ref = 0;
539
+ }
540
+
541
+ ag->corridor.reset(ref, nearest);
542
+ ag->boundary.reset();
543
+ ag->partial = false;
544
+
545
+ ag->topologyOptTime = 0;
546
+ ag->targetReplanTime = 0;
547
+ ag->nneis = 0;
548
+
549
+ dtVset(ag->dvel, 0,0,0);
550
+ dtVset(ag->nvel, 0,0,0);
551
+ dtVset(ag->vel, 0,0,0);
552
+ dtVcopy(ag->npos, nearest);
553
+
554
+ ag->desiredSpeed = 0;
555
+
556
+ if (ref)
557
+ ag->state = DT_CROWDAGENT_STATE_WALKING;
558
+ else
559
+ ag->state = DT_CROWDAGENT_STATE_INVALID;
560
+
561
+ ag->targetState = DT_CROWDAGENT_TARGET_NONE;
562
+
563
+ ag->active = true;
564
+
565
+ return idx;
566
+ }
567
+
568
+ /// @par
569
+ ///
570
+ /// The agent is deactivated and will no longer be processed. Its #dtCrowdAgent object
571
+ /// is not removed from the pool. It is marked as inactive so that it is available for reuse.
572
+ void dtCrowd::removeAgent(const int idx)
573
+ {
574
+ if (idx >= 0 && idx < m_maxAgents)
575
+ {
576
+ m_agents[idx].active = false;
577
+ }
578
+ }
579
+
580
+ bool dtCrowd::requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos)
581
+ {
582
+ if (idx < 0 || idx >= m_maxAgents)
583
+ return false;
584
+
585
+ dtCrowdAgent* ag = &m_agents[idx];
586
+
587
+ // Initialize request.
588
+ ag->targetRef = ref;
589
+ dtVcopy(ag->targetPos, pos);
590
+ ag->targetPathqRef = DT_PATHQ_INVALID;
591
+ ag->targetReplan = true;
592
+ if (ag->targetRef)
593
+ ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
594
+ else
595
+ ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
596
+
597
+ return true;
598
+ }
599
+
600
+ /// @par
601
+ ///
602
+ /// This method is used when a new target is set.
603
+ ///
604
+ /// The position will be constrained to the surface of the navigation mesh.
605
+ ///
606
+ /// The request will be processed during the next #update().
607
+ bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
608
+ {
609
+ if (idx < 0 || idx >= m_maxAgents)
610
+ return false;
611
+ if (!ref)
612
+ return false;
613
+
614
+ dtCrowdAgent* ag = &m_agents[idx];
615
+
616
+ // Initialize request.
617
+ ag->targetRef = ref;
618
+ dtVcopy(ag->targetPos, pos);
619
+ ag->targetPathqRef = DT_PATHQ_INVALID;
620
+ ag->targetReplan = false;
621
+ if (ag->targetRef)
622
+ ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
623
+ else
624
+ ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
625
+
626
+ return true;
627
+ }
628
+
629
+ bool dtCrowd::requestMoveVelocity(const int idx, const float* vel)
630
+ {
631
+ if (idx < 0 || idx >= m_maxAgents)
632
+ return false;
633
+
634
+ dtCrowdAgent* ag = &m_agents[idx];
635
+
636
+ // Initialize request.
637
+ ag->targetRef = 0;
638
+ dtVcopy(ag->targetPos, vel);
639
+ ag->targetPathqRef = DT_PATHQ_INVALID;
640
+ ag->targetReplan = false;
641
+ ag->targetState = DT_CROWDAGENT_TARGET_VELOCITY;
642
+
643
+ return true;
644
+ }
645
+
646
+ bool dtCrowd::resetMoveTarget(const int idx)
647
+ {
648
+ if (idx < 0 || idx >= m_maxAgents)
649
+ return false;
650
+
651
+ dtCrowdAgent* ag = &m_agents[idx];
652
+
653
+ // Initialize request.
654
+ ag->targetRef = 0;
655
+ dtVset(ag->targetPos, 0,0,0);
656
+ ag->targetPathqRef = DT_PATHQ_INVALID;
657
+ ag->targetReplan = false;
658
+ ag->targetState = DT_CROWDAGENT_TARGET_NONE;
659
+
660
+ return true;
661
+ }
662
+
663
+ int dtCrowd::getActiveAgents(dtCrowdAgent** agents, const int maxAgents)
664
+ {
665
+ int n = 0;
666
+ for (int i = 0; i < m_maxAgents; ++i)
667
+ {
668
+ if (!m_agents[i].active) continue;
669
+ if (n < maxAgents)
670
+ agents[n++] = &m_agents[i];
671
+ }
672
+ return n;
673
+ }
674
+
675
+
676
+ void dtCrowd::updateMoveRequest(const float /*dt*/)
677
+ {
678
+ const int PATH_MAX_AGENTS = 8;
679
+ dtCrowdAgent* queue[PATH_MAX_AGENTS];
680
+ int nqueue = 0;
681
+
682
+ // Fire off new requests.
683
+ for (int i = 0; i < m_maxAgents; ++i)
684
+ {
685
+ dtCrowdAgent* ag = &m_agents[i];
686
+ if (!ag->active)
687
+ continue;
688
+ if (ag->state == DT_CROWDAGENT_STATE_INVALID)
689
+ continue;
690
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
691
+ continue;
692
+
693
+ if (ag->targetState == DT_CROWDAGENT_TARGET_REQUESTING)
694
+ {
695
+ const dtPolyRef* path = ag->corridor.getPath();
696
+ const int npath = ag->corridor.getPathCount();
697
+ dtAssert(npath);
698
+
699
+ static const int MAX_RES = 32;
700
+ float reqPos[3];
701
+ dtPolyRef reqPath[MAX_RES]; // The path to the request location
702
+ int reqPathCount = 0;
703
+
704
+ // Quick search towards the goal.
705
+ static const int MAX_ITER = 20;
706
+ m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
707
+ m_navquery->updateSlicedFindPath(MAX_ITER, 0);
708
+ dtStatus status = 0;
709
+ if (ag->targetReplan) // && npath > 10)
710
+ {
711
+ // Try to use existing steady path during replan if possible.
712
+ status = m_navquery->finalizeSlicedFindPathPartial(path, npath, reqPath, &reqPathCount, MAX_RES);
713
+ }
714
+ else
715
+ {
716
+ // Try to move towards target when goal changes.
717
+ status = m_navquery->finalizeSlicedFindPath(reqPath, &reqPathCount, MAX_RES);
718
+ }
719
+
720
+ if (!dtStatusFailed(status) && reqPathCount > 0)
721
+ {
722
+ // In progress or succeed.
723
+ if (reqPath[reqPathCount-1] != ag->targetRef)
724
+ {
725
+ // Partial path, constrain target position inside the last polygon.
726
+ status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos, 0);
727
+ if (dtStatusFailed(status))
728
+ reqPathCount = 0;
729
+ }
730
+ else
731
+ {
732
+ dtVcopy(reqPos, ag->targetPos);
733
+ }
734
+ }
735
+ else
736
+ {
737
+ reqPathCount = 0;
738
+ }
739
+
740
+ if (!reqPathCount)
741
+ {
742
+ // Could not find path, start the request from current location.
743
+ dtVcopy(reqPos, ag->npos);
744
+ reqPath[0] = path[0];
745
+ reqPathCount = 1;
746
+ }
747
+
748
+ ag->corridor.setCorridor(reqPos, reqPath, reqPathCount);
749
+ ag->boundary.reset();
750
+ ag->partial = false;
751
+
752
+ if (reqPath[reqPathCount-1] == ag->targetRef)
753
+ {
754
+ ag->targetState = DT_CROWDAGENT_TARGET_VALID;
755
+ ag->targetReplanTime = 0.0;
756
+ }
757
+ else
758
+ {
759
+ // The path is longer or potentially unreachable, full plan.
760
+ ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE;
761
+ }
762
+ }
763
+
764
+ if (ag->targetState == DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE)
765
+ {
766
+ nqueue = addToPathQueue(ag, queue, nqueue, PATH_MAX_AGENTS);
767
+ }
768
+ }
769
+
770
+ for (int i = 0; i < nqueue; ++i)
771
+ {
772
+ dtCrowdAgent* ag = queue[i];
773
+ ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
774
+ ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
775
+ if (ag->targetPathqRef != DT_PATHQ_INVALID)
776
+ ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
777
+ }
778
+
779
+
780
+ // Update requests.
781
+ m_pathq.update(MAX_ITERS_PER_UPDATE);
782
+
783
+ dtStatus status;
784
+
785
+ // Process path results.
786
+ for (int i = 0; i < m_maxAgents; ++i)
787
+ {
788
+ dtCrowdAgent* ag = &m_agents[i];
789
+ if (!ag->active)
790
+ continue;
791
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
792
+ continue;
793
+
794
+ if (ag->targetState == DT_CROWDAGENT_TARGET_WAITING_FOR_PATH)
795
+ {
796
+ // Poll path queue.
797
+ status = m_pathq.getRequestStatus(ag->targetPathqRef);
798
+ if (dtStatusFailed(status))
799
+ {
800
+ // Path find failed, retry if the target location is still valid.
801
+ ag->targetPathqRef = DT_PATHQ_INVALID;
802
+ if (ag->targetRef)
803
+ ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
804
+ else
805
+ ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
806
+ ag->targetReplanTime = 0.0;
807
+ }
808
+ else if (dtStatusSucceed(status))
809
+ {
810
+ const dtPolyRef* path = ag->corridor.getPath();
811
+ const int npath = ag->corridor.getPathCount();
812
+ dtAssert(npath);
813
+
814
+ // Apply results.
815
+ float targetPos[3];
816
+ dtVcopy(targetPos, ag->targetPos);
817
+
818
+ dtPolyRef* res = m_pathResult;
819
+ bool valid = true;
820
+ int nres = 0;
821
+ status = m_pathq.getPathResult(ag->targetPathqRef, res, &nres, m_maxPathResult);
822
+ if (dtStatusFailed(status) || !nres)
823
+ valid = false;
824
+
825
+ if (dtStatusDetail(status, DT_PARTIAL_RESULT))
826
+ ag->partial = true;
827
+ else
828
+ ag->partial = false;
829
+
830
+ // Merge result and existing path.
831
+ // The agent might have moved whilst the request is
832
+ // being processed, so the path may have changed.
833
+ // We assume that the end of the path is at the same location
834
+ // where the request was issued.
835
+
836
+ // The last ref in the old path should be the same as
837
+ // the location where the request was issued..
838
+ if (valid && path[npath-1] != res[0])
839
+ valid = false;
840
+
841
+ if (valid)
842
+ {
843
+ // Put the old path infront of the old path.
844
+ if (npath > 1)
845
+ {
846
+ // Make space for the old path.
847
+ if ((npath-1)+nres > m_maxPathResult)
848
+ nres = m_maxPathResult - (npath-1);
849
+
850
+ memmove(res+npath-1, res, sizeof(dtPolyRef)*nres);
851
+ // Copy old path in the beginning.
852
+ memcpy(res, path, sizeof(dtPolyRef)*(npath-1));
853
+ nres += npath-1;
854
+
855
+ // Remove trackbacks
856
+ for (int j = 0; j < nres; ++j)
857
+ {
858
+ if (j-1 >= 0 && j+1 < nres)
859
+ {
860
+ if (res[j-1] == res[j+1])
861
+ {
862
+ memmove(res+(j-1), res+(j+1), sizeof(dtPolyRef)*(nres-(j+1)));
863
+ nres -= 2;
864
+ j -= 2;
865
+ }
866
+ }
867
+ }
868
+
869
+ }
870
+
871
+ // Check for partial path.
872
+ if (res[nres-1] != ag->targetRef)
873
+ {
874
+ // Partial path, constrain target position inside the last polygon.
875
+ float nearest[3];
876
+ status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest, 0);
877
+ if (dtStatusSucceed(status))
878
+ dtVcopy(targetPos, nearest);
879
+ else
880
+ valid = false;
881
+ }
882
+ }
883
+
884
+ if (valid)
885
+ {
886
+ // Set current corridor.
887
+ ag->corridor.setCorridor(targetPos, res, nres);
888
+ // Force to update boundary.
889
+ ag->boundary.reset();
890
+ ag->targetState = DT_CROWDAGENT_TARGET_VALID;
891
+ }
892
+ else
893
+ {
894
+ // Something went wrong.
895
+ ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
896
+ }
897
+
898
+ ag->targetReplanTime = 0.0;
899
+ }
900
+ }
901
+ }
902
+
903
+ }
904
+
905
+
906
+ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt)
907
+ {
908
+ if (!nagents)
909
+ return;
910
+
911
+ const float OPT_TIME_THR = 0.5f; // seconds
912
+ const int OPT_MAX_AGENTS = 1;
913
+ dtCrowdAgent* queue[OPT_MAX_AGENTS];
914
+ int nqueue = 0;
915
+
916
+ for (int i = 0; i < nagents; ++i)
917
+ {
918
+ dtCrowdAgent* ag = agents[i];
919
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
920
+ continue;
921
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
922
+ continue;
923
+ if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_TOPO) == 0)
924
+ continue;
925
+ ag->topologyOptTime += dt;
926
+ if (ag->topologyOptTime >= OPT_TIME_THR)
927
+ nqueue = addToOptQueue(ag, queue, nqueue, OPT_MAX_AGENTS);
928
+ }
929
+
930
+ for (int i = 0; i < nqueue; ++i)
931
+ {
932
+ dtCrowdAgent* ag = queue[i];
933
+ ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
934
+ ag->topologyOptTime = 0;
935
+ }
936
+
937
+ }
938
+
939
+ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt)
940
+ {
941
+ static const int CHECK_LOOKAHEAD = 10;
942
+ static const float TARGET_REPLAN_DELAY = 1.0; // seconds
943
+
944
+ for (int i = 0; i < nagents; ++i)
945
+ {
946
+ dtCrowdAgent* ag = agents[i];
947
+
948
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
949
+ continue;
950
+
951
+ ag->targetReplanTime += dt;
952
+
953
+ bool replan = false;
954
+
955
+ // First check that the current location is valid.
956
+ const int idx = getAgentIndex(ag);
957
+ float agentPos[3];
958
+ dtPolyRef agentRef = ag->corridor.getFirstPoly();
959
+ dtVcopy(agentPos, ag->npos);
960
+ if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
961
+ {
962
+ // Current location is not valid, try to reposition.
963
+ // TODO: this can snap agents, how to handle that?
964
+ float nearest[3];
965
+ dtVcopy(nearest, agentPos);
966
+ agentRef = 0;
967
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
968
+ dtVcopy(agentPos, nearest);
969
+
970
+ if (!agentRef)
971
+ {
972
+ // Could not find location in navmesh, set state to invalid.
973
+ ag->corridor.reset(0, agentPos);
974
+ ag->partial = false;
975
+ ag->boundary.reset();
976
+ ag->state = DT_CROWDAGENT_STATE_INVALID;
977
+ continue;
978
+ }
979
+
980
+ // Make sure the first polygon is valid, but leave other valid
981
+ // polygons in the path so that replanner can adjust the path better.
982
+ ag->corridor.fixPathStart(agentRef, agentPos);
983
+ // ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
984
+ ag->boundary.reset();
985
+ dtVcopy(ag->npos, agentPos);
986
+
987
+ replan = true;
988
+ }
989
+
990
+ // If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
991
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
992
+ continue;
993
+
994
+ // Try to recover move request position.
995
+ if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
996
+ {
997
+ if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
998
+ {
999
+ // Current target is not valid, try to reposition.
1000
+ float nearest[3];
1001
+ dtVcopy(nearest, ag->targetPos);
1002
+ ag->targetRef = 0;
1003
+ m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
1004
+ dtVcopy(ag->targetPos, nearest);
1005
+ replan = true;
1006
+ }
1007
+ if (!ag->targetRef)
1008
+ {
1009
+ // Failed to reposition target, fail moverequest.
1010
+ ag->corridor.reset(agentRef, agentPos);
1011
+ ag->partial = false;
1012
+ ag->targetState = DT_CROWDAGENT_TARGET_NONE;
1013
+ }
1014
+ }
1015
+
1016
+ // If nearby corridor is not valid, replan.
1017
+ if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
1018
+ {
1019
+ // Fix current path.
1020
+ // ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
1021
+ // ag->boundary.reset();
1022
+ replan = true;
1023
+ }
1024
+
1025
+ // If the end of the path is near and it is not the requested location, replan.
1026
+ if (ag->targetState == DT_CROWDAGENT_TARGET_VALID)
1027
+ {
1028
+ if (ag->targetReplanTime > TARGET_REPLAN_DELAY &&
1029
+ ag->corridor.getPathCount() < CHECK_LOOKAHEAD &&
1030
+ ag->corridor.getLastPoly() != ag->targetRef)
1031
+ replan = true;
1032
+ }
1033
+
1034
+ // Try to replan path to goal.
1035
+ if (replan)
1036
+ {
1037
+ if (ag->targetState != DT_CROWDAGENT_TARGET_NONE)
1038
+ {
1039
+ requestMoveTargetReplan(idx, ag->targetRef, ag->targetPos);
1040
+ }
1041
+ }
1042
+ }
1043
+ }
1044
+
1045
+ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
1046
+ {
1047
+ m_velocitySampleCount = 0;
1048
+
1049
+ const int debugIdx = debug ? debug->idx : -1;
1050
+
1051
+ dtCrowdAgent** agents = m_activeAgents;
1052
+ int nagents = getActiveAgents(agents, m_maxAgents);
1053
+
1054
+ // Check that all agents still have valid paths.
1055
+ checkPathValidity(agents, nagents, dt);
1056
+
1057
+ // Update async move request and path finder.
1058
+ updateMoveRequest(dt);
1059
+
1060
+ // Optimize path topology.
1061
+ updateTopologyOptimization(agents, nagents, dt);
1062
+
1063
+ // Register agents to proximity grid.
1064
+ m_grid->clear();
1065
+ for (int i = 0; i < nagents; ++i)
1066
+ {
1067
+ dtCrowdAgent* ag = agents[i];
1068
+ const float* p = ag->npos;
1069
+ const float r = ag->params.radius;
1070
+ m_grid->addItem((unsigned short)i, p[0]-r, p[2]-r, p[0]+r, p[2]+r);
1071
+ }
1072
+
1073
+ // Get nearby navmesh segments and agents to collide with.
1074
+ for (int i = 0; i < nagents; ++i)
1075
+ {
1076
+ dtCrowdAgent* ag = agents[i];
1077
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1078
+ continue;
1079
+
1080
+ // Update the collision boundary after certain distance has been passed or
1081
+ // if it has become invalid.
1082
+ const float updateThr = ag->params.collisionQueryRange*0.25f;
1083
+ if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
1084
+ !ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
1085
+ {
1086
+ ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
1087
+ m_navquery, &m_filters[ag->params.queryFilterType]);
1088
+ }
1089
+ // Query neighbour agents
1090
+ ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
1091
+ ag, ag->neis, DT_CROWDAGENT_MAX_NEIGHBOURS,
1092
+ agents, nagents, m_grid);
1093
+ for (int j = 0; j < ag->nneis; j++)
1094
+ ag->neis[j].idx = getAgentIndex(agents[ag->neis[j].idx]);
1095
+ }
1096
+
1097
+ // Find next corner to steer to.
1098
+ for (int i = 0; i < nagents; ++i)
1099
+ {
1100
+ dtCrowdAgent* ag = agents[i];
1101
+
1102
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1103
+ continue;
1104
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
1105
+ continue;
1106
+
1107
+ // Find corners for steering
1108
+ ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
1109
+ DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
1110
+
1111
+ // Check to see if the corner after the next corner is directly visible,
1112
+ // and short cut to there.
1113
+ if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
1114
+ {
1115
+ const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
1116
+ ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
1117
+
1118
+ // Copy data for debug purposes.
1119
+ if (debugIdx == i)
1120
+ {
1121
+ dtVcopy(debug->optStart, ag->corridor.getPos());
1122
+ dtVcopy(debug->optEnd, target);
1123
+ }
1124
+ }
1125
+ else
1126
+ {
1127
+ // Copy data for debug purposes.
1128
+ if (debugIdx == i)
1129
+ {
1130
+ dtVset(debug->optStart, 0,0,0);
1131
+ dtVset(debug->optEnd, 0,0,0);
1132
+ }
1133
+ }
1134
+ }
1135
+
1136
+ // Trigger off-mesh connections (depends on corners).
1137
+ for (int i = 0; i < nagents; ++i)
1138
+ {
1139
+ dtCrowdAgent* ag = agents[i];
1140
+
1141
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1142
+ continue;
1143
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
1144
+ continue;
1145
+
1146
+ // Check
1147
+ const float triggerRadius = ag->params.radius*2.25f;
1148
+ if (overOffmeshConnection(ag, triggerRadius))
1149
+ {
1150
+ // Prepare to off-mesh connection.
1151
+ const int idx = (int)(ag - m_agents);
1152
+ dtCrowdAgentAnimation* anim = &m_agentAnims[idx];
1153
+
1154
+ // Adjust the path over the off-mesh connection.
1155
+ dtPolyRef refs[2];
1156
+ if (ag->corridor.moveOverOffmeshConnection(ag->cornerPolys[ag->ncorners-1], refs,
1157
+ anim->startPos, anim->endPos, m_navquery))
1158
+ {
1159
+ dtVcopy(anim->initPos, ag->npos);
1160
+ anim->polyRef = refs[1];
1161
+ anim->active = true;
1162
+ anim->t = 0.0f;
1163
+ anim->tmax = (dtVdist2D(anim->startPos, anim->endPos) / ag->params.maxSpeed) * 0.5f;
1164
+
1165
+ ag->state = DT_CROWDAGENT_STATE_OFFMESH;
1166
+ ag->ncorners = 0;
1167
+ ag->nneis = 0;
1168
+ continue;
1169
+ }
1170
+ else
1171
+ {
1172
+ // Path validity check will ensure that bad/blocked connections will be replanned.
1173
+ }
1174
+ }
1175
+ }
1176
+
1177
+ // Calculate steering.
1178
+ for (int i = 0; i < nagents; ++i)
1179
+ {
1180
+ dtCrowdAgent* ag = agents[i];
1181
+
1182
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1183
+ continue;
1184
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE)
1185
+ continue;
1186
+
1187
+ float dvel[3] = {0,0,0};
1188
+
1189
+ if (ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
1190
+ {
1191
+ dtVcopy(dvel, ag->targetPos);
1192
+ ag->desiredSpeed = dtVlen(ag->targetPos);
1193
+ }
1194
+ else
1195
+ {
1196
+ // Calculate steering direction.
1197
+ if (ag->params.updateFlags & DT_CROWD_ANTICIPATE_TURNS)
1198
+ calcSmoothSteerDirection(ag, dvel);
1199
+ else
1200
+ calcStraightSteerDirection(ag, dvel);
1201
+
1202
+ // Calculate speed scale, which tells the agent to slowdown at the end of the path.
1203
+ const float slowDownRadius = ag->params.radius*2; // TODO: make less hacky.
1204
+ const float speedScale = getDistanceToGoal(ag, slowDownRadius) / slowDownRadius;
1205
+
1206
+ ag->desiredSpeed = ag->params.maxSpeed;
1207
+ dtVscale(dvel, dvel, ag->desiredSpeed * speedScale);
1208
+ }
1209
+
1210
+ // Separation
1211
+ if (ag->params.updateFlags & DT_CROWD_SEPARATION)
1212
+ {
1213
+ const float separationDist = ag->params.collisionQueryRange;
1214
+ const float invSeparationDist = 1.0f / separationDist;
1215
+ const float separationWeight = ag->params.separationWeight;
1216
+
1217
+ float w = 0;
1218
+ float disp[3] = {0,0,0};
1219
+
1220
+ for (int j = 0; j < ag->nneis; ++j)
1221
+ {
1222
+ const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
1223
+
1224
+ float diff[3];
1225
+ dtVsub(diff, ag->npos, nei->npos);
1226
+ diff[1] = 0;
1227
+
1228
+ const float distSqr = dtVlenSqr(diff);
1229
+ if (distSqr < 0.00001f)
1230
+ continue;
1231
+ if (distSqr > dtSqr(separationDist))
1232
+ continue;
1233
+ const float dist = dtMathSqrtf(distSqr);
1234
+ const float weight = separationWeight * (1.0f - dtSqr(dist*invSeparationDist));
1235
+
1236
+ dtVmad(disp, disp, diff, weight/dist);
1237
+ w += 1.0f;
1238
+ }
1239
+
1240
+ if (w > 0.0001f)
1241
+ {
1242
+ // Adjust desired velocity.
1243
+ dtVmad(dvel, dvel, disp, 1.0f/w);
1244
+ // Clamp desired velocity to desired speed.
1245
+ const float speedSqr = dtVlenSqr(dvel);
1246
+ const float desiredSqr = dtSqr(ag->desiredSpeed);
1247
+ if (speedSqr > desiredSqr)
1248
+ dtVscale(dvel, dvel, desiredSqr/speedSqr);
1249
+ }
1250
+ }
1251
+
1252
+ // Set the desired velocity.
1253
+ dtVcopy(ag->dvel, dvel);
1254
+ }
1255
+
1256
+ // Velocity planning.
1257
+ for (int i = 0; i < nagents; ++i)
1258
+ {
1259
+ dtCrowdAgent* ag = agents[i];
1260
+
1261
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1262
+ continue;
1263
+
1264
+ if (ag->params.updateFlags & DT_CROWD_OBSTACLE_AVOIDANCE)
1265
+ {
1266
+ m_obstacleQuery->reset();
1267
+
1268
+ // Add neighbours as obstacles.
1269
+ for (int j = 0; j < ag->nneis; ++j)
1270
+ {
1271
+ const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
1272
+ m_obstacleQuery->addCircle(nei->npos, nei->params.radius, nei->vel, nei->dvel);
1273
+ }
1274
+
1275
+ // Append neighbour segments as obstacles.
1276
+ for (int j = 0; j < ag->boundary.getSegmentCount(); ++j)
1277
+ {
1278
+ const float* s = ag->boundary.getSegment(j);
1279
+ if (dtTriArea2D(ag->npos, s, s+3) < 0.0f)
1280
+ continue;
1281
+ m_obstacleQuery->addSegment(s, s+3);
1282
+ }
1283
+
1284
+ dtObstacleAvoidanceDebugData* vod = 0;
1285
+ if (debugIdx == i)
1286
+ vod = debug->vod;
1287
+
1288
+ // Sample new safe velocity.
1289
+ bool adaptive = true;
1290
+ int ns = 0;
1291
+
1292
+ const dtObstacleAvoidanceParams* params = &m_obstacleQueryParams[ag->params.obstacleAvoidanceType];
1293
+
1294
+ if (adaptive)
1295
+ {
1296
+ ns = m_obstacleQuery->sampleVelocityAdaptive(ag->npos, ag->params.radius, ag->desiredSpeed,
1297
+ ag->vel, ag->dvel, ag->nvel, params, vod);
1298
+ }
1299
+ else
1300
+ {
1301
+ ns = m_obstacleQuery->sampleVelocityGrid(ag->npos, ag->params.radius, ag->desiredSpeed,
1302
+ ag->vel, ag->dvel, ag->nvel, params, vod);
1303
+ }
1304
+ m_velocitySampleCount += ns;
1305
+ }
1306
+ else
1307
+ {
1308
+ // If not using velocity planning, new velocity is directly the desired velocity.
1309
+ dtVcopy(ag->nvel, ag->dvel);
1310
+ }
1311
+ }
1312
+
1313
+ // Integrate.
1314
+ for (int i = 0; i < nagents; ++i)
1315
+ {
1316
+ dtCrowdAgent* ag = agents[i];
1317
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1318
+ continue;
1319
+ integrate(ag, dt);
1320
+ }
1321
+
1322
+ // Handle collisions.
1323
+ static const float COLLISION_RESOLVE_FACTOR = 0.7f;
1324
+
1325
+ for (int iter = 0; iter < 4; ++iter)
1326
+ {
1327
+ for (int i = 0; i < nagents; ++i)
1328
+ {
1329
+ dtCrowdAgent* ag = agents[i];
1330
+ const int idx0 = getAgentIndex(ag);
1331
+
1332
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1333
+ continue;
1334
+
1335
+ dtVset(ag->disp, 0,0,0);
1336
+
1337
+ float w = 0;
1338
+
1339
+ for (int j = 0; j < ag->nneis; ++j)
1340
+ {
1341
+ const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
1342
+ const int idx1 = getAgentIndex(nei);
1343
+
1344
+ float diff[3];
1345
+ dtVsub(diff, ag->npos, nei->npos);
1346
+ diff[1] = 0;
1347
+
1348
+ float dist = dtVlenSqr(diff);
1349
+ if (dist > dtSqr(ag->params.radius + nei->params.radius))
1350
+ continue;
1351
+ dist = dtMathSqrtf(dist);
1352
+ float pen = (ag->params.radius + nei->params.radius) - dist;
1353
+ if (dist < 0.0001f)
1354
+ {
1355
+ // Agents on top of each other, try to choose diverging separation directions.
1356
+ if (idx0 > idx1)
1357
+ dtVset(diff, -ag->dvel[2],0,ag->dvel[0]);
1358
+ else
1359
+ dtVset(diff, ag->dvel[2],0,-ag->dvel[0]);
1360
+ pen = 0.01f;
1361
+ }
1362
+ else
1363
+ {
1364
+ pen = (1.0f/dist) * (pen*0.5f) * COLLISION_RESOLVE_FACTOR;
1365
+ }
1366
+
1367
+ dtVmad(ag->disp, ag->disp, diff, pen);
1368
+
1369
+ w += 1.0f;
1370
+ }
1371
+
1372
+ if (w > 0.0001f)
1373
+ {
1374
+ const float iw = 1.0f / w;
1375
+ dtVscale(ag->disp, ag->disp, iw);
1376
+ }
1377
+ }
1378
+
1379
+ for (int i = 0; i < nagents; ++i)
1380
+ {
1381
+ dtCrowdAgent* ag = agents[i];
1382
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1383
+ continue;
1384
+
1385
+ dtVadd(ag->npos, ag->npos, ag->disp);
1386
+ }
1387
+ }
1388
+
1389
+ for (int i = 0; i < nagents; ++i)
1390
+ {
1391
+ dtCrowdAgent* ag = agents[i];
1392
+ if (ag->state != DT_CROWDAGENT_STATE_WALKING)
1393
+ continue;
1394
+
1395
+ // Move along navmesh.
1396
+ ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
1397
+ // Get valid constrained position back.
1398
+ dtVcopy(ag->npos, ag->corridor.getPos());
1399
+
1400
+ // If not using path, truncate the corridor to just one poly.
1401
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
1402
+ {
1403
+ ag->corridor.reset(ag->corridor.getFirstPoly(), ag->npos);
1404
+ ag->partial = false;
1405
+ }
1406
+
1407
+ }
1408
+
1409
+ // Update agents using off-mesh connection.
1410
+ for (int i = 0; i < m_maxAgents; ++i)
1411
+ {
1412
+ dtCrowdAgentAnimation* anim = &m_agentAnims[i];
1413
+ if (!anim->active)
1414
+ continue;
1415
+ dtCrowdAgent* ag = agents[i];
1416
+
1417
+ anim->t += dt;
1418
+ if (anim->t > anim->tmax)
1419
+ {
1420
+ // Reset animation
1421
+ anim->active = false;
1422
+ // Prepare agent for walking.
1423
+ ag->state = DT_CROWDAGENT_STATE_WALKING;
1424
+ continue;
1425
+ }
1426
+
1427
+ // Update position
1428
+ const float ta = anim->tmax*0.15f;
1429
+ const float tb = anim->tmax;
1430
+ if (anim->t < ta)
1431
+ {
1432
+ const float u = tween(anim->t, 0.0, ta);
1433
+ dtVlerp(ag->npos, anim->initPos, anim->startPos, u);
1434
+ }
1435
+ else
1436
+ {
1437
+ const float u = tween(anim->t, ta, tb);
1438
+ dtVlerp(ag->npos, anim->startPos, anim->endPos, u);
1439
+ }
1440
+
1441
+ // Update velocity.
1442
+ dtVset(ag->vel, 0,0,0);
1443
+ dtVset(ag->dvel, 0,0,0);
1444
+ }
1445
+
1446
+ }