From 96231e17f098e4a56c0a5015ad1e027ab674604a Mon Sep 17 00:00:00 2001 From: Austin Salgat Date: Sun, 10 Apr 2016 20:21:18 -0500 Subject: [PATCH 1/2] Fix wandering NPCs going off pathgrid If multiple pathgrids exist in the same cell, sometimes an NPC would port to a different pathgrid that was nearby. This is an issue since it allowed them to do things like travel through walls to inaccessible areas. Now they will only wander along the closest connected pathgrid. --- apps/openmw/mwmechanics/aiwander.cpp | 35 +++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 27c7f6a93..ad4e596c1 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -39,6 +39,33 @@ namespace MWMechanics // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; + float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos) + { + return (MWMechanics::PathFinder::MakeOsgVec3(point) - pos).length2(); + } + + 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; + } + const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), @@ -729,6 +756,7 @@ namespace MWMechanics // infrequently used, therefore no benefit in caching it as a member const ESM::Pathgrid * pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); + const MWWorld::CellStore* cellStore = actor.getCell(); mAllowedNodes.clear(); @@ -749,14 +777,19 @@ namespace MWMechanics // get NPC's position in local (i.e. cell) co-ordinates osg::Vec3f npcPos(mInitialActorPosition); CoordinateConverter(cell).toLocal(npcPos); + + // Find closest pathgrid point + int closestPointIndex = getClosestPoint(pathgrid, npcPos); // 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 int pointIndex = 0; for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); 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]); pointIndex = counter; From 38f5a225eaf9e893bbb581d4e9f7ddcab337302a Mon Sep 17 00:00:00 2001 From: Austin Salgat Date: Sun, 10 Apr 2016 20:44:08 -0500 Subject: [PATCH 2/2] Refactor PathFinder functions to be useable by AiWander This avoids having to duplicate the distanceSquared and getClosestPoint functions. --- apps/openmw/mwmechanics/aiwander.cpp | 29 +------------- apps/openmw/mwmechanics/pathfinding.cpp | 51 ++++--------------------- apps/openmw/mwmechanics/pathfinding.hpp | 38 ++++++++++++++++++ 3 files changed, 46 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index ad4e596c1..f0295f331 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -39,33 +39,6 @@ namespace MWMechanics // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; - float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos) - { - return (MWMechanics::PathFinder::MakeOsgVec3(point) - pos).length2(); - } - - 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; - } - const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), @@ -779,7 +752,7 @@ namespace MWMechanics CoordinateConverter(cell).toLocal(npcPos); // Find closest pathgrid point - int closestPointIndex = getClosestPoint(pathgrid, npcPos); + int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, npcPos); // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // and if the point is connected to the closest current point diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index dbe20fdc0..c8ba86703 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -10,43 +10,6 @@ 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. std::pair getClosestReachablePoint(const ESM::Pathgrid* grid, const MWWorld::CellStore *cell, @@ -62,7 +25,7 @@ namespace // points to a quadtree may help 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) { // found a closer one @@ -146,7 +109,7 @@ namespace MWMechanics * pathgrid point (e.g. wander) then it may be worth while to call * 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 @@ -199,16 +162,16 @@ namespace MWMechanics return; } - // NOTE: getClosestPoint expects local co-ordinates + // NOTE: GetClosestPoint expects local co-ordinates 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 // outside an area enclosed by walls, but there is a pathgrid // point right behind the wall that is closer than any pathgrid // point outside the wall osg::Vec3f startPointInLocalCoords(converter.toLocalVec3(startPoint)); - int startNode = getClosestPoint(mPathgrid, startPointInLocalCoords); + int startNode = GetClosestPoint(mPathgrid, startPointInLocalCoords); osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint)); std::pair 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 // start or end to nearest pathgrid point, just travel from start to end. float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); - float endTolastNodeLength2 = distanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords); - float startTo1stNodeLength2 = distanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords); + float endTolastNodeLength2 = DistanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords); + float startTo1stNodeLength2 = DistanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords); if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2)) { mPath.push_back(endPoint); diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 1765a5cc5..7262842dc 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace MWWorld { @@ -89,6 +90,43 @@ namespace MWMechanics { return osg::Vec3f(static_cast(p.mX), static_cast(p.mY), static_cast(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: void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,