Merge pull request #927 from Salgat/bug-2981

When waiting, NPCs can go where they wouldn't go normally (Bug #2981)
coverity_scan^2
scrawl 9 years ago
commit d15901b454

@ -729,6 +729,7 @@ namespace MWMechanics
// infrequently used, therefore no benefit in caching it as a member // infrequently used, therefore no benefit in caching it as a member
const ESM::Pathgrid * const ESM::Pathgrid *
pathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell); pathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell);
const MWWorld::CellStore* cellStore = actor.getCell();
mAllowedNodes.clear(); mAllowedNodes.clear();
@ -750,13 +751,18 @@ namespace MWMechanics
osg::Vec3f npcPos(mInitialActorPosition); osg::Vec3f npcPos(mInitialActorPosition);
CoordinateConverter(cell).toLocal(npcPos); CoordinateConverter(cell).toLocal(npcPos);
// Find closest pathgrid point
int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, npcPos);
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance // mAllowedNodes for this actor with pathgrid point indexes based on mDistance
// and if the point is connected to the closest current point
// NOTE: mPoints and mAllowedNodes are in local co-ordinates // NOTE: mPoints and mAllowedNodes are in local co-ordinates
int pointIndex = 0; int pointIndex = 0;
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{ {
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))
{ {
mAllowedNodes.push_back(pathgrid->mPoints[counter]); mAllowedNodes.push_back(pathgrid->mPoints[counter]);
pointIndex = counter; pointIndex = counter;

@ -10,43 +10,6 @@
namespace namespace
{ {
// 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
//
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 co
// -ordinates. 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 co-ordinates, as is grid->mPoints
//
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;
}
// 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 MWWorld::CellStore *cell,
@ -62,7 +25,7 @@ namespace
// points to a quadtree may help // points to a quadtree may help
for(unsigned int counter = 0; counter < grid->mPoints.size(); counter++) for(unsigned int counter = 0; counter < grid->mPoints.size(); counter++)
{ {
float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos); float potentialDistBetween = MWMechanics::PathFinder::DistanceSquared(grid->mPoints[counter], pos);
if (potentialDistBetween < closestDistanceReachable) if (potentialDistBetween < closestDistanceReachable)
{ {
// found a closer one // found a closer one
@ -146,7 +109,7 @@ namespace MWMechanics
* pathgrid point (e.g. wander) then it may be worth while to call * pathgrid point (e.g. wander) then it may be worth while to call
* pop_back() to remove the redundant entry. * pop_back() to remove the redundant entry.
* *
* NOTE: co-ordinates must be converted prior to calling getClosestPoint() * NOTE: co-ordinates must be converted prior to calling GetClosestPoint()
* *
* | * |
* | cell * | cell
@ -199,16 +162,16 @@ namespace MWMechanics
return; return;
} }
// NOTE: getClosestPoint expects local co-ordinates // NOTE: GetClosestPoint expects local co-ordinates
CoordinateConverter converter(mCell->getCell()); CoordinateConverter converter(mCell->getCell());
// NOTE: It is possible that getClosestPoint returns a pathgrind point index // NOTE: It is possible that GetClosestPoint returns a pathgrind point index
// that is unreachable in some situations. e.g. actor is standing // that is unreachable in some situations. e.g. actor is standing
// outside an area enclosed by walls, but there is a pathgrid // outside an area enclosed by walls, but there is a pathgrid
// point right behind the wall that is closer than any pathgrid // point right behind the wall that is closer than any pathgrid
// point outside the wall // point outside the wall
osg::Vec3f startPointInLocalCoords(converter.toLocalVec3(startPoint)); osg::Vec3f startPointInLocalCoords(converter.toLocalVec3(startPoint));
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, cell,
@ -218,8 +181,8 @@ namespace MWMechanics
// if it's shorter for actor to travel from start to end, than to travel from either // if it's shorter for actor to travel from start to end, than to travel from either
// start or end to nearest pathgrid point, just travel from start to end. // start or end to nearest pathgrid point, just travel from start to end.
float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();
float endTolastNodeLength2 = distanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords); float endTolastNodeLength2 = DistanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords);
float startTo1stNodeLength2 = distanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords); float startTo1stNodeLength2 = DistanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords);
if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2)) if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2))
{ {
mPath.push_back(endPoint); mPath.push_back(endPoint);

@ -4,6 +4,7 @@
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
#include <list> #include <list>
#include <cassert>
namespace MWWorld namespace MWWorld
{ {
@ -90,6 +91,43 @@ namespace MWMechanics
return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ)); 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 co
// -ordinates. 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 co-ordinates, 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: private:
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, bool allowShortcuts = true); const MWWorld::CellStore* cell, bool allowShortcuts = true);

Loading…
Cancel
Save