CrowdManager.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. //
  2. // Copyright (c) 2008-2017 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 "../Core/Context.h"
  24. #include "../Core/Profiler.h"
  25. #include "../Graphics/DebugRenderer.h"
  26. #include "../IO/Log.h"
  27. #include "../Navigation/CrowdAgent.h"
  28. #include "../Navigation/CrowdManager.h"
  29. #include "../Navigation/DynamicNavigationMesh.h"
  30. #include "../Navigation/NavigationEvents.h"
  31. #include "../Scene/Node.h"
  32. #include "../Scene/Scene.h"
  33. #include "../Scene/SceneEvents.h"
  34. #include <DetourCrowd/DetourCrowd.h>
  35. #include "../DebugNew.h"
  36. namespace Urho3D
  37. {
  38. extern const char* NAVIGATION_CATEGORY;
  39. static const unsigned DEFAULT_MAX_AGENTS = 512;
  40. static const float DEFAULT_MAX_AGENT_RADIUS = 0.f;
  41. static const StringVector filterTypesStructureElementNames =
  42. {
  43. "Query Filter Type Count",
  44. " Include Flags",
  45. " Exclude Flags",
  46. " >AreaCost"
  47. };
  48. static const StringVector obstacleAvoidanceTypesStructureElementNames =
  49. {
  50. "Obstacle Avoid. Type Count",
  51. " Velocity Bias",
  52. " Desired Velocity Weight",
  53. " Current Velocity Weight",
  54. " Side Bias Weight",
  55. " Time of Impact Weight",
  56. " Time Horizon",
  57. " Grid Size",
  58. " Adaptive Divs",
  59. " Adaptive Rings",
  60. " Adaptive Depth"
  61. };
  62. void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt)
  63. {
  64. static_cast<CrowdAgent*>(ag->params.userData)->OnCrowdUpdate(ag, dt);
  65. }
  66. CrowdManager::CrowdManager(Context* context) :
  67. Component(context),
  68. crowd_(nullptr),
  69. navigationMeshId_(0),
  70. maxAgents_(DEFAULT_MAX_AGENTS),
  71. maxAgentRadius_(DEFAULT_MAX_AGENT_RADIUS),
  72. numQueryFilterTypes_(0),
  73. numObstacleAvoidanceTypes_(0)
  74. {
  75. // The actual buffer is allocated inside dtCrowd, we only track the number of "slots" being configured explicitly
  76. numAreas_.Reserve(DT_CROWD_MAX_QUERY_FILTER_TYPE);
  77. for (unsigned i = 0; i < DT_CROWD_MAX_QUERY_FILTER_TYPE; ++i)
  78. numAreas_.Push(0);
  79. }
  80. CrowdManager::~CrowdManager()
  81. {
  82. dtFreeCrowd(crowd_);
  83. crowd_ = nullptr;
  84. }
  85. void CrowdManager::RegisterObject(Context* context)
  86. {
  87. context->RegisterFactory<CrowdManager>(NAVIGATION_CATEGORY);
  88. URHO3D_ATTRIBUTE("Max Agents", unsigned, maxAgents_, DEFAULT_MAX_AGENTS, AM_DEFAULT);
  89. URHO3D_ATTRIBUTE("Max Agent Radius", float, maxAgentRadius_, DEFAULT_MAX_AGENT_RADIUS, AM_DEFAULT);
  90. URHO3D_ATTRIBUTE("Navigation Mesh", unsigned, navigationMeshId_, 0, AM_DEFAULT | AM_COMPONENTID);
  91. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Filter Types", GetQueryFilterTypesAttr, SetQueryFilterTypesAttr,
  92. VariantVector, Variant::emptyVariantVector, AM_DEFAULT)
  93. .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, filterTypesStructureElementNames);
  94. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Obstacle Avoidance Types", GetObstacleAvoidanceTypesAttr, SetObstacleAvoidanceTypesAttr,
  95. VariantVector, Variant::emptyVariantVector, AM_DEFAULT)
  96. .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, obstacleAvoidanceTypesStructureElementNames);
  97. }
  98. void CrowdManager::ApplyAttributes()
  99. {
  100. // Values from Editor, saved-file, or network must be checked before applying
  101. maxAgents_ = Max(1U, maxAgents_);
  102. maxAgentRadius_ = Max(0.f, maxAgentRadius_);
  103. bool navMeshChange = false;
  104. Scene* scene = GetScene();
  105. if (scene && navigationMeshId_)
  106. {
  107. NavigationMesh* navMesh = dynamic_cast<NavigationMesh*>(scene->GetComponent(navigationMeshId_));
  108. if (navMesh && navMesh != navigationMesh_)
  109. {
  110. SetNavigationMesh(navMesh); // This will also CreateCrowd(), so the rest of the function is unnecessary
  111. return;
  112. }
  113. }
  114. // In case of receiving an invalid component id, revert it back to the existing navmesh component id (if any)
  115. navigationMeshId_ = navigationMesh_ ? navigationMesh_->GetID() : 0;
  116. // If the Detour crowd initialization parameters have changed then recreate it
  117. if (crowd_ && (navMeshChange || crowd_->getAgentCount() != maxAgents_ || crowd_->getMaxAgentRadius() != maxAgentRadius_))
  118. CreateCrowd();
  119. }
  120. void CrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  121. {
  122. if (debug && crowd_)
  123. {
  124. // Current position-to-target line
  125. for (int i = 0; i < crowd_->getAgentCount(); i++)
  126. {
  127. const dtCrowdAgent* ag = crowd_->getAgent(i);
  128. if (!ag->active)
  129. continue;
  130. // Draw CrowdAgent shape (from its radius & height)
  131. CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
  132. crowdAgent->DrawDebugGeometry(debug, depthTest);
  133. // Draw move target if any
  134. if (crowdAgent->GetTargetState() == CA_TARGET_NONE || crowdAgent->GetTargetState() == CA_TARGET_VELOCITY)
  135. continue;
  136. Color color(0.6f, 0.2f, 0.2f, 1.0f);
  137. // Draw line to target
  138. Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
  139. Vector3 pos2;
  140. for (int i = 0; i < ag->ncorners; ++i)
  141. {
  142. pos2.x_ = ag->cornerVerts[i * 3];
  143. pos2.y_ = ag->cornerVerts[i * 3 + 1];
  144. pos2.z_ = ag->cornerVerts[i * 3 + 2];
  145. debug->AddLine(pos1, pos2, color, depthTest);
  146. pos1 = pos2;
  147. }
  148. pos2.x_ = ag->targetPos[0];
  149. pos2.y_ = ag->targetPos[1];
  150. pos2.z_ = ag->targetPos[2];
  151. debug->AddLine(pos1, pos2, color, depthTest);
  152. // Draw target circle
  153. debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
  154. }
  155. }
  156. }
  157. void CrowdManager::DrawDebugGeometry(bool depthTest)
  158. {
  159. Scene* scene = GetScene();
  160. if (scene)
  161. {
  162. DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
  163. if (debug)
  164. DrawDebugGeometry(debug, depthTest);
  165. }
  166. }
  167. void CrowdManager::SetCrowdTarget(const Vector3& position, Node* node)
  168. {
  169. if (!crowd_)
  170. return;
  171. PODVector<CrowdAgent*> agents = GetAgents(node, false); // Get all crowd agent components
  172. Vector3 moveTarget(position);
  173. for (unsigned i = 0; i < agents.Size(); ++i)
  174. {
  175. // Give application a chance to determine the desired crowd formation when they reach the target position
  176. CrowdAgent* agent = agents[i];
  177. using namespace CrowdAgentFormation;
  178. VariantMap& map = GetEventDataMap();
  179. map[P_NODE] = agent->GetNode();
  180. map[P_CROWD_AGENT] = agent;
  181. map[P_INDEX] = i;
  182. map[P_SIZE] = agents.Size();
  183. map[P_POSITION] = moveTarget; // Expect the event handler will modify this position accordingly
  184. SendEvent(E_CROWD_AGENT_FORMATION, map);
  185. moveTarget = map[P_POSITION].GetVector3();
  186. agent->SetTargetPosition(moveTarget);
  187. }
  188. }
  189. void CrowdManager::SetCrowdVelocity(const Vector3& velocity, Node* node)
  190. {
  191. if (!crowd_)
  192. return;
  193. PODVector<CrowdAgent*> agents = GetAgents(node, true); // Get only crowd agent components already in the crowd
  194. for (unsigned i = 0; i < agents.Size(); ++i)
  195. agents[i]->SetTargetVelocity(velocity);
  196. }
  197. void CrowdManager::ResetCrowdTarget(Node* node)
  198. {
  199. if (!crowd_)
  200. return;
  201. PODVector<CrowdAgent*> agents = GetAgents(node, true);
  202. for (unsigned i = 0; i < agents.Size(); ++i)
  203. agents[i]->ResetTarget();
  204. }
  205. void CrowdManager::SetMaxAgents(unsigned maxAgents)
  206. {
  207. if (maxAgents != maxAgents_ && maxAgents > 0)
  208. {
  209. maxAgents_ = maxAgents;
  210. CreateCrowd();
  211. MarkNetworkUpdate();
  212. }
  213. }
  214. void CrowdManager::SetMaxAgentRadius(float maxAgentRadius)
  215. {
  216. if (maxAgentRadius != maxAgentRadius_ && maxAgentRadius > 0.f)
  217. {
  218. maxAgentRadius_ = maxAgentRadius;
  219. CreateCrowd();
  220. MarkNetworkUpdate();
  221. }
  222. }
  223. void CrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
  224. {
  225. UnsubscribeFromEvent(E_COMPONENTADDED);
  226. UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
  227. UnsubscribeFromEvent(E_COMPONENTREMOVED);
  228. if (navMesh != navigationMesh_) // It is possible to reset navmesh pointer back to 0
  229. {
  230. Scene* scene = GetScene();
  231. navigationMesh_ = navMesh;
  232. navigationMeshId_ = navMesh ? navMesh->GetID() : 0;
  233. if (navMesh)
  234. {
  235. SubscribeToEvent(navMesh, E_NAVIGATION_MESH_REBUILT, URHO3D_HANDLER(CrowdManager, HandleNavMeshChanged));
  236. SubscribeToEvent(scene, E_COMPONENTREMOVED, URHO3D_HANDLER(CrowdManager, HandleNavMeshChanged));
  237. }
  238. CreateCrowd();
  239. MarkNetworkUpdate();
  240. }
  241. }
  242. void CrowdManager::SetQueryFilterTypesAttr(const VariantVector& value)
  243. {
  244. if (!crowd_)
  245. return;
  246. unsigned index = 0;
  247. unsigned queryFilterType = 0;
  248. numQueryFilterTypes_ = index < value.Size() ? Min(value[index++].GetUInt(), (unsigned)DT_CROWD_MAX_QUERY_FILTER_TYPE) : 0;
  249. while (queryFilterType < numQueryFilterTypes_)
  250. {
  251. if (index + 3 <= value.Size())
  252. {
  253. dtQueryFilter* filter = crowd_->getEditableFilter(queryFilterType);
  254. assert(filter);
  255. filter->setIncludeFlags((unsigned short)value[index++].GetUInt());
  256. filter->setExcludeFlags((unsigned short)value[index++].GetUInt());
  257. unsigned prevNumAreas = numAreas_[queryFilterType];
  258. numAreas_[queryFilterType] = Min(value[index++].GetUInt(), (unsigned)DT_MAX_AREAS);
  259. // Must loop through based on previous number of areas, the new area cost (if any) can only be set in the next attribute get/set iteration
  260. if (index + prevNumAreas <= value.Size())
  261. {
  262. for (unsigned i = 0; i < prevNumAreas; ++i)
  263. filter->setAreaCost(i, value[index++].GetFloat());
  264. }
  265. }
  266. ++queryFilterType;
  267. }
  268. }
  269. void CrowdManager::SetIncludeFlags(unsigned queryFilterType, unsigned short flags)
  270. {
  271. dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
  272. if (filter)
  273. {
  274. filter->setIncludeFlags(flags);
  275. if (numQueryFilterTypes_ < queryFilterType + 1)
  276. numQueryFilterTypes_ = queryFilterType + 1;
  277. MarkNetworkUpdate();
  278. }
  279. }
  280. void CrowdManager::SetExcludeFlags(unsigned queryFilterType, unsigned short flags)
  281. {
  282. dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
  283. if (filter)
  284. {
  285. filter->setExcludeFlags(flags);
  286. if (numQueryFilterTypes_ < queryFilterType + 1)
  287. numQueryFilterTypes_ = queryFilterType + 1;
  288. MarkNetworkUpdate();
  289. }
  290. }
  291. void CrowdManager::SetAreaCost(unsigned queryFilterType, unsigned areaID, float cost)
  292. {
  293. dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
  294. if (filter && areaID < DT_MAX_AREAS)
  295. {
  296. filter->setAreaCost((int)areaID, cost);
  297. if (numQueryFilterTypes_ < queryFilterType + 1)
  298. numQueryFilterTypes_ = queryFilterType + 1;
  299. if (numAreas_[queryFilterType] < areaID + 1)
  300. numAreas_[queryFilterType] = areaID + 1;
  301. MarkNetworkUpdate();
  302. }
  303. }
  304. void CrowdManager::SetObstacleAvoidanceTypesAttr(const VariantVector& value)
  305. {
  306. if (!crowd_)
  307. return;
  308. unsigned index = 0;
  309. unsigned obstacleAvoidanceType = 0;
  310. numObstacleAvoidanceTypes_ = index < value.Size() ? Min(value[index++].GetUInt(), (unsigned)DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) : 0;
  311. while (obstacleAvoidanceType < numObstacleAvoidanceTypes_)
  312. {
  313. if (index + 10 <= value.Size())
  314. {
  315. dtObstacleAvoidanceParams params;
  316. params.velBias = value[index++].GetFloat();
  317. params.weightDesVel = value[index++].GetFloat();
  318. params.weightCurVel = value[index++].GetFloat();
  319. params.weightSide = value[index++].GetFloat();
  320. params.weightToi = value[index++].GetFloat();
  321. params.horizTime = value[index++].GetFloat();
  322. params.gridSize = (unsigned char)value[index++].GetUInt();
  323. params.adaptiveDivs = (unsigned char)value[index++].GetUInt();
  324. params.adaptiveRings = (unsigned char)value[index++].GetUInt();
  325. params.adaptiveDepth = (unsigned char)value[index++].GetUInt();
  326. crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, &params);
  327. }
  328. ++obstacleAvoidanceType;
  329. }
  330. }
  331. void CrowdManager::SetObstacleAvoidanceParams(unsigned obstacleAvoidanceType, const CrowdObstacleAvoidanceParams& params)
  332. {
  333. if (crowd_ && obstacleAvoidanceType < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
  334. {
  335. crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, reinterpret_cast<const dtObstacleAvoidanceParams*>(&params));
  336. if (numObstacleAvoidanceTypes_ < obstacleAvoidanceType + 1)
  337. numObstacleAvoidanceTypes_ = obstacleAvoidanceType + 1;
  338. MarkNetworkUpdate();
  339. }
  340. }
  341. Vector3 CrowdManager::FindNearestPoint(const Vector3& point, int queryFilterType, dtPolyRef* nearestRef)
  342. {
  343. if (nearestRef)
  344. *nearestRef = 0;
  345. return crowd_ && navigationMesh_ ?
  346. navigationMesh_->FindNearestPoint(point, Vector3(crowd_->getQueryExtents()), crowd_->getFilter(queryFilterType), nearestRef) : point;
  347. }
  348. Vector3 CrowdManager::MoveAlongSurface(const Vector3& start, const Vector3& end, int queryFilterType, int maxVisited)
  349. {
  350. return crowd_ && navigationMesh_ ?
  351. navigationMesh_->MoveAlongSurface(start, end, Vector3(crowd_->getQueryExtents()), maxVisited, crowd_->getFilter(queryFilterType)) :
  352. end;
  353. }
  354. void CrowdManager::FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, int queryFilterType)
  355. {
  356. if (crowd_ && navigationMesh_)
  357. navigationMesh_->FindPath(dest, start, end, Vector3(crowd_->getQueryExtents()), crowd_->getFilter(queryFilterType));
  358. }
  359. Vector3 CrowdManager::GetRandomPoint(int queryFilterType, dtPolyRef* randomRef)
  360. {
  361. if (randomRef)
  362. *randomRef = 0;
  363. return crowd_ && navigationMesh_ ? navigationMesh_->GetRandomPoint(crowd_->getFilter(queryFilterType), randomRef) :
  364. Vector3::ZERO;
  365. }
  366. Vector3 CrowdManager::GetRandomPointInCircle(const Vector3& center, float radius, int queryFilterType, dtPolyRef* randomRef)
  367. {
  368. if (randomRef)
  369. *randomRef = 0;
  370. return crowd_ && navigationMesh_ ?
  371. navigationMesh_->GetRandomPointInCircle(center, radius, Vector3(crowd_->getQueryExtents()),
  372. crowd_->getFilter(queryFilterType), randomRef) : center;
  373. }
  374. float CrowdManager::GetDistanceToWall(const Vector3& point, float radius, int queryFilterType, Vector3* hitPos, Vector3* hitNormal)
  375. {
  376. if (hitPos)
  377. *hitPos = Vector3::ZERO;
  378. if (hitNormal)
  379. *hitNormal = Vector3::DOWN;
  380. return crowd_ && navigationMesh_ ?
  381. navigationMesh_->GetDistanceToWall(point, radius, Vector3(crowd_->getQueryExtents()), crowd_->getFilter(queryFilterType),
  382. hitPos, hitNormal) : radius;
  383. }
  384. Vector3 CrowdManager::Raycast(const Vector3& start, const Vector3& end, int queryFilterType, Vector3* hitNormal)
  385. {
  386. if (hitNormal)
  387. *hitNormal = Vector3::DOWN;
  388. return crowd_ && navigationMesh_ ?
  389. navigationMesh_->Raycast(start, end, Vector3(crowd_->getQueryExtents()), crowd_->getFilter(queryFilterType), hitNormal)
  390. : end;
  391. }
  392. unsigned CrowdManager::GetNumAreas(unsigned queryFilterType) const
  393. {
  394. return queryFilterType < numQueryFilterTypes_ ? numAreas_[queryFilterType] : 0;
  395. }
  396. VariantVector CrowdManager::GetQueryFilterTypesAttr() const
  397. {
  398. VariantVector ret;
  399. if (crowd_)
  400. {
  401. unsigned totalNumAreas = 0;
  402. for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
  403. totalNumAreas += numAreas_[i];
  404. ret.Reserve(numQueryFilterTypes_ * 3 + totalNumAreas + 1);
  405. ret.Push(numQueryFilterTypes_);
  406. for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
  407. {
  408. const dtQueryFilter* filter = crowd_->getFilter(i);
  409. assert(filter);
  410. ret.Push(filter->getIncludeFlags());
  411. ret.Push(filter->getExcludeFlags());
  412. ret.Push(numAreas_[i]);
  413. for (unsigned j = 0; j < numAreas_[i]; ++j)
  414. ret.Push(filter->getAreaCost(j));
  415. }
  416. }
  417. else
  418. ret.Push(0);
  419. return ret;
  420. }
  421. unsigned short CrowdManager::GetIncludeFlags(unsigned queryFilterType) const
  422. {
  423. if (queryFilterType >= numQueryFilterTypes_)
  424. URHO3D_LOGWARNINGF("Query filter type %d is not configured yet, returning the default include flags initialized by dtCrowd",
  425. queryFilterType);
  426. const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
  427. return (unsigned short)(filter ? filter->getIncludeFlags() : 0xffff);
  428. }
  429. unsigned short CrowdManager::GetExcludeFlags(unsigned queryFilterType) const
  430. {
  431. if (queryFilterType >= numQueryFilterTypes_)
  432. URHO3D_LOGWARNINGF("Query filter type %d is not configured yet, returning the default exclude flags initialized by dtCrowd",
  433. queryFilterType);
  434. const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
  435. return (unsigned short)(filter ? filter->getExcludeFlags() : 0);
  436. }
  437. float CrowdManager::GetAreaCost(unsigned queryFilterType, unsigned areaID) const
  438. {
  439. if (queryFilterType >= numQueryFilterTypes_ || areaID >= numAreas_[queryFilterType])
  440. URHO3D_LOGWARNINGF(
  441. "Query filter type %d and/or area id %d are not configured yet, returning the default area cost initialized by dtCrowd",
  442. queryFilterType, areaID);
  443. const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
  444. return filter ? filter->getAreaCost((int)areaID) : 1.f;
  445. }
  446. VariantVector CrowdManager::GetObstacleAvoidanceTypesAttr() const
  447. {
  448. VariantVector ret;
  449. if (crowd_)
  450. {
  451. ret.Reserve(numObstacleAvoidanceTypes_ * 10 + 1);
  452. ret.Push(numObstacleAvoidanceTypes_);
  453. for (unsigned i = 0; i < numObstacleAvoidanceTypes_; ++i)
  454. {
  455. const dtObstacleAvoidanceParams* params = crowd_->getObstacleAvoidanceParams(i);
  456. assert(params);
  457. ret.Push(params->velBias);
  458. ret.Push(params->weightDesVel);
  459. ret.Push(params->weightCurVel);
  460. ret.Push(params->weightSide);
  461. ret.Push(params->weightToi);
  462. ret.Push(params->horizTime);
  463. ret.Push(params->gridSize);
  464. ret.Push(params->adaptiveDivs);
  465. ret.Push(params->adaptiveRings);
  466. ret.Push(params->adaptiveDepth);
  467. }
  468. }
  469. else
  470. ret.Push(0);
  471. return ret;
  472. }
  473. const CrowdObstacleAvoidanceParams& CrowdManager::GetObstacleAvoidanceParams(unsigned obstacleAvoidanceType) const
  474. {
  475. static const CrowdObstacleAvoidanceParams EMPTY_PARAMS = CrowdObstacleAvoidanceParams();
  476. const dtObstacleAvoidanceParams* params = crowd_ ? crowd_->getObstacleAvoidanceParams(obstacleAvoidanceType) : nullptr;
  477. return params ? *reinterpret_cast<const CrowdObstacleAvoidanceParams*>(params) : EMPTY_PARAMS;
  478. }
  479. PODVector<CrowdAgent*> CrowdManager::GetAgents(Node* node, bool inCrowdFilter) const
  480. {
  481. if (!node)
  482. node = GetScene();
  483. PODVector<CrowdAgent*> agents;
  484. node->GetComponents<CrowdAgent>(agents, true);
  485. if (inCrowdFilter)
  486. {
  487. PODVector<CrowdAgent*>::Iterator i = agents.Begin();
  488. while (i != agents.End())
  489. {
  490. if ((*i)->IsInCrowd())
  491. ++i;
  492. else
  493. i = agents.Erase(i);
  494. }
  495. }
  496. return agents;
  497. }
  498. bool CrowdManager::CreateCrowd()
  499. {
  500. if (!navigationMesh_ || !navigationMesh_->InitializeQuery())
  501. return false;
  502. // Preserve the existing crowd configuration before recreating it
  503. VariantVector queryFilterTypeConfiguration, obstacleAvoidanceTypeConfiguration;
  504. bool recreate = crowd_ != nullptr;
  505. if (recreate)
  506. {
  507. queryFilterTypeConfiguration = GetQueryFilterTypesAttr();
  508. obstacleAvoidanceTypeConfiguration = GetObstacleAvoidanceTypesAttr();
  509. dtFreeCrowd(crowd_);
  510. }
  511. crowd_ = dtAllocCrowd();
  512. // Initialize the crowd
  513. if (maxAgentRadius_ == 0.f)
  514. maxAgentRadius_ = navigationMesh_->GetAgentRadius();
  515. if (!crowd_->init(maxAgents_, maxAgentRadius_, navigationMesh_->navMesh_, CrowdAgentUpdateCallback))
  516. {
  517. URHO3D_LOGERROR("Could not initialize DetourCrowd");
  518. return false;
  519. }
  520. if (recreate)
  521. {
  522. // Reconfigure the newly initialized crowd
  523. SetQueryFilterTypesAttr(queryFilterTypeConfiguration);
  524. SetObstacleAvoidanceTypesAttr(obstacleAvoidanceTypeConfiguration);
  525. // Re-add the existing crowd agents
  526. PODVector<CrowdAgent*> agents = GetAgents();
  527. for (unsigned i = 0; i < agents.Size(); ++i)
  528. {
  529. // Keep adding until the crowd cannot take it anymore
  530. if (agents[i]->AddAgentToCrowd(true) == -1)
  531. {
  532. URHO3D_LOGWARNINGF("CrowdManager: %d crowd agents orphaned", agents.Size() - i);
  533. break;
  534. }
  535. }
  536. }
  537. return true;
  538. }
  539. int CrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
  540. {
  541. if (!crowd_ || !navigationMesh_ || !agent)
  542. return -1;
  543. dtCrowdAgentParams params;
  544. params.userData = agent;
  545. if (agent->radius_ == 0.f)
  546. agent->radius_ = navigationMesh_->GetAgentRadius();
  547. if (agent->height_ == 0.f)
  548. agent->height_ = navigationMesh_->GetAgentHeight();
  549. // dtCrowd::addAgent() requires the query filter type to find the nearest position on navmesh as the initial agent's position
  550. params.queryFilterType = (unsigned char)agent->GetQueryFilterType();
  551. return crowd_->addAgent(pos.Data(), &params);
  552. }
  553. void CrowdManager::RemoveAgent(CrowdAgent* agent)
  554. {
  555. if (!crowd_ || !agent)
  556. return;
  557. dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
  558. if (agt)
  559. agt->params.userData = nullptr;
  560. crowd_->removeAgent(agent->GetAgentCrowdId());
  561. }
  562. void CrowdManager::OnSceneSet(Scene* scene)
  563. {
  564. // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
  565. // to the scene's NavigationMesh
  566. if (scene)
  567. {
  568. if (scene != node_)
  569. {
  570. URHO3D_LOGERROR("CrowdManager is a scene component and should only be attached to the scene node");
  571. return;
  572. }
  573. SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, URHO3D_HANDLER(CrowdManager, HandleSceneSubsystemUpdate));
  574. // Attempt to auto discover a NavigationMesh component (or its derivative) under the scene node
  575. if (navigationMeshId_ == 0)
  576. {
  577. NavigationMesh* navMesh = scene->GetDerivedComponent<NavigationMesh>(true);
  578. if (navMesh)
  579. SetNavigationMesh(navMesh);
  580. else
  581. {
  582. // If not found, attempt to find in a delayed manner
  583. SubscribeToEvent(scene, E_COMPONENTADDED, URHO3D_HANDLER(CrowdManager, HandleComponentAdded));
  584. }
  585. }
  586. }
  587. else
  588. {
  589. UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
  590. UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
  591. UnsubscribeFromEvent(E_COMPONENTADDED);
  592. UnsubscribeFromEvent(E_COMPONENTREMOVED);
  593. navigationMesh_ = nullptr;
  594. }
  595. }
  596. void CrowdManager::Update(float delta)
  597. {
  598. assert(crowd_ && navigationMesh_);
  599. URHO3D_PROFILE(UpdateCrowd);
  600. crowd_->update(delta, nullptr);
  601. }
  602. const dtCrowdAgent* CrowdManager::GetDetourCrowdAgent(int agent) const
  603. {
  604. return crowd_ ? crowd_->getAgent(agent) : nullptr;
  605. }
  606. const dtQueryFilter* CrowdManager::GetDetourQueryFilter(unsigned queryFilterType) const
  607. {
  608. return crowd_ ? crowd_->getFilter(queryFilterType) : nullptr;
  609. }
  610. void CrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
  611. {
  612. // Perform update tick as long as the crowd is initialized and the associated navmesh has not been removed
  613. if (crowd_ && navigationMesh_)
  614. {
  615. using namespace SceneSubsystemUpdate;
  616. if (IsEnabledEffective())
  617. Update(eventData[P_TIMESTEP].GetFloat());
  618. }
  619. }
  620. void CrowdManager::HandleNavMeshChanged(StringHash eventType, VariantMap& eventData)
  621. {
  622. NavigationMesh* navMesh;
  623. if (eventType == E_NAVIGATION_MESH_REBUILT)
  624. {
  625. navMesh = static_cast<NavigationMesh*>(eventData[NavigationMeshRebuilt::P_MESH].GetPtr());
  626. // Reset internal pointer so that the same navmesh can be reassigned and the crowd creation be reattempted
  627. if (navMesh == navigationMesh_)
  628. navigationMesh_.Reset();
  629. }
  630. else
  631. {
  632. // eventType == E_COMPONENTREMOVED
  633. navMesh = static_cast<NavigationMesh*>(eventData[ComponentRemoved::P_COMPONENT].GetPtr());
  634. // Only interested in navmesh component being used to initialized the crowd
  635. if (navMesh != navigationMesh_)
  636. return;
  637. // Since this is a component removed event, reset our own navmesh pointer
  638. navMesh = nullptr;
  639. }
  640. SetNavigationMesh(navMesh);
  641. }
  642. void CrowdManager::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
  643. {
  644. Scene* scene = GetScene();
  645. if (scene)
  646. {
  647. NavigationMesh* navMesh = scene->GetDerivedComponent<NavigationMesh>(true);
  648. if (navMesh)
  649. SetNavigationMesh(navMesh);
  650. }
  651. }
  652. }