Skip to content

Commit 7ea3406

Browse files
authored
More accurate get_random_navigable_point_near (#2221)
* Add new getRandomNavigablePointInCircle function to replace getRandomNavigablePointAroundSphere with improved accuracy
1 parent 7210f87 commit 7ea3406

1 file changed

Lines changed: 131 additions & 2 deletions

File tree

src/esp/nav/PathFinder.cpp

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "esp/assets/MeshData.h"
2828
#include "esp/core/Esp.h"
2929

30+
#include "DetourCommon.h"
3031
#include "DetourNavMesh.h"
3132
#include "DetourNavMeshBuilder.h"
3233
#include "DetourNavMeshQuery.h"
@@ -313,6 +314,79 @@ class IslandSystem {
313314
}
314315
}
315316

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+
316390
// Some polygons have zero area for some reason. When we navigate into a zero
317391
// area polygon, things crash. So we find all zero area polygons and mark
318392
// them as disabled/not navigable.
@@ -404,6 +478,10 @@ struct PathFinder::Impl {
404478
float radius,
405479
int maxTries,
406480
int islandIndex /*= ID_UNDEFINED*/);
481+
vec3f getRandomNavigablePointInCircle(const vec3f& circleCenter,
482+
float radius,
483+
int maxTries,
484+
int islandIndex /*= ID_UNDEFINED*/);
407485

408486
bool findPath(ShortestPath& path);
409487
bool findPath(MultiGoalShortestPath& path);
@@ -1199,6 +1277,57 @@ vec3f PathFinder::Impl::getRandomNavigablePoint(
11991277
return pt;
12001278
}
12011279

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+
12021331
vec3f PathFinder::Impl::getRandomNavigablePointAroundSphere(
12031332
const vec3f& circleCenter,
12041333
const float radius,
@@ -1770,8 +1899,8 @@ vec3f PathFinder::getRandomNavigablePointAroundSphere(
17701899
const float radius,
17711900
const int maxTries,
17721901
int islandIndex /*= ID_UNDEFINED*/) {
1773-
return pimpl_->getRandomNavigablePointAroundSphere(circleCenter, radius,
1774-
maxTries, islandIndex);
1902+
return pimpl_->getRandomNavigablePointInCircle(circleCenter, radius, maxTries,
1903+
islandIndex);
17751904
}
17761905

17771906
bool PathFinder::findPath(ShortestPath& path) {

0 commit comments

Comments
 (0)