DynamicNavigationMesh.cpp 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  1. //
  2. // Copyright (c) 2008-2022 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 "../IO/MemoryBuffer.h"
  28. #include "../Navigation/CrowdAgent.h"
  29. #include "../Navigation/DynamicNavigationMesh.h"
  30. #include "../Navigation/NavArea.h"
  31. #include "../Navigation/NavBuildData.h"
  32. #include "../Navigation/NavigationEvents.h"
  33. #include "../Navigation/Obstacle.h"
  34. #include "../Navigation/OffMeshConnection.h"
  35. #include "../Scene/Node.h"
  36. #include "../Scene/Scene.h"
  37. #include "../Scene/SceneEvents.h"
  38. #include <LZ4/lz4.h>
  39. #include <Detour/DetourNavMesh.h>
  40. #include <Detour/DetourNavMeshBuilder.h>
  41. #include <DetourTileCache/DetourTileCache.h>
  42. #include <DetourTileCache/DetourTileCacheBuilder.h>
  43. #include <Recast/Recast.h>
  44. using namespace std;
  45. // DebugNew is deliberately not used because the macro 'free' conflicts with DetourTileCache's LinearAllocator interface
  46. //#include "../DebugNew.h"
  47. static const unsigned TILECACHE_MAXLAYERS = 255;
  48. namespace Urho3D
  49. {
  50. extern const char* NAVIGATION_CATEGORY;
  51. static const int DEFAULT_MAX_OBSTACLES = 1024;
  52. static const int DEFAULT_MAX_LAYERS = 16;
  53. struct DynamicNavigationMesh::TileCacheData
  54. {
  55. unsigned char* data;
  56. int dataSize;
  57. };
  58. struct TileCompressor : public dtTileCacheCompressor
  59. {
  60. int maxCompressedSize(const int bufferSize) override
  61. {
  62. return (int)(bufferSize * 1.05f);
  63. }
  64. dtStatus compress(const unsigned char* buffer, const int bufferSize,
  65. unsigned char* compressed, const int /*maxCompressedSize*/, int* compressedSize) override
  66. {
  67. *compressedSize = LZ4_compress_default((const char*)buffer, (char*)compressed, bufferSize, LZ4_compressBound(bufferSize));
  68. return DT_SUCCESS;
  69. }
  70. dtStatus decompress(const unsigned char* compressed, const int compressedSize,
  71. unsigned char* buffer, const int maxBufferSize, int* bufferSize) override
  72. {
  73. *bufferSize = LZ4_decompress_safe((const char*)compressed, (char*)buffer, compressedSize, maxBufferSize);
  74. return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
  75. }
  76. };
  77. struct MeshProcess : public dtTileCacheMeshProcess
  78. {
  79. DynamicNavigationMesh* owner_;
  80. PODVector<Vector3> offMeshVertices_;
  81. PODVector<float> offMeshRadii_;
  82. PODVector<unsigned short> offMeshFlags_;
  83. PODVector<unsigned char> offMeshAreas_;
  84. PODVector<unsigned char> offMeshDir_;
  85. inline explicit MeshProcess(DynamicNavigationMesh* owner) :
  86. owner_(owner)
  87. {
  88. }
  89. void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags) override
  90. {
  91. // Update poly flags from areas.
  92. // \todo Assignment of flags from areas?
  93. for (int i = 0; i < params->polyCount; ++i)
  94. {
  95. if (polyAreas[i] != RC_NULL_AREA)
  96. polyFlags[i] = RC_WALKABLE_AREA;
  97. }
  98. BoundingBox bounds;
  99. rcVcopy(&bounds.min_.x_, params->bmin);
  100. rcVcopy(&bounds.max_.x_, params->bmin);
  101. // collect off-mesh connections
  102. PODVector<OffMeshConnection*> offMeshConnections = owner_->CollectOffMeshConnections(bounds);
  103. if (offMeshConnections.Size() > 0)
  104. {
  105. if (offMeshConnections.Size() != offMeshRadii_.Size())
  106. {
  107. Matrix3x4 inverse = owner_->GetNode()->GetWorldTransform().Inverse();
  108. ClearConnectionData();
  109. for (unsigned i = 0; i < offMeshConnections.Size(); ++i)
  110. {
  111. OffMeshConnection* connection = offMeshConnections[i];
  112. Vector3 start = inverse * connection->GetNode()->GetWorldPosition();
  113. Vector3 end = inverse * connection->GetEndPoint()->GetWorldPosition();
  114. offMeshVertices_.Push(start);
  115. offMeshVertices_.Push(end);
  116. offMeshRadii_.Push(connection->GetRadius());
  117. offMeshFlags_.Push((unsigned short)connection->GetMask());
  118. offMeshAreas_.Push((unsigned char)connection->GetAreaID());
  119. offMeshDir_.Push((unsigned char)(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0));
  120. }
  121. }
  122. params->offMeshConCount = offMeshRadii_.Size();
  123. params->offMeshConVerts = &offMeshVertices_[0].x_;
  124. params->offMeshConRad = &offMeshRadii_[0];
  125. params->offMeshConFlags = &offMeshFlags_[0];
  126. params->offMeshConAreas = &offMeshAreas_[0];
  127. params->offMeshConDir = &offMeshDir_[0];
  128. }
  129. }
  130. void ClearConnectionData()
  131. {
  132. offMeshVertices_.Clear();
  133. offMeshRadii_.Clear();
  134. offMeshFlags_.Clear();
  135. offMeshAreas_.Clear();
  136. offMeshDir_.Clear();
  137. }
  138. };
  139. // From the Detour/Recast Sample_TempObstacles.cpp
  140. struct LinearAllocator : public dtTileCacheAlloc
  141. {
  142. unsigned char* buffer;
  143. int capacity;
  144. int top;
  145. int high;
  146. explicit LinearAllocator(const int cap) :
  147. buffer(nullptr), capacity(0), top(0), high(0)
  148. {
  149. resize(cap);
  150. }
  151. ~LinearAllocator() override
  152. {
  153. dtFree(buffer);
  154. }
  155. void resize(const int cap)
  156. {
  157. if (buffer)
  158. dtFree(buffer);
  159. buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
  160. capacity = cap;
  161. }
  162. void reset() override
  163. {
  164. high = Max(high, top);
  165. top = 0;
  166. }
  167. void* alloc(const size_t size) override
  168. {
  169. if (!buffer)
  170. return nullptr;
  171. if (top + size > capacity)
  172. return nullptr;
  173. unsigned char* mem = &buffer[top];
  174. top += size;
  175. return mem;
  176. }
  177. void free(void*) override
  178. {
  179. }
  180. };
  181. DynamicNavigationMesh::DynamicNavigationMesh(Context* context) :
  182. NavigationMesh(context),
  183. maxLayers_(DEFAULT_MAX_LAYERS)
  184. {
  185. // 64 is the largest tile-size that DetourTileCache will tolerate without silently failing
  186. tileSize_ = 64;
  187. partitionType_ = NAVMESH_PARTITION_MONOTONE;
  188. allocator_ = make_unique<LinearAllocator>(32000); //32kb to start
  189. compressor_ = make_unique<TileCompressor>();
  190. meshProcessor_ = make_unique<MeshProcess>(this);
  191. }
  192. DynamicNavigationMesh::~DynamicNavigationMesh()
  193. {
  194. ReleaseNavigationMesh();
  195. }
  196. void DynamicNavigationMesh::RegisterObject(Context* context)
  197. {
  198. context->RegisterFactory<DynamicNavigationMesh>(NAVIGATION_CATEGORY);
  199. URHO3D_COPY_BASE_ATTRIBUTES(NavigationMesh);
  200. URHO3D_ACCESSOR_ATTRIBUTE("Max Obstacles", GetMaxObstacles, SetMaxObstacles, unsigned, DEFAULT_MAX_OBSTACLES, AM_DEFAULT);
  201. URHO3D_ACCESSOR_ATTRIBUTE("Max Layers", GetMaxLayers, SetMaxLayers, unsigned, DEFAULT_MAX_LAYERS, AM_DEFAULT);
  202. URHO3D_ACCESSOR_ATTRIBUTE("Draw Obstacles", GetDrawObstacles, SetDrawObstacles, bool, false, AM_DEFAULT);
  203. }
  204. bool DynamicNavigationMesh::Allocate(const BoundingBox& boundingBox, unsigned maxTiles)
  205. {
  206. // Release existing navigation data and zero the bounding box
  207. ReleaseNavigationMesh();
  208. if (!node_)
  209. return false;
  210. if (!node_->GetWorldScale().Equals(Vector3::ONE))
  211. URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
  212. boundingBox_ = boundingBox.Transformed(node_->GetWorldTransform().Inverse());
  213. maxTiles = NextPowerOfTwo(maxTiles);
  214. // Calculate number of tiles
  215. int gridW = 0, gridH = 0;
  216. float tileEdgeLength = (float)tileSize_ * cellSize_;
  217. rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH);
  218. numTilesX_ = (gridW + tileSize_ - 1) / tileSize_;
  219. numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_;
  220. // Calculate max number of polygons, 22 bits available to identify both tile & polygon within tile
  221. unsigned tileBits = LogBaseTwo(maxTiles);
  222. unsigned maxPolys = 1u << (22 - tileBits);
  223. dtNavMeshParams params; // NOLINT(hicpp-member-init)
  224. rcVcopy(params.orig, &boundingBox_.min_.x_);
  225. params.tileWidth = tileEdgeLength;
  226. params.tileHeight = tileEdgeLength;
  227. params.maxTiles = maxTiles;
  228. params.maxPolys = maxPolys;
  229. navMesh_ = dtAllocNavMesh();
  230. if (!navMesh_)
  231. {
  232. URHO3D_LOGERROR("Could not allocate navigation mesh");
  233. return false;
  234. }
  235. if (dtStatusFailed(navMesh_->init(&params)))
  236. {
  237. URHO3D_LOGERROR("Could not initialize navigation mesh");
  238. ReleaseNavigationMesh();
  239. return false;
  240. }
  241. dtTileCacheParams tileCacheParams; // NOLINT(hicpp-member-init)
  242. memset(&tileCacheParams, 0, sizeof(tileCacheParams));
  243. rcVcopy(tileCacheParams.orig, &boundingBox_.min_.x_);
  244. tileCacheParams.ch = cellHeight_;
  245. tileCacheParams.cs = cellSize_;
  246. tileCacheParams.width = tileSize_;
  247. tileCacheParams.height = tileSize_;
  248. tileCacheParams.maxSimplificationError = edgeMaxError_;
  249. tileCacheParams.maxTiles = maxTiles * maxLayers_;
  250. tileCacheParams.maxObstacles = maxObstacles_;
  251. // Settings from NavigationMesh
  252. tileCacheParams.walkableClimb = agentMaxClimb_;
  253. tileCacheParams.walkableHeight = agentHeight_;
  254. tileCacheParams.walkableRadius = agentRadius_;
  255. tileCache_ = dtAllocTileCache();
  256. if (!tileCache_)
  257. {
  258. URHO3D_LOGERROR("Could not allocate tile cache");
  259. ReleaseNavigationMesh();
  260. return false;
  261. }
  262. if (dtStatusFailed(tileCache_->init(&tileCacheParams, allocator_.get(), compressor_.get(), meshProcessor_.get())))
  263. {
  264. URHO3D_LOGERROR("Could not initialize tile cache");
  265. ReleaseNavigationMesh();
  266. return false;
  267. }
  268. URHO3D_LOGDEBUG("Allocated empty navigation mesh with max " + String(maxTiles) + " tiles");
  269. // Scan for obstacles to insert into us
  270. PODVector<Node*> obstacles;
  271. GetScene()->GetChildrenWithComponent<Obstacle>(obstacles, true);
  272. for (unsigned i = 0; i < obstacles.Size(); ++i)
  273. {
  274. auto* obs = obstacles[i]->GetComponent<Obstacle>();
  275. if (obs && obs->IsEnabledEffective())
  276. AddObstacle(obs);
  277. }
  278. // Send a notification event to concerned parties that we've been fully rebuilt
  279. {
  280. using namespace NavigationMeshRebuilt;
  281. VariantMap& buildEventParams = GetContext()->GetEventDataMap();
  282. buildEventParams[P_NODE] = node_;
  283. buildEventParams[P_MESH] = this;
  284. SendEvent(E_NAVIGATION_MESH_REBUILT, buildEventParams);
  285. }
  286. return true;
  287. }
  288. bool DynamicNavigationMesh::Build()
  289. {
  290. URHO3D_PROFILE(BuildNavigationMesh);
  291. // Release existing navigation data and zero the bounding box
  292. ReleaseNavigationMesh();
  293. if (!node_)
  294. return false;
  295. if (!node_->GetWorldScale().Equals(Vector3::ONE))
  296. URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
  297. Vector<NavigationGeometryInfo> geometryList;
  298. CollectGeometries(geometryList);
  299. if (geometryList.Empty())
  300. return true; // Nothing to do
  301. // Build the combined bounding box
  302. for (unsigned i = 0; i < geometryList.Size(); ++i)
  303. boundingBox_.Merge(geometryList[i].boundingBox_);
  304. // Expand bounding box by padding
  305. boundingBox_.min_ -= padding_;
  306. boundingBox_.max_ += padding_;
  307. {
  308. URHO3D_PROFILE(BuildNavigationMesh);
  309. // Calculate number of tiles
  310. int gridW = 0, gridH = 0;
  311. float tileEdgeLength = (float)tileSize_ * cellSize_;
  312. rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH);
  313. numTilesX_ = (gridW + tileSize_ - 1) / tileSize_;
  314. numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_;
  315. // Calculate max. number of tiles and polygons, 22 bits available to identify both tile & polygon within tile
  316. unsigned maxTiles = NextPowerOfTwo((unsigned)(numTilesX_ * numTilesZ_)) * maxLayers_;
  317. unsigned tileBits = LogBaseTwo(maxTiles);
  318. unsigned maxPolys = 1u << (22 - tileBits);
  319. dtNavMeshParams params; // NOLINT(hicpp-member-init)
  320. rcVcopy(params.orig, &boundingBox_.min_.x_);
  321. params.tileWidth = tileEdgeLength;
  322. params.tileHeight = tileEdgeLength;
  323. params.maxTiles = maxTiles;
  324. params.maxPolys = maxPolys;
  325. navMesh_ = dtAllocNavMesh();
  326. if (!navMesh_)
  327. {
  328. URHO3D_LOGERROR("Could not allocate navigation mesh");
  329. return false;
  330. }
  331. if (dtStatusFailed(navMesh_->init(&params)))
  332. {
  333. URHO3D_LOGERROR("Could not initialize navigation mesh");
  334. ReleaseNavigationMesh();
  335. return false;
  336. }
  337. dtTileCacheParams tileCacheParams; // NOLINT(hicpp-member-init)
  338. memset(&tileCacheParams, 0, sizeof(tileCacheParams));
  339. rcVcopy(tileCacheParams.orig, &boundingBox_.min_.x_);
  340. tileCacheParams.ch = cellHeight_;
  341. tileCacheParams.cs = cellSize_;
  342. tileCacheParams.width = tileSize_;
  343. tileCacheParams.height = tileSize_;
  344. tileCacheParams.maxSimplificationError = edgeMaxError_;
  345. tileCacheParams.maxTiles = numTilesX_ * numTilesZ_ * maxLayers_;
  346. tileCacheParams.maxObstacles = maxObstacles_;
  347. // Settings from NavigationMesh
  348. tileCacheParams.walkableClimb = agentMaxClimb_;
  349. tileCacheParams.walkableHeight = agentHeight_;
  350. tileCacheParams.walkableRadius = agentRadius_;
  351. tileCache_ = dtAllocTileCache();
  352. if (!tileCache_)
  353. {
  354. URHO3D_LOGERROR("Could not allocate tile cache");
  355. ReleaseNavigationMesh();
  356. return false;
  357. }
  358. if (dtStatusFailed(tileCache_->init(&tileCacheParams, allocator_.get(), compressor_.get(), meshProcessor_.get())))
  359. {
  360. URHO3D_LOGERROR("Could not initialize tile cache");
  361. ReleaseNavigationMesh();
  362. return false;
  363. }
  364. // Build each tile
  365. unsigned numTiles = 0;
  366. for (int z = 0; z < numTilesZ_; ++z)
  367. {
  368. for (int x = 0; x < numTilesX_; ++x)
  369. {
  370. TileCacheData tiles[TILECACHE_MAXLAYERS];
  371. int layerCt = BuildTile(geometryList, x, z, tiles);
  372. for (int i = 0; i < layerCt; ++i)
  373. {
  374. dtCompressedTileRef tileRef;
  375. int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
  376. if (dtStatusFailed((dtStatus)status))
  377. {
  378. dtFree(tiles[i].data);
  379. tiles[i].data = nullptr;
  380. }
  381. }
  382. tileCache_->buildNavMeshTilesAt(x, z, navMesh_);
  383. ++numTiles;
  384. }
  385. }
  386. // For a full build it's necessary to update the nav mesh
  387. // not doing so will cause dependent components to crash, like CrowdManager
  388. tileCache_->update(0, navMesh_);
  389. URHO3D_LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
  390. // Send a notification event to concerned parties that we've been fully rebuilt
  391. {
  392. using namespace NavigationMeshRebuilt;
  393. VariantMap& buildEventParams = GetContext()->GetEventDataMap();
  394. buildEventParams[P_NODE] = node_;
  395. buildEventParams[P_MESH] = this;
  396. SendEvent(E_NAVIGATION_MESH_REBUILT, buildEventParams);
  397. }
  398. // Scan for obstacles to insert into us
  399. PODVector<Node*> obstacles;
  400. GetScene()->GetChildrenWithComponent<Obstacle>(obstacles, true);
  401. for (unsigned i = 0; i < obstacles.Size(); ++i)
  402. {
  403. auto* obs = obstacles[i]->GetComponent<Obstacle>();
  404. if (obs && obs->IsEnabledEffective())
  405. AddObstacle(obs);
  406. }
  407. return true;
  408. }
  409. }
  410. bool DynamicNavigationMesh::Build(const BoundingBox& boundingBox)
  411. {
  412. URHO3D_PROFILE(BuildPartialNavigationMesh);
  413. if (!node_)
  414. return false;
  415. if (!navMesh_)
  416. {
  417. URHO3D_LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt");
  418. return false;
  419. }
  420. if (!node_->GetWorldScale().Equals(Vector3::ONE))
  421. URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
  422. BoundingBox localSpaceBox = boundingBox.Transformed(node_->GetWorldTransform().Inverse());
  423. float tileEdgeLength = (float)tileSize_ * cellSize_;
  424. Vector<NavigationGeometryInfo> geometryList;
  425. CollectGeometries(geometryList);
  426. int sx = Clamp((int)((localSpaceBox.min_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
  427. int sz = Clamp((int)((localSpaceBox.min_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
  428. int ex = Clamp((int)((localSpaceBox.max_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
  429. int ez = Clamp((int)((localSpaceBox.max_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
  430. unsigned numTiles = BuildTiles(geometryList, IntVector2(sx, sz), IntVector2(ex, ez));
  431. URHO3D_LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh");
  432. return true;
  433. }
  434. bool DynamicNavigationMesh::Build(const IntVector2& from, const IntVector2& to)
  435. {
  436. URHO3D_PROFILE(BuildPartialNavigationMesh);
  437. if (!node_)
  438. return false;
  439. if (!navMesh_)
  440. {
  441. URHO3D_LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt");
  442. return false;
  443. }
  444. if (!node_->GetWorldScale().Equals(Vector3::ONE))
  445. URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
  446. Vector<NavigationGeometryInfo> geometryList;
  447. CollectGeometries(geometryList);
  448. unsigned numTiles = BuildTiles(geometryList, from, to);
  449. URHO3D_LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh");
  450. return true;
  451. }
  452. PODVector<unsigned char> DynamicNavigationMesh::GetTileData(const IntVector2& tile) const
  453. {
  454. VectorBuffer ret;
  455. WriteTiles(ret, tile.x_, tile.y_);
  456. return ret.GetBuffer();
  457. }
  458. bool DynamicNavigationMesh::IsObstacleInTile(Obstacle* obstacle, const IntVector2& tile) const
  459. {
  460. const BoundingBox tileBoundingBox = GetTileBoundingBox(tile);
  461. const Vector3 obstaclePosition = obstacle->GetNode()->GetWorldPosition();
  462. return tileBoundingBox.DistanceToPoint(obstaclePosition) < obstacle->GetRadius();
  463. }
  464. bool DynamicNavigationMesh::AddTile(const PODVector<unsigned char>& tileData)
  465. {
  466. MemoryBuffer buffer(tileData);
  467. return ReadTiles(buffer, false);
  468. }
  469. void DynamicNavigationMesh::RemoveTile(const IntVector2& tile)
  470. {
  471. if (!navMesh_)
  472. return;
  473. dtCompressedTileRef existing[TILECACHE_MAXLAYERS];
  474. const int existingCt = tileCache_->getTilesAt(tile.x_, tile.y_, existing, maxLayers_);
  475. for (int i = 0; i < existingCt; ++i)
  476. {
  477. unsigned char* data = nullptr;
  478. if (!dtStatusFailed(tileCache_->removeTile(existing[i], &data, nullptr)) && data != nullptr)
  479. dtFree(data);
  480. }
  481. NavigationMesh::RemoveTile(tile);
  482. }
  483. void DynamicNavigationMesh::RemoveAllTiles()
  484. {
  485. int numTiles = tileCache_->getTileCount();
  486. for (int i = 0; i < numTiles; ++i)
  487. {
  488. const dtCompressedTile* tile = tileCache_->getTile(i);
  489. assert(tile);
  490. if (tile->header)
  491. tileCache_->removeTile(tileCache_->getTileRef(tile), nullptr, nullptr);
  492. }
  493. NavigationMesh::RemoveAllTiles();
  494. }
  495. void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  496. {
  497. if (!debug || !navMesh_ || !node_)
  498. return;
  499. const Matrix3x4& worldTransform = node_->GetWorldTransform();
  500. const dtNavMesh* navMesh = navMesh_;
  501. for (int j = 0; j < navMesh->getMaxTiles(); ++j)
  502. {
  503. const dtMeshTile* tile = navMesh->getTile(j);
  504. assert(tile);
  505. if (!tile->header)
  506. continue;
  507. for (int i = 0; i < tile->header->polyCount; ++i)
  508. {
  509. dtPoly* poly = tile->polys + i;
  510. for (unsigned j = 0; j < poly->vertCount; ++j)
  511. {
  512. debug->AddLine(worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]),
  513. worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]),
  514. Color::YELLOW, depthTest);
  515. }
  516. }
  517. }
  518. Scene* scene = GetScene();
  519. if (scene)
  520. {
  521. // Draw Obstacle components
  522. if (drawObstacles_)
  523. {
  524. PODVector<Node*> obstacles;
  525. scene->GetChildrenWithComponent<Obstacle>(obstacles, true);
  526. for (unsigned i = 0; i < obstacles.Size(); ++i)
  527. {
  528. auto* obstacle = obstacles[i]->GetComponent<Obstacle>();
  529. if (obstacle && obstacle->IsEnabledEffective())
  530. obstacle->DrawDebugGeometry(debug, depthTest);
  531. }
  532. }
  533. // Draw OffMeshConnection components
  534. if (drawOffMeshConnections_)
  535. {
  536. PODVector<Node*> connections;
  537. scene->GetChildrenWithComponent<OffMeshConnection>(connections, true);
  538. for (unsigned i = 0; i < connections.Size(); ++i)
  539. {
  540. auto* connection = connections[i]->GetComponent<OffMeshConnection>();
  541. if (connection && connection->IsEnabledEffective())
  542. connection->DrawDebugGeometry(debug, depthTest);
  543. }
  544. }
  545. // Draw NavArea components
  546. if (drawNavAreas_)
  547. {
  548. PODVector<Node*> areas;
  549. scene->GetChildrenWithComponent<NavArea>(areas, true);
  550. for (unsigned i = 0; i < areas.Size(); ++i)
  551. {
  552. auto* area = areas[i]->GetComponent<NavArea>();
  553. if (area && area->IsEnabledEffective())
  554. area->DrawDebugGeometry(debug, depthTest);
  555. }
  556. }
  557. }
  558. }
  559. void DynamicNavigationMesh::DrawDebugGeometry(bool depthTest)
  560. {
  561. Scene* scene = GetScene();
  562. if (scene)
  563. {
  564. auto* debug = scene->GetComponent<DebugRenderer>();
  565. if (debug)
  566. DrawDebugGeometry(debug, depthTest);
  567. }
  568. }
  569. void DynamicNavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& value)
  570. {
  571. ReleaseNavigationMesh();
  572. if (value.Empty())
  573. return;
  574. MemoryBuffer buffer(value);
  575. boundingBox_ = buffer.ReadBoundingBox();
  576. numTilesX_ = buffer.ReadInt();
  577. numTilesZ_ = buffer.ReadInt();
  578. dtNavMeshParams params; // NOLINT(hicpp-member-init)
  579. buffer.Read(&params, sizeof(dtNavMeshParams));
  580. navMesh_ = dtAllocNavMesh();
  581. if (!navMesh_)
  582. {
  583. URHO3D_LOGERROR("Could not allocate navigation mesh");
  584. return;
  585. }
  586. if (dtStatusFailed(navMesh_->init(&params)))
  587. {
  588. URHO3D_LOGERROR("Could not initialize navigation mesh");
  589. ReleaseNavigationMesh();
  590. return;
  591. }
  592. dtTileCacheParams tcParams; // NOLINT(hicpp-member-init)
  593. buffer.Read(&tcParams, sizeof(tcParams));
  594. tileCache_ = dtAllocTileCache();
  595. if (!tileCache_)
  596. {
  597. URHO3D_LOGERROR("Could not allocate tile cache");
  598. ReleaseNavigationMesh();
  599. return;
  600. }
  601. if (dtStatusFailed(tileCache_->init(&tcParams, allocator_.get(), compressor_.get(), meshProcessor_.get())))
  602. {
  603. URHO3D_LOGERROR("Could not initialize tile cache");
  604. ReleaseNavigationMesh();
  605. return;
  606. }
  607. ReadTiles(buffer, true);
  608. // \todo Shall we send E_NAVIGATION_MESH_REBUILT here?
  609. }
  610. PODVector<unsigned char> DynamicNavigationMesh::GetNavigationDataAttr() const
  611. {
  612. VectorBuffer ret;
  613. if (navMesh_ && tileCache_)
  614. {
  615. ret.WriteBoundingBox(boundingBox_);
  616. ret.WriteInt(numTilesX_);
  617. ret.WriteInt(numTilesZ_);
  618. const dtNavMeshParams* params = navMesh_->getParams();
  619. ret.Write(params, sizeof(dtNavMeshParams));
  620. const dtTileCacheParams* tcParams = tileCache_->getParams();
  621. ret.Write(tcParams, sizeof(dtTileCacheParams));
  622. for (int z = 0; z < numTilesZ_; ++z)
  623. for (int x = 0; x < numTilesX_; ++x)
  624. WriteTiles(ret, x, z);
  625. }
  626. return ret.GetBuffer();
  627. }
  628. void DynamicNavigationMesh::SetMaxLayers(unsigned maxLayers)
  629. {
  630. // Set 3 as a minimum due to the tendency of layers to be constructed inside the hollow space of stacked objects
  631. // That behavior is unlikely to be expected by the end user
  632. maxLayers_ = Max(3U, Min(maxLayers, TILECACHE_MAXLAYERS));
  633. }
  634. void DynamicNavigationMesh::WriteTiles(Serializer& dest, int x, int z) const
  635. {
  636. dtCompressedTileRef tiles[TILECACHE_MAXLAYERS];
  637. const int ct = tileCache_->getTilesAt(x, z, tiles, maxLayers_);
  638. for (int i = 0; i < ct; ++i)
  639. {
  640. const dtCompressedTile* tile = tileCache_->getTileByRef(tiles[i]);
  641. if (!tile || !tile->header || !tile->dataSize)
  642. continue; // Don't write "void-space" tiles
  643. // The header conveniently has the majority of the information required
  644. dest.Write(tile->header, sizeof(dtTileCacheLayerHeader));
  645. dest.WriteInt(tile->dataSize);
  646. dest.Write(tile->data, (unsigned)tile->dataSize);
  647. }
  648. }
  649. bool DynamicNavigationMesh::ReadTiles(Deserializer& source, bool silent)
  650. {
  651. tileQueue_.Clear();
  652. while (!source.IsEof())
  653. {
  654. dtTileCacheLayerHeader header; // NOLINT(hicpp-member-init)
  655. source.Read(&header, sizeof(dtTileCacheLayerHeader));
  656. const int dataSize = source.ReadInt();
  657. auto* data = (unsigned char*)dtAlloc(dataSize, DT_ALLOC_PERM);
  658. if (!data)
  659. {
  660. URHO3D_LOGERROR("Could not allocate data for navigation mesh tile");
  661. return false;
  662. }
  663. source.Read(data, (unsigned)dataSize);
  664. if (dtStatusFailed(tileCache_->addTile(data, dataSize, DT_TILE_FREE_DATA, nullptr)))
  665. {
  666. URHO3D_LOGERROR("Failed to add tile");
  667. dtFree(data);
  668. return false;
  669. }
  670. const IntVector2 tileIdx = IntVector2(header.tx, header.ty);
  671. if (tileQueue_.Empty() || tileQueue_.Back() != tileIdx)
  672. tileQueue_.Push(tileIdx);
  673. }
  674. for (unsigned i = 0; i < tileQueue_.Size(); ++i)
  675. tileCache_->buildNavMeshTilesAt(tileQueue_[i].x_, tileQueue_[i].y_, navMesh_);
  676. tileCache_->update(0, navMesh_);
  677. // Send event
  678. if (!silent)
  679. {
  680. for (unsigned i = 0; i < tileQueue_.Size(); ++i)
  681. {
  682. using namespace NavigationTileAdded;
  683. VariantMap& eventData = GetContext()->GetEventDataMap();
  684. eventData[P_NODE] = GetNode();
  685. eventData[P_MESH] = this;
  686. eventData[P_TILE] = tileQueue_[i];
  687. SendEvent(E_NAVIGATION_TILE_ADDED, eventData);
  688. }
  689. }
  690. return true;
  691. }
  692. int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z, TileCacheData* tiles)
  693. {
  694. URHO3D_PROFILE(BuildNavigationMeshTile);
  695. tileCache_->removeTile(navMesh_->getTileRefAt(x, z, 0), nullptr, nullptr);
  696. const BoundingBox tileBoundingBox = GetTileBoundingBox(IntVector2(x, z));
  697. DynamicNavBuildData build(allocator_.get());
  698. rcConfig cfg; // NOLINT(hicpp-member-init)
  699. memset(&cfg, 0, sizeof cfg);
  700. cfg.cs = cellSize_;
  701. cfg.ch = cellHeight_;
  702. cfg.walkableSlopeAngle = agentMaxSlope_;
  703. cfg.walkableHeight = (int)ceilf(agentHeight_ / cfg.ch);
  704. cfg.walkableClimb = (int)floorf(agentMaxClimb_ / cfg.ch);
  705. cfg.walkableRadius = (int)ceilf(agentRadius_ / cfg.cs);
  706. cfg.maxEdgeLen = (int)(edgeMaxLength_ / cellSize_);
  707. cfg.maxSimplificationError = edgeMaxError_;
  708. cfg.minRegionArea = (int)sqrtf(regionMinSize_);
  709. cfg.mergeRegionArea = (int)sqrtf(regionMergeSize_);
  710. cfg.maxVertsPerPoly = 6;
  711. cfg.tileSize = tileSize_;
  712. cfg.borderSize = cfg.walkableRadius + 3; // Add padding
  713. cfg.width = cfg.tileSize + cfg.borderSize * 2;
  714. cfg.height = cfg.tileSize + cfg.borderSize * 2;
  715. cfg.detailSampleDist = detailSampleDistance_ < 0.9f ? 0.0f : cellSize_ * detailSampleDistance_;
  716. cfg.detailSampleMaxError = cellHeight_ * detailSampleMaxError_;
  717. rcVcopy(cfg.bmin, &tileBoundingBox.min_.x_);
  718. rcVcopy(cfg.bmax, &tileBoundingBox.max_.x_);
  719. cfg.bmin[0] -= cfg.borderSize * cfg.cs;
  720. cfg.bmin[2] -= cfg.borderSize * cfg.cs;
  721. cfg.bmax[0] += cfg.borderSize * cfg.cs;
  722. cfg.bmax[2] += cfg.borderSize * cfg.cs;
  723. BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax));
  724. GetTileGeometry(&build, geometryList, expandedBox);
  725. if (build.vertices_.Empty() || build.indices_.Empty())
  726. return 0; // Nothing to do
  727. build.heightField_ = rcAllocHeightfield();
  728. if (!build.heightField_)
  729. {
  730. URHO3D_LOGERROR("Could not allocate heightfield");
  731. return 0;
  732. }
  733. if (!rcCreateHeightfield(build.ctx_, *build.heightField_, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs,
  734. cfg.ch))
  735. {
  736. URHO3D_LOGERROR("Could not create heightfield");
  737. return 0;
  738. }
  739. unsigned numTriangles = build.indices_.Size() / 3;
  740. SharedArrayPtr<unsigned char> triAreas(new unsigned char[numTriangles]);
  741. memset(triAreas.Get(), 0, numTriangles);
  742. rcMarkWalkableTriangles(build.ctx_, cfg.walkableSlopeAngle, &build.vertices_[0].x_, build.vertices_.Size(),
  743. &build.indices_[0], numTriangles, triAreas.Get());
  744. rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
  745. triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
  746. rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
  747. rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
  748. rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
  749. build.compactHeightField_ = rcAllocCompactHeightfield();
  750. if (!build.compactHeightField_)
  751. {
  752. URHO3D_LOGERROR("Could not allocate create compact heightfield");
  753. return 0;
  754. }
  755. if (!rcBuildCompactHeightfield(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_,
  756. *build.compactHeightField_))
  757. {
  758. URHO3D_LOGERROR("Could not build compact heightfield");
  759. return 0;
  760. }
  761. if (!rcErodeWalkableArea(build.ctx_, cfg.walkableRadius, *build.compactHeightField_))
  762. {
  763. URHO3D_LOGERROR("Could not erode compact heightfield");
  764. return 0;
  765. }
  766. // area volumes
  767. for (unsigned i = 0; i < build.navAreas_.Size(); ++i)
  768. rcMarkBoxArea(build.ctx_, &build.navAreas_[i].bounds_.min_.x_, &build.navAreas_[i].bounds_.max_.x_,
  769. build.navAreas_[i].areaID_, *build.compactHeightField_);
  770. if (this->partitionType_ == NAVMESH_PARTITION_WATERSHED)
  771. {
  772. if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
  773. {
  774. URHO3D_LOGERROR("Could not build distance field");
  775. return 0;
  776. }
  777. if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
  778. cfg.mergeRegionArea))
  779. {
  780. URHO3D_LOGERROR("Could not build regions");
  781. return 0;
  782. }
  783. }
  784. else
  785. {
  786. if (!rcBuildRegionsMonotone(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea))
  787. {
  788. URHO3D_LOGERROR("Could not build monotone regions");
  789. return 0;
  790. }
  791. }
  792. build.heightFieldLayers_ = rcAllocHeightfieldLayerSet();
  793. if (!build.heightFieldLayers_)
  794. {
  795. URHO3D_LOGERROR("Could not allocate height field layer set");
  796. return 0;
  797. }
  798. if (!rcBuildHeightfieldLayers(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.walkableHeight,
  799. *build.heightFieldLayers_))
  800. {
  801. URHO3D_LOGERROR("Could not build height field layers");
  802. return 0;
  803. }
  804. int retCt = 0;
  805. for (int i = 0; i < build.heightFieldLayers_->nlayers; ++i)
  806. {
  807. dtTileCacheLayerHeader header; // NOLINT(hicpp-member-init)
  808. header.magic = DT_TILECACHE_MAGIC;
  809. header.version = DT_TILECACHE_VERSION;
  810. header.tx = x;
  811. header.ty = z;
  812. header.tlayer = i;
  813. rcHeightfieldLayer* layer = &build.heightFieldLayers_->layers[i];
  814. // Tile info.
  815. rcVcopy(header.bmin, layer->bmin);
  816. rcVcopy(header.bmax, layer->bmax);
  817. header.width = (unsigned char)layer->width;
  818. header.height = (unsigned char)layer->height;
  819. header.minx = (unsigned char)layer->minx;
  820. header.maxx = (unsigned char)layer->maxx;
  821. header.miny = (unsigned char)layer->miny;
  822. header.maxy = (unsigned char)layer->maxy;
  823. header.hmin = (unsigned short)layer->hmin;
  824. header.hmax = (unsigned short)layer->hmax;
  825. if (dtStatusFailed(
  826. dtBuildTileCacheLayer(compressor_.get(), &header, layer->heights, layer->areas, layer->cons,
  827. &(tiles[retCt].data), &tiles[retCt].dataSize)))
  828. {
  829. URHO3D_LOGERROR("Failed to build tile cache layers");
  830. return 0;
  831. }
  832. else
  833. ++retCt;
  834. }
  835. // Send a notification of the rebuild of this tile to anyone interested
  836. {
  837. using namespace NavigationAreaRebuilt;
  838. VariantMap& eventData = GetContext()->GetEventDataMap();
  839. eventData[P_NODE] = GetNode();
  840. eventData[P_MESH] = this;
  841. eventData[P_BOUNDSMIN] = Variant(tileBoundingBox.min_);
  842. eventData[P_BOUNDSMAX] = Variant(tileBoundingBox.max_);
  843. SendEvent(E_NAVIGATION_AREA_REBUILT, eventData);
  844. }
  845. return retCt;
  846. }
  847. unsigned DynamicNavigationMesh::BuildTiles(Vector<NavigationGeometryInfo>& geometryList, const IntVector2& from, const IntVector2& to)
  848. {
  849. unsigned numTiles = 0;
  850. for (int z = from.y_; z <= to.y_; ++z)
  851. {
  852. for (int x = from.x_; x <= to.x_; ++x)
  853. {
  854. dtCompressedTileRef existing[TILECACHE_MAXLAYERS];
  855. const int existingCt = tileCache_->getTilesAt(x, z, existing, maxLayers_);
  856. for (int i = 0; i < existingCt; ++i)
  857. {
  858. unsigned char* data = nullptr;
  859. if (!dtStatusFailed(tileCache_->removeTile(existing[i], &data, nullptr)) && data != nullptr)
  860. dtFree(data);
  861. }
  862. TileCacheData tiles[TILECACHE_MAXLAYERS];
  863. int layerCt = BuildTile(geometryList, x, z, tiles);
  864. for (int i = 0; i < layerCt; ++i)
  865. {
  866. dtCompressedTileRef tileRef;
  867. int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
  868. if (dtStatusFailed((dtStatus)status))
  869. {
  870. dtFree(tiles[i].data);
  871. tiles[i].data = nullptr;
  872. }
  873. else
  874. {
  875. tileCache_->buildNavMeshTile(tileRef, navMesh_);
  876. ++numTiles;
  877. }
  878. }
  879. }
  880. }
  881. return numTiles;
  882. }
  883. PODVector<OffMeshConnection*> DynamicNavigationMesh::CollectOffMeshConnections(const BoundingBox& bounds)
  884. {
  885. PODVector<OffMeshConnection*> connections;
  886. node_->GetComponents<OffMeshConnection>(connections, true);
  887. for (unsigned i = 0; i < connections.Size(); ++i)
  888. {
  889. OffMeshConnection* connection = connections[i];
  890. if (!(connection->IsEnabledEffective() && connection->GetEndPoint()))
  891. {
  892. // discard this connection
  893. connections.Erase(i);
  894. --i;
  895. }
  896. }
  897. return connections;
  898. }
  899. void DynamicNavigationMesh::ReleaseNavigationMesh()
  900. {
  901. NavigationMesh::ReleaseNavigationMesh();
  902. ReleaseTileCache();
  903. }
  904. void DynamicNavigationMesh::ReleaseTileCache()
  905. {
  906. dtFreeTileCache(tileCache_);
  907. tileCache_ = nullptr;
  908. }
  909. void DynamicNavigationMesh::OnSceneSet(Scene* scene)
  910. {
  911. // Subscribe to the scene subsystem update, which will trigger the tile cache to update the nav mesh
  912. if (scene)
  913. SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, URHO3D_HANDLER(DynamicNavigationMesh, HandleSceneSubsystemUpdate));
  914. else
  915. UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
  916. }
  917. void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle, bool silent)
  918. {
  919. if (tileCache_)
  920. {
  921. float pos[3];
  922. Vector3 obsPos = obstacle->GetNode()->GetWorldPosition();
  923. rcVcopy(pos, &obsPos.x_);
  924. dtObstacleRef refHolder;
  925. // Because dtTileCache doesn't process obstacle requests while updating tiles
  926. // it's necessary update until sufficient request space is available
  927. while (tileCache_->isObstacleQueueFull())
  928. tileCache_->update(1, navMesh_);
  929. if (dtStatusFailed(tileCache_->addObstacle(pos, obstacle->GetRadius(), obstacle->GetHeight(), &refHolder)))
  930. {
  931. URHO3D_LOGERROR("Failed to add obstacle");
  932. return;
  933. }
  934. obstacle->obstacleId_ = refHolder;
  935. assert(refHolder > 0);
  936. if (!silent)
  937. {
  938. using namespace NavigationObstacleAdded;
  939. VariantMap& eventData = GetContext()->GetEventDataMap();
  940. eventData[P_NODE] = obstacle->GetNode();
  941. eventData[P_OBSTACLE] = obstacle;
  942. eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
  943. eventData[P_RADIUS] = obstacle->GetRadius();
  944. eventData[P_HEIGHT] = obstacle->GetHeight();
  945. SendEvent(E_NAVIGATION_OBSTACLE_ADDED, eventData);
  946. }
  947. }
  948. }
  949. void DynamicNavigationMesh::ObstacleChanged(Obstacle* obstacle)
  950. {
  951. if (tileCache_)
  952. {
  953. RemoveObstacle(obstacle, true);
  954. AddObstacle(obstacle, true);
  955. }
  956. }
  957. void DynamicNavigationMesh::RemoveObstacle(Obstacle* obstacle, bool silent)
  958. {
  959. if (tileCache_ && obstacle->obstacleId_ > 0)
  960. {
  961. // Because dtTileCache doesn't process obstacle requests while updating tiles
  962. // it's necessary update until sufficient request space is available
  963. while (tileCache_->isObstacleQueueFull())
  964. tileCache_->update(1, navMesh_);
  965. if (dtStatusFailed(tileCache_->removeObstacle(obstacle->obstacleId_)))
  966. {
  967. URHO3D_LOGERROR("Failed to remove obstacle");
  968. return;
  969. }
  970. obstacle->obstacleId_ = 0;
  971. // Require a node in order to send an event
  972. if (!silent && obstacle->GetNode())
  973. {
  974. using namespace NavigationObstacleRemoved;
  975. VariantMap& eventData = GetContext()->GetEventDataMap();
  976. eventData[P_NODE] = obstacle->GetNode();
  977. eventData[P_OBSTACLE] = obstacle;
  978. eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
  979. eventData[P_RADIUS] = obstacle->GetRadius();
  980. eventData[P_HEIGHT] = obstacle->GetHeight();
  981. SendEvent(E_NAVIGATION_OBSTACLE_REMOVED, eventData);
  982. }
  983. }
  984. }
  985. void DynamicNavigationMesh::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
  986. {
  987. using namespace SceneSubsystemUpdate;
  988. if (tileCache_ && navMesh_ && IsEnabledEffective())
  989. tileCache_->update(eventData[P_TIMESTEP].GetFloat(), navMesh_);
  990. }
  991. }