DetourCrowdManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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. void DetourCrowdManager::SetCrowdTarget(const Vector3& position, int startId, int endId)
  106. {
  107. startId = Max(0, startId);
  108. endId = Clamp(endId, startId, agents_.Size() - 1);
  109. Vector3 moveTarget(position);
  110. for (int i = startId; i <= endId; ++i)
  111. {
  112. // Skip agent that does not have acceleration
  113. if (agents_[i]->GetMaxAccel() > 0.f)
  114. {
  115. agents_[i]->SetMoveTarget(moveTarget);
  116. // 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
  117. if (navigationMesh_)
  118. moveTarget = navigationMesh_->FindNearestPoint(position + Vector3(Random(-4.5f, 4.5f), 0.0f, Random(-4.5f, 4.5f)), Vector3(1.0f, 1.0f, 1.0f));
  119. }
  120. }
  121. }
  122. void DetourCrowdManager::ResetCrowdTarget(int startId, int endId)
  123. {
  124. startId = Max(0, startId);
  125. endId = Clamp(endId, startId, agents_.Size() - 1);
  126. for (int i = startId; i <= endId; ++i)
  127. {
  128. if (agents_[i]->GetMaxAccel() > 0.f)
  129. agents_[i]->ResetMoveTarget();
  130. }
  131. }
  132. void DetourCrowdManager::SetCrowdVelocity(const Vector3& velocity, int startId, int endId)
  133. {
  134. startId = Max(0, startId);
  135. endId = Clamp(endId, startId, agents_.Size() - 1);
  136. for (int i = startId; i <= endId; ++i)
  137. {
  138. if (agents_[i]->GetMaxAccel() > 0.f)
  139. agents_[i]->SetMoveVelocity(velocity);
  140. }
  141. }
  142. float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
  143. {
  144. if (crowd_ && navigationMesh_)
  145. {
  146. const dtQueryFilter* filter = crowd_->getFilter((int)filterID);
  147. if (filter)
  148. return filter->getAreaCost((int)areaID);
  149. }
  150. return 0.0f;
  151. }
  152. unsigned DetourCrowdManager::GetAgentCount() const
  153. {
  154. return crowd_ ? crowd_->getAgentCount() : 0;
  155. }
  156. void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  157. {
  158. if (debug && navigationMesh_.NotNull() && crowd_)
  159. {
  160. // Current position-to-target line
  161. for (int i = 0; i < crowd_->getAgentCount(); i++)
  162. {
  163. const dtCrowdAgent* ag = crowd_->getAgent(i);
  164. if (!ag->active)
  165. continue;
  166. // Draw CrowdAgent shape (from its radius & height)
  167. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
  168. crowdAgent->DrawDebugGeometry(debug, depthTest);
  169. // Draw move target if any
  170. if (crowdAgent->GetTargetState() == CROWD_AGENT_TARGET_NONE)
  171. continue;
  172. Color color(0.6f, 0.2f, 0.2f, 1.0f);
  173. // Draw line to target
  174. Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
  175. Vector3 pos2;
  176. for (int i = 0; i < ag->ncorners; ++i)
  177. {
  178. pos2.x_ = ag->cornerVerts[i * 3];
  179. pos2.y_ = ag->cornerVerts[i * 3 + 1];
  180. pos2.z_ = ag->cornerVerts[i * 3 + 2];
  181. debug->AddLine(pos1, pos2, color, depthTest);
  182. pos1 = pos2;
  183. }
  184. pos2.x_ = ag->targetPos[0];
  185. pos2.y_ = ag->targetPos[1];
  186. pos2.z_ = ag->targetPos[2];
  187. debug->AddLine(pos1, pos2, color, depthTest);
  188. // Draw target circle
  189. debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
  190. }
  191. }
  192. }
  193. void DetourCrowdManager::DrawDebugGeometry(bool depthTest)
  194. {
  195. Scene* scene = GetScene();
  196. if (scene)
  197. {
  198. DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
  199. if (debug)
  200. DrawDebugGeometry(debug, depthTest);
  201. }
  202. }
  203. bool DetourCrowdManager::CreateCrowd()
  204. {
  205. if (!navigationMesh_ || !navigationMesh_->navMesh_)
  206. return false;
  207. if (crowd_)
  208. dtFreeCrowd(crowd_);
  209. crowd_ = dtAllocCrowd();
  210. if (!agentDebug_)
  211. agentDebug_ = new dtCrowdAgentDebugInfo();
  212. // Initialize the crowd
  213. if (!crowd_->init(maxAgents_, navigationMesh_->GetAgentRadius(), navigationMesh_->navMesh_))
  214. {
  215. LOGERROR("Could not initialize DetourCrowd");
  216. return false;
  217. }
  218. // Setup local avoidance params to different qualities.
  219. dtObstacleAvoidanceParams params;
  220. memcpy(&params, crowd_->getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
  221. // Low (11)
  222. params.velBias = 0.5f;
  223. params.adaptiveDivs = 5;
  224. params.adaptiveRings = 2;
  225. params.adaptiveDepth = 1;
  226. crowd_->setObstacleAvoidanceParams(0, &params);
  227. // Medium (22)
  228. params.velBias = 0.5f;
  229. params.adaptiveDivs = 5;
  230. params.adaptiveRings = 2;
  231. params.adaptiveDepth = 2;
  232. crowd_->setObstacleAvoidanceParams(1, &params);
  233. // Good (45)
  234. params.velBias = 0.5f;
  235. params.adaptiveDivs = 7;
  236. params.adaptiveRings = 2;
  237. params.adaptiveDepth = 3;
  238. crowd_->setObstacleAvoidanceParams(2, &params);
  239. // High (66)
  240. params.velBias = 0.5f;
  241. params.adaptiveDivs = 7;
  242. params.adaptiveRings = 3;
  243. params.adaptiveDepth = 3;
  244. crowd_->setObstacleAvoidanceParams(3, &params);
  245. return true;
  246. }
  247. int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
  248. {
  249. if (!crowd_ || navigationMesh_.Expired())
  250. return -1;
  251. dtCrowdAgentParams params;
  252. params.userData = agent;
  253. if (agent->radius_ <= 0.0f)
  254. agent->radius_ = navigationMesh_->GetAgentRadius();
  255. params.radius = agent->radius_;
  256. if (agent->height_ <= 0.0f)
  257. agent->height_ = navigationMesh_->GetAgentHeight();
  258. params.height = agent->height_;
  259. params.queryFilterType = (unsigned char)agent->filterType_;
  260. params.maxAcceleration = agent->maxAccel_;
  261. params.maxSpeed = agent->maxSpeed_;
  262. params.collisionQueryRange = params.radius * 8.0f;
  263. params.pathOptimizationRange = params.radius * 30.0f;
  264. params.updateFlags = DT_CROWD_ANTICIPATE_TURNS
  265. | DT_CROWD_OPTIMIZE_VIS
  266. | DT_CROWD_OPTIMIZE_TOPO
  267. | DT_CROWD_OBSTACLE_AVOIDANCE;
  268. params.obstacleAvoidanceType = 3;
  269. params.separationWeight = 2.0f;
  270. params.queryFilterType = 0;
  271. dtPolyRef polyRef;
  272. float nearestPos[3];
  273. rcVcopy(nearestPos, &pos.x_);
  274. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  275. pos.Data(),
  276. crowd_->getQueryExtents(),
  277. crowd_->getFilter(agent->filterType_),
  278. &polyRef,
  279. nearestPos);
  280. const int agentID = crowd_->addAgent(nearestPos, &params);
  281. if (agentID != -1)
  282. agents_.Push(agent);
  283. return agentID;
  284. }
  285. void DetourCrowdManager::RemoveAgent(CrowdAgent* agent)
  286. {
  287. if (!crowd_)
  288. return;
  289. // Clear user data
  290. dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
  291. if (agt)
  292. agt->params.userData = 0;
  293. crowd_->removeAgent(agent->GetAgentCrowdId());
  294. agents_.Remove(agent);
  295. }
  296. void DetourCrowdManager::UpdateAgentNavigationQuality(CrowdAgent* agent, NavigationQuality nq)
  297. {
  298. if (!crowd_)
  299. return;
  300. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  301. switch (nq)
  302. {
  303. case NAVIGATIONQUALITY_LOW:
  304. {
  305. params.updateFlags &= ~0
  306. & ~DT_CROWD_ANTICIPATE_TURNS
  307. & ~DT_CROWD_OPTIMIZE_VIS
  308. & ~DT_CROWD_OPTIMIZE_TOPO
  309. & ~DT_CROWD_OBSTACLE_AVOIDANCE;
  310. }
  311. break;
  312. case NAVIGATIONQUALITY_MEDIUM:
  313. {
  314. params.updateFlags |= 0;
  315. params.updateFlags &= ~0
  316. & ~DT_CROWD_OBSTACLE_AVOIDANCE
  317. & ~DT_CROWD_ANTICIPATE_TURNS
  318. & ~DT_CROWD_OPTIMIZE_VIS
  319. & ~DT_CROWD_OPTIMIZE_TOPO;
  320. }
  321. break;
  322. case NAVIGATIONQUALITY_HIGH:
  323. {
  324. params.obstacleAvoidanceType = 3;
  325. params.updateFlags |= 0
  326. | DT_CROWD_ANTICIPATE_TURNS
  327. | DT_CROWD_OPTIMIZE_VIS
  328. | DT_CROWD_OPTIMIZE_TOPO
  329. | DT_CROWD_OBSTACLE_AVOIDANCE;
  330. }
  331. break;
  332. }
  333. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  334. }
  335. void DetourCrowdManager::UpdateAgentPushiness(CrowdAgent* agent, NavigationPushiness pushiness)
  336. {
  337. if (!crowd_)
  338. return;
  339. dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
  340. switch (pushiness)
  341. {
  342. case PUSHINESS_LOW:
  343. params.separationWeight = 4.0f;
  344. params.collisionQueryRange = params.radius * 16.0f;
  345. break;
  346. case PUSHINESS_MEDIUM:
  347. params.separationWeight = 2.0f;
  348. params.collisionQueryRange = params.radius * 8.0f;
  349. break;
  350. case PUSHINESS_HIGH:
  351. params.separationWeight = 0.5f;
  352. params.collisionQueryRange = params.radius * 1.0f;
  353. break;
  354. }
  355. crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
  356. }
  357. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target)
  358. {
  359. if (!crowd_)
  360. return false;
  361. dtPolyRef polyRef;
  362. float nearestPos[3];
  363. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  364. target.Data(),
  365. crowd_->getQueryExtents(),
  366. crowd_->getFilter(agent->filterType_),
  367. &polyRef,
  368. nearestPos);
  369. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), polyRef, nearestPos);
  370. }
  371. bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target, unsigned int& targetRef)
  372. {
  373. if (crowd_ == 0)
  374. return false;
  375. float nearestPos[3];
  376. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  377. target.Data(),
  378. crowd_->getQueryExtents(),
  379. crowd_->getFilter(agent->filterType_),
  380. &targetRef,
  381. nearestPos);
  382. // Return true if detour has determined it can do something with our move target
  383. return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), targetRef, nearestPos) &&
  384. crowd_->getAgent(agent->GetAgentCrowdId())->targetState != DT_CROWDAGENT_TARGET_FAILED;
  385. }
  386. Vector3 DetourCrowdManager::GetClosestWalkablePosition(Vector3 pos) const
  387. {
  388. if (!crowd_)
  389. return Vector3::ZERO;
  390. float closest[3];
  391. const static float extents[] = { 1.0f, 20.0f, 1.0f };
  392. dtPolyRef closestPoly;
  393. dtQueryFilter filter;
  394. dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
  395. pos.Data(),
  396. crowd_->getQueryExtents(),
  397. &filter,
  398. &closestPoly,
  399. closest);
  400. return Vector3(closest);
  401. }
  402. void DetourCrowdManager::Update(float delta)
  403. {
  404. if (!crowd_)
  405. return;
  406. PROFILE(UpdateCrowd);
  407. crowd_->update(delta, agentDebug_);
  408. memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
  409. const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
  410. {
  411. PROFILE(ApplyCrowdUpdates);
  412. for (int i = 0; i < count; i++)
  413. {
  414. dtCrowdAgent* agent = agentBuffer_[i];
  415. if (agent)
  416. {
  417. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);
  418. if (crowdAgent)
  419. crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
  420. }
  421. }
  422. }
  423. }
  424. const dtCrowdAgent* DetourCrowdManager::GetCrowdAgent(int agent)
  425. {
  426. return crowd_ ? crowd_->getAgent(agent) : 0;
  427. }
  428. void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
  429. {
  430. using namespace SceneSubsystemUpdate;
  431. if (IsEnabledEffective())
  432. Update(eventData[P_TIMESTEP].GetFloat());
  433. }
  434. void DetourCrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData)
  435. {
  436. using namespace NavigationMeshRebuilt;
  437. // The mesh being rebuilt may not have existed before
  438. NavigationMesh* navMesh = static_cast<NavigationMesh*>(eventData[P_MESH].GetPtr());
  439. if (!navigationMesh_ || !crowd_)
  440. {
  441. SetNavigationMesh(navMesh);
  442. // Scan for existing agents that are potentially important
  443. PODVector<Node*> agents;
  444. GetScene()->GetChildrenWithComponent<CrowdAgent>(agents, true);
  445. for (unsigned i = 0; i < agents.Size(); ++i)
  446. {
  447. CrowdAgent* agent = agents[i]->GetComponent<CrowdAgent>();
  448. if (agent && agent->IsEnabledEffective())
  449. agent->AddAgentToCrowd();
  450. }
  451. }
  452. }
  453. void DetourCrowdManager::OnNodeSet(Node* node)
  454. {
  455. // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
  456. // to the scene's NavigationMesh
  457. if (node)
  458. {
  459. SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
  460. SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
  461. NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
  462. if (!mesh)
  463. mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
  464. if (mesh)
  465. SetNavigationMesh(mesh);
  466. else
  467. LOGERROR("DetourCrowdManager requires an existing navigation mesh");
  468. }
  469. }
  470. }