CrowdManager.cpp 24 KB

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