diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a180f87074..de4c39fd9d 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -1144,7 +1144,7 @@ namespace EsmTool std::cout << " Cell: " << mData.mCell << std::endl; std::cout << " Coordinates: (" << mData.mData.mX << "," << mData.mData.mY << ")" << std::endl; std::cout << " Unknown S1: " << mData.mData.mS1 << std::endl; - if ((unsigned int)mData.mData.mS2 != mData.mPoints.size()) + if (static_cast(mData.mData.mS2) != mData.mPoints.size()) std::cout << " Reported Point Count: " << mData.mData.mS2 << std::endl; std::cout << " Point Count: " << mData.mPoints.size() << std::endl; std::cout << " Edge Count: " << mData.mEdges.size() << std::endl; @@ -1164,7 +1164,7 @@ namespace EsmTool for (const ESM::Pathgrid::Edge& edge : mData.mEdges) { std::cout << " Edge[" << i << "]: " << edge.mV0 << " -> " << edge.mV1 << std::endl; - if (edge.mV0 >= mData.mData.mS2 || edge.mV1 >= mData.mData.mS2) + if (edge.mV0 >= static_cast(mData.mData.mS2) || edge.mV1 >= static_cast(mData.mData.mS2)) std::cout << " BAD POINT IN EDGE!" << std::endl; i++; } diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 54c800e07c..6420c1c83c 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -50,48 +50,48 @@ void CSMTools::PathgridCheckStage::perform(int stage, CSMDoc::Messages& messages messages.add(id, "More points than expected", "", CSMDoc::Message::Severity_Error); std::vector pointList(pathgrid.mPoints.size()); - std::vector duplList; + std::vector duplList; - for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i) + for (const auto& edge : pathgrid.mEdges) { - if (pathgrid.mEdges[i].mV0 < static_cast(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0) + if (edge.mV0 < pathgrid.mPoints.size()) { - pointList[pathgrid.mEdges[i].mV0].mConnectionNum++; + auto& point = pointList[edge.mV0]; + point.mConnectionNum++; // first check for duplicate edges - unsigned int j = 0; - for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j) + size_t j = 0; + for (; j < point.mOtherIndex.size(); ++j) { - if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) + if (point.mOtherIndex[j] == edge.mV1) { std::ostringstream ss; - ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" - << pathgrid.mEdges[i].mV1; - messages.add(id, ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Duplicate edge between points #" << edge.mV0 << " and #" << edge.mV1; + messages.add(id, ss.str(), {}, CSMDoc::Message::Severity_Error); break; } } // only add if not a duplicate - if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size()) - pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1); + if (j == point.mOtherIndex.size()) + point.mOtherIndex.push_back(edge.mV1); } else { std::ostringstream ss; - ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0; - messages.add(id, ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "An edge is connected to a non-existent point #" << edge.mV0; + messages.add(id, ss.str(), {}, CSMDoc::Message::Severity_Error); } } - for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) + for (size_t i = 0; i < pathgrid.mPoints.size(); ++i) { // check that edges are bidirectional bool foundReverse = false; - for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) + for (const auto& otherIndex : pointList[i].mOtherIndex) { - for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k) + for (const auto& other : pointList[otherIndex].mOtherIndex) { - if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast(i)) + if (other == i) { foundReverse = true; break; @@ -101,25 +101,25 @@ void CSMTools::PathgridCheckStage::perform(int stage, CSMDoc::Messages& messages if (!foundReverse) { std::ostringstream ss; - ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j]; - messages.add(id, ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Missing edge between points #" << i << " and #" << otherIndex; + messages.add(id, ss.str(), {}, CSMDoc::Message::Severity_Error); } } // check duplicate points // FIXME: how to do this efficiently? - for (unsigned int j = 0; j != i; ++j) + for (size_t j = 0; j != i; ++j) { if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) { - std::vector::const_iterator it = find(duplList.begin(), duplList.end(), static_cast(i)); + auto it = std::find(duplList.begin(), duplList.end(), i); if (it == duplList.end()) { std::ostringstream ss; ss << "Point #" << i << " duplicates point #" << j << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; - messages.add(id, ss.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, ss.str(), {}, CSMDoc::Message::Severity_Warning); duplList.push_back(i); break; @@ -129,14 +129,14 @@ void CSMTools::PathgridCheckStage::perform(int stage, CSMDoc::Messages& messages } // check pathgrid points that are not connected to anything - for (unsigned int i = 0; i < pointList.size(); ++i) + for (size_t i = 0; i < pointList.size(); ++i) { if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; ss << "Point #" << i << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ") is disconnected from other points"; - messages.add(id, ss.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, ss.str(), {}, CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp index 48d7254f1d..29c705f206 100644 --- a/apps/opencs/model/tools/pathgridcheck.hpp +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -23,7 +23,7 @@ namespace CSMTools struct Point { unsigned char mConnectionNum; - std::vector mOtherIndex; + std::vector mOtherIndex; Point() : mConnectionNum(0) , mOtherIndex(0) diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 90525c8f60..f609e26483 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -200,9 +200,9 @@ namespace CSMWorld case 0: return subRowIndex; case 1: - return edge.mV0; + return static_cast(edge.mV0); case 2: - return edge.mV1; + return static_cast(edge.mV1); default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index d6879c9668..fe5d87fb81 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -424,9 +424,9 @@ namespace CSVRender int adjustment1 = 0; // Determine necessary adjustment - for (std::vector::iterator point = mSelected.begin(); point != mSelected.end(); ++point) + for (const auto point : mSelected) { - if (source->mEdges[edge].mV0 == *point || source->mEdges[edge].mV1 == *point) + if (source->mEdges[edge].mV0 == point || source->mEdges[edge].mV1 == point) { edgeRowsToRemove.insert(static_cast(edge)); @@ -435,10 +435,10 @@ namespace CSVRender break; } - if (source->mEdges[edge].mV0 > *point) + if (source->mEdges[edge].mV0 > point) --adjustment0; - if (source->mEdges[edge].mV1 > *point) + if (source->mEdges[edge].mV1 > point) --adjustment1; } @@ -457,10 +457,9 @@ namespace CSVRender } } - std::set>::iterator row; - for (row = edgeRowsToRemove.begin(); row != edgeRowsToRemove.end(); ++row) + for (const auto row : edgeRowsToRemove) { - commands.push(new CSMWorld::DeleteNestedCommand(*model, mId.getRefIdString(), *row, parentColumn)); + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId.getRefIdString(), row, parentColumn)); } } diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 6a7dcb6c7c..1fe3bcae15 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -13,6 +13,7 @@ #include "../mwworld/action.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwphysics/raycasting.hpp" @@ -328,7 +329,7 @@ void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor) } } -const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const MWWorld::CellStore* cell) +const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const MWWorld::CellStore* cell) const { const ESM::RefId id = cell->getCell()->getId(); // static cache is OK for now, pathgrids can never change during runtime @@ -337,10 +338,17 @@ const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const CacheMap::iterator found = cache.find(id); if (found == cache.end()) { - cache.insert( - std::make_pair(id, std::make_unique(MWMechanics::PathgridGraph(cell)))); + const ESM::Pathgrid* pathgrid + = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell()); + std::unique_ptr ptr; + if (pathgrid) + ptr = std::make_unique(MWMechanics::PathgridGraph(*pathgrid)); + found = cache.emplace(id, std::move(ptr)).first; } - return *cache[id].get(); + const MWMechanics::PathgridGraph* graph = found->second.get(); + if (!graph) + return MWMechanics::PathgridGraph::sEmpty; + return *graph; } bool MWMechanics::AiPackage::shortcutPath(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 16f477dae7..3914ce2d3f 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -145,7 +145,7 @@ namespace MWMechanics void openDoors(const MWWorld::Ptr& actor); - const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell); + const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell) const; DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 54ed2feebe..b47e04da89 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -273,7 +273,7 @@ namespace MWMechanics // Initialization to discover & store allowed node points for this actor. if (storage.mPopulateAvailableNodes) { - getAllowedNodes(actor, actor.getCell()->getCell(), storage); + getAllowedNodes(actor, storage); } auto& prng = MWBase::Environment::get().getWorld()->getPrng(); @@ -722,7 +722,7 @@ namespace MWMechanics AiWanderStorage& storage = state.get(); if (storage.mPopulateAvailableNodes) - getAllowedNodes(actor, actor.getCell()->getCell(), storage); + getAllowedNodes(actor, storage); if (storage.mAllowedNodes.empty()) return; @@ -811,12 +811,12 @@ namespace MWMechanics getPathGridGraph(currentCell).getNeighbouringPoints(index, points); } - void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const MWWorld::Cell* cell, AiWanderStorage& storage) + void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, AiWanderStorage& storage) { // infrequently used, therefore no benefit in caching it as a member - const ESM::Pathgrid* pathgrid - = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); const MWWorld::CellStore* cellStore = actor.getCell(); + const ESM::Pathgrid* pathgrid + = MWBase::Environment::get().getWorld()->getStore().get().search(*cellStore->getCell()); storage.mAllowedNodes.clear(); @@ -835,7 +835,7 @@ namespace MWMechanics if (mDistance && storage.mCanWanderAlongPathGrid && !actor.getClass().isPureWaterCreature(actor)) { // get NPC's position in local (i.e. cell) coordinates - auto converter = Misc::CoordinateConverter(*cell); + auto converter = Misc::CoordinateConverter(*cellStore->getCell()); const osg::Vec3f npcPos = converter.toLocalVec3(mInitialActorPosition); // Find closest pathgrid point @@ -844,8 +844,8 @@ namespace MWMechanics // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // and if the point is connected to the closest current point // NOTE: mPoints is in local coordinates - int pointIndex = 0; - for (unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) + size_t pointIndex = 0; + for (size_t counter = 0; counter < pathgrid->mPoints.size(); counter++) { osg::Vec3f nodePos(PathFinder::makeOsgVec3(pathgrid->mPoints[counter])); if ((npcPos - nodePos).length2() <= mDistance * mDistance @@ -873,10 +873,10 @@ namespace MWMechanics // additional points for NPC to wander to are: // 1. NPC's initial location // 2. Partway along the path between the point and its connected points. - void AiWander::addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, int pointIndex, AiWanderStorage& storage, - const Misc::CoordinateConverter& converter) + void AiWander::addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, size_t pointIndex, + AiWanderStorage& storage, const Misc::CoordinateConverter& converter) { - for (auto& edge : pathGrid->mEdges) + for (const auto& edge : pathGrid->mEdges) { if (edge.mV0 == pointIndex) { diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index dec80a6d33..8b74ed72da 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -151,7 +151,7 @@ namespace MWMechanics void getNeighbouringNodes( ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points); - void getAllowedNodes(const MWWorld::Ptr& actor, const MWWorld::Cell* cell, AiWanderStorage& storage); + void getAllowedNodes(const MWWorld::Ptr& actor, AiWanderStorage& storage); void trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder); @@ -164,7 +164,7 @@ namespace MWMechanics void setCurrentNodeToClosestAllowedNode(AiWanderStorage& storage); - void addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, int pointIndex, AiWanderStorage& storage, + void addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, size_t pointIndex, AiWanderStorage& storage, const Misc::CoordinateConverter& converter); void AddPointBetweenPathGridPoints( diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 4673045495..1cad425a33 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -1,10 +1,7 @@ #include "pathgrid.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "../mwworld/cellstore.hpp" -#include "../mwworld/esmstore.hpp" +#include +#include namespace { @@ -45,20 +42,97 @@ namespace // return distance(a, b); return manhattan(a, b); } + + constexpr size_t NoIndex = static_cast(-1); } namespace MWMechanics { - PathgridGraph::PathgridGraph(const MWWorld::CellStore* cell) - : mCell(nullptr) - , mPathgrid(nullptr) - , mGraph(0) - , mIsGraphConstructed(false) - , mSCCId(0) - , mSCCIndex(0) + + class PathgridGraph::Builder { - load(cell); - } + std::vector& mGraph; + + // variables used to calculate connected components + int mSCCId = 0; + size_t mSCCIndex = 0; + std::vector mSCCStack; + std::vector> mSCCPoint; // first is index, second is lowlink + + // v is the pathgrid point index (some call them vertices) + void recursiveStrongConnect(const size_t v) + { + mSCCPoint[v].first = mSCCIndex; // index + mSCCPoint[v].second = mSCCIndex; // lowlink + mSCCIndex++; + mSCCStack.push_back(v); + size_t w; + + for (const auto& edge : mGraph[v].edges) + { + w = edge.index; + if (mSCCPoint[w].first == NoIndex) // not visited + { + recursiveStrongConnect(w); // recurse + mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].second); + } + else if (std::find(mSCCStack.begin(), mSCCStack.end(), w) != mSCCStack.end()) + mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].first); + } + + if (mSCCPoint[v].second == mSCCPoint[v].first) + { // new component + do + { + w = mSCCStack.back(); + mSCCStack.pop_back(); + mGraph[w].componentId = mSCCId; + } while (w != v); + mSCCId++; + } + } + + public: + /* + * mGraph contains the strongly connected component group id's along + * with pre-calculated edge costs. + * + * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 + * + * mGraph for Seyda Neen will therefore have 3 different values. When + * selecting a random pathgrid point for AiWander, mGraph can be checked + * for quickly finding whether the destination is reachable. + * + * Otherwise, buildPath can automatically select a closest reachable end + * pathgrid point (reachable from the closest start point). + * + * Using Tarjan's algorithm: + * + * mGraph | graph G | + * mSCCPoint | V | derived from mPoints + * mGraph[v].edges | E (for v) | + * mSCCIndex | index | tracking smallest unused index + * mSCCStack | S | + * mGraph[v].edges[i].index | w | + * + */ + explicit Builder(PathgridGraph& graph) + : mGraph(graph.mGraph) + { + // both of these are set to zero in the constructor + // mSCCId = 0; // how many strongly connected components in this cell + // mSCCIndex = 0; + size_t pointsSize = graph.mPathgrid->mPoints.size(); + mSCCPoint.resize(pointsSize, std::pair(NoIndex, NoIndex)); + mSCCStack.reserve(pointsSize); + + for (size_t v = 0; v < pointsSize; ++v) + { + if (mSCCPoint[v].first == NoIndex) // undefined (haven't visited) + recursiveStrongConnect(v); + } + } + }; /* * mGraph is populated with the cost of each allowed edge. @@ -96,132 +170,38 @@ namespace MWMechanics * +----------------> * high cost */ - bool PathgridGraph::load(const MWWorld::CellStore* cell) + PathgridGraph::PathgridGraph(const ESM::Pathgrid& pathgrid) + : mPathgrid(&pathgrid) { - if (!cell) - return false; - - if (mIsGraphConstructed) - return true; - - mCell = cell->getCell(); - - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell); - if (!mPathgrid) - return false; - mGraph.resize(mPathgrid->mPoints.size()); - for (int i = 0; i < static_cast(mPathgrid->mEdges.size()); i++) + for (const auto& edge : mPathgrid->mEdges) { ConnectedPoint neighbour; - neighbour.cost - = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0], mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]); + neighbour.cost = costAStar(mPathgrid->mPoints[edge.mV0], mPathgrid->mPoints[edge.mV1]); // forward path of the edge - neighbour.index = mPathgrid->mEdges[i].mV1; - mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour); + neighbour.index = edge.mV1; + mGraph[edge.mV0].edges.push_back(neighbour); // reverse path of the edge // NOTE: These are redundant, ESM already contains the required reverse paths - // neighbour.index = mPathgrid->mEdges[i].mV0; - // mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour); - } - buildConnectedPoints(); - mIsGraphConstructed = true; - return true; - } - - const ESM::Pathgrid* PathgridGraph::getPathgrid() const - { - return mPathgrid; - } - - // v is the pathgrid point index (some call them vertices) - void PathgridGraph::recursiveStrongConnect(int v) - { - mSCCPoint[v].first = mSCCIndex; // index - mSCCPoint[v].second = mSCCIndex; // lowlink - mSCCIndex++; - mSCCStack.push_back(v); - int w; - - for (int i = 0; i < static_cast(mGraph[v].edges.size()); i++) - { - w = mGraph[v].edges[i].index; - if (mSCCPoint[w].first == -1) // not visited - { - recursiveStrongConnect(w); // recurse - mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].second); - } - else - { - if (find(mSCCStack.begin(), mSCCStack.end(), w) != mSCCStack.end()) - mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].first); - } - } - - if (mSCCPoint[v].second == mSCCPoint[v].first) - { // new component - do - { - w = mSCCStack.back(); - mSCCStack.pop_back(); - mGraph[w].componentId = mSCCId; - } while (w != v); - mSCCId++; + // neighbour.index = edge.mV0; + // mGraph[edge.mV1].edges.push_back(neighbour); } - return; + Builder(*this); } - /* - * mGraph contains the strongly connected component group id's along - * with pre-calculated edge costs. - * - * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 - * - * mGraph for Seyda Neen will therefore have 3 different values. When - * selecting a random pathgrid point for AiWander, mGraph can be checked - * for quickly finding whether the destination is reachable. - * - * Otherwise, buildPath can automatically select a closest reachable end - * pathgrid point (reachable from the closest start point). - * - * Using Tarjan's algorithm: - * - * mGraph | graph G | - * mSCCPoint | V | derived from mPoints - * mGraph[v].edges | E (for v) | - * mSCCIndex | index | tracking smallest unused index - * mSCCStack | S | - * mGraph[v].edges[i].index | w | - * - */ - void PathgridGraph::buildConnectedPoints() - { - // both of these are set to zero in the constructor - // mSCCId = 0; // how many strongly connected components in this cell - // mSCCIndex = 0; - int pointsSize = static_cast(mPathgrid->mPoints.size()); - mSCCPoint.resize(pointsSize, std::pair(-1, -1)); - mSCCStack.reserve(pointsSize); - - for (int v = 0; v < pointsSize; v++) - { - if (mSCCPoint[v].first == -1) // undefined (haven't visited) - recursiveStrongConnect(v); - } - } + const PathgridGraph PathgridGraph::sEmpty = {}; - bool PathgridGraph::isPointConnected(const int start, const int end) const + bool PathgridGraph::isPointConnected(const size_t start, const size_t end) const { return (mGraph[start].componentId == mGraph[end].componentId); } - void PathgridGraph::getNeighbouringPoints(const int index, ESM::Pathgrid::PointList& nodes) const + void PathgridGraph::getNeighbouringPoints(const size_t index, ESM::Pathgrid::PointList& nodes) const { - for (int i = 0; i < static_cast(mGraph[index].edges.size()); i++) + for (const auto& edge : mGraph[index].edges) { - int neighbourIndex = mGraph[index].edges[i].index; - if (neighbourIndex != index) - nodes.push_back(mPathgrid->mPoints[neighbourIndex]); + if (edge.index != index) + nodes.push_back(mPathgrid->mPoints[edge.index]); } } @@ -252,7 +232,7 @@ namespace MWMechanics * pathgrid points form (currently they are converted to world * coordinates). Essentially trading speed w/ memory. */ - std::deque PathgridGraph::aStarSearch(const int start, const int goal) const + std::deque PathgridGraph::aStarSearch(const size_t start, const size_t goal) const { std::deque path; if (!isPointConnected(start, goal)) @@ -260,20 +240,20 @@ namespace MWMechanics return path; // there is no path, return an empty path } - int graphSize = static_cast(mGraph.size()); + size_t graphSize = mGraph.size(); std::vector gScore(graphSize, -1); std::vector fScore(graphSize, -1); - std::vector graphParent(graphSize, -1); + std::vector graphParent(graphSize, NoIndex); // gScore & fScore keep costs for each pathgrid point in mPoints gScore[start] = 0; fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]); - std::list openset; - std::list closedset; + std::list openset; + std::set closedset; openset.push_back(start); - int current = -1; + size_t current = start; while (!openset.empty()) { @@ -283,16 +263,16 @@ namespace MWMechanics if (current == goal) break; - closedset.push_back(current); // remember we've been here + closedset.insert(current); // remember we've been here // check all edges for the current point index - for (int j = 0; j < static_cast(mGraph[current].edges.size()); j++) + for (const auto& edge : mGraph[current].edges) { - if (std::find(closedset.begin(), closedset.end(), mGraph[current].edges[j].index) == closedset.end()) + if (!closedset.contains(edge.index)) { // not in closedset - i.e. have not traversed this edge destination - int dest = mGraph[current].edges[j].index; - float tentative_g = gScore[current] + mGraph[current].edges[j].cost; + size_t dest = edge.index; + float tentative_g = gScore[current] + edge.cost; bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); if (!isInOpenSet || tentative_g < gScore[dest]) { @@ -303,8 +283,8 @@ namespace MWMechanics { // add this edge to openset, lowest cost goes to the front // TODO: if this causes performance problems a hash table may help - std::list::iterator it = openset.begin(); - for (it = openset.begin(); it != openset.end(); ++it) + auto it = openset.begin(); + for (; it != openset.end(); ++it) { if (fScore[*it] > fScore[dest]) break; @@ -320,7 +300,7 @@ namespace MWMechanics return path; // for some reason couldn't build a path // reconstruct path to return, using local coordinates - while (graphParent[current] != -1) + while (graphParent[current] != NoIndex) { path.push_front(mPathgrid->mPoints[current]); current = graphParent[current]; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 9dd3745a2d..942b6426b7 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -5,49 +5,44 @@ #include -namespace ESM -{ - struct Cell; -} - -namespace MWWorld -{ - class CellStore; - class Cell; -} - namespace MWMechanics { class PathgridGraph { - public: - PathgridGraph(const MWWorld::CellStore* cell); + PathgridGraph() + : mPathgrid(nullptr) + { + } - bool load(const MWWorld::CellStore* cell); + public: + explicit PathgridGraph(const ESM::Pathgrid& pathGrid); - const ESM::Pathgrid* getPathgrid() const; + const ESM::Pathgrid* getPathgrid() const { return mPathgrid; } // returns true if end point is strongly connected (i.e. reachable // from start point) both start and end are pathgrid point indexes - bool isPointConnected(const int start, const int end) const; + bool isPointConnected(const size_t start, const size_t end) const; // get neighbouring nodes for index node and put them to "nodes" vector - void getNeighbouringPoints(const int index, ESM::Pathgrid::PointList& nodes) const; + void getNeighbouringPoints(const size_t index, ESM::Pathgrid::PointList& nodes) const; // the input parameters are pathgrid point indexes // the output list is in local (internal cells) or world (external // cells) coordinates // // NOTE: if start equals end an empty path is returned - std::deque aStarSearch(const int start, const int end) const; + std::deque aStarSearch(const size_t start, const size_t end) const; + + static const PathgridGraph sEmpty; private: - const MWWorld::Cell* mCell; const ESM::Pathgrid* mPathgrid; + class Builder; + struct ConnectedPoint // edge { - int index; // pathgrid point index of neighbour + size_t index; // pathgrid point index of neighbour float cost; }; @@ -67,17 +62,6 @@ namespace MWMechanics // all other pathgrid points are the third set // std::vector mGraph; - bool mIsGraphConstructed; - - // variables used to calculate connected components - int mSCCId; - int mSCCIndex; - std::vector mSCCStack; - typedef std::pair VPair; // first is index, second is lowlink - std::vector mSCCPoint; - // methods used to calculate connected components - void recursiveStrongConnect(int v); - void buildConnectedPoints(); }; } diff --git a/components/esm3/loadpgrd.cpp b/components/esm3/loadpgrd.cpp index c2a94e6e65..807929cdeb 100644 --- a/components/esm3/loadpgrd.cpp +++ b/components/esm3/loadpgrd.cpp @@ -87,21 +87,22 @@ namespace ESM else { int rawConnNum = size / sizeof(int); - std::vector rawConnections; + std::vector rawConnections; rawConnections.reserve(rawConnNum); for (int i = 0; i < rawConnNum; ++i) { int currentValue; esm.getT(currentValue); - rawConnections.push_back(currentValue); + assert(currentValue >= 0); + rawConnections.push_back(static_cast(currentValue)); } - std::vector::const_iterator rawIt = rawConnections.begin(); - int pointIndex = 0; + auto rawIt = rawConnections.begin(); + size_t pointIndex = 0; mEdges.reserve(edgeCount); - for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) + for (const auto& point : mPoints) { - unsigned char connectionNum = (*it).mConnectionNum; + unsigned char connectionNum = point.mConnectionNum; if (rawConnections.end() - rawIt < connectionNum) esm.fail("Not enough connections"); for (int i = 0; i < connectionNum; ++i) @@ -112,6 +113,7 @@ namespace ESM ++rawIt; mEdges.push_back(edge); } + ++pointIndex; } } break; @@ -143,11 +145,11 @@ namespace ESM { correctedPoints[point].mConnectionNum = 0; - for (EdgeList::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (const auto& edge : mEdges) { - if (static_cast(it->mV0) == point) + if (edge.mV0 == point) { - sortedEdges.push_back(it->mV1); + sortedEdges.push_back(static_cast(edge.mV1)); ++correctedPoints[point].mConnectionNum; } } diff --git a/components/esm3/loadpgrd.hpp b/components/esm3/loadpgrd.hpp index 9609b32c0e..464358fef4 100644 --- a/components/esm3/loadpgrd.hpp +++ b/components/esm3/loadpgrd.hpp @@ -53,8 +53,8 @@ namespace ESM struct Edge // path grid edge { - int mV0, mV1; // index of points connected with this edge - }; // 8 bytes + size_t mV0, mV1; // index of points connected with this edge + }; ESM::RefId mCell; // Cell name DATAstruct mData; diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index 2d19913341..09f28e098a 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -96,8 +96,7 @@ namespace SceneUtil for (ESM::Pathgrid::EdgeList::const_iterator edge = pathgrid.mEdges.begin(); edge != pathgrid.mEdges.end(); ++edge) { - if (edge->mV0 == edge->mV1 || edge->mV0 < 0 || edge->mV0 >= PointCount || edge->mV1 < 0 - || edge->mV1 >= PointCount) + if (edge->mV0 == edge->mV1 || edge->mV0 >= PointCount || edge->mV1 >= PointCount) continue; const ESM::Pathgrid::Point& from = pathgrid.mPoints[edge->mV0];