|
27 | 27 | #include "esp/assets/MeshData.h" |
28 | 28 | #include "esp/core/Esp.h" |
29 | 29 |
|
| 30 | +#include "DetourCommon.h" |
30 | 31 | #include "DetourNavMesh.h" |
31 | 32 | #include "DetourNavMeshBuilder.h" |
32 | 33 | #include "DetourNavMeshQuery.h" |
@@ -313,6 +314,79 @@ class IslandSystem { |
313 | 314 | } |
314 | 315 | } |
315 | 316 |
|
| 317 | + /** |
| 318 | + * @brief Sets a specified poly flag for all polys specified by the |
| 319 | + * islandIndex within range of a given circle. |
| 320 | + * |
| 321 | + * @param[in] navMesh The navmesh to operate on. |
| 322 | + * @param[in] flag The flag to set or clear. |
| 323 | + * @param[in] circleCenter The center of the circle. |
| 324 | + * @param[in] radius The radius of the circle. |
| 325 | + * @param[in] islandIndex Specify the island. islandIndex == ID_UNDEFINED |
| 326 | + * specifies all islands. |
| 327 | + */ |
| 328 | + inline void setPolyFlagForIslandCircle(dtNavMesh* navMesh, |
| 329 | + ushort flag, |
| 330 | + const vec3f& circleCenter, |
| 331 | + const float radius, |
| 332 | + int islandIndex = ID_UNDEFINED) { |
| 333 | + assertValidIsland(islandIndex); |
| 334 | + CORRADE_ASSERT(navMesh != nullptr, "invalid navMesh pointer", ); |
| 335 | + std::vector<int> islands; |
| 336 | + |
| 337 | + float radSqr = radius * radius; |
| 338 | + |
| 339 | + // all islands |
| 340 | + islands.reserve(islandsToPolys_.size()); |
| 341 | + for (auto& itr : islandsToPolys_) { |
| 342 | + islands.push_back(itr.first); |
| 343 | + } |
| 344 | + |
| 345 | + // for each island |
| 346 | + for (int island : islands) { |
| 347 | + // for each poly |
| 348 | + for (auto& polyRef : islandsToPolys_[island]) { |
| 349 | + // remove all off-island polys immediately |
| 350 | + if (islandIndex != ID_UNDEFINED && islandIndex != island) { |
| 351 | + // get current flags |
| 352 | + ushort f = 0; |
| 353 | + navMesh->getPolyFlags(polyRef, &f); |
| 354 | + // set the modified flags |
| 355 | + navMesh->setPolyFlags(polyRef, orFlag(f, flag)); |
| 356 | + continue; |
| 357 | + } |
| 358 | + |
| 359 | + // poly is on island, check if it is outside of range |
| 360 | + const dtMeshTile* tile = nullptr; |
| 361 | + const dtPoly* poly = nullptr; |
| 362 | + navMesh->getTileAndPolyByRefUnsafe(polyRef, &tile, &poly); |
| 363 | + |
| 364 | + // check if this poly is within range of the circle |
| 365 | + bool inRange = false; |
| 366 | + for (int iVert = 0; iVert < poly->vertCount; ++iVert) { |
| 367 | + int nVert = (iVert + 1) % 3; |
| 368 | + float tseg = 0; |
| 369 | + float distSqr = dtDistancePtSegSqr2D( |
| 370 | + circleCenter.data(), |
| 371 | + &tile->verts[static_cast<size_t>(poly->verts[iVert]) * 3], |
| 372 | + &tile->verts[static_cast<size_t>(poly->verts[nVert]) * 3], tseg); |
| 373 | + if (distSqr < radSqr) { |
| 374 | + // skip this poly if any edge is within radius |
| 375 | + inRange = true; |
| 376 | + break; |
| 377 | + } |
| 378 | + } |
| 379 | + if (!inRange) { |
| 380 | + // get current flags |
| 381 | + ushort f = 0; |
| 382 | + navMesh->getPolyFlags(polyRef, &f); |
| 383 | + // set the modified flags |
| 384 | + navMesh->setPolyFlags(polyRef, orFlag(f, flag)); |
| 385 | + } |
| 386 | + } |
| 387 | + } |
| 388 | + } |
| 389 | + |
316 | 390 | // Some polygons have zero area for some reason. When we navigate into a zero |
317 | 391 | // area polygon, things crash. So we find all zero area polygons and mark |
318 | 392 | // them as disabled/not navigable. |
@@ -404,6 +478,10 @@ struct PathFinder::Impl { |
404 | 478 | float radius, |
405 | 479 | int maxTries, |
406 | 480 | int islandIndex /*= ID_UNDEFINED*/); |
| 481 | + vec3f getRandomNavigablePointInCircle(const vec3f& circleCenter, |
| 482 | + float radius, |
| 483 | + int maxTries, |
| 484 | + int islandIndex /*= ID_UNDEFINED*/); |
407 | 485 |
|
408 | 486 | bool findPath(ShortestPath& path); |
409 | 487 | bool findPath(MultiGoalShortestPath& path); |
@@ -1199,6 +1277,57 @@ vec3f PathFinder::Impl::getRandomNavigablePoint( |
1199 | 1277 | return pt; |
1200 | 1278 | } |
1201 | 1279 |
|
| 1280 | +vec3f PathFinder::Impl::getRandomNavigablePointInCircle( |
| 1281 | + const vec3f& circleCenter, |
| 1282 | + const float radius, |
| 1283 | + const int maxTries, |
| 1284 | + int islandIndex) { |
| 1285 | + float radSqr = radius * radius; |
| 1286 | + |
| 1287 | + islandSystem_->assertValidIsland(islandIndex); |
| 1288 | + if (getNavigableArea(islandIndex) <= 0.0) |
| 1289 | + throw std::runtime_error( |
| 1290 | + "NavMesh has no navigable area, this indicates an issue with the " |
| 1291 | + "NavMesh"); |
| 1292 | + |
| 1293 | + // set the poly flag to identify polys not on the target island |
| 1294 | + islandSystem_->setPolyFlagForIslandCircle(navMesh_.get(), |
| 1295 | + PolyFlags::POLYFLAGS_OFF_ISLAND, |
| 1296 | + circleCenter, radius, islandIndex); |
| 1297 | + filter_->setExcludeFlags(filter_->getExcludeFlags() | |
| 1298 | + PolyFlags::POLYFLAGS_OFF_ISLAND); |
| 1299 | + |
| 1300 | + vec3f pt; |
| 1301 | + int i = 0; |
| 1302 | + for (i = 0; i < maxTries; ++i) { |
| 1303 | + dtPolyRef ref = 0; |
| 1304 | + dtStatus status = |
| 1305 | + navQuery_->findRandomPoint(filter_.get(), frand, &ref, pt.data()); |
| 1306 | + if (dtStatusSucceed(status)) { |
| 1307 | + float xd = circleCenter[0] - pt[0]; |
| 1308 | + float yd = circleCenter[2] - pt[2]; |
| 1309 | + float d2 = xd * xd + yd * yd; |
| 1310 | + if (d2 < radSqr) { |
| 1311 | + break; |
| 1312 | + } |
| 1313 | + } |
| 1314 | + } |
| 1315 | + |
| 1316 | + // reset the poly flag identifying polys off the target island |
| 1317 | + islandSystem_->setPolyFlagForIsland( |
| 1318 | + navMesh_.get(), PolyFlags::POLYFLAGS_OFF_ISLAND, ID_UNDEFINED, |
| 1319 | + /*setFlag=*/false, /*invert=*/true); |
| 1320 | + filter_->setExcludeFlags(filter_->getExcludeFlags() & |
| 1321 | + ~PolyFlags::POLYFLAGS_OFF_ISLAND); |
| 1322 | + |
| 1323 | + if (i == maxTries) { |
| 1324 | + ESP_ERROR() << "Failed to getRandomNavigablePoint. Try increasing max " |
| 1325 | + "tries if the navmesh is fine but just hard to sample from"; |
| 1326 | + return vec3f::Constant(Mn::Constants::nan()); |
| 1327 | + } |
| 1328 | + return pt; |
| 1329 | +} |
| 1330 | + |
1202 | 1331 | vec3f PathFinder::Impl::getRandomNavigablePointAroundSphere( |
1203 | 1332 | const vec3f& circleCenter, |
1204 | 1333 | const float radius, |
@@ -1770,8 +1899,8 @@ vec3f PathFinder::getRandomNavigablePointAroundSphere( |
1770 | 1899 | const float radius, |
1771 | 1900 | const int maxTries, |
1772 | 1901 | int islandIndex /*= ID_UNDEFINED*/) { |
1773 | | - return pimpl_->getRandomNavigablePointAroundSphere(circleCenter, radius, |
1774 | | - maxTries, islandIndex); |
| 1902 | + return pimpl_->getRandomNavigablePointInCircle(circleCenter, radius, maxTries, |
| 1903 | + islandIndex); |
1775 | 1904 | } |
1776 | 1905 |
|
1777 | 1906 | bool PathFinder::findPath(ShortestPath& path) { |
|
0 commit comments