Move PathgridGraph out of CellStore

By definition this is not 'Mutable state of a cell' and does not belong in CellStore.

This change should improve startup times (graph is now loaded on demand) and edits to 'pathgrid.hpp' no longer cause the entirety of OpenMW to be rebuilt.
pull/349/head
scrawl 7 years ago
parent 5fe68ab062
commit c50b18b3bb
No known key found for this signature in database
GPG Key ID: 2E6CC3676024C402

@ -13,6 +13,7 @@
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "pathgrid.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -372,7 +373,7 @@ namespace MWMechanics
int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos); int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos);
for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++) for (int i = 0; i < static_cast<int>(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<size_t>(i)]); points.push_back(pathgrid->mPoints[static_cast<size_t>(i)]);
} }

@ -14,6 +14,7 @@
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "pathgrid.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "steering.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 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; mRotateOnTheRunChecks = 3;
// give priority to go directly on target if there is minimal opportunity // 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<ESM::CellId, std::unique_ptr<MWMechanics::PathgridGraph> > CacheMap;
static CacheMap cache;
CacheMap::iterator found = cache.find(id);
if (found == cache.end())
{
cache.insert(std::make_pair(id, std::unique_ptr<MWMechanics::PathgridGraph>(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) 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(); const MWWorld::Class& actorClass = actor.getClass();

@ -27,6 +27,7 @@ namespace MWMechanics
const float AI_REACTION_TIME = 0.25f; const float AI_REACTION_TIME = 0.25f;
class CharacterController; class CharacterController;
class PathgridGraph;
/// \brief Base class for AI packages /// \brief Base class for AI packages
class AiPackage class AiPackage
@ -119,6 +120,8 @@ namespace MWMechanics
void evadeObstacles(const MWWorld::Ptr& actor, float duration, const ESM::Position& pos); 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 // TODO: all this does not belong here, move into temporary storage
PathFinder mPathFinder; PathFinder mPathFinder;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;

@ -17,6 +17,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "pathgrid.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -217,7 +218,7 @@ namespace MWMechanics
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination)); ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination));
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); 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()) if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking); storage.setState(Wander_Walking);
@ -349,7 +350,7 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
mPathFinder.buildSyncedPath(start, dest, actor.getCell()); mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
if (mPathFinder.isPathConstructed()) 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 // Check if land creature will walk onto water or if water creature will swim onto land
if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) || if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) ||
(isWaterCreature && !destinationThroughGround(currentPositionVec3f, mDestination))) { (isWaterCreature && !destinationThroughGround(currentPositionVec3f, mDestination))) {
mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell()); mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell(), getPathGridGraph(actor.getCell()));
mPathFinder.addPointToPath(destinationPosition); mPathFinder.addPointToPath(destinationPosition);
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
@ -666,7 +667,7 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
mPathFinder.buildSyncedPath(start, dest, actor.getCell()); mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
@ -872,7 +873,7 @@ namespace MWMechanics
int index = PathFinder::GetClosestPoint(pathgrid, PathFinder::MakeOsgVec3(dest)); 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) 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])); osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter]));
if((npcPos - nodePos).length2() <= mDistance * mDistance && if((npcPos - nodePos).length2() <= mDistance * mDistance &&
cellStore->isPointConnected(closestPointIndex, counter)) getPathGridGraph(cellStore).isPointConnected(closestPointIndex, counter))
{ {
storage.mAllowedNodes.push_back(pathgrid->mPoints[counter]); storage.mAllowedNodes.push_back(pathgrid->mPoints[counter]);
pointIndex = counter; pointIndex = counter;

@ -5,16 +5,16 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "pathgrid.hpp"
#include "coordinateconverter.hpp" #include "coordinateconverter.hpp"
namespace namespace
{ {
// Chooses a reachable end pathgrid point. start is assumed reachable. // Chooses a reachable end pathgrid point. start is assumed reachable.
std::pair<int, bool> getClosestReachablePoint(const ESM::Pathgrid* grid, std::pair<int, bool> getClosestReachablePoint(const ESM::Pathgrid* grid,
const MWWorld::CellStore *cell, const MWMechanics::PathgridGraph *graph,
const osg::Vec3f& pos, int start) const osg::Vec3f& pos, int start)
{ {
assert(grid && !grid->mPoints.empty()); assert(grid && !grid->mPoints.empty());
@ -31,7 +31,7 @@ namespace
if (potentialDistBetween < closestDistanceReachable) if (potentialDistBetween < closestDistanceReachable)
{ {
// found a closer one // found a closer one
if (cell->isPointConnected(start, counter)) if (graph->isPointConnected(start, counter))
{ {
closestDistanceReachable = potentialDistBetween; closestDistanceReachable = potentialDistBetween;
closestReachableIndex = counter; closestReachableIndex = counter;
@ -45,7 +45,7 @@ namespace
} }
// post-condition: start and endpoint must be connected // 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 // 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 // allowed nodes if not. Hence a path needs to be created even if the start
@ -120,8 +120,8 @@ namespace MWMechanics
} }
PathFinder::PathFinder() PathFinder::PathFinder()
: mPathgrid(NULL), : mPathgrid(NULL)
mCell(NULL) , mCell(NULL)
{ {
} }
@ -169,14 +169,15 @@ namespace MWMechanics
*/ */
void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint,
const ESM::Pathgrid::Point &endPoint, const ESM::Pathgrid::Point &endPoint,
const MWWorld::CellStore* cell) const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph)
{ {
mPath.clear(); mPath.clear();
// TODO: consider removing mCell / mPathgrid in favor of mPathgridGraph
if(mCell != cell || !mPathgrid) if(mCell != cell || !mPathgrid)
{ {
mCell = cell; mCell = cell;
mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*mCell->getCell()); mPathgrid = pathgridGraph.getPathgrid();
} }
// Refer to AiWander reseach topic on openmw forums for some background. // Refer to AiWander reseach topic on openmw forums for some background.
@ -200,7 +201,7 @@ namespace MWMechanics
int startNode = GetClosestPoint(mPathgrid, startPointInLocalCoords); int startNode = GetClosestPoint(mPathgrid, startPointInLocalCoords);
osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint)); osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint));
std::pair<int, bool> endNode = getClosestReachablePoint(mPathgrid, cell, std::pair<int, bool> endNode = getClosestReachablePoint(mPathgrid, &pathgridGraph,
endPointInLocalCoords, endPointInLocalCoords,
startNode); startNode);
@ -228,7 +229,7 @@ namespace MWMechanics
} }
else else
{ {
mPath = mCell->aStarSearch(startNode, endNode.first); mPath = pathgridGraph.aStarSearch(startNode, endNode.first);
// convert supplied path to world coordinates // convert supplied path to world coordinates
for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter)
@ -301,18 +302,18 @@ namespace MWMechanics
// see header for the rationale // see header for the rationale
void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint, void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint,
const ESM::Pathgrid::Point &endPoint, const ESM::Pathgrid::Point &endPoint,
const MWWorld::CellStore* cell) const MWWorld::CellStore* cell, const MWMechanics::PathgridGraph& pathgridGraph)
{ {
if (mPath.size() < 2) if (mPath.size() < 2)
{ {
// if path has one point, then it's the destination. // if path has one point, then it's the destination.
// don't need to worry about bad path for this case // don't need to worry about bad path for this case
buildPath(startPoint, endPoint, cell); buildPath(startPoint, endPoint, cell, pathgridGraph);
} }
else else
{ {
const ESM::Pathgrid::Point oldStart(*getPath().begin()); const ESM::Pathgrid::Point oldStart(*getPath().begin());
buildPath(startPoint, endPoint, cell); buildPath(startPoint, endPoint, cell, pathgridGraph);
if (mPath.size() >= 2) if (mPath.size() >= 2)
{ {
// if 2nd waypoint of new path == 1st waypoint of old, // if 2nd waypoint of new path == 1st waypoint of old,

@ -14,6 +14,8 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
class PathgridGraph;
float distance(const ESM::Pathgrid::Point& point, float x, float y, float); 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 distance(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b);
float getZAngleToDir(const osg::Vec3f& dir); float getZAngleToDir(const osg::Vec3f& dir);
@ -54,7 +56,7 @@ namespace MWMechanics
void clearPath(); void clearPath();
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, 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); bool checkPathCompleted(float x, float y, float tolerance = PathTolerance);
///< \Returns true if we are within \a tolerance units of the last path point. ///< \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. 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, 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) void addPointToPath(const ESM::Pathgrid::Point &point)
{ {

@ -49,7 +49,7 @@ namespace
namespace MWMechanics namespace MWMechanics
{ {
PathgridGraph::PathgridGraph() PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell)
: mCell(NULL) : mCell(NULL)
, mPathgrid(NULL) , mPathgrid(NULL)
, mIsExterior(0) , mIsExterior(0)
@ -58,6 +58,7 @@ namespace MWMechanics
, mSCCId(0) , mSCCId(0)
, mSCCIndex(0) , mSCCIndex(0)
{ {
load(cell);
} }
/* /*
@ -130,6 +131,11 @@ namespace MWMechanics
return true; return true;
} }
const ESM::Pathgrid *PathgridGraph::getPathgrid() const
{
return mPathgrid;
}
// v is the pathgrid point index (some call them vertices) // v is the pathgrid point index (some call them vertices)
void PathgridGraph::recursiveStrongConnect(int v) void PathgridGraph::recursiveStrongConnect(int v)
{ {

@ -20,10 +20,12 @@ namespace MWMechanics
class PathgridGraph class PathgridGraph
{ {
public: public:
PathgridGraph(); PathgridGraph(const MWWorld::CellStore* cell);
bool load(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 // returns true if end point is strongly connected (i.e. reachable
// from start point) both start and end are pathgrid point indexes // from start point) both start and end are pathgrid point indexes
bool isPointConnected(const int start, const int end) const; bool isPointConnected(const int start, const int end) const;

@ -445,10 +445,6 @@ namespace MWWorld
loadRefs (); loadRefs ();
mState = State_Loaded; 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); 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<ESM::Pathgrid::Point> CellStore::aStarSearch(const int start, const int end) const
{
return mPathgridGraph.aStarSearch(start, end);
}
void CellStore::setFog(ESM::FogState *fog) void CellStore::setFog(ESM::FogState *fog)
{ {
mFogState.reset(fog); mFogState.reset(fog);

@ -32,13 +32,12 @@
#include <components/esm/loadmisc.hpp> #include <components/esm/loadmisc.hpp>
#include <components/esm/loadbody.hpp> #include <components/esm/loadbody.hpp>
#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld
#include "timestamp.hpp" #include "timestamp.hpp"
#include "ptr.hpp" #include "ptr.hpp"
namespace ESM namespace ESM
{ {
struct Cell;
struct CellState; struct CellState;
struct FogState; struct FogState;
struct CellId; struct CellId;
@ -376,11 +375,6 @@ namespace MWWorld
void respawn (); void respawn ();
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. ///< 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<ESM::Pathgrid::Point> aStarSearch(const int start, const int end) const;
private: private:
/// Run through references and store IDs /// 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. ///< Make case-adjustments to \a ref and insert it into the respective container.
/// ///
/// Invalid \a ref objects are silently dropped. /// Invalid \a ref objects are silently dropped.
MWMechanics::PathgridGraph mPathgridGraph;
}; };
template<> template<>

Loading…
Cancel
Save