diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ee1013fe4e..83ebc67d9d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -13,6 +13,7 @@ #include "../mwrender/animation.hpp" +#include "pathgrid.hpp" #include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" @@ -372,7 +373,7 @@ namespace MWMechanics int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos); for (int i = 0; i < static_cast(pathgrid->mPoints.size()); i++) { - if (i != closestPointIndex && storage.mCell->isPointConnected(closestPointIndex, i)) + if (i != closestPointIndex && getPathGridGraph(storage.mCell).isPointConnected(closestPointIndex, i)) { points.push_back(pathgrid->mPoints[static_cast(i)]); } diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index f837ad2eec..198c8fc4b0 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -14,6 +14,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/inventorystore.hpp" +#include "pathgrid.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "steering.hpp" @@ -107,7 +108,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr { if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path { - mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); mRotateOnTheRunChecks = 3; // give priority to go directly on target if there is minimal opportunity @@ -220,6 +221,20 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur } } +const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const MWWorld::CellStore *cell) +{ + const ESM::CellId& id = cell->getCell()->getCellId(); + // static cache is OK for now, pathgrids can never change during runtime + typedef std::map > CacheMap; + static CacheMap cache; + CacheMap::iterator found = cache.find(id); + if (found == cache.end()) + { + cache.insert(std::make_pair(id, std::unique_ptr(new MWMechanics::PathgridGraph(cell)))); + } + return *cache[id].get(); +} + bool MWMechanics::AiPackage::shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS) { const MWWorld::Class& actorClass = actor.getClass(); diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index acbd879087..a9c69ad7fc 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -27,6 +27,7 @@ namespace MWMechanics const float AI_REACTION_TIME = 0.25f; class CharacterController; + class PathgridGraph; /// \brief Base class for AI packages class AiPackage @@ -119,6 +120,8 @@ namespace MWMechanics void evadeObstacles(const MWWorld::Ptr& actor, float duration, const ESM::Position& pos); + const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell); + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 343e03f6ce..7bdb3a11de 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -17,6 +17,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "pathgrid.hpp" #include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" @@ -217,7 +218,7 @@ namespace MWMechanics ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); - mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); if (mPathFinder.isPathConstructed()) storage.setState(Wander_Walking); @@ -349,7 +350,7 @@ namespace MWMechanics ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); // don't take shortcuts for wandering - mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); if (mPathFinder.isPathConstructed()) { @@ -383,7 +384,7 @@ namespace MWMechanics // Check if land creature will walk onto water or if water creature will swim onto land if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) || (isWaterCreature && !destinationThroughGround(currentPositionVec3f, mDestination))) { - mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell()); + mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell(), getPathGridGraph(actor.getCell())); mPathFinder.addPointToPath(destinationPosition); if (mPathFinder.isPathConstructed()) @@ -666,7 +667,7 @@ namespace MWMechanics ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos)); // don't take shortcuts for wandering - mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); if (mPathFinder.isPathConstructed()) { @@ -872,7 +873,7 @@ namespace MWMechanics int index = PathFinder::GetClosestPoint(pathgrid, PathFinder::MakeOsgVec3(dest)); - currentCell->getNeighbouringPoints(index, points); + getPathGridGraph(currentCell).getNeighbouringPoints(index, points); } void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage) @@ -913,7 +914,7 @@ namespace MWMechanics { osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter])); if((npcPos - nodePos).length2() <= mDistance * mDistance && - cellStore->isPointConnected(closestPointIndex, counter)) + getPathGridGraph(cellStore).isPointConnected(closestPointIndex, counter)) { storage.mAllowedNodes.push_back(pathgrid->mPoints[counter]); pointIndex = counter; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5c04560960..1dccb6c9a7 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -5,16 +5,16 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "pathgrid.hpp" #include "coordinateconverter.hpp" namespace { // Chooses a reachable end pathgrid point. start is assumed reachable. std::pair getClosestReachablePoint(const ESM::Pathgrid* grid, - const MWWorld::CellStore *cell, + const MWMechanics::PathgridGraph *graph, const osg::Vec3f& pos, int start) { assert(grid && !grid->mPoints.empty()); @@ -31,7 +31,7 @@ namespace if (potentialDistBetween < closestDistanceReachable) { // found a closer one - if (cell->isPointConnected(start, counter)) + if (graph->isPointConnected(start, counter)) { closestDistanceReachable = potentialDistBetween; closestReachableIndex = counter; @@ -45,7 +45,7 @@ namespace } // post-condition: start and endpoint must be connected - assert(cell->isPointConnected(start, closestReachableIndex)); + assert(graph->isPointConnected(start, closestReachableIndex)); // AiWander has logic that depends on whether a path was created, deleting // allowed nodes if not. Hence a path needs to be created even if the start @@ -120,8 +120,8 @@ namespace MWMechanics } PathFinder::PathFinder() - : mPathgrid(NULL), - mCell(NULL) + : mPathgrid(NULL) + , mCell(NULL) { } @@ -169,14 +169,15 @@ namespace MWMechanics */ void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const MWWorld::CellStore* cell) + const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph) { mPath.clear(); + // TODO: consider removing mCell / mPathgrid in favor of mPathgridGraph if(mCell != cell || !mPathgrid) { mCell = cell; - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + mPathgrid = pathgridGraph.getPathgrid(); } // Refer to AiWander reseach topic on openmw forums for some background. @@ -200,7 +201,7 @@ namespace MWMechanics int startNode = GetClosestPoint(mPathgrid, startPointInLocalCoords); osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint)); - std::pair endNode = getClosestReachablePoint(mPathgrid, cell, + std::pair endNode = getClosestReachablePoint(mPathgrid, &pathgridGraph, endPointInLocalCoords, startNode); @@ -228,7 +229,7 @@ namespace MWMechanics } else { - mPath = mCell->aStarSearch(startNode, endNode.first); + mPath = pathgridGraph.aStarSearch(startNode, endNode.first); // convert supplied path to world coordinates for (std::list::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) @@ -301,18 +302,18 @@ namespace MWMechanics // see header for the rationale void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const MWWorld::CellStore* cell) + const MWWorld::CellStore* cell, const MWMechanics::PathgridGraph& pathgridGraph) { if (mPath.size() < 2) { // if path has one point, then it's the destination. // don't need to worry about bad path for this case - buildPath(startPoint, endPoint, cell); + buildPath(startPoint, endPoint, cell, pathgridGraph); } else { const ESM::Pathgrid::Point oldStart(*getPath().begin()); - buildPath(startPoint, endPoint, cell); + buildPath(startPoint, endPoint, cell, pathgridGraph); if (mPath.size() >= 2) { // if 2nd waypoint of new path == 1st waypoint of old, diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 945a7f9272..ebc22f10d4 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -14,6 +14,8 @@ namespace MWWorld namespace MWMechanics { + class PathgridGraph; + float distance(const ESM::Pathgrid::Point& point, float x, float y, float); float distance(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b); float getZAngleToDir(const osg::Vec3f& dir); @@ -54,7 +56,7 @@ namespace MWMechanics void clearPath(); void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const MWWorld::CellStore* cell); + const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); bool checkPathCompleted(float x, float y, float tolerance = PathTolerance); ///< \Returns true if we are within \a tolerance units of the last path point. @@ -89,7 +91,7 @@ namespace MWMechanics Which results in NPC "running in a circle" back to the just passed waypoint. */ void buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const MWWorld::CellStore* cell); + const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); void addPointToPath(const ESM::Pathgrid::Point &point) { diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 85264095ca..c0122a861a 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -49,7 +49,7 @@ namespace namespace MWMechanics { - PathgridGraph::PathgridGraph() + PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) : mCell(NULL) , mPathgrid(NULL) , mIsExterior(0) @@ -58,6 +58,7 @@ namespace MWMechanics , mSCCId(0) , mSCCIndex(0) { + load(cell); } /* @@ -130,6 +131,11 @@ namespace MWMechanics 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) { diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 84b84652cc..0c71c45617 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -20,10 +20,12 @@ namespace MWMechanics class PathgridGraph { public: - PathgridGraph(); + PathgridGraph(const MWWorld::CellStore* cell); bool load(const MWWorld::CellStore *cell); + const ESM::Pathgrid* getPathgrid() const; + // 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; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index f6e70dc94a..f33c7bb67b 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -445,10 +445,6 @@ namespace MWWorld loadRefs (); mState = State_Loaded; - - // TODO: the pathgrid graph only needs to be loaded for active cells, so move this somewhere else. - // In a simple test, loading the graph for all cells in MW + expansions took 200 ms - mPathgridGraph.load(this); } } @@ -937,21 +933,6 @@ namespace MWWorld return !(left==right); } - bool CellStore::isPointConnected(const int start, const int end) const - { - return mPathgridGraph.isPointConnected(start, end); - } - - void CellStore::getNeighbouringPoints(const int index, ESM::Pathgrid::PointList &nodes) const - { - return mPathgridGraph.getNeighbouringPoints(index, nodes); - } - - std::list CellStore::aStarSearch(const int start, const int end) const - { - return mPathgridGraph.aStarSearch(start, end); - } - void CellStore::setFog(ESM::FogState *fog) { mFogState.reset(fog); diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 2f6277aec4..dd54bdd6ac 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -32,13 +32,12 @@ #include #include -#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld - #include "timestamp.hpp" #include "ptr.hpp" namespace ESM { + struct Cell; struct CellState; struct FogState; struct CellId; @@ -376,11 +375,6 @@ namespace MWWorld void respawn (); ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. - bool isPointConnected(const int start, const int end) const; - void getNeighbouringPoints(const int index, ESM::Pathgrid::PointList &nodes) const; - - std::list aStarSearch(const int start, const int end) const; - private: /// Run through references and store IDs @@ -392,8 +386,6 @@ namespace MWWorld ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. - - MWMechanics::PathgridGraph mPathgridGraph; }; template<>