Unverified Commit d6c67466 authored by Bret Curtis's avatar Bret Curtis Committed by GitHub

Merge pull request #1633 from elsid/pathfinder_detour

Use recastnavigation for pathfinding (#2229)
parents 854d85cb 49d81241
Pipeline #34872801 passed with stage
#!/bin/sh
#!/bin/sh -e
sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang
sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++
#!/bin/sh
#!/bin/sh -e
brew update
......
......@@ -5,6 +5,9 @@ free -m
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
GOOGLETEST_DIR="$(pwd)/googletest/build"
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_recastnavigation.sh
RECASTNAVIGATION_DIR="$(pwd)/recastnavigation/build"
mkdir build
cd build
export CODE_COVERAGE=1
......@@ -18,4 +21,5 @@ ${ANALYZE}cmake \
-DUSE_SYSTEM_TINYXML=TRUE \
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
-DRecastNavigation_ROOT="${RECASTNAVIGATION_DIR}" \
..
......@@ -304,6 +304,10 @@ if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
if [ ${BITS} -eq 64 ]; then
GENERATOR="${GENERATOR} Win64"
fi
echo
echo "==================================="
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
......@@ -449,7 +453,7 @@ fi
else
LIB_SUFFIX="0"
fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
......@@ -644,6 +648,13 @@ printf "SDL 2.0.7... "
echo Done.
}
echo
# recastnavigation
printf 'recastnavigation...'
{
env GENERATOR="${GENERATOR}" CONFIGURATION="${CONFIGURATION}" ${DEPS_INSTALL}/../../CI/build_recastnavigation.sh
add_cmake_opts -DRecastNavigation_ROOT="$(pwd)/recastnavigation/build"
}
echo
cd $DEPS_INSTALL/..
echo
echo "Setting up OpenMW build..."
......
#!/bin/sh
#!/bin/sh -e
export CXX=clang++
export CC=clang
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_recastnavigation.sh
RECASTNAVIGATION_DIR="$(pwd)/recastnavigation/build"
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH=`brew --prefix qt`
mkdir build
......@@ -17,5 +20,6 @@ cmake \
-D DESIRED_QT_VERSION=5 \
-D BUILD_ESMTOOL=FALSE \
-D BUILD_MYGUI_PLUGIN=FALSE \
-D RecastNavigation_ROOT="${RECASTNAVIGATION_DIR}" \
-G"Unix Makefiles" \
..
#!/bin/sh -e
git clone https://github.com/recastnavigation/recastnavigation.git
cd recastnavigation
mkdir build
cd build
cmake \
-DCMAKE_BUILD_TYPE="${CONFIGURATION}" \
-DRECASTNAVIGATION_DEMO=OFF \
-DRECASTNAVIGATION_TESTS=OFF \
-DRECASTNAVIGATION_EXAMPLES=OFF \
-DRECASTNAVIGATION_STATIC=ON \
-DCMAKE_INSTALL_PREFIX=. \
-G "${GENERATOR}" \
..
cmake --build . --config "${CONFIGURATION}"
cmake --build . --target install --config "${CONFIGURATION}"
......@@ -21,7 +21,7 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager
renderbin actoranimation landmanager navmesh actorspaths
)
add_openmw_dir (mwinput
......@@ -70,7 +70,7 @@ add_openmw_dir (mwworld
)
add_openmw_dir (mwphysics
physicssystem trace collisiontype actor convert
physicssystem trace collisiontype actor convert object heightfield
)
add_openmw_dir (mwclass
......@@ -117,6 +117,10 @@ include_directories(
${FFmpeg_INCLUDE_DIRS}
)
find_package(RecastNavigation COMPONENTS DebugUtils Detour Recast REQUIRED)
include_directories(SYSTEM ${RecastNavigation_INCLUDE_DIRS})
target_link_libraries(openmw
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
......@@ -133,6 +137,7 @@ target_link_libraries(openmw
${FFmpeg_LIBRARIES}
${MyGUI_LIBRARIES}
${SDL2_LIBRARY}
${RecastNavigation_LIBRARIES}
"osg-ffmpeg-videoplayer"
"oics"
components
......
......@@ -4,6 +4,7 @@
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <components/esm/cellid.hpp>
......@@ -54,6 +55,11 @@ namespace MWMechanics
struct Movement;
}
namespace DetourNavigator
{
class Navigator;
}
namespace MWWorld
{
class CellStore;
......@@ -595,6 +601,13 @@ namespace MWBase
/// Preload VFX associated with this effect list
virtual void preloadEffects(const ESM::EffectList* effectList) = 0;
virtual DetourNavigator::Navigator* getNavigator() const = 0;
virtual void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const = 0;
virtual void setNavMeshNumberToRender(const std::size_t value) = 0;
};
}
......
......@@ -36,7 +36,7 @@ namespace MWMechanics
return true; //Target doesn't exist
//Set the target destination for the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
const auto dest = target.getRefData().getPosition().asVec3();
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
{
......
......@@ -37,7 +37,7 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
if (!target)
return true;
if (!mManual && !pathTo(actor, target.getRefData().getPosition().pos, duration, mDistance))
if (!mManual && !pathTo(actor, target.getRefData().getPosition().asVec3(), duration, mDistance))
{
return false;
}
......
......@@ -128,7 +128,7 @@ namespace MWMechanics
float targetReachedTolerance = 0.0f;
if (storage.mLOS)
targetReachedTolerance = storage.mAttackRange;
bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, targetReachedTolerance);
const bool is_target_reached = pathTo(actor, target.getRefData().getPosition().asVec3(), duration, targetReachedTolerance);
if (is_target_reached) storage.mReadyToAttack = true;
}
......@@ -307,7 +307,7 @@ namespace MWMechanics
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
coords.toLocal(localPos);
int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos);
int closestPointIndex = PathFinder::getClosestPoint(pathgrid, localPos);
for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++)
{
if (i != closestPointIndex && getPathGridGraph(storage.mCell).isPointConnected(closestPointIndex, i))
......@@ -359,7 +359,7 @@ namespace MWMechanics
float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length();
if ((dist > fFleeDistance && !storage.mLOS)
|| pathTo(actor, storage.mFleeDest, duration))
|| pathTo(actor, PathFinder::makeOsgVec3(storage.mFleeDest), duration))
{
state = AiCombatStorage::FleeState_Idle;
}
......
......@@ -100,7 +100,7 @@ namespace MWMechanics
point.mAutogenerated = 0;
point.mConnectionNum = 0;
point.mUnknown = 0;
if (pathTo(actor,point,duration)) //Returns true on path complete
if (pathTo(actor, osg::Vec3f(mX, mY, mZ), duration)) //Returns true on path complete
{
mRemainingDuration = mDuration;
return true;
......
......@@ -163,7 +163,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
}
//Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
const auto dest = target.getRefData().getPosition().asVec3();
short baseFollowDistance = followDistance;
short threshold = 30; // to avoid constant switching between moving/stopping
......@@ -196,7 +196,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
if (storage.mMoving)
{
//Check if you're far away
float dist = distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]);
float dist = distance(dest, pos.asVec3());
if (dist > 450)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
......
This diff is collapsed.
......@@ -7,6 +7,8 @@
#include "obstacle.hpp"
#include "aistate.hpp"
#include <boost/optional.hpp>
namespace MWWorld
{
class Ptr;
......@@ -105,29 +107,35 @@ namespace MWMechanics
bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
/// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing.
static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest);
static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest);
protected:
/// Handles path building and shortcutting with obstacles avoiding
/** \return If the actor has arrived at his destination **/
bool pathTo(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest, float duration, float destTolerance = 0.0f);
bool pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance = 0.0f);
/// Check if there aren't any obstacles along the path to make shortcut possible
/// If a shortcut is possible then path will be cleared and filled with the destination point.
/// \param destInLOS If not nullptr function will return ray cast check result
/// \return If can shortcut the path
bool shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS, bool isPathClear);
bool shortcutPath(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor,
bool *destInLOS, bool isPathClear);
/// Check if the way to the destination is clear, taking into account actor speed
bool checkWayIsClearForActor(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor);
bool checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor);
bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell);
virtual bool doesPathNeedRecalc(const ESM::Pathgrid::Point& newDest, const MWWorld::CellStore* currentCell);
void evadeObstacles(const MWWorld::Ptr& actor);
void evadeObstacles(const MWWorld::Ptr& actor, float duration, const ESM::Position& pos);
void openDoors(const MWWorld::Ptr& actor);
const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell);
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;
bool canActorMoveByZAxis(const MWWorld::Ptr& actor) const;
// TODO: all this does not belong here, move into temporary storage
PathFinder mPathFinder;
ObstacleCheck mObstacleCheck;
......@@ -137,16 +145,14 @@ namespace MWMechanics
std::string mTargetActorRefId;
mutable int mTargetActorId;
osg::Vec3f mLastActorPos;
short mRotateOnTheRunChecks; // attempts to check rotation to the pathpoint on the run possibility
bool mIsShortcutting; // if shortcutting at the moment
bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt
ESM::Pathgrid::Point mShortcutFailPos; // position of last shortcut fail
osg::Vec3f mShortcutFailPos; // position of last shortcut fail
private:
bool isNearInactiveCell(const ESM::Position& actorPos);
bool isNearInactiveCell(osg::Vec3f position);
};
}
......
......@@ -49,13 +49,13 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
//Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
const auto dest = target.getRefData().getPosition().asVec3();
ESM::Position aPos = actor.getRefData().getPosition();
float pathTolerance = 100.0;
if (pathTo(actor, dest, duration, pathTolerance) &&
std::abs(dest.mZ - aPos.pos[2]) < pathTolerance) // check the true distance in case the target is far away in Z-direction
std::abs(dest.z() - aPos.pos[2]) < pathTolerance) // check the true distance in case the target is far away in Z-direction
{
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached
return true;
......
......@@ -58,8 +58,7 @@ namespace MWMechanics
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
// If we got close to target, check for actors nearby. If they are, finish AI package.
int destinationTolerance = 64;
ESM::Pathgrid::Point dest(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ));
if (distance(pos.pos, dest) <= destinationTolerance)
if (distance(pos.asVec3(), osg::Vec3f(mX, mY, mZ)) <= destinationTolerance)
{
std::vector<MWWorld::Ptr> targetActors;
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
......@@ -71,7 +70,7 @@ namespace MWMechanics
}
}
if (pathTo(actor, dest, duration))
if (pathTo(actor, osg::Vec3f(mX, mY, mZ), duration))
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
return true;
......
This diff is collapsed.
......@@ -137,15 +137,15 @@ namespace MWMechanics
short unsigned getRandomIdle();
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos);
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage);
void playIdleDialogueRandomly(const MWWorld::Ptr& actor);
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos);
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos);
void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage);
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos, float duration);
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos);
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
......@@ -164,6 +164,7 @@ namespace MWMechanics
bool mHasDestination;
osg::Vec3f mDestination;
bool mUsePathgrid;
void getNeighbouringNodes(ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points);
......
......@@ -33,11 +33,12 @@ namespace MWMechanics
point.y() -= static_cast<float>(mCellY);
}
osg::Vec3f CoordinateConverter::toLocalVec3(const ESM::Pathgrid::Point& point)
osg::Vec3f CoordinateConverter::toLocalVec3(const osg::Vec3f& point)
{
return osg::Vec3f(
static_cast<float>(point.mX - mCellX),
static_cast<float>(point.mY - mCellY),
static_cast<float>(point.mZ));
point.x() - static_cast<float>(mCellX),
point.y() - static_cast<float>(mCellY),
point.z()
);
}
}
......@@ -26,7 +26,7 @@ namespace MWMechanics
/// in-place conversion from world to local
void toLocal(osg::Vec3f& point);
osg::Vec3f toLocalVec3(const ESM::Pathgrid::Point& point);
osg::Vec3f toLocalVec3(const osg::Vec3f& point);
private:
int mCellX;
......
......@@ -96,11 +96,6 @@ namespace MWMechanics
mEvadeDuration = 0;
}
bool ObstacleCheck::isNormalState() const
{
return mWalkState == State_Norm;
}
bool ObstacleCheck::isEvading() const
{
return mWalkState == State_Evade;
......@@ -128,7 +123,7 @@ namespace MWMechanics
* u = how long to move sideways
*
*/
bool ObstacleCheck::check(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance)
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance)
{
const MWWorld::Class& cls = actor.getClass();
ESM::Position pos = actor.getRefData().getPosition();
......@@ -180,9 +175,7 @@ namespace MWMechanics
case State_Evade:
{
mEvadeDuration += duration;
if(mEvadeDuration < DURATION_TO_EVADE)
return true;
else
if(mEvadeDuration >= DURATION_TO_EVADE)
{
// tried to evade, assume all is ok and start again
mWalkState = State_Norm;
......@@ -191,10 +184,9 @@ namespace MWMechanics
}
/* NO DEFAULT CASE */
}
return false; // no obstacles to evade (yet)
}
void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement)
void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement) const
{
actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0];
actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1];
......
......@@ -27,15 +27,13 @@ namespace MWMechanics
// Clear the timers and set the state machine to default
void clear();
bool isNormalState() const;
bool isEvading() const;
// Returns true if there is an obstacle and an evasive action
// should be taken
bool check(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance = 1.0f);
// Updates internal state, call each frame for moving actor
void update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance = 1.0f);
// change direction to try to fix "stuck" actor
void takeEvasiveAction(MWMechanics::Movement& actorMovement);
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
private:
......
This diff is collapsed.
#ifndef GAME_MWMECHANICS_PATHFINDING_H
#define GAME_MWMECHANICS_PATHFINDING_H
#include <list>
#include <deque>
#include <cassert>
#include <iterator>
#include <components/detournavigator/flags.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/loadpgrd.hpp>
namespace MWWorld
{
class CellStore;
class ConstPtr;
}
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);
float getXAngleToDir(const osg::Vec3f& dir);
float getZAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest);
float getXAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest);
inline float distance(const osg::Vec3f& lhs, const osg::Vec3f& rhs)
{
return (lhs - rhs).length();
}
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;
const float PATHFIND_Z_REACH = 50.0f;
//static const float sMaxSlope = 49.0f; // duplicate as in physicssystem
// distance after which actor (failed previously to shortcut) will try again
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.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);
......@@ -35,31 +59,33 @@ namespace MWMechanics
class PathFinder
{
public:
PathFinder();
static const int PathTolerance = 32;
static float sgn(float val)
PathFinder()
: mConstructed(false)
, mCell(nullptr)
{
if(val > 0)
return 1.0;
return -1.0;
}
static int sgn(int a)
void clearPath()
{
if(a > 0)
return 1;
return -1;
mConstructed = false;
mPath.clear();
mCell = nullptr;
}
void clearPath();
void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
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);
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
/// Remove front point if exist and within tolerance
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
bool checkPathCompleted(float x, float y, float tolerance = PathTolerance);
///< \Returns true if we are within \a tolerance units of the last path point.
bool checkPathCompleted() const
{
return mConstructed && mPath.empty();
}
/// In radians
float getZAngleToNext(float x, float y) const;
......@@ -68,60 +94,54 @@ namespace MWMechanics
bool isPathConstructed() const
{
return !mPath.empty();
return mConstructed && !mPath.empty();
}
int getPathSize() const
std::size_t getPathSize() const
{
return mPath.size();
}
const std::list<ESM::Pathgrid::Point>& getPath() const
const std::deque<osg::Vec3f>& getPath() const
{
return mPath;
}
const MWWorld::CellStore* getPathCell() const;
/** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
@note
BuildPath() takes closest PathGrid point to NPC as first point of path.
This is undesirable if NPC has just passed a Pathgrid point, as this
makes the 2nd point of the new path == the 1st point of old path.
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 PathgridGraph& pathgridGraph);
const MWWorld::CellStore* getPathCell() const
{
return mCell;
}
void addPointToPath(const ESM::Pathgrid::Point &point)
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)
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)
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]));