mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Recast: rewrite polygon island grouping algorithm
Flood-fill assign instead to make sure all polygon islands are mapped to a contiguous range of group id's on the first pass. This is required as we will otherwise reach the DT_MAX_POLY_GROUP_COUNT limit on complex and large geometry before the prune pass while we don't have DT_MAX_POLY_GROUP_COUNT polygon islands inside the navmesh. Also implemented poly group overflow protection and simplified the API.
This commit is contained in:
parent
d027621028
commit
a8b302f165
@ -131,7 +131,7 @@ Editor::Editor() :
|
||||
m_filterLedgeSpans(true),
|
||||
m_filterWalkableLowHeightSpans(true),
|
||||
m_traverseRayDynamicOffset(true),
|
||||
m_collapseLinkedPolyGroups(true),
|
||||
m_collapseLinkedPolyGroups(false),
|
||||
m_buildBvTree(true),
|
||||
m_selectedNavMeshType(NAVMESH_SMALL),
|
||||
m_loadedNavMeshType(NAVMESH_SMALL),
|
||||
@ -430,6 +430,10 @@ void Editor::handleCommonSettings()
|
||||
m_traverseLinkDrawParams.extraOffset = m_traverseRayExtraOffset;
|
||||
|
||||
ImGui::SliderFloat("Min Overlap", &m_traverseEdgeMinOverlap, 0.0f, m_tileSize*m_cellSize, "%g");
|
||||
|
||||
if (ImGui::Button("Rebuild Static Pathing Data"))
|
||||
createStaticPathingData();
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
@ -779,38 +783,6 @@ bool Editor::createTraverseLinks()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Editor::createStaticPathingData(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
if (!params->nav) return false;
|
||||
|
||||
if (!dtCreateDisjointPolyGroups(params))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "createStaticPathingData: Failed to build disjoint poly groups.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Editor::updateStaticPathingData(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
if (!params->nav) return false;
|
||||
|
||||
if (!dtUpdateDisjointPolyGroups(params))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "updateStaticPathingData: Failed to update disjoint poly groups.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dtCreateTraverseTableData(params))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "updateStaticPathingData: Failed to build traverse table data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool animTypeSupportsTraverseLink(const dtTraverseTableCreateParams* params, const dtLink* link, const int tableIndex)
|
||||
{
|
||||
const NavMeshType_e navMeshType = (NavMeshType_e)params->navMeshType;
|
||||
@ -823,23 +795,32 @@ static bool animTypeSupportsTraverseLink(const dtTraverseTableCreateParams* para
|
||||
return rdBitCellBit(link->traverseType) & s_traverseAnimTraverseFlags[traverseAnimType];
|
||||
}
|
||||
|
||||
void Editor::createTraverseTableParams(dtTraverseTableCreateParams* params)
|
||||
bool Editor::createStaticPathingData()
|
||||
{
|
||||
params->nav = m_navMesh;
|
||||
params->sets = m_djs;
|
||||
params->tableCount = NavMesh_GetTraverseTableCountForNavMeshType(m_selectedNavMeshType);
|
||||
params->navMeshType = m_selectedNavMeshType;
|
||||
params->canTraverse = animTypeSupportsTraverseLink;
|
||||
params->collapseGroups = m_collapseLinkedPolyGroups;
|
||||
}
|
||||
if (!m_navMesh)
|
||||
return false;
|
||||
|
||||
void Editor::buildStaticPathingData()
|
||||
{
|
||||
dtTraverseTableCreateParams params;
|
||||
createTraverseTableParams(¶ms);
|
||||
params.nav = m_navMesh;
|
||||
params.sets = m_djs;
|
||||
params.tableCount = NavMesh_GetTraverseTableCountForNavMeshType(m_selectedNavMeshType);
|
||||
params.navMeshType = m_selectedNavMeshType;
|
||||
params.canTraverse = animTypeSupportsTraverseLink;
|
||||
params.collapseGroups = m_collapseLinkedPolyGroups;
|
||||
|
||||
createStaticPathingData(¶ms);
|
||||
updateStaticPathingData(¶ms);
|
||||
if (!dtCreateDisjointPolyGroups(¶ms))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "createStaticPathingData: Failed to build disjoint poly groups.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dtCreateTraverseTableData(¶ms))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "createStaticPathingData: Failed to build traverse table data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Editor::connectOffMeshLinks()
|
||||
|
@ -739,7 +739,7 @@ void Editor_TileMesh::buildTile(const float* pos)
|
||||
params.linkToNeighbor = true;
|
||||
m_navMesh->connectTraverseLinks(tileRef, params);
|
||||
|
||||
buildStaticPathingData();
|
||||
createStaticPathingData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -803,7 +803,7 @@ void Editor_TileMesh::removeTile(const float* pos)
|
||||
++it;
|
||||
}
|
||||
|
||||
buildStaticPathingData();
|
||||
createStaticPathingData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -850,7 +850,7 @@ void Editor_TileMesh::buildAllTiles()
|
||||
connectOffMeshLinks();
|
||||
createTraverseLinks();
|
||||
|
||||
buildStaticPathingData();
|
||||
createStaticPathingData();
|
||||
|
||||
// Start the build process.
|
||||
m_ctx->stopTimer(RC_TIMER_TEMP);
|
||||
@ -877,7 +877,7 @@ void Editor_TileMesh::removeAllTiles()
|
||||
m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0);
|
||||
|
||||
m_traverseLinkPolyMap.clear();
|
||||
buildStaticPathingData();
|
||||
createStaticPathingData();
|
||||
}
|
||||
|
||||
void Editor_TileMesh::buildAllHulls()
|
||||
|
@ -120,7 +120,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
static void floodNavmesh(dtNavMesh* nav, NavmeshFlags* flags, dtPolyRef start, unsigned char flag)
|
||||
static void floodNavMesh(dtNavMesh* nav, NavmeshFlags* flags, dtPolyRef start, unsigned char flag)
|
||||
{
|
||||
// If already visited, skip.
|
||||
if (flags->getFlags(start))
|
||||
@ -203,8 +203,7 @@ void NavMeshPruneTool::pruneUnvisitedTilesAndPolys(dtNavMesh* nav, NavmeshFlags*
|
||||
NavMeshPruneTool::NavMeshPruneTool() :
|
||||
m_editor(0),
|
||||
m_flags(0),
|
||||
m_hitPosSet(false),
|
||||
m_ranPruneTool(false)
|
||||
m_hitPosSet(false)
|
||||
{
|
||||
m_hitPos[0] = 0.0f;
|
||||
m_hitPos[1] = 0.0f;
|
||||
@ -226,7 +225,6 @@ void NavMeshPruneTool::init(Editor* editor)
|
||||
void NavMeshPruneTool::reset()
|
||||
{
|
||||
m_hitPosSet = false;
|
||||
m_ranPruneTool = false;
|
||||
|
||||
if (m_flags)
|
||||
{
|
||||
@ -240,25 +238,23 @@ void NavMeshPruneTool::handleMenu()
|
||||
dtNavMesh* nav = m_editor->getNavMesh();
|
||||
if (!nav) return;
|
||||
|
||||
if (!m_flags) return;
|
||||
if (!m_hitPosSet) return;
|
||||
|
||||
if (ImGui::Button("Clear Selection"))
|
||||
{
|
||||
m_flags->clearAllFlags();
|
||||
m_hitPosSet = false;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Prune Unselected"))
|
||||
{
|
||||
pruneUnvisitedTilesAndPolys(nav, m_flags);
|
||||
dtTraverseTableCreateParams params;
|
||||
|
||||
m_editor->createTraverseTableParams(¶ms);
|
||||
m_editor->updateStaticPathingData(¶ms);
|
||||
m_editor->createStaticPathingData();
|
||||
|
||||
delete m_flags;
|
||||
m_flags = 0;
|
||||
|
||||
m_ranPruneTool = true;
|
||||
m_hitPosSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,7 +286,7 @@ void NavMeshPruneTool::handleClick(const float* s, const float* p, const int /*v
|
||||
m_flags->init(nav);
|
||||
}
|
||||
|
||||
floodNavmesh(nav, m_flags, ref, 1);
|
||||
floodNavMesh(nav, m_flags, ref, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,14 +359,11 @@ public:
|
||||
void handleCommonSettings();
|
||||
|
||||
void createTraverseLinkParams(dtTraverseLinkConnectParams& params);
|
||||
void createTraverseTableParams(dtTraverseTableCreateParams* params);
|
||||
|
||||
bool createTraverseLinks();
|
||||
void connectOffMeshLinks();
|
||||
|
||||
void buildStaticPathingData();
|
||||
bool createStaticPathingData(const dtTraverseTableCreateParams* params);
|
||||
bool updateStaticPathingData(const dtTraverseTableCreateParams* params);
|
||||
bool createStaticPathingData();
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
|
@ -31,7 +31,6 @@ class NavMeshPruneTool : public EditorTool
|
||||
|
||||
float m_hitPos[3];
|
||||
bool m_hitPosSet;
|
||||
bool m_ranPruneTool;
|
||||
|
||||
public:
|
||||
NavMeshPruneTool();
|
||||
|
@ -76,7 +76,7 @@ static const int DT_SEMI_UNLINKED_TILE_USER_ID = 2;
|
||||
/// A value that indicates that this poly hasn't been assigned to a group yet.
|
||||
static const unsigned short DT_NULL_POLY_GROUP = 0;
|
||||
|
||||
/// A poly group that holds all unconnected polys (not linked to anything).
|
||||
/// A polygon group that holds all unconnected polys (not linked to anything).
|
||||
/// These are considered 'trash' by the game engine; see [r5apex_ds + CA88B2].
|
||||
/// For reference, Titanfall 2 single player NavMeshes also marked everything unconnected as '1'.
|
||||
static const unsigned short DT_UNLINKED_POLY_GROUP = 1;
|
||||
@ -84,13 +84,15 @@ static const unsigned short DT_UNLINKED_POLY_GROUP = 1;
|
||||
/// The first non-reserved poly group; #DT_UNLINKED_POLY_GROUP and below are reserved.
|
||||
static const unsigned short DT_FIRST_USABLE_POLY_GROUP = 2;
|
||||
|
||||
/// The minimum required number of poly groups for static pathing logic to work.
|
||||
/// (E.g. if we have 2 poly groups, group id 1 (#DT_UNLINKED_POLY_GROUP), and
|
||||
/// group id 2, then 1 is never reachable as its considered 'trash' by design,
|
||||
/// and 2 is always reachable as that's the only group id. If group id 3 is
|
||||
/// involved then code can use the static patching logic to quickly query if we
|
||||
/// are even on the same (or connected) poly island before trying to compute a path).
|
||||
static const int DT_MIN_POLY_GROUP_COUNT = 3;
|
||||
/// The minimum number of polygon groups (islands) before an actual table has
|
||||
/// to be built and allocated. Static pathing can work without a table if we
|
||||
/// only have 2 polygon groups, as all linked polygons are tagged under the
|
||||
/// group #DT_FIRST_USABLE_POLY_GROUP, while the dead polygons are tagged under
|
||||
/// the group #DT_UNLINKED_POLY_GROUP.
|
||||
static const unsigned short DT_MIN_POLY_GROUP_COUNT = 3;
|
||||
|
||||
/// The maximum number of polygon groups (islands) per navmesh.
|
||||
static const unsigned short DT_MAX_POLY_GROUP_COUNT = 65535;
|
||||
|
||||
/// The maximum number of traversal tables per navmesh that will be used for static pathing.
|
||||
static const int DT_MAX_TRAVERSE_TABLES = 5;
|
||||
|
@ -119,9 +119,9 @@ class dtDisjointSet
|
||||
{
|
||||
public:
|
||||
dtDisjointSet() = default;
|
||||
dtDisjointSet(const int size)
|
||||
dtDisjointSet(const int size, const int max = -1)
|
||||
{
|
||||
init(size);
|
||||
init(size, max);
|
||||
}
|
||||
|
||||
void copy(dtDisjointSet& other)
|
||||
@ -137,19 +137,25 @@ public:
|
||||
other.parent[i] = parent[i];
|
||||
}
|
||||
|
||||
void init(const int size)
|
||||
void init(const int size, const int max = -1)
|
||||
{
|
||||
rdAssert(size <= max);
|
||||
|
||||
rank.resize(size);
|
||||
parent.resize(size);
|
||||
limit = max;
|
||||
|
||||
for (int i = 0; i < parent.size(); i++)
|
||||
parent[i] = i;
|
||||
}
|
||||
int insertNew()
|
||||
{
|
||||
rank.push(0);
|
||||
|
||||
const int newId = parent.size();
|
||||
|
||||
if (limit > 0 && newId > limit)
|
||||
return -1; // Limit has been reached.
|
||||
|
||||
rank.push(0);
|
||||
parent.push(newId);
|
||||
|
||||
return newId;
|
||||
@ -190,6 +196,7 @@ public:
|
||||
private:
|
||||
rdIntArray rank;
|
||||
mutable rdIntArray parent;
|
||||
int limit;
|
||||
};
|
||||
|
||||
struct dtLink;
|
||||
@ -218,12 +225,6 @@ struct dtTraverseTableCreateParams
|
||||
/// @return True if the disjoint set data was successfully created.
|
||||
bool dtCreateDisjointPolyGroups(const dtTraverseTableCreateParams* params);
|
||||
|
||||
/// Updates navigation mesh disjoint poly groups from the provided parameters.
|
||||
/// @ingroup detour
|
||||
/// @param[in] params The build parameters.
|
||||
/// @return True if the disjoint set data was successfully updated.
|
||||
bool dtUpdateDisjointPolyGroups(const dtTraverseTableCreateParams* params);
|
||||
|
||||
/// Builds navigation mesh static traverse table from the provided parameters.
|
||||
/// @ingroup detour
|
||||
/// @param[in] params The build parameters.
|
||||
|
@ -322,150 +322,27 @@ static void setPolyGroupsTraversalReachability(int* const tableData, const int n
|
||||
tableData[index] &= ~value;
|
||||
}
|
||||
|
||||
bool dtCreateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
dtNavMesh* nav = params->nav;
|
||||
dtDisjointSet& set = params->sets[0];
|
||||
|
||||
rdAssert(nav);
|
||||
|
||||
// Reserve the first poly groups
|
||||
// 0 = DT_NULL_POLY_GROUP.
|
||||
// 1 = DT_UNLINKED_POLY_GROUP.
|
||||
set.init(DT_FIRST_USABLE_POLY_GROUP);
|
||||
|
||||
// Clear all labels.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
int pcount = tile->header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
|
||||
poly.groupId = DT_NULL_POLY_GROUP;
|
||||
#if DT_NAVMESH_SET_VERSION >= 7
|
||||
// NOTE: these fields are unknown and need to be reversed.
|
||||
// It is possible these are used internally only.
|
||||
poly.unk1 = (unsigned short)-1;
|
||||
poly.unk2 = (unsigned short)-1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// First pass to group poly islands.
|
||||
std::set<unsigned short> linkedGroups;
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
unsigned int plink = poly.firstLink;
|
||||
|
||||
if (params->collapseGroups)
|
||||
{
|
||||
if (plink != DT_NULL_LINK)
|
||||
poly.groupId = DT_FIRST_USABLE_POLY_GROUP;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Off-mesh connections need their own ID's, skip the assignment
|
||||
// here since else we will be marking 2 (or more) poly islands
|
||||
// under the same group id.
|
||||
// NOTE: when we implement jump links, we will have to check on
|
||||
// these here as well! They also shouldn't merge 2 islands together.
|
||||
// Ultimately, the jump links should only be used during traverse
|
||||
// table building to mark linked islands as reachable.
|
||||
if (poly.getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
while (plink != DT_NULL_LINK)
|
||||
{
|
||||
const dtLink l = tile->links[plink];
|
||||
|
||||
// Polygons linked with traverse links are not necessarily on
|
||||
// the same group, these should be skipped.
|
||||
if (l.traverseType != DT_NULL_TRAVERSE_TYPE)
|
||||
{
|
||||
plink = l.next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const dtMeshTile* t;
|
||||
const dtPoly* p;
|
||||
nav->getTileAndPolyByRefUnsafe(l.ref, &t, &p);
|
||||
|
||||
if (p->groupId != DT_NULL_POLY_GROUP)
|
||||
linkedGroups.insert(p->groupId);
|
||||
|
||||
plink = l.next;
|
||||
}
|
||||
}
|
||||
|
||||
const bool noLinkedGroups = linkedGroups.empty();
|
||||
|
||||
if (noLinkedGroups)
|
||||
poly.groupId = (unsigned short)set.insertNew();
|
||||
else
|
||||
{
|
||||
const unsigned short rootGroup = *linkedGroups.begin();
|
||||
poly.groupId = rootGroup;
|
||||
|
||||
for (const int linkedGroup : linkedGroups)
|
||||
set.setUnion(rootGroup, linkedGroup);
|
||||
}
|
||||
|
||||
if (!noLinkedGroups)
|
||||
linkedGroups.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass to ensure all poly's have their root disjoint set ID.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
if (poly.groupId != DT_UNLINKED_POLY_GROUP)
|
||||
{
|
||||
const int id = params->collapseGroups
|
||||
? DT_FIRST_USABLE_POLY_GROUP
|
||||
: set.find(poly.groupId);
|
||||
|
||||
poly.groupId = (unsigned short)id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav->setPolyGroupCount(set.getSetCount());
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unionTraverseLinkedPolyGroups(const dtTraverseTableCreateParams* params, const int tableIndex)
|
||||
{
|
||||
if (params->collapseGroups)
|
||||
return;
|
||||
|
||||
rdAssert(!params->collapseGroups);
|
||||
dtDisjointSet& set = params->sets[tableIndex];
|
||||
|
||||
if (!set.getSetCount())
|
||||
return;
|
||||
|
||||
dtNavMesh* nav = params->nav;
|
||||
const int maxTiles = nav->getMaxTiles();
|
||||
|
||||
// Sixth pass to handle traverse linked poly's.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
// Handle traverse linked poly's.
|
||||
for (int i = 0; i < maxTiles; ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
const dtMeshHeader* header = tile->header;
|
||||
|
||||
if (!header)
|
||||
continue;
|
||||
|
||||
const int pcount = header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
@ -487,77 +364,77 @@ static void unionTraverseLinkedPolyGroups(const dtTraverseTableCreateParams* par
|
||||
}
|
||||
}
|
||||
|
||||
bool dtUpdateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
|
||||
static bool floodPolygonIsland(dtNavMesh* nav, dtDisjointSet& set, const dtPolyRef startRef)
|
||||
{
|
||||
std::set<dtPolyRef> visitedPolys;
|
||||
rdPermVector<dtPolyRef> openList;
|
||||
|
||||
openList.push_back(startRef);
|
||||
|
||||
while (!openList.empty())
|
||||
{
|
||||
dtPolyRef polyRef = openList.back();
|
||||
openList.pop_back();
|
||||
|
||||
// Skip already visited polygons.
|
||||
if (visitedPolys.find(polyRef) != visitedPolys.end())
|
||||
continue;
|
||||
|
||||
visitedPolys.insert(polyRef);
|
||||
unsigned int salt, it, ip;
|
||||
|
||||
nav->decodePolyId(polyRef, salt, it, ip);
|
||||
|
||||
dtMeshTile* currentTile = nav->getTile(it);
|
||||
dtPoly* poly = ¤tTile->polys[ip];
|
||||
|
||||
if (poly->groupId == DT_NULL_POLY_GROUP)
|
||||
{
|
||||
const int newGroup = set.insertNew();
|
||||
|
||||
// Overflow, too many polygon islands.
|
||||
if (newGroup == -1)
|
||||
return false;
|
||||
|
||||
poly->groupId = (unsigned short)newGroup;
|
||||
}
|
||||
|
||||
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = currentTile->links[i].next)
|
||||
{
|
||||
const dtLink& link = currentTile->links[i];
|
||||
|
||||
// Skip traverse links as these can join separate islands together.
|
||||
if (link.traverseType != DT_NULL_TRAVERSE_TYPE)
|
||||
continue;
|
||||
|
||||
const dtPolyRef neiRef = link.ref;
|
||||
|
||||
if (visitedPolys.find(neiRef) == visitedPolys.end())
|
||||
{
|
||||
nav->decodePolyId(neiRef, salt, it, ip);
|
||||
|
||||
dtMeshTile* neiTile = nav->getTile(it);
|
||||
dtPoly* neiPoly = &neiTile->polys[ip];
|
||||
|
||||
if (neiPoly->groupId != DT_NULL_POLY_GROUP)
|
||||
continue;
|
||||
|
||||
if (neiPoly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
neiPoly->groupId = poly->groupId;
|
||||
openList.push_back(neiRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void copyBaseDisjointSets(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
dtNavMesh* nav = params->nav;
|
||||
dtDisjointSet& set = params->sets[0];
|
||||
|
||||
// Third pass to mark all unlinked poly's.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
int numUnlinkedPolys = 0;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
|
||||
// This poly isn't connected to anything, mark it so the game
|
||||
// won't consider this poly in path generation.
|
||||
if (poly.firstLink == DT_NULL_LINK)
|
||||
{
|
||||
poly.groupId = DT_UNLINKED_POLY_GROUP;
|
||||
numUnlinkedPolys++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numUnlinkedPolys)
|
||||
{
|
||||
tile->header->userId = (numUnlinkedPolys == tile->header->polyCount)
|
||||
? DT_FULL_UNLINKED_TILE_USER_ID
|
||||
: DT_SEMI_UNLINKED_TILE_USER_ID;
|
||||
}
|
||||
else if (tile->header->userId == DT_FULL_UNLINKED_TILE_USER_ID
|
||||
|| tile->header->userId == DT_SEMI_UNLINKED_TILE_USER_ID)
|
||||
tile->header->userId = 0;
|
||||
}
|
||||
|
||||
if (!params->collapseGroups)
|
||||
{
|
||||
// Gather all unique polygroups and map them to a contiguous range.
|
||||
std::map<unsigned short, unsigned short> groupMap;
|
||||
set.init(DT_FIRST_USABLE_POLY_GROUP);
|
||||
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
unsigned short oldId = poly.groupId;
|
||||
if (oldId != DT_UNLINKED_POLY_GROUP && groupMap.find(oldId) == groupMap.end())
|
||||
groupMap[oldId] = (unsigned short)set.insertNew();
|
||||
}
|
||||
}
|
||||
|
||||
// Fourth pass to apply the new mapping to all polys.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
const int pcount = tile->header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
if (poly.groupId != DT_UNLINKED_POLY_GROUP)
|
||||
poly.groupId = groupMap[poly.groupId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy base disjoint set results to sets for each traverse table.
|
||||
for (int i = 0; i < params->tableCount; i++)
|
||||
{
|
||||
@ -568,53 +445,162 @@ bool dtUpdateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
|
||||
|
||||
unionTraverseLinkedPolyGroups(params, i);
|
||||
}
|
||||
}
|
||||
|
||||
nav->setPolyGroupCount(set.getSetCount());
|
||||
return true;
|
||||
bool dtCreateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
dtNavMesh* nav = params->nav;
|
||||
dtDisjointSet& set = params->sets[0];
|
||||
|
||||
rdAssert(nav);
|
||||
|
||||
// Reserve the first poly groups
|
||||
// 0 = DT_NULL_POLY_GROUP.
|
||||
// 1 = DT_UNLINKED_POLY_GROUP.
|
||||
set.init(DT_FIRST_USABLE_POLY_GROUP, DT_MAX_POLY_GROUP_COUNT);
|
||||
|
||||
const int maxTiles = nav->getMaxTiles();
|
||||
|
||||
// Clear all labels.
|
||||
for (int i = 0; i < maxTiles; ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
const dtMeshHeader* header = tile->header;
|
||||
if (!header)
|
||||
continue;
|
||||
|
||||
const int pcount = header->polyCount;
|
||||
for (int j = 0; j < pcount; j++)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
|
||||
poly.groupId = DT_NULL_POLY_GROUP;
|
||||
#if DT_NAVMESH_SET_VERSION >= 7
|
||||
// NOTE: these fields are unknown and need to be reversed.
|
||||
// It is possible these are used internally only.
|
||||
poly.unk1 = (unsigned short)-1;
|
||||
poly.unk2 = (unsigned short)-1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// True if we have more than DT_MAX_POLY_GROUPS polygon islands.
|
||||
bool failure = false;
|
||||
|
||||
// Mark polygon islands and unlinked polygons.
|
||||
for (int i = 0; i < maxTiles; ++i)
|
||||
{
|
||||
dtMeshTile* tile = nav->getTile(i);
|
||||
const dtMeshHeader* header = tile->header;
|
||||
|
||||
if (!header)
|
||||
continue;
|
||||
|
||||
const int pcount = header->polyCount;
|
||||
for (int j = 0; j < pcount; ++j)
|
||||
{
|
||||
dtPoly& poly = tile->polys[j];
|
||||
|
||||
// Skip if the polygon is already part of an island.
|
||||
if (poly.groupId != DT_NULL_POLY_GROUP)
|
||||
continue;
|
||||
|
||||
if (poly.firstLink == DT_NULL_LINK)
|
||||
{
|
||||
poly.groupId = DT_UNLINKED_POLY_GROUP;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failure)
|
||||
continue;
|
||||
|
||||
if (params->collapseGroups)
|
||||
{
|
||||
poly.groupId = DT_FIRST_USABLE_POLY_GROUP;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Off-mesh connections need their own ID's, skip the assignment
|
||||
// here since else we will be marking 2 (or more) poly islands
|
||||
// under the same group id.
|
||||
if (poly.getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
const int newId = set.insertNew();
|
||||
|
||||
if (newId == -1)
|
||||
{
|
||||
failure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
poly.groupId = (unsigned short)newId;
|
||||
continue;
|
||||
}
|
||||
|
||||
const dtPolyRef polyRefBase = nav->getPolyRefBase(tile);
|
||||
|
||||
if (!floodPolygonIsland(nav, set, polyRefBase | j))
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !failure;
|
||||
}
|
||||
|
||||
bool dtCreateTraverseTableData(const dtTraverseTableCreateParams* params)
|
||||
{
|
||||
const dtDisjointSet& baseSet = params->sets[0];
|
||||
const int polyGroupCount = baseSet.getSetCount();
|
||||
|
||||
dtNavMesh* nav = params->nav;
|
||||
|
||||
const int polyGroupCount = nav->getPolyGroupCount();
|
||||
const int tableSize = dtCalcTraverseTableSize(polyGroupCount);
|
||||
const int tableCount = params->tableCount;
|
||||
if (polyGroupCount < DT_MIN_POLY_GROUP_COUNT)
|
||||
{
|
||||
nav->setTraverseTableCount(0);
|
||||
nav->setTraverseTableSize(0);
|
||||
nav->setPolyGroupCount(polyGroupCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nav->freeTraverseTables();
|
||||
const int tableCount = params->tableCount;
|
||||
|
||||
if (!nav->allocTraverseTables(tableCount))
|
||||
return false;
|
||||
|
||||
nav->setTraverseTableSize(tableSize);
|
||||
nav->setTraverseTableCount(tableCount);
|
||||
copyBaseDisjointSets(params);
|
||||
|
||||
if (!tableSize)
|
||||
return true;
|
||||
const int tableSize = dtCalcTraverseTableSize(polyGroupCount);
|
||||
nav->setTraverseTableSize(tableSize);
|
||||
|
||||
for (int i = 0; i < tableCount; i++)
|
||||
{
|
||||
int* const traverseTable = (int*)rdAlloc(sizeof(int)*tableSize, RD_ALLOC_PERM);
|
||||
const rdSizeType bufferSize = sizeof(int)*tableSize;
|
||||
int* const traverseTable = (int*)rdAlloc(bufferSize, RD_ALLOC_PERM);
|
||||
|
||||
if (!traverseTable)
|
||||
return false;
|
||||
|
||||
memset(traverseTable, 0, bufferSize);
|
||||
nav->setTraverseTable(i, traverseTable);
|
||||
memset(traverseTable, 0, sizeof(int)*tableSize);
|
||||
|
||||
const dtDisjointSet& set = params->sets[i];
|
||||
rdAssert(set.getSetCount() >= DT_MIN_POLY_GROUP_COUNT);
|
||||
|
||||
for (unsigned short j = 0; j < polyGroupCount; j++)
|
||||
{
|
||||
for (unsigned short k = 0; k < polyGroupCount; k++)
|
||||
{
|
||||
// Only reachable if its the same polygroup or if they are linked!
|
||||
// Only reachable if its the same poly group or if they are linked!
|
||||
const bool isReachable = j == k || set.find(j) == set.find(k);
|
||||
setPolyGroupsTraversalReachability(traverseTable, polyGroupCount, j, k, isReachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav->setPolyGroupCount(baseSet.getSetCount());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user