diff --git a/src/naveditor/Editor.cpp b/src/naveditor/Editor.cpp index cef64185..94530010 100644 --- a/src/naveditor/Editor.cpp +++ b/src/naveditor/Editor.cpp @@ -1131,6 +1131,32 @@ void Editor::buildStaticPathingData() updateStaticPathingData(¶ms); } +void Editor::connectOffMeshLinks() +{ + for (int i = 0; i < m_navMesh->getTileCount(); i++) + { + dtMeshTile* target = m_navMesh->getTile(i); + const dtMeshHeader* header = target->header; + + if (!header) + continue; + + const int offMeshConCount = header->offMeshConCount; + + if (!offMeshConCount) + continue; + + const dtTileRef targetRef = m_navMesh->getTileRef(target); + + // Base off-mesh connections to their starting polygons + // and connect connections inside the tile. + m_navMesh->baseOffMeshLinks(targetRef); + + // Connect off-mesh polygons to outer tiles. + m_navMesh->connectExtOffMeshLinks(targetRef); + } +} + void Editor::updateToolStates(const float dt) { for (int i = 0; i < MAX_TOOLS; i++) @@ -1287,8 +1313,8 @@ void Editor::renderDetourDebugMenu() if (isEnabled && m_navMesh) // Supplemental options only available with a valid navmesh! { ImGui::PushItemWidth(190); - ImGui::SliderInt("Traverse Type", &m_traverseLinkParams.traverseLinkType, -1, 31); - ImGui::SliderInt("Traverse Dist", &m_traverseLinkParams.traverseLinkDistance, -1, 255); + ImGui::SliderInt("Traverse Type", &m_traverseLinkParams.traverseLinkType, -1, DT_MAX_TRAVERSE_TYPES-1); + ImGui::SliderInt("Traverse Dist", &m_traverseLinkParams.traverseLinkDistance, -1, dtQuantLinkDistance(DT_TRAVERSE_DIST_MAX)); ImGui::SliderInt("Traverse Anim", &m_traverseLinkParams.traverseAnimType, -2, m_navMesh->getParams()->traverseTableCount-1); ImGui::PopItemWidth(); } diff --git a/src/naveditor/Editor_TileMesh.cpp b/src/naveditor/Editor_TileMesh.cpp index cf06a382..7902f259 100644 --- a/src/naveditor/Editor_TileMesh.cpp +++ b/src/naveditor/Editor_TileMesh.cpp @@ -51,6 +51,7 @@ class NavMeshTileTool : public EditorTool dtNavMesh* m_navMesh; float m_hitPos[3]; float m_nearestPos[3]; + int m_selectedSide; int m_selectedTraverseType; dtTileRef m_markedTileRef; @@ -91,6 +92,7 @@ public: NavMeshTileTool() : m_editor(0), m_navMesh(0), + m_selectedSide(-1), m_selectedTraverseType(-2), m_markedTileRef(0), m_markedPolyRef(0), @@ -179,6 +181,7 @@ public: char* pEnd = nullptr; m_markedPolyRef = (dtPolyRef)STR_TO_ID(m_polyRefTextInput, &pEnd, 10); } + ImGui::SliderInt("Tile Side", &m_selectedSide, -1, 8, "%d", ImGuiSliderFlags_NoInput); ImGui::PopItemWidth(); } @@ -296,12 +299,14 @@ public: { const dtMeshTile* tile = m_navMesh->getTileByRef(m_markedTileRef); - if (tile) + if (tile && tile->header) { duDrawTraverseLinkParams params; duDebugDrawMeshTile(&m_editor->getDebugDraw(), *m_navMesh, 0, tile, debugDrawOffset, m_editor->getNavMeshDrawFlags(), params); - const unsigned char side = rdClassifyPointInsideBounds(m_hitPos, tile->header->bmin, tile->header->bmax); + const int side = (m_selectedSide != -1) + ? m_selectedSide + : rdClassifyPointInsideBounds(m_hitPos, tile->header->bmin, tile->header->bmax); const int MAX_NEIS = 32; // Max neighbors dtMeshTile* neis[MAX_NEIS]; @@ -675,13 +680,54 @@ void Editor_TileMesh::buildTile(const float* pos) // Add tile, or leave the location empty. if (data) { + const dtMeshHeader* header = (const dtMeshHeader*)data; + // Let the navmesh own the data. dtTileRef tileRef = 0; dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,&tileRef); - if (dtStatusFailed(status)) + bool failure = false; + + if (dtStatusFailed(status) || dtStatusFailed(m_navMesh->connectTile(tileRef))) + { rdFree(data); - else - m_navMesh->connectTile(tileRef); + failure = true; + } + else if (header->offMeshConCount) + { + m_navMesh->baseOffMeshLinks(tileRef); + m_navMesh->connectExtOffMeshLinks(tileRef); + } + + if (!failure) + { + // If there are external off-mesh links landing on + // this tile, connect them. + for (int i = 0; i < m_navMesh->getTileCount(); i++) + { + dtMeshTile* target = m_navMesh->getTile(i); + const dtTileRef targetRef = m_navMesh->getTileRef(target); + + // Connection to self has already been done above. + if (targetRef == tileRef) + continue; + + const dtMeshHeader* targetHeader = target->header; + + if (!targetHeader) + continue; + + for (int j = 0; j < targetHeader->offMeshConCount; j++) + { + const dtOffMeshConnection* con = &target->offMeshCons[j]; + + int landTx, landTy; + getTilePos(&con->pos[3], landTx, landTy); + + if (landTx == tx && landTy == ty) + m_navMesh->connectExtOffMeshLinks(targetRef); + } + } + } } m_ctx->dumpLog("Build Tile (%d,%d):", tx,ty); @@ -722,7 +768,6 @@ void Editor_TileMesh::removeTile(const float* pos) getTileExtents(tx, ty, m_lastBuiltTileBmin, m_lastBuiltTileBmax); m_tileCol = duRGBA(255,0,0,180); - m_navMesh->removeTile(m_navMesh->getTileRefAt(tx,ty,0),0,0); } @@ -766,6 +811,7 @@ void Editor_TileMesh::buildAllTiles() } } + connectOffMeshLinks(); buildStaticPathingData(); // Start the build process. @@ -1191,6 +1237,8 @@ unsigned char* Editor_TileMesh::buildTileMesh(const int tx, const int ty, const params.offMeshConVerts = m_geom->getOffMeshConnectionVerts(); params.offMeshConRad = m_geom->getOffMeshConnectionRads(); params.offMeshConDir = m_geom->getOffMeshConnectionDirs(); + params.offMeshConJumps = m_geom->getOffMeshConnectionJumps(); + params.offMeshConOrders = m_geom->getOffMeshConnectionOrders(); params.offMeshConAreas = m_geom->getOffMeshConnectionAreas(); params.offMeshConFlags = m_geom->getOffMeshConnectionFlags(); params.offMeshConUserID = m_geom->getOffMeshConnectionId(); diff --git a/src/naveditor/InputGeom.cpp b/src/naveditor/InputGeom.cpp index ca5b8351..439f770a 100644 --- a/src/naveditor/InputGeom.cpp +++ b/src/naveditor/InputGeom.cpp @@ -546,9 +546,12 @@ bool InputGeom::raycastMesh(const float* src, const float* dst, float* tmin) con } void InputGeom::addOffMeshConnection(const float* spos, const float* epos, const float rad, - unsigned char bidir, unsigned char area, unsigned short flags) + unsigned char bidir, unsigned char jump, unsigned char order, + unsigned char area, unsigned short flags) { if (m_offMeshConCount >= MAX_OFFMESH_CONNECTIONS) return; + rdAssert(jump < DT_MAX_TRAVERSE_TYPES); + float* refs = &m_offMeshConRefPos[m_offMeshConCount*3]; float* verts = &m_offMeshConVerts[m_offMeshConCount*3*2]; float yaw = dtCalcOffMeshRefYaw(spos, epos); @@ -558,6 +561,8 @@ void InputGeom::addOffMeshConnection(const float* spos, const float* epos, const m_offMeshConRads[m_offMeshConCount] = rad; m_offMeshConRefYaws[m_offMeshConCount] = yaw; m_offMeshConDirs[m_offMeshConCount] = bidir; + m_offMeshConJumps[m_offMeshConCount] = jump; + m_offMeshConOrders[m_offMeshConCount] = order; m_offMeshConAreas[m_offMeshConCount] = area; m_offMeshConFlags[m_offMeshConCount] = flags; m_offMeshConId[m_offMeshConCount] = 1000 + m_offMeshConCount; @@ -579,6 +584,8 @@ void InputGeom::deleteOffMeshConnection(int i) m_offMeshConRads[i] = m_offMeshConRads[m_offMeshConCount]; m_offMeshConRefYaws[i] = m_offMeshConRefYaws[m_offMeshConCount]; m_offMeshConDirs[i] = m_offMeshConDirs[m_offMeshConCount]; + m_offMeshConJumps[i] = m_offMeshConJumps[m_offMeshConCount]; + m_offMeshConOrders[i] = m_offMeshConOrders[m_offMeshConCount]; m_offMeshConAreas[i] = m_offMeshConAreas[m_offMeshConCount]; m_offMeshConFlags[i] = m_offMeshConFlags[m_offMeshConCount]; } diff --git a/src/naveditor/OffMeshConnectionTool.cpp b/src/naveditor/OffMeshConnectionTool.cpp index da44dfb4..128ecb09 100644 --- a/src/naveditor/OffMeshConnectionTool.cpp +++ b/src/naveditor/OffMeshConnectionTool.cpp @@ -31,6 +31,8 @@ OffMeshConnectionTool::OffMeshConnectionTool() : m_editor(0), m_hitPosSet(0), m_bidir(true), + m_invertVertexLookupOrder(false), + m_traverseType(0), m_oldFlags(0) { rdVset(m_hitPos, 0.0f,0.0f,0.0f); @@ -65,15 +67,12 @@ void OffMeshConnectionTool::reset() void OffMeshConnectionTool::handleMenu() { - bool isOneWay = !m_bidir; + ImGui::Checkbox("Bidirectional", &m_bidir); + ImGui::Checkbox("Invert Lookup Order", &m_invertVertexLookupOrder); - if (ImGui::Checkbox("One Way", &isOneWay)) - m_bidir = false; - - bool isBiDirectional = m_bidir; - - if (ImGui::Checkbox("Bidirectional", &isBiDirectional)) - m_bidir = true; + ImGui::PushItemWidth(140); + ImGui::SliderInt("Traverse Type##OffMeshConnectionTool", &m_traverseType, 0, DT_MAX_TRAVERSE_TYPES-1, "%d", ImGuiSliderFlags_NoInput); + ImGui::PopItemWidth(); } void OffMeshConnectionTool::handleClick(const float* /*s*/, const float* p, bool shift) @@ -118,7 +117,8 @@ void OffMeshConnectionTool::handleClick(const float* /*s*/, const float* p, bool { const unsigned char area = EDITOR_POLYAREA_JUMP; const unsigned short flags = EDITOR_POLYFLAGS_WALK; - geom->addOffMeshConnection(m_hitPos, p, m_editor->getAgentRadius(), m_bidir ? 1 : 0, area, flags); + geom->addOffMeshConnection(m_hitPos, p, m_editor->getAgentRadius(), m_bidir ? 1 : 0, + (unsigned char)m_traverseType, m_invertVertexLookupOrder ? 1 : 0, area, flags); m_hitPosSet = false; } } diff --git a/src/naveditor/include/Editor.h b/src/naveditor/include/Editor.h index 79a928af..a73a7a61 100644 --- a/src/naveditor/include/Editor.h +++ b/src/naveditor/include/Editor.h @@ -380,6 +380,7 @@ public: void createTraverseTableParams(dtTraverseTableCreateParams* params); + void connectOffMeshLinks(); void buildStaticPathingData(); bool createStaticPathingData(const dtTraverseTableCreateParams* params); diff --git a/src/naveditor/include/InputGeom.h b/src/naveditor/include/InputGeom.h index c2a8e098..3f12bf7a 100644 --- a/src/naveditor/include/InputGeom.h +++ b/src/naveditor/include/InputGeom.h @@ -89,6 +89,8 @@ class InputGeom float m_offMeshConVerts[MAX_OFFMESH_CONNECTIONS*3*2]; float m_offMeshConRads[MAX_OFFMESH_CONNECTIONS]; unsigned char m_offMeshConDirs[MAX_OFFMESH_CONNECTIONS]; + unsigned char m_offMeshConJumps[MAX_OFFMESH_CONNECTIONS]; + unsigned char m_offMeshConOrders[MAX_OFFMESH_CONNECTIONS]; unsigned char m_offMeshConAreas[MAX_OFFMESH_CONNECTIONS]; unsigned short m_offMeshConFlags[MAX_OFFMESH_CONNECTIONS]; unsigned short m_offMeshConId[MAX_OFFMESH_CONNECTIONS]; @@ -139,13 +141,16 @@ public: const float* getOffMeshConnectionVerts() const { return m_offMeshConVerts; } const float* getOffMeshConnectionRads() const { return m_offMeshConRads; } const unsigned char* getOffMeshConnectionDirs() const { return m_offMeshConDirs; } + const unsigned char* getOffMeshConnectionJumps() const { return m_offMeshConJumps; } + const unsigned char* getOffMeshConnectionOrders() const { return m_offMeshConOrders; } const unsigned char* getOffMeshConnectionAreas() const { return m_offMeshConAreas; } const unsigned short* getOffMeshConnectionFlags() const { return m_offMeshConFlags; } const unsigned short* getOffMeshConnectionId() const { return m_offMeshConId; } const float* getOffMeshConnectionRefPos() const { return m_offMeshConRefPos; } const float* getOffMeshConnectionRefYaws() const { return m_offMeshConRefYaws; } void addOffMeshConnection(const float* spos, const float* epos, const float rad, - unsigned char bidir, unsigned char area, unsigned short flags); + unsigned char bidir, unsigned char jump, unsigned char order, + unsigned char area, unsigned short flags); void deleteOffMeshConnection(int i); void drawOffMeshConnections(struct duDebugDraw* dd, const float* offset, bool hilight = false); ///@} diff --git a/src/naveditor/include/OffMeshConnectionTool.h b/src/naveditor/include/OffMeshConnectionTool.h index 1b1657e9..c163eb50 100644 --- a/src/naveditor/include/OffMeshConnectionTool.h +++ b/src/naveditor/include/OffMeshConnectionTool.h @@ -29,6 +29,8 @@ class OffMeshConnectionTool : public EditorTool float m_hitPos[3]; bool m_hitPosSet; bool m_bidir; + bool m_invertVertexLookupOrder; + int m_traverseType; unsigned int m_oldFlags; public: diff --git a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h index 3beccde5..c0257383 100644 --- a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h +++ b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h @@ -94,6 +94,8 @@ static const int DT_MAX_TRAVERSE_TABLES = 5; /// A value that indicates the link doesn't require a traverse action. (Jumping, climbing, etc.) static const unsigned char DT_NULL_TRAVERSE_TYPE = 0xff; +static const unsigned char DT_MAX_TRAVERSE_TYPES = 32; + /// A value that indicates the link doesn't contain a reverse traverse link. static const unsigned short DT_NULL_TRAVERSE_REVERSE_LINK = 0xffff; @@ -139,6 +141,12 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1; /// A value that determines the offset between the start pos and the ref pos in an off-mesh connection. static const float DT_OFFMESH_CON_REFPOS_OFFSET = 35.f; +/// A flag that indicates that the off-mesh link should be traversed from or towards the off-mesh vert. +static const unsigned char DT_OFFMESH_CON_TRAVERSE_ON_VERT = 1<<6; + +/// A flag that indicates that the off-mesh link can be traversed from or towards the polygon it connects to. +static const unsigned char DT_OFFMESH_CON_TRAVERSE_ON_POLY = 1<<7; + /// A value that determines the maximum number of points describing the straight path result. static const int DT_STRAIGHT_PATH_RESOLUTION = 5; @@ -325,6 +333,14 @@ struct dtBVNode /// An off-mesh connection is a user defined traversable connection made up to two vertices. struct dtOffMeshConnection { + unsigned char getTraverseType() { return traverseContext & 0xff; } + unsigned char getVertLookupOrder() { return (traverseContext >> 8) & 0xff; } + void setTraverseType(unsigned char type, unsigned char order) { traverseContext = type | (order << 8); } + + /// The hint index of the off-mesh connection. (Or #DT_NULL_HINT if there is no hint.) + unsigned short getHintIndex() { return traverseContext; }; + void setHintIndex(unsigned short index) { traverseContext = index; }; + /// The endpoints of the connection. [(ax, ay, az, bx, by, bz)] float pos[6]; @@ -342,14 +358,11 @@ struct dtOffMeshConnection /// End point side. unsigned char side; -#if DT_NAVMESH_SET_VERSION == 5 - /// NOTE: this is unconfirmed, it might not be the jumpType. - unsigned char jumpType; - unsigned char unk1; -#elif DT_NAVMESH_SET_VERSION >= 7 - /// The hint index of the off-mesh connection. (Or #DT_NULL_HINT if there is no hint.) - unsigned short hintIdx; -#endif + /// The traverse types or hint indices. (If the off-mesh connection is used for wall running, + /// it needs a corresponding probe which this field will reference. Otherwise this field will + /// contain the traverse type and lookup order.) + unsigned short traverseContext; + /// The id of the off-mesh connection. (User assigned when the navigation mesh is built.) unsigned short userId; /// The reference position set to the start of the off-mesh connection with an offset of DT_OFFMESH_CON_REFPOS_OFFSET @@ -833,6 +846,13 @@ public: int getNeighbourTilesAt(const int x, const int y, const int side, dtMeshTile** tiles, const int maxTiles) const; + /// Builds external polygon links for a tile. + dtStatus connectExtOffMeshLinks(const dtTileRef tileRef); + /// Builds internal polygons links for a tile. + dtStatus baseOffMeshLinks(const dtTileRef tileRef); + + dtPolyRef clampOffMeshVertToPoly(const dtOffMeshConnection* con, dtMeshTile* conTile, const dtMeshTile* lookupTile, const bool start); + private: /// Returns all polygons in neighbour tile based on portal defined by the segment. int findConnectingPolys(const float* va, const float* vb, @@ -841,13 +861,9 @@ private: /// Builds internal polygons links for a tile. void connectIntLinks(dtMeshTile* tile); - /// Builds internal polygons links for a tile. - void baseOffMeshLinks(dtMeshTile* tile); /// Builds external polygon links for a tile. void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side); - /// Builds external polygon links for a tile. - void connectExtOffMeshLinks(dtMeshTile* target); /// Removes external links at specified side. void unconnectLinks(dtMeshTile* tile, dtMeshTile* target); diff --git a/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h b/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h index 0326c9dd..b8ed2be2 100644 --- a/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h +++ b/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h @@ -71,6 +71,10 @@ struct dtNavMeshCreateParams /// 0 = Travel only from endpoint A to endpoint B.
/// #DT_OFFMESH_CON_BIDIR = Bidirectional travel. const unsigned char* offMeshConDir; + /// The user defined jump type of the off-mesh connection. [Size: #offMeshConCount] + const unsigned char* offMeshConJumps; + /// The user defined lookup order of the off-mesh connection poly verts. [Size: #offMeshConCount] + const unsigned char* offMeshConOrders; /// The user defined ids of the off-mesh connection. [Size: #offMeshConCount] const unsigned short* offMeshConUserID; /// Off-mesh connection reference positions. [(x, y, z) * #offMeshConCount] [Unit: wu] diff --git a/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp b/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp index a6008746..8301124d 100644 --- a/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp +++ b/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp @@ -532,27 +532,74 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side) } } -void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* target) +dtPolyRef dtNavMesh::clampOffMeshVertToPoly(const dtOffMeshConnection* con, dtMeshTile* conTile, + const dtMeshTile* lookupTile, const bool start) { - const dtMeshHeader* targetHeader = target->header; + const float* p = start ? &con->pos[0] : &con->pos[3]; + const float halfExtents[3] = { con->rad, con->rad, lookupTile->header->walkableClimb }; - if (!targetHeader) - return; + float nearestPt[3]; + dtPolyRef ref = findNearestPolyInTile(lookupTile, p, halfExtents, nearestPt); + if (!ref) + return 0; + // findNearestPoly may return too optimistic results, further check to make sure. + if (rdSqr(nearestPt[0]-p[0])+rdSqr(nearestPt[1]-p[1]) > rdSqr(con->rad)) + return 0; - for (int i = 0; i < targetHeader->offMeshConCount; ++i) + const dtPoly* poly = &conTile->polys[con->poly]; + + // Make sure the location is on current mesh. + float* v = &conTile->verts[poly->verts[start?0:1]*3]; + rdVcopy(v, nearestPt); + + return ref; +} + +static bool connectOffMeshLink(dtMeshTile* tile, dtPoly* fromPoly, const dtPolyRef toPolyRef, const unsigned char side, + unsigned char edge, unsigned char traverseType, unsigned char order) +{ + unsigned int idx = tile->allocLink(); + + if (idx == DT_NULL_LINK) + return false; + + dtLink* link = &tile->links[idx]; + link->ref = toPolyRef; + link->edge = edge; + link->side = side; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = fromPoly->firstLink; + fromPoly->firstLink = idx; + link->traverseType = traverseType | order; + link->traverseDist = 0; + link->reverseLink = DT_NULL_TRAVERSE_REVERSE_LINK; + + return true; +} + +dtStatus dtNavMesh::connectExtOffMeshLinks(const dtTileRef tileRef) +{ + const int tileIndex = (int)decodePolyIdTile((dtPolyRef)tileRef); + if (tileIndex >= m_maxTiles) + return DT_FAILURE | DT_OUT_OF_MEMORY; + + dtMeshTile* tile = &m_tiles[tileIndex]; + const dtMeshHeader* header = tile->header; + + dtPolyRef base = getPolyRefBase(tile); + + for (int i = 0; i < header->offMeshConCount; ++i) { - dtOffMeshConnection* targetCon = &target->offMeshCons[i]; - - dtPoly* targetPoly = &target->polys[targetCon->poly]; - // Skip off-mesh connections which start location could not be connected at all. - if (targetPoly->firstLink == DT_NULL_LINK) - continue; - - const float halfExtents[3] = { targetCon->rad, targetCon->rad, targetHeader->walkableClimb }; + // Base off-mesh connection start points. + dtOffMeshConnection* con = &tile->offMeshCons[i]; + dtPoly* conPoly = &tile->polys[con->poly]; + // connect to land points. + const float halfExtents[3] = { con->rad, con->rad, header->walkableClimb }; float bmin[3], bmax[3]; - rdVsub(bmin, &targetCon->pos[3], halfExtents); - rdVadd(bmax, &targetCon->pos[3], halfExtents); + rdVsub(bmin, &con->pos[3], halfExtents); + rdVadd(bmax, &con->pos[3], halfExtents); // Find tiles the query touches. int minx, miny, maxx, maxy; @@ -562,74 +609,51 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* target) static const int MAX_NEIS = 32; dtMeshTile* neis[MAX_NEIS]; + const dtPolyRef conPolyRef = base | (dtPolyRef)(con->poly); + + const unsigned char side = rdClassifyPointOutsideBounds(&con->pos[3], header->bmin, header->bmax); + const unsigned char oppositeSide = (side == 0xff) ? 0xff : (unsigned char)rdOppositeTile(side); + + const unsigned char traverseType = con->getTraverseType(); + const bool invertVertLookup = con->getVertLookupOrder(); + for (int y = miny; y <= maxy; ++y) { for (int x = minx; x <= maxx; ++x) { - const int nneis = this->getTilesAt(x, y, neis, MAX_NEIS); + const int nneis = getTilesAt(x, y, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) { - dtMeshTile* tile = neis[j]; + dtMeshTile* neiTile = neis[j]; // Find polygon to connect to. - const float* p = &targetCon->pos[3]; - float nearestPt[3]; - dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt); - if (!ref) - continue; - // findNearestPoly may return too optimistic results, further check to make sure. - if (rdSqr(nearestPt[0]-p[0])+rdSqr(nearestPt[1]-p[1]) > rdSqr(targetCon->rad)) - continue; - // Make sure the location is on current mesh. - float* v = &target->verts[targetPoly->verts[1] * 3]; - rdVcopy(v, nearestPt); + const dtPolyRef landPolyRef = clampOffMeshVertToPoly(con, tile, neiTile, false); - const bool sameTile = target == tile; + if (!landPolyRef) + continue; + + const bool sameTile = tile == neiTile; // Link off-mesh connection to target poly. - unsigned int idx = target->allocLink(); - if (idx != DT_NULL_LINK) - { - dtLink* link = &target->links[idx]; - link->ref = ref; - link->edge = (unsigned char)1; - link->side = sameTile ? 0xff : rdClassifyPointOutsideBounds(nearestPt, targetHeader->bmin, targetHeader->bmax); - link->bmin = link->bmax = 0; - // Add to linked list. - link->next = targetPoly->firstLink; - targetPoly->firstLink = idx; - - link->traverseType = DT_NULL_TRAVERSE_TYPE; - link->traverseDist = 0; - link->reverseLink = DT_NULL_TRAVERSE_REVERSE_LINK; - } + if (!connectOffMeshLink(tile, conPoly, landPolyRef, oppositeSide, 1, DT_NULL_TRAVERSE_TYPE, 0)) + return DT_FAILURE | DT_OUT_OF_MEMORY; // Link target poly to off-mesh connection. - if (targetCon->flags & DT_OFFMESH_CON_BIDIR) + if (con->flags & DT_OFFMESH_CON_BIDIR) { - unsigned int tidx = tile->allocLink(); - if (tidx != DT_NULL_LINK) - { - const unsigned int landPolyIdx = decodePolyIdPoly(ref); - dtPoly* landPoly = &tile->polys[landPolyIdx]; - dtLink* link = &tile->links[tidx]; - link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly); - link->edge = 0xff; - link->side = sameTile ? 0xff : rdClassifyPointInsideBounds(nearestPt, targetHeader->bmin, targetHeader->bmax); - link->bmin = link->bmax = 0; - // Add to linked list. - link->next = landPoly->firstLink; - landPoly->firstLink = tidx; + const unsigned int landPolyIdx = decodePolyIdPoly(landPolyRef); + dtPoly* landPoly = &neiTile->polys[landPolyIdx]; - link->traverseType = DT_NULL_TRAVERSE_TYPE; - link->traverseDist = 0; - link->reverseLink = DT_NULL_TRAVERSE_REVERSE_LINK; - } + if (!connectOffMeshLink(neiTile, landPoly, conPolyRef, side, 0xff, traverseType, + invertVertLookup ? DT_OFFMESH_CON_TRAVERSE_ON_POLY : DT_OFFMESH_CON_TRAVERSE_ON_POLY)) + return DT_FAILURE | DT_OUT_OF_MEMORY; } } } } } + + return DT_SUCCESS; } void dtNavMesh::connectIntLinks(dtMeshTile* tile) @@ -672,68 +696,46 @@ void dtNavMesh::connectIntLinks(dtMeshTile* tile) } } -void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) +dtStatus dtNavMesh::baseOffMeshLinks(const dtTileRef tileRef) { - if (!tile) return; + const int tileIndex = (int)decodePolyIdTile((dtPolyRef)tileRef); + if (tileIndex >= m_maxTiles) + return DT_FAILURE | DT_OUT_OF_MEMORY; + + dtMeshTile* tile = &m_tiles[tileIndex]; + const dtMeshHeader* header = tile->header; dtPolyRef base = getPolyRefBase(tile); - // Base off-mesh connection start points. - for (int i = 0; i < tile->header->offMeshConCount; ++i) + for (int i = 0; i < header->offMeshConCount; ++i) { + // Base off-mesh connection start points. dtOffMeshConnection* con = &tile->offMeshCons[i]; - dtPoly* poly = &tile->polys[con->poly]; - - const float halfExtents[3] = { con->rad, con->rad, tile->header->walkableClimb }; - - // Find polygon to connect to. - const float* p = &con->pos[0]; // First vertex - float nearestPt[3]; - dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt); - if (!ref) continue; - // findNearestPoly may return too optimistic results, further check to make sure. - if (rdSqr(nearestPt[0]-p[0])+rdSqr(nearestPt[1]-p[1]) > rdSqr(con->rad)) + dtPoly* conPoly = &tile->polys[con->poly]; + + const dtPolyRef basePolyRef = clampOffMeshVertToPoly(con, tile, tile, true); + + if (!basePolyRef) continue; - // Make sure the location is on current mesh. - float* v = &tile->verts[poly->verts[0]*3]; - rdVcopy(v, nearestPt); + + const unsigned char traverseType = con->getTraverseType(); + const bool invertVertLookup = con->getVertLookupOrder(); // Link off-mesh connection to target poly. - unsigned int idx = tile->allocLink(); - if (idx != DT_NULL_LINK) - { - dtLink* link = &tile->links[idx]; - link->ref = ref; - link->edge = (unsigned char)0; - link->side = 0xff; - link->bmin = link->bmax = 0; - // Add to linked list. - link->next = poly->firstLink; - poly->firstLink = idx; - link->traverseType = DT_NULL_TRAVERSE_TYPE; - link->traverseDist = 0; - link->reverseLink = DT_NULL_TRAVERSE_REVERSE_LINK; - } + if (!connectOffMeshLink(tile, conPoly, basePolyRef, 0xff, 0, DT_NULL_TRAVERSE_TYPE, 0)) + return DT_FAILURE | DT_OUT_OF_MEMORY; - // Start end-point is always connect back to off-mesh connection. - unsigned int tidx = tile->allocLink(); - if (tidx != DT_NULL_LINK) - { - const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); - dtPoly* landPoly = &tile->polys[landPolyIdx]; - dtLink* link = &tile->links[tidx]; - link->ref = base | (dtPolyRef)(con->poly); - link->edge = 0xff; - link->side = 0xff; - link->bmin = link->bmax = 0; - // Add to linked list. - link->next = landPoly->firstLink; - landPoly->firstLink = tidx; - link->traverseType = DT_NULL_TRAVERSE_TYPE; - link->traverseDist = 0; - link->reverseLink = DT_NULL_TRAVERSE_REVERSE_LINK; - } + // Start end-point is always connect back to off-mesh connection. + const unsigned short basePolyIdx = (unsigned short)decodePolyIdPoly(basePolyRef); + dtPoly* basePoly = &tile->polys[basePolyIdx]; + + const dtPolyRef conPolyRef = base | (dtPolyRef)(con->poly); + if (!connectOffMeshLink(tile, basePoly, conPolyRef, 0xff, 0xff, traverseType, + invertVertLookup ? DT_OFFMESH_CON_TRAVERSE_ON_VERT : DT_OFFMESH_CON_TRAVERSE_ON_POLY)) + return DT_FAILURE | DT_OUT_OF_MEMORY; } + + return DT_SUCCESS; } namespace @@ -1163,16 +1165,6 @@ dtStatus dtNavMesh::connectTile(const dtTileRef tileRef) connectIntLinks(tile); - // Base off-mesh connections to their starting polygons - // and connect connections inside and outside the tile. - baseOffMeshLinks(tile); - - for (int i = 0; i < m_tileCount; i++) - { - dtMeshTile* target = &m_tiles[i]; - connectExtOffMeshLinks(target); - } - // Create connections with neighbour tiles. static const int MAX_NEIS = 32; dtMeshTile* neis[MAX_NEIS]; @@ -1457,32 +1449,15 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz prev = cur; cur = cur->next; } - - // Remove connections to neighbour tiles. - static const int MAX_NEIS = 32; - dtMeshTile* neis[MAX_NEIS]; - int nneis; - - // Disconnect from other layers in current tile. - nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS); - for (int j = 0; j < nneis; ++j) - { - if (neis[j] == tile) continue; - unconnectLinks(neis[j], tile); - } - - // Disconnect from neighbour tiles. - for (int i = 0; i < 8; ++i) - { - nneis = getNeighbourTilesAt(header->x, header->y, i, neis, MAX_NEIS); - for (int j = 0; j < nneis; ++j) - unconnectLinks(neis[j], tile); - } // Disconnect from off-mesh links originating from other tiles. for (int i = 0; i < m_tileCount; ++i) { dtMeshTile* offTile = &m_tiles[i]; + + if (offTile == tile) + continue; + const dtMeshHeader* offHeader = offTile->header; if (!offHeader) @@ -1508,9 +1483,34 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz decodePolyId(link.ref, salt, it, ip); dtMeshTile* landTile = &m_tiles[it]; + + if (landTile == tile) + continue; + unconnectLinks(landTile, tile); } } + + // Remove connections to neighbour tiles. + static const int MAX_NEIS = 32; + dtMeshTile* neis[MAX_NEIS]; + int nneis; + + // Disconnect from other layers in current tile. + nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + if (neis[j] == tile) continue; + unconnectLinks(neis[j], tile); + } + + // Disconnect from neighbour tiles. + for (int i = 0; i < 8; ++i) + { + nneis = getNeighbourTilesAt(header->x, header->y, i, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + unconnectLinks(neis[j], tile); + } // Reset tile. if (tile->flags & DT_TILE_FREE_DATA) diff --git a/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp b/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp index c65113ce..6c7ba95e 100644 --- a/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp +++ b/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp @@ -433,8 +433,10 @@ static void unionTraverseLinkedPolyGroups(const dtTraverseTableCreateParams* par { const dtLink* link = &tile->links[k]; - // Skip normal links. - if (link->traverseType == DT_NULL_TRAVERSE_TYPE) + // Skip normal and off-mesh links. + if (link->traverseType == DT_NULL_TRAVERSE_TYPE || + (link->traverseType & DT_OFFMESH_CON_TRAVERSE_ON_VERT) || + (link->traverseType & DT_OFFMESH_CON_TRAVERSE_ON_POLY)) continue; const dtMeshTile* landTile; @@ -1141,14 +1143,7 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, con->refYaw = params->offMeshConRefYaw[i]; con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; con->side = offMeshConClass[i*2+1]; -#if DT_NAVMESH_SET_VERSION == 5 - con->jumpType = 0; // unknown - con->unk1 = 1; // unknown -#endif - con->userId = params->offMeshConUserID[i]; -#if DT_NAVMESH_SET_VERSION >= 7 - con->hintIdx = DT_NULL_HINT; // todo(amos): hints are currently not supported. -#endif + con->setTraverseType(params->offMeshConJumps[i], params->offMeshConOrders[i]); n++; } }