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,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
+ }