2013-03-31 17:30:03 +00:00
|
|
|
#ifndef GAME_MWMECHANICS_PATHFINDING_H
|
|
|
|
#define GAME_MWMECHANICS_PATHFINDING_H
|
|
|
|
|
2018-08-18 10:40:04 +00:00
|
|
|
#include <deque>
|
2016-04-11 01:44:08 +00:00
|
|
|
#include <cassert>
|
2018-08-22 21:20:25 +00:00
|
|
|
#include <iterator>
|
2014-01-12 17:42:31 +00:00
|
|
|
|
2018-07-21 11:16:19 +00:00
|
|
|
#include <components/detournavigator/flags.hpp>
|
2020-06-12 23:56:04 +00:00
|
|
|
#include <components/detournavigator/areatype.hpp>
|
2021-07-01 08:59:13 +00:00
|
|
|
#include <components/detournavigator/status.hpp>
|
2015-03-08 04:42:07 +00:00
|
|
|
#include <components/esm/defs.hpp>
|
2013-03-31 17:30:03 +00:00
|
|
|
#include <components/esm/loadpgrd.hpp>
|
2014-01-12 17:42:31 +00:00
|
|
|
|
|
|
|
namespace MWWorld
|
|
|
|
{
|
|
|
|
class CellStore;
|
2018-10-15 20:07:52 +00:00
|
|
|
class ConstPtr;
|
2019-10-19 10:40:34 +00:00
|
|
|
class Ptr;
|
2014-01-12 17:42:31 +00:00
|
|
|
}
|
2013-03-31 17:30:03 +00:00
|
|
|
|
|
|
|
namespace MWMechanics
|
|
|
|
{
|
2017-11-27 17:30:31 +00:00
|
|
|
class PathgridGraph;
|
|
|
|
|
2019-10-19 10:40:34 +00:00
|
|
|
template <class T>
|
|
|
|
inline float distance(const T& lhs, const T& rhs)
|
2018-07-21 09:30:14 +00:00
|
|
|
{
|
2019-10-19 10:40:34 +00:00
|
|
|
static_assert(std::is_same<T, osg::Vec2f>::value
|
|
|
|
|| std::is_same<T, osg::Vec3f>::value,
|
|
|
|
"T is not a position");
|
2018-07-21 09:30:14 +00:00
|
|
|
return (lhs - rhs).length();
|
|
|
|
}
|
|
|
|
|
2019-10-19 10:40:34 +00:00
|
|
|
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);
|
|
|
|
|
2018-07-21 09:30:14 +00:00
|
|
|
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);
|
|
|
|
}
|
2016-08-19 19:15:26 +00:00
|
|
|
|
2018-08-18 10:43:29 +00:00
|
|
|
const float PATHFIND_Z_REACH = 50.0f;
|
2016-08-19 19:15:26 +00:00
|
|
|
// distance after which actor (failed previously to shortcut) will try again
|
|
|
|
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
|
|
|
|
|
2019-03-05 20:15:15 +00:00
|
|
|
const float MIN_TOLERANCE = 1.0f;
|
2018-08-18 20:46:06 +00:00
|
|
|
const float DEFAULT_TOLERANCE = 32.0f;
|
|
|
|
|
2016-08-19 19:15:26 +00:00
|
|
|
// 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);
|
|
|
|
|
2013-03-31 17:30:03 +00:00
|
|
|
class PathFinder
|
|
|
|
{
|
2013-05-29 22:59:23 +00:00
|
|
|
public:
|
2018-08-19 22:13:59 +00:00
|
|
|
PathFinder()
|
2018-08-19 22:26:58 +00:00
|
|
|
: mConstructed(false)
|
|
|
|
, mCell(nullptr)
|
2018-08-19 22:13:59 +00:00
|
|
|
{
|
|
|
|
}
|
2013-03-31 17:30:03 +00:00
|
|
|
|
2018-08-19 22:13:16 +00:00
|
|
|
void clearPath()
|
|
|
|
{
|
2018-08-19 22:26:58 +00:00
|
|
|
mConstructed = false;
|
2018-08-19 22:13:16 +00:00
|
|
|
mPath.clear();
|
|
|
|
mCell = nullptr;
|
|
|
|
}
|
2014-01-12 17:42:31 +00:00
|
|
|
|
2019-03-19 22:08:06 +00:00
|
|
|
void buildStraightPath(const osg::Vec3f& endPoint);
|
|
|
|
|
2018-08-22 21:20:25 +00:00
|
|
|
void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
|
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
|
|
|
|
|
2019-03-19 22:10:46 +00:00
|
|
|
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
2020-06-12 23:56:04 +00:00
|
|
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
|
|
|
const DetourNavigator::AreaCosts& areaCosts);
|
2019-03-19 22:10:46 +00:00
|
|
|
|
2018-10-15 20:07:52 +00:00
|
|
|
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
2018-08-22 21:20:25 +00:00
|
|
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
2020-06-12 23:56:04 +00:00
|
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
2016-08-19 19:15:26 +00:00
|
|
|
|
2019-04-08 16:01:05 +00:00
|
|
|
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
|
2020-06-12 23:56:04 +00:00
|
|
|
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
2019-04-08 16:01:05 +00:00
|
|
|
|
2021-03-23 22:15:13 +00:00
|
|
|
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);
|
|
|
|
|
2018-08-19 22:26:58 +00:00
|
|
|
/// Remove front point if exist and within tolerance
|
2021-01-29 20:27:22 +00:00
|
|
|
void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
|
2021-06-21 19:54:17 +00:00
|
|
|
bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents,
|
|
|
|
const DetourNavigator::Flags flags);
|
2018-08-19 22:26:58 +00:00
|
|
|
|
|
|
|
bool checkPathCompleted() const
|
|
|
|
{
|
|
|
|
return mConstructed && mPath.empty();
|
|
|
|
}
|
2014-01-29 19:29:07 +00:00
|
|
|
|
2015-07-19 06:01:25 +00:00
|
|
|
/// In radians
|
2013-08-30 02:17:27 +00:00
|
|
|
float getZAngleToNext(float x, float y) const;
|
2013-03-31 17:30:03 +00:00
|
|
|
|
2016-08-19 19:15:26 +00:00
|
|
|
float getXAngleToNext(float x, float y, float z) const;
|
|
|
|
|
2013-08-30 02:17:27 +00:00
|
|
|
bool isPathConstructed() const
|
|
|
|
{
|
2018-08-19 22:26:58 +00:00
|
|
|
return mConstructed && !mPath.empty();
|
2013-08-30 02:17:27 +00:00
|
|
|
}
|
2013-03-31 17:30:03 +00:00
|
|
|
|
2018-08-18 10:35:37 +00:00
|
|
|
std::size_t getPathSize() const
|
2013-10-07 08:20:02 +00:00
|
|
|
{
|
|
|
|
return mPath.size();
|
|
|
|
}
|
|
|
|
|
2018-08-18 10:40:04 +00:00
|
|
|
const std::deque<osg::Vec3f>& getPath() const
|
2013-10-07 08:20:02 +00:00
|
|
|
{
|
|
|
|
return mPath;
|
|
|
|
}
|
|
|
|
|
2018-08-19 22:08:14 +00:00
|
|
|
const MWWorld::CellStore* getPathCell() const
|
|
|
|
{
|
|
|
|
return mCell;
|
|
|
|
}
|
2016-09-05 22:11:10 +00:00
|
|
|
|
2018-07-21 09:30:14 +00:00
|
|
|
void addPointToPath(const osg::Vec3f& point)
|
2014-01-07 20:10:57 +00:00
|
|
|
{
|
2018-08-19 22:26:58 +00:00
|
|
|
mConstructed = true;
|
2014-01-07 20:10:57 +00:00
|
|
|
mPath.push_back(point);
|
|
|
|
}
|
|
|
|
|
2015-06-03 17:41:19 +00:00
|
|
|
/// utility function to convert a osg::Vec3f to a Pathgrid::Point
|
2018-08-18 10:42:11 +00:00
|
|
|
static ESM::Pathgrid::Point makePathgridPoint(const osg::Vec3f& v)
|
2015-03-08 04:42:07 +00:00
|
|
|
{
|
|
|
|
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
|
2018-08-18 10:42:11 +00:00
|
|
|
static ESM::Pathgrid::Point makePathgridPoint(const ESM::Position& p)
|
2015-03-08 04:42:07 +00:00
|
|
|
{
|
|
|
|
return ESM::Pathgrid::Point(static_cast<int>(p.pos[0]), static_cast<int>(p.pos[1]), static_cast<int>(p.pos[2]));
|
|
|
|
}
|
|
|
|
|
2018-08-18 10:42:11 +00:00
|
|
|
static osg::Vec3f makeOsgVec3(const ESM::Pathgrid::Point& p)
|
2015-03-08 04:42:07 +00:00
|
|
|
{
|
2015-05-02 20:45:27 +00:00
|
|
|
return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ));
|
2015-03-08 04:42:07 +00:00
|
|
|
}
|
2018-07-21 09:30:14 +00:00
|
|
|
|
2016-04-11 01:44:08 +00:00
|
|
|
// 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
|
|
|
|
//
|
2018-08-18 10:42:11 +00:00
|
|
|
static float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos)
|
2016-04-11 01:44:08 +00:00
|
|
|
{
|
2018-08-18 10:42:11 +00:00
|
|
|
return (MWMechanics::PathFinder::makeOsgVec3(point) - pos).length2();
|
2016-04-11 01:44:08 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 21:11:22 +00:00
|
|
|
// Return the closest pathgrid point index from the specified position
|
|
|
|
// coordinates. NOTE: Does not check if there is a sensible way to get there
|
2016-04-11 01:44:08 +00:00
|
|
|
// (e.g. a cliff in front).
|
|
|
|
//
|
2016-12-14 21:11:22 +00:00
|
|
|
// NOTE: pos is expected to be in local coordinates, as is grid->mPoints
|
2016-04-11 01:44:08 +00:00
|
|
|
//
|
2018-08-18 10:42:11 +00:00
|
|
|
static int getClosestPoint(const ESM::Pathgrid* grid, const osg::Vec3f& pos)
|
2016-04-11 01:44:08 +00:00
|
|
|
{
|
|
|
|
assert(grid && !grid->mPoints.empty());
|
|
|
|
|
2018-08-18 10:42:11 +00:00
|
|
|
float distanceBetween = distanceSquared(grid->mPoints[0], pos);
|
2016-04-11 01:44:08 +00:00
|
|
|
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++)
|
|
|
|
{
|
2018-08-18 10:42:11 +00:00
|
|
|
float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos);
|
2016-04-11 01:44:08 +00:00
|
|
|
if(potentialDistBetween < distanceBetween)
|
|
|
|
{
|
|
|
|
distanceBetween = potentialDistBetween;
|
|
|
|
closestIndex = counter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return closestIndex;
|
|
|
|
}
|
2015-03-08 04:42:07 +00:00
|
|
|
|
2013-05-29 22:59:23 +00:00
|
|
|
private:
|
2018-08-19 22:26:58 +00:00
|
|
|
bool mConstructed;
|
2018-08-18 10:40:04 +00:00
|
|
|
std::deque<osg::Vec3f> mPath;
|
2014-04-03 10:41:34 +00:00
|
|
|
|
2014-01-12 17:42:31 +00:00
|
|
|
const MWWorld::CellStore* mCell;
|
2018-08-22 21:20:25 +00:00
|
|
|
|
|
|
|
void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
|
|
|
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
|
|
|
|
2021-07-01 08:59:13 +00:00
|
|
|
[[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);
|
2013-03-31 17:30:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-05-29 22:59:23 +00:00
|
|
|
#endif
|