DetourCrowdManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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 "../Precompiled.h"
  23. #include "../Scene/Component.h"
  24. #include "../Core/Context.h"
  25. #include "../Navigation/CrowdAgent.h"
  26. #include "../Graphics/DebugRenderer.h"
  27. #include "../Navigation/DetourCrowdManager.h"
  28. #include "../Navigation/DynamicNavigationMesh.h"
  29. #include "../IO/Log.h"
  30. #include "../Navigation/NavigationEvents.h"
  31. #include "../Navigation/NavigationMesh.h"
  32. #include "../Scene/Node.h"
  33. #include "../Core/Profiler.h"
  34. #include "../Scene/Scene.h"
  35. #include "../Scene/SceneEvents.h"
  36. #include "../Container/Vector.h"
  37. #ifdef ATOMIC_PHYSICS
  38. #include "../Physics/PhysicsEvents.h"
  39. #endif
  40. #include <DetourCrowd/include/DetourCrowd.h>
  41. #include <Recast/include/Recast.h>
  42. #include "../DebugNew.h"
  43. namespace Atomic
  44. {
  45. extern const char* NAVIGATION_CATEGORY;
  46. static const unsigned DEFAULT_MAX_AGENTS = 512;
  47. DetourCrowdManager::DetourCrowdManager(Context* context) :
  48. Component(context),
  49. maxAgents_(DEFAULT_MAX_AGENTS),
  50. crowd_(0),
  51. navigationMesh_(0),
  52. agentDebug_(0)
  53. {
  54. agentBuffer_.Resize(maxAgents_);
  55. }
  56. DetourCrowdManager::~DetourCrowdManager()
  57. {
  58. dtFreeCrowd(crowd_);
  59. crowd_ = 0;
  60. delete agentDebug_;
  61. agentDebug_ = 0;
  62. }
  63. void DetourCrowdManager::RegisterObject(Context* context)
  64. {
  65. context->RegisterFactory<DetourCrowdManager>(NAVIGATION_CATEGORY);
  66. ACCESSOR_ATTRIBUTE("Max Agents", GetMaxAgents, SetMaxAgents, unsigned, DEFAULT_MAX_AGENTS, AM_DEFAULT);
  67. }
  68. void DetourCrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
  69. {
  70. navigationMesh_ = navMesh;
  71. if (navigationMesh_ && !navigationMesh_->navMeshQuery_)
  72. navigationMesh_->InitializeQuery();
  73. CreateCrowd();
  74. MarkNetworkUpdate();
  75. }
  76. void DetourCrowdManager::SetAreaCost(unsigned filterID, unsigned areaID, float weight)
  77. {
  78. dtQueryFilter* filter = crowd_->getEditableFilter(filterID);
  79. if (filter)
  80. filter->setAreaCost((int)areaID, weight);
  81. }
  82. void DetourCrowdManager::SetMaxAgents(unsigned agentCt)
  83. {
  84. maxAgents_ = agentCt;
  85. if (crowd_ && crowd_->getAgentCount() > 0)
  86. LOGERROR("DetourCrowdManager contains active agents, their state will be lost");
  87. agentBuffer_.Resize(maxAgents_);
  88. CreateCrowd();
  89. if (crowd_)
  90. {
  91. PODVector<CrowdAgent*> agents = agents_;
  92. // Reset the existing values in the agent
  93. for (unsigned i = 0; i < agents.Size(); ++i)
  94. {
  95. agents[i]->inCrowd_ = false;
  96. agents[i]->agentCrowdId_ = -1;
  97. }
  98. // Add the agents back in
  99. for (unsigned i = 0; i < agents.Size() && i < maxAgents_; ++i)
  100. agents[i]->AddAgentToCrowd();
  101. if (agents.Size() > maxAgents_)
  102. LOGERROR("DetourCrowdManager: resize left " + String(agents.Size() - maxAgents_) + " agents orphaned");
  103. }
  104. MarkNetworkUpdate();
  105. }
  106. void DetourCrowdManager::SetCrowdTarget(const Vector3& position, int startId, int endId)
  107. {
  108. startId = Max(0, startId);
  109. endId = Clamp(endId, startId, agents_.Size() - 1);
  110. Vector3 moveTarget(position);
  111. for (int i = startId; i <= endId; ++i)
  112. {
  113. // Skip agent that does not have acceleration
  114. if (agents_[i]->GetMaxAccel() > 0.f)
  115. {
  116. agents_[i]->SetMoveTarget(moveTarget);
  117. // FIXME: Should reimplement this using event callback, i.e. it should be application-specific to decide what is the desired crowd formation when they reach the target
  118. if (navigationMesh_)
  119. moveTarget = navigationMesh_->FindNearestPoint(position + Vector3(Random(-4.5f, 4.5f), 0.0f, Random(-4.5f, 4.5f)), Vector3(1.0f, 1.0f, 1.0f));
  120. }
  121. }
  122. }
  123. void DetourCrowdManager::ResetCrowdTarget(int startId, int endId)
  124. {
  125. startId = Max(0, startId);
  126. endId = Clamp(endId, startId, agents_.Size() - 1);
  127. for (int i = startId; i <= endId; ++i)
  128. {
  129. if (agents_[i]->GetMaxAccel() > 0.f)
  130. agents_[i]->ResetMoveTarget();
  131. }
  132. }
  133. void DetourCrowdManager::SetCrowdVelocity(const Vector3& velocity, int startId, int endId)
  134. {
  135. startId = Max(0, startId);
  136. endId = Clamp(endId, startId, agents_.Size() - 1);
  137. for (int i = startId; i <= endId; ++i)
  138. {
  139. if (agents_[i]->GetMaxAccel() > 0.f)
  140. agents_[i]->SetMoveVelocity(velocity);
  141. }
  142. }
  143. float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
  144. {
  145. if (crowd_ && navigationMesh_)
  146. {
  147. const dtQueryFilter* filter = crowd_->getFilter((int)filterID);
  148. if (filter)
  149. return filter->getAreaCost((int)areaID);
  150. }
  151. return 0.0f;
  152. }
  153. unsigned DetourCrowdManager::GetAgentCount() const
  154. {
  155. return crowd_ ? crowd_->getAgentCount() : 0;
  156. }
  157. void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  158. {
  159. if (debug && navigationMesh_.NotNull() && crowd_)
  160. {
  161. // Current position-to-target line
  162. for (int i = 0; i < crowd_->getAgentCount(); i++)
  163. {
  164. const dtCrowdAgent* ag = crowd_->getAgent(i);
  165. if (!ag->active)
  166. continue;
  167. // Draw CrowdAgent shape (from its radius & height)
  168. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
  169. crowdAgent->DrawDebugGeometry(debug, depthTest);
  170. // Draw move target if any
  171. if (crowdAgent->GetTargetState() == CROWD_AGENT_TARGET_NONE)
  172. continue;
  173. Color color(0.6f, 0.2f, 0.2f, 1.0f);
  174. // Draw line to target
  175. Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
  176. Vector3 pos2;
  177. for (int i = 0; i < ag->ncorners; ++i)
  178. {
  179. pos2.x_ = ag->cornerVerts[i * 3];
  180. pos2.y_ = ag->cornerVerts[i * 3 + 1];
  181. pos2.z_ = ag->cornerVerts[i * 3 + 2];
  182. debug->AddLine(pos1, pos2, color, depthTest);
  183. pos1 = pos2;
  184. }
  185. pos2.x_ = ag->targetPos[0];
  186. pos2.y_ = ag->targetPos[1];
  187. pos2.z_ = ag->targetPos[2];
  188. debug->AddLine(pos1, pos2, color, depthTest);
  189. // Draw target circle
  190. debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
  191. }
  192. }
  193. }
  194. void DetourCrowdManager::DrawDebugGeometry(bool depthTest)
  195. {
  196. Scene* scene = GetScene();
  197. if (scene)
  198. {
  199. DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
  200. if (debug)
  201. DrawDebugGeometry(debug, depthTest);
  202. }
  203. }
  204. bool DetourCrowdManager::CreateCrowd()
  205. {
  206. if (!navigationMesh_ || !navigationMesh_->navMesh_)
  207. return false;
  208. if (crowd_)
  209. dtFreeCrowd(crowd_);
  210. crowd_ = dtAllocCrowd();
  211. if (!agentDebug_)
  212. agentDebug_ = new dtCrowdAgentDebugInfo();
  213. // Initialize the crowd
  214. if (!crowd_->init(maxAgents_, navigationMesh_->GetAgentRadius(), navigationMesh_->navMesh_))
  215. {
  216. LOGERROR("Could not initialize DetourCrowd");
  217. return false;
  218. }
  219. // Setup local avoidance params to different qualities.
  220. dtObstacleAvoidanceParams params;
  221. memcpy(&params, crowd_->getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
  222. // Low (11)
  223. params.velBias = 0.5f;
  224. params.adaptiveDivs = 5;
  225. params.adaptiveRings = 2;
  226. params.adaptiveDepth = 1;
  227. crowd_->setObstacleAvoidanceParams(0, &params);
  228. // Medium (22)
  229. params.velBias = 0.5f;
  230. params.adaptiveDivs = 5;
  231. params.adaptiveRings = 2;
  232. params.adaptiveDepth = 2;
  233. crowd_->setObstacleAvoidanceParams(1, &params);
  234. // Good (45)
  235. params.velBias = 0.5f;
  236. params.adaptiveDivs = 7;
  237. params.adaptiveRings = 2;
  238. params.adaptiveDepth = 3;
  239. crowd_->setObstacleAvoidanceParams(2, &params);
  240. // High (66)
  241. params.velBias = 0.5f;
  242. params.adaptiveDivs = 7;
  243. params.adaptiveRings = 3;
  244. params.adaptiveDepth = 3;
  245. crowd_->setObstacleAvoidanceParams(3, &params);
  246. return true;
  247. }
  248. int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
  249. {
  250. if (!crowd_ || navigationMesh_.Expired())
  251. return -1;
  252. dtCrowdAgentParams params;
  253. params.userData = agent;
  254. if (agent->radius_ <= 0.0f)
  255. agent->radius_ = navigationMesh_->GetAgentRadius();
  256. params.radius = agent->radius_;
  257. if (agent->height_ <= 0.0f)
  258. agent->height_ = navigationMesh_->GetAgentHeight();
  259. params.height = agent->height_;
  260. params.queryFilterType = (unsigned char)agent->filterType_;
  261. params.maxAcceleration = agent->maxAccel_;
  262. params.maxSpeed = agent->maxSpeed_;
  263. params.collisionQueryRange = params.radius * 8.0f;
  264. params.pathOptimizationRange = params.radius * 30.0f;
  265. params.updateFlags = DT_CROWD_ANTICIPATE_TURNS
  266. | DT_CROWD_OPTIMIZE_VIS
  267. | DT_CROWD_OPTIMIZE_TOPO
  268. | DT_CROWD_OBSTACLE_AVOIDANCE;
  269. params.obstacleAvoidanceType = 3;
  270. params.separationWeight = 2.0f;
  271. params.queryFilterType = 0;
  272. dtPolyRef polyRef;
  273. float nearestPos[3];
  274. rcVcopy(nearestPos, &pos.x_);
  275. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  276. pos.Data(),
  277. crowd_->getQueryExtents(),
  278. crowd_->getFilter(agent->filterType_),
  279. &polyRef,
  280. nearestPos);
  281. const int agentID = crowd_->addAgent(nearestPos, &params);
  282. if (agentID != -1)
  283. agents_.Push(agent);
  284. return agentID;
  285. }
  286. void DetourCrowdManager::RemoveAgent(CrowdAgent* agent)
  287. {
  288. if (!crowd_)
  289. return;
  290. // Clear user data
  291. dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
  292. if (agt)
  293. agt->params.userData = 0;
  294. crowd_->removeAgent(agent->GetAgentCrowdId());
  295. agents_.Remove(agent);
  296. }
  297. void DetourCrowdManager::UpdateAgentNavigationQuality(CrowdAgent* agent, NavigationQuality nq)
  298. {
  299. if (!crowd_)
  300. return;
  301. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  302. switch (nq)
  303. {
  304. case NAVIGATIONQUALITY_LOW:
  305. {
  306. params.updateFlags &= ~0
  307. & ~DT_CROWD_ANTICIPATE_TURNS
  308. & ~DT_CROWD_OPTIMIZE_VIS
  309. & ~DT_CROWD_OPTIMIZE_TOPO
  310. & ~DT_CROWD_OBSTACLE_AVOIDANCE;
  311. }
  312. break;
  313. case NAVIGATIONQUALITY_MEDIUM:
  314. {
  315. params.updateFlags |= 0;
  316. params.updateFlags &= ~0
  317. & ~DT_CROWD_OBSTACLE_AVOIDANCE
  318. & ~DT_CROWD_ANTICIPATE_TURNS
  319. & ~DT_CROWD_OPTIMIZE_VIS
  320. & ~DT_CROWD_OPTIMIZE_TOPO;
  321. }
  322. break;
  323. case NAVIGATIONQUALITY_HIGH:
  324. {
  325. params.obstacleAvoidanceType = 3;
  326. params.updateFlags |= 0
  327. | DT_CROWD_ANTICIPATE_TURNS
  328. | DT_CROWD_OPTIMIZE_VIS
  329. | DT_CROWD_OPTIMIZE_TOPO
  330. | DT_CROWD_OBSTACLE_AVOIDANCE;
  331. }
  332. break;
  333. }
  334. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  335. }
  336. void DetourCrowdManager::UpdateAgentPushiness(CrowdAgent* agent, NavigationPushiness pushiness)
  337. {
  338. if (!crowd_)
  339. return;
  340. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  341. switch (pushiness)
  342. {
  343. case PUSHINESS_LOW:
  344. params.separationWeight = 4.0f;
  345. params.collisionQueryRange = params.radius * 16.0f;
  346. break;
  347. case PUSHINESS_MEDIUM:
  348. params.separationWeight = 2.0f;
  349. params.collisionQueryRange = params.radius * 8.0f;
  350. break;
  351. case PUSHINESS_HIGH:
  352. params.separationWeight = 0.5f;
  353. params.collisionQueryRange = params.radius * 1.0f;
  354. break;
  355. }
  356. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  357. }
  358. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target)
  359. {
  360. if (!crowd_)
  361. return false;
  362. dtPolyRef polyRef;
  363. float nearestPos[3];
  364. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  365. target.Data(),
  366. crowd_->getQueryExtents(),
  367. crowd_->getFilter(agent->filterType_),
  368. &polyRef,
  369. nearestPos);
  370. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), polyRef, nearestPos);
  371. }
  372. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target, unsigned& targetRef)
  373. {
  374. if (crowd_ == 0)
  375. return false;
  376. float nearestPos[3];
  377. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  378. target.Data(),
  379. crowd_->getQueryExtents(),
  380. crowd_->getFilter(agent->filterType_),
  381. &targetRef,
  382. nearestPos);
  383. // Return true if detour has determined it can do something with our move target
  384. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), targetRef, nearestPos) &&
  385. crowd_->getAgent(agent->GetAgentCrowdId())->targetState != DT_CROWDAGENT_TARGET_FAILED;
  386. }
  387. Vector3 DetourCrowdManager::GetClosestWalkablePosition(Vector3 pos) const
  388. {
  389. if (!crowd_)
  390. return Vector3::ZERO;
  391. float closest[3];
  392. const static float extents[] = { 1.0f, 20.0f, 1.0f };
  393. dtPolyRef closestPoly;
  394. dtQueryFilter filter;
  395. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  396. pos.Data(),
  397. crowd_->getQueryExtents(),
  398. &filter,
  399. &closestPoly,
  400. closest);
  401. return Vector3(closest);
  402. }
  403. void DetourCrowdManager::Update(float delta)
  404. {
  405. if (!crowd_)
  406. return;
  407. PROFILE(UpdateCrowd);
  408. crowd_->update(delta, agentDebug_);
  409. memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
  410. const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
  411. {
  412. PROFILE(ApplyCrowdUpdates);
  413. for (int i = 0; i < count; i++)
  414. {
  415. dtCrowdAgent* agent = agentBuffer_[i];
  416. if (agent)
  417. {
  418. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);
  419. if (crowdAgent)
  420. crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
  421. }
  422. }
  423. }
  424. }
  425. const dtCrowdAgent* DetourCrowdManager::GetCrowdAgent(int agent)
  426. {
  427. return crowd_ ? crowd_->getAgent(agent) : 0;
  428. }
  429. void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
  430. {
  431. using namespace SceneSubsystemUpdate;
  432. if (IsEnabledEffective())
  433. Update(eventData[P_TIMESTEP].GetFloat());
  434. }
  435. void DetourCrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData)
  436. {
  437. using namespace NavigationMeshRebuilt;
  438. // The mesh being rebuilt may not have existed before
  439. NavigationMesh* navMesh = static_cast<NavigationMesh*>(eventData[P_MESH].GetPtr());
  440. if (!navigationMesh_ || !crowd_)
  441. {
  442. SetNavigationMesh(navMesh);
  443. // Scan for existing agents that are potentially important
  444. PODVector<Node*> agents;
  445. GetScene()->GetChildrenWithComponent<CrowdAgent>(agents, true);
  446. for (unsigned i = 0; i < agents.Size(); ++i)
  447. {
  448. CrowdAgent* agent = agents[i]->GetComponent<CrowdAgent>();
  449. if (agent && agent->IsEnabledEffective())
  450. agent->AddAgentToCrowd();
  451. }
  452. }
  453. }
  454. void DetourCrowdManager::OnSceneSet(Scene* scene)
  455. {
  456. // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
  457. // to the scene's NavigationMesh
  458. if (scene)
  459. {
  460. SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
  461. NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
  462. if (!mesh)
  463. mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
  464. if (mesh)
  465. {
  466. SubscribeToEvent(mesh, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
  467. SetNavigationMesh(mesh);
  468. }
  469. else
  470. LOGERROR("DetourCrowdManager requires an existing navigation mesh");
  471. }
  472. else
  473. {
  474. UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
  475. UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
  476. navigationMesh_.Reset();
  477. }
  478. }
  479. }