You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
8.2 KiB
C++
221 lines
8.2 KiB
C++
#ifndef GAME_MWMECHANICS_PATHFINDING_H
|
|
#define GAME_MWMECHANICS_PATHFINDING_H
|
|
|
|
#include <deque>
|
|
#include <cassert>
|
|
#include <iterator>
|
|
|
|
#include <components/detournavigator/flags.hpp>
|
|
#include <components/detournavigator/areatype.hpp>
|
|
#include <components/detournavigator/status.hpp>
|
|
#include <components/esm/defs.hpp>
|
|
#include <components/esm/loadpgrd.hpp>
|
|
|
|
namespace MWWorld
|
|
{
|
|
class CellStore;
|
|
class ConstPtr;
|
|
class Ptr;
|
|
}
|
|
|
|
namespace MWMechanics
|
|
{
|
|
class PathgridGraph;
|
|
|
|
template <class T>
|
|
inline float distance(const T& lhs, const T& rhs)
|
|
{
|
|
static_assert(std::is_same<T, osg::Vec2f>::value
|
|
|| std::is_same<T, osg::Vec3f>::value,
|
|
"T is not a position");
|
|
return (lhs - rhs).length();
|
|
}
|
|
|
|
inline float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs)
|
|
{
|
|
return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));
|
|
}
|
|
|
|
float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);
|
|
|
|
inline float getZAngleToDir(const osg::Vec3f& dir)
|
|
{
|
|
return std::atan2(dir.x(), dir.y());
|
|
}
|
|
|
|
inline float getXAngleToDir(const osg::Vec3f& dir)
|
|
{
|
|
float dirLen = dir.length();
|
|
return (dirLen != 0) ? -std::asin(dir.z() / dirLen) : 0;
|
|
}
|
|
|
|
inline float getZAngleToPoint(const osg::Vec3f& origin, const osg::Vec3f& dest)
|
|
{
|
|
return getZAngleToDir(dest - origin);
|
|
}
|
|
|
|
inline float getXAngleToPoint(const osg::Vec3f& origin, const osg::Vec3f& dest)
|
|
{
|
|
return getXAngleToDir(dest - origin);
|
|
}
|
|
|
|
const float PATHFIND_Z_REACH = 50.0f;
|
|
// distance after which actor (failed previously to shortcut) will try again
|
|
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
|
|
|
|
const float MIN_TOLERANCE = 1.0f;
|
|
const float DEFAULT_TOLERANCE = 32.0f;
|
|
|
|
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
|
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
|
bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY);
|
|
|
|
class PathFinder
|
|
{
|
|
public:
|
|
PathFinder()
|
|
: mConstructed(false)
|
|
, mCell(nullptr)
|
|
{
|
|
}
|
|
|
|
void clearPath()
|
|
{
|
|
mConstructed = false;
|
|
mPath.clear();
|
|
mCell = nullptr;
|
|
}
|
|
|
|
void buildStraightPath(const osg::Vec3f& endPoint);
|
|
|
|
void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
|
|
|
|
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
|
const DetourNavigator::AreaCosts& areaCosts);
|
|
|
|
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
|
|
|
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
|
|
|
void buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
|
|
|
/// Remove front point if exist and within tolerance
|
|
void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
|
|
bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents,
|
|
const DetourNavigator::Flags flags);
|
|
|
|
bool checkPathCompleted() const
|
|
{
|
|
return mConstructed && mPath.empty();
|
|
}
|
|
|
|
/// In radians
|
|
float getZAngleToNext(float x, float y) const;
|
|
|
|
float getXAngleToNext(float x, float y, float z) const;
|
|
|
|
bool isPathConstructed() const
|
|
{
|
|
return mConstructed && !mPath.empty();
|
|
}
|
|
|
|
std::size_t getPathSize() const
|
|
{
|
|
return mPath.size();
|
|
}
|
|
|
|
const std::deque<osg::Vec3f>& getPath() const
|
|
{
|
|
return mPath;
|
|
}
|
|
|
|
const MWWorld::CellStore* getPathCell() const
|
|
{
|
|
return mCell;
|
|
}
|
|
|
|
void addPointToPath(const osg::Vec3f& point)
|
|
{
|
|
mConstructed = true;
|
|
mPath.push_back(point);
|
|
}
|
|
|
|
/// utility function to convert a osg::Vec3f to a Pathgrid::Point
|
|
static ESM::Pathgrid::Point makePathgridPoint(const osg::Vec3f& v)
|
|
{
|
|
return ESM::Pathgrid::Point(static_cast<int>(v[0]), static_cast<int>(v[1]), static_cast<int>(v[2]));
|
|
}
|
|
|
|
/// utility function to convert an ESM::Position to a Pathgrid::Point
|
|
static ESM::Pathgrid::Point makePathgridPoint(const ESM::Position& p)
|
|
{
|
|
return ESM::Pathgrid::Point(static_cast<int>(p.pos[0]), static_cast<int>(p.pos[1]), static_cast<int>(p.pos[2]));
|
|
}
|
|
|
|
static osg::Vec3f makeOsgVec3(const ESM::Pathgrid::Point& p)
|
|
{
|
|
return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ));
|
|
}
|
|
|
|
// Slightly cheaper version for comparisons.
|
|
// Caller needs to be careful for very short distances (i.e. less than 1)
|
|
// or when accumuating the results i.e. (a + b)^2 != a^2 + b^2
|
|
//
|
|
static float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos)
|
|
{
|
|
return (MWMechanics::PathFinder::makeOsgVec3(point) - pos).length2();
|
|
}
|
|
|
|
// Return the closest pathgrid point index from the specified position
|
|
// coordinates. NOTE: Does not check if there is a sensible way to get there
|
|
// (e.g. a cliff in front).
|
|
//
|
|
// NOTE: pos is expected to be in local coordinates, as is grid->mPoints
|
|
//
|
|
static int getClosestPoint(const ESM::Pathgrid* grid, const osg::Vec3f& pos)
|
|
{
|
|
assert(grid && !grid->mPoints.empty());
|
|
|
|
float distanceBetween = distanceSquared(grid->mPoints[0], pos);
|
|
int closestIndex = 0;
|
|
|
|
// TODO: if this full scan causes performance problems mapping pathgrid
|
|
// points to a quadtree may help
|
|
for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++)
|
|
{
|
|
float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos);
|
|
if(potentialDistBetween < distanceBetween)
|
|
{
|
|
distanceBetween = potentialDistBetween;
|
|
closestIndex = counter;
|
|
}
|
|
}
|
|
|
|
return closestIndex;
|
|
}
|
|
|
|
private:
|
|
bool mConstructed;
|
|
std::deque<osg::Vec3f> mPath;
|
|
|
|
const MWWorld::CellStore* mCell;
|
|
|
|
void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
|
|
|
[[nodiscard]] DetourNavigator::Status buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor,
|
|
const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents,
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts,
|
|
std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
|
};
|
|
}
|
|
|
|
#endif
|