DetourCrowdManager.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. //
  2. // Copyright (c) 2008-2015 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Scene/Component.h"
  23. #include "../Core/Context.h"
  24. #include "../Navigation/CrowdAgent.h"
  25. #include "../Graphics/DebugRenderer.h"
  26. #include "../Navigation/DetourCrowdManager.h"
  27. #include "../Navigation/DynamicNavigationMesh.h"
  28. #include "../IO/Log.h"
  29. #include "../Navigation/NavigationEvents.h"
  30. #include "../Navigation/NavigationMesh.h"
  31. #include "../Scene/Node.h"
  32. #include "../Core/Profiler.h"
  33. #include "../Scene/Scene.h"
  34. #include "../Scene/SceneEvents.h"
  35. #include "../Container/Vector.h"
  36. #ifdef URHO3D_PHYSICS
  37. #include "../Physics/PhysicsEvents.h"
  38. #endif
  39. #include <DetourCrowd/include/DetourCrowd.h>
  40. #include <Recast/include/Recast.h>
  41. #include "../DebugNew.h"
  42. namespace Atomic
  43. {
  44. extern const char* NAVIGATION_CATEGORY;
  45. static const unsigned DEFAULT_MAX_AGENTS = 512;
  46. DetourCrowdManager::DetourCrowdManager(Context* context) :
  47. Component(context),
  48. maxAgents_(DEFAULT_MAX_AGENTS),
  49. crowd_(0),
  50. navigationMesh_(0),
  51. agentDebug_(0)
  52. {
  53. agentBuffer_.Resize(maxAgents_);
  54. }
  55. DetourCrowdManager::~DetourCrowdManager()
  56. {
  57. dtFreeCrowd(crowd_);
  58. crowd_ = 0;
  59. delete agentDebug_;
  60. agentDebug_ = 0;
  61. }
  62. void DetourCrowdManager::RegisterObject(Context* context)
  63. {
  64. context->RegisterFactory<DetourCrowdManager>(NAVIGATION_CATEGORY);
  65. ACCESSOR_ATTRIBUTE("Max Agents", GetMaxAgents, SetMaxAgents, unsigned, DEFAULT_MAX_AGENTS, AM_DEFAULT);
  66. }
  67. void DetourCrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
  68. {
  69. navigationMesh_ = WeakPtr<NavigationMesh>(navMesh);
  70. if (navigationMesh_ && !navigationMesh_->navMeshQuery_)
  71. navigationMesh_->InitializeQuery();
  72. CreateCrowd();
  73. MarkNetworkUpdate();
  74. }
  75. void DetourCrowdManager::SetAreaCost(unsigned filterID, unsigned areaID, float weight)
  76. {
  77. dtQueryFilter* filter = crowd_->getEditableFilter(filterID);
  78. if (filter)
  79. filter->setAreaCost((int)areaID, weight);
  80. }
  81. void DetourCrowdManager::SetMaxAgents(unsigned agentCt)
  82. {
  83. maxAgents_ = agentCt;
  84. if (crowd_ && crowd_->getAgentCount() > 0)
  85. LOGERROR("DetourCrowdManager contains active agents, their state will be lost");
  86. agentBuffer_.Resize(maxAgents_);
  87. CreateCrowd();
  88. if (crowd_)
  89. {
  90. PODVector<CrowdAgent*> agents = agents_;
  91. // Reset the existing values in the agent
  92. for (unsigned i = 0; i < agents.Size(); ++i)
  93. {
  94. agents[i]->inCrowd_ = false;
  95. agents[i]->agentCrowdId_ = -1;
  96. }
  97. // Add the agents back in
  98. for (unsigned i = 0; i < agents.Size() && i < maxAgents_; ++i)
  99. agents[i]->AddAgentToCrowd();
  100. if (agents.Size() > maxAgents_)
  101. LOGERROR("DetourCrowdManager: resize left " + String(agents.Size() - maxAgents_) + " agents orphaned");
  102. }
  103. MarkNetworkUpdate();
  104. }
  105. NavigationMesh* DetourCrowdManager::GetNavigationMesh()
  106. {
  107. return navigationMesh_.Get();
  108. }
  109. float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
  110. {
  111. if (crowd_ && navigationMesh_)
  112. {
  113. const dtQueryFilter* filter = crowd_->getFilter((int)filterID);
  114. if (filter)
  115. return filter->getAreaCost((int)areaID);
  116. }
  117. return 0.0f;
  118. }
  119. unsigned DetourCrowdManager::GetAgentCount() const
  120. {
  121. return crowd_ ? crowd_->getAgentCount() : 0;
  122. }
  123. void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  124. {
  125. if (debug && navigationMesh_.NotNull() && crowd_)
  126. {
  127. // Current position-to-target line
  128. for (int i = 0; i < crowd_->getAgentCount(); i++)
  129. {
  130. const dtCrowdAgent* ag = crowd_->getAgent(i);
  131. if (!ag->active)
  132. continue;
  133. Color color(0.6f, 0.2f, 0.2f, 1.0f);
  134. // Render line to target:
  135. Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
  136. Vector3 pos2;
  137. for (int i = 0; i < ag->ncorners; ++i)
  138. {
  139. pos2.x_ = ag->cornerVerts[i * 3];
  140. pos2.y_ = ag->cornerVerts[i * 3 + 1];
  141. pos2.z_ = ag->cornerVerts[i * 3 + 2];
  142. debug->AddLine(pos1, pos2, color, depthTest);
  143. pos1 = pos2;
  144. }
  145. pos2.x_ = ag->targetPos[0];
  146. pos2.y_ = ag->targetPos[1];
  147. pos2.z_ = ag->targetPos[2];
  148. debug->AddLine(pos1, pos2, color, depthTest);
  149. // Target circle
  150. debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
  151. }
  152. }
  153. }
  154. bool DetourCrowdManager::CreateCrowd()
  155. {
  156. if (!navigationMesh_ || !navigationMesh_->navMesh_)
  157. return false;
  158. if (crowd_)
  159. dtFreeCrowd(crowd_);
  160. crowd_ = dtAllocCrowd();
  161. if (!agentDebug_)
  162. agentDebug_ = new dtCrowdAgentDebugInfo();
  163. // Initialize the crowd
  164. if (!crowd_->init(maxAgents_, navigationMesh_->GetAgentRadius(), navigationMesh_->navMesh_))
  165. {
  166. LOGERROR("Could not initialize DetourCrowd");
  167. return false;
  168. }
  169. // Setup local avoidance params to different qualities.
  170. dtObstacleAvoidanceParams params;
  171. memcpy(&params, crowd_->getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
  172. // Low (11)
  173. params.velBias = 0.5f;
  174. params.adaptiveDivs = 5;
  175. params.adaptiveRings = 2;
  176. params.adaptiveDepth = 1;
  177. crowd_->setObstacleAvoidanceParams(0, &params);
  178. // Medium (22)
  179. params.velBias = 0.5f;
  180. params.adaptiveDivs = 5;
  181. params.adaptiveRings = 2;
  182. params.adaptiveDepth = 2;
  183. crowd_->setObstacleAvoidanceParams(1, &params);
  184. // Good (45)
  185. params.velBias = 0.5f;
  186. params.adaptiveDivs = 7;
  187. params.adaptiveRings = 2;
  188. params.adaptiveDepth = 3;
  189. crowd_->setObstacleAvoidanceParams(2, &params);
  190. // High (66)
  191. params.velBias = 0.5f;
  192. params.adaptiveDivs = 7;
  193. params.adaptiveRings = 3;
  194. params.adaptiveDepth = 3;
  195. crowd_->setObstacleAvoidanceParams(3, &params);
  196. return true;
  197. }
  198. int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
  199. {
  200. if (!crowd_ || navigationMesh_.Expired())
  201. return -1;
  202. dtCrowdAgentParams params;
  203. if (agent->radius_ <= 0.0f)
  204. agent->radius_ = navigationMesh_->GetAgentRadius();
  205. params.radius = agent->radius_;
  206. if (agent->height_ <= 0.0f)
  207. agent->height_ = navigationMesh_->GetAgentHeight();
  208. params.height = agent->height_;
  209. params.queryFilterType = (unsigned char)agent->filterType_;
  210. params.maxAcceleration = agent->maxAccel_;
  211. params.maxSpeed = agent->maxSpeed_;
  212. params.collisionQueryRange = params.radius * 8.0f;
  213. params.pathOptimizationRange = params.radius * 30.0f;
  214. params.updateFlags = DT_CROWD_ANTICIPATE_TURNS
  215. | DT_CROWD_OPTIMIZE_VIS
  216. | DT_CROWD_OPTIMIZE_TOPO
  217. | DT_CROWD_OBSTACLE_AVOIDANCE;
  218. params.obstacleAvoidanceType = 3;
  219. params.separationWeight = 2.0f;
  220. params.queryFilterType = 0;
  221. dtPolyRef polyRef;
  222. float nearestPos[3];
  223. rcVcopy(nearestPos, &pos.x_);
  224. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  225. pos.Data(),
  226. crowd_->getQueryExtents(),
  227. crowd_->getFilter(agent->filterType_),
  228. &polyRef,
  229. nearestPos);
  230. const int agentID = crowd_->addAgent(nearestPos, &params);
  231. if (agentID != -1)
  232. agents_.Push(agent);
  233. return agentID;
  234. }
  235. void DetourCrowdManager::RemoveAgent(CrowdAgent* agent)
  236. {
  237. if (!crowd_)
  238. return;
  239. // Clear user data
  240. dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
  241. if (agt)
  242. agt->params.userData = 0;
  243. crowd_->removeAgent(agent->GetAgentCrowdId());
  244. agents_.Remove(agent);
  245. }
  246. void DetourCrowdManager::UpdateAgentNavigationQuality(CrowdAgent* agent, NavigationQuality nq)
  247. {
  248. if (!crowd_)
  249. return;
  250. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  251. switch (nq)
  252. {
  253. case NAVIGATIONQUALITY_LOW:
  254. {
  255. params.updateFlags &= ~0
  256. & ~DT_CROWD_ANTICIPATE_TURNS
  257. & ~DT_CROWD_OPTIMIZE_VIS
  258. & ~DT_CROWD_OPTIMIZE_TOPO
  259. & ~DT_CROWD_OBSTACLE_AVOIDANCE;
  260. }
  261. break;
  262. case NAVIGATIONQUALITY_MEDIUM:
  263. {
  264. params.updateFlags |= 0;
  265. params.updateFlags &= ~0
  266. & ~DT_CROWD_OBSTACLE_AVOIDANCE
  267. & ~DT_CROWD_ANTICIPATE_TURNS
  268. & ~DT_CROWD_OPTIMIZE_VIS
  269. & ~DT_CROWD_OPTIMIZE_TOPO;
  270. }
  271. break;
  272. case NAVIGATIONQUALITY_HIGH:
  273. {
  274. params.obstacleAvoidanceType = 3;
  275. params.updateFlags |= 0
  276. | DT_CROWD_ANTICIPATE_TURNS
  277. | DT_CROWD_OPTIMIZE_VIS
  278. | DT_CROWD_OPTIMIZE_TOPO
  279. | DT_CROWD_OBSTACLE_AVOIDANCE;
  280. }
  281. break;
  282. }
  283. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  284. }
  285. void DetourCrowdManager::UpdateAgentPushiness(CrowdAgent* agent, NavigationPushiness pushiness)
  286. {
  287. if (!crowd_)
  288. return;
  289. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  290. switch (pushiness)
  291. {
  292. case PUSHINESS_LOW:
  293. params.separationWeight = 4.0f;
  294. params.collisionQueryRange = params.radius * 16.0f;
  295. break;
  296. case PUSHINESS_MEDIUM:
  297. params.separationWeight = 2.0f;
  298. params.collisionQueryRange = params.radius * 8.0f;
  299. break;
  300. case PUSHINESS_HIGH:
  301. params.separationWeight = 0.5f;
  302. params.collisionQueryRange = params.radius * 1.0f;
  303. break;
  304. }
  305. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  306. }
  307. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target)
  308. {
  309. if (!crowd_)
  310. return false;
  311. dtPolyRef polyRef;
  312. float nearestPos[3];
  313. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  314. target.Data(),
  315. crowd_->getQueryExtents(),
  316. crowd_->getFilter(agent->filterType_),
  317. &polyRef,
  318. nearestPos);
  319. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), polyRef, nearestPos);
  320. }
  321. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target, unsigned int& targetRef)
  322. {
  323. if (crowd_ == 0)
  324. return false;
  325. float nearestPos[3];
  326. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  327. target.Data(),
  328. crowd_->getQueryExtents(),
  329. crowd_->getFilter(agent->filterType_),
  330. &targetRef,
  331. nearestPos);
  332. // Return true if detour has determined it can do something with our move target
  333. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), targetRef, nearestPos) &&
  334. crowd_->getAgent(agent->GetAgentCrowdId())->targetState != DT_CROWDAGENT_TARGET_FAILED;
  335. }
  336. Vector3 DetourCrowdManager::GetClosestWalkablePosition(Vector3 pos) const
  337. {
  338. if (!crowd_)
  339. return Vector3::ZERO;
  340. float closest[3];
  341. const static float extents[] = { 1.0f, 20.0f, 1.0f };
  342. dtPolyRef closestPoly;
  343. dtQueryFilter filter;
  344. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  345. pos.Data(),
  346. crowd_->getQueryExtents(),
  347. &filter,
  348. &closestPoly,
  349. closest);
  350. return Vector3(closest);
  351. }
  352. void DetourCrowdManager::Update(float delta)
  353. {
  354. if (!crowd_)
  355. return;
  356. PROFILE(UpdateCrowd);
  357. crowd_->update(delta, agentDebug_);
  358. memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
  359. const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
  360. {
  361. PROFILE(ApplyCrowdUpdates);
  362. for (int i = 0; i < count; i++)
  363. {
  364. dtCrowdAgent* agent = agentBuffer_[i];
  365. if (agent)
  366. {
  367. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);
  368. if (crowdAgent)
  369. crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
  370. }
  371. }
  372. }
  373. }
  374. const dtCrowdAgent* DetourCrowdManager::GetCrowdAgent(int agent)
  375. {
  376. return crowd_ ? crowd_->getAgent(agent) : 0;
  377. }
  378. dtCrowd* DetourCrowdManager::GetCrowd()
  379. {
  380. return crowd_;
  381. }
  382. void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
  383. {
  384. using namespace SceneSubsystemUpdate;
  385. if (IsEnabledEffective())
  386. Update(eventData[P_TIMESTEP].GetFloat());
  387. }
  388. void DetourCrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData)
  389. {
  390. using namespace NavigationMeshRebuilt;
  391. // The mesh being rebuilt may not have existed before
  392. NavigationMesh* navMesh = static_cast<NavigationMesh*>(eventData[P_MESH].GetPtr());
  393. if (!navigationMesh_ || !crowd_)
  394. {
  395. SetNavigationMesh(navMesh);
  396. // Scan for existing agents that are potentially important
  397. PODVector<Node*> agents;
  398. GetScene()->GetChildrenWithComponent<CrowdAgent>(agents, true);
  399. for (unsigned i = 0; i < agents.Size(); ++i)
  400. {
  401. CrowdAgent* agent = agents[i]->GetComponent<CrowdAgent>();
  402. if (agent && agent->IsEnabledEffective())
  403. agent->AddAgentToCrowd();
  404. }
  405. }
  406. }
  407. void DetourCrowdManager::OnNodeSet(Node* node)
  408. {
  409. // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
  410. // to the scene's NavigationMesh
  411. if (node)
  412. {
  413. SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
  414. SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
  415. NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
  416. if (!mesh)
  417. mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
  418. if (mesh)
  419. SetNavigationMesh(mesh);
  420. else
  421. LOGERROR("DetourCrowdManager requires an existing navigation mesh");
  422. }
  423. }
  424. }