1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 04:09:48 +00:00

Merge branch 'navmesh_agent_bounds' into 'master'

Support different agent collision shape type for pathfinding

See merge request OpenMW/openmw!2030
This commit is contained in:
psi29a 2022-06-21 16:13:41 +00:00
commit 1a478875f0
51 changed files with 515 additions and 389 deletions

View file

@ -13,7 +13,7 @@ namespace
struct Key struct Key
{ {
osg::Vec3f mAgentHalfExtents; AgentBounds mAgentBounds;
TilePosition mTilePosition; TilePosition mTilePosition;
RecastMesh mRecastMesh; RecastMesh mRecastMesh;
}; };
@ -137,6 +137,7 @@ namespace
template <class Random> template <class Random>
Key generateKey(std::size_t triangles, Random& random) Key generateKey(std::size_t triangles, Random& random)
{ {
const CollisionShapeType agentShapeType = CollisionShapeType::Aabb;
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random); const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
const TilePosition tilePosition = generateVec2i(10000, random); const TilePosition tilePosition = generateVec2i(10000, random);
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random); const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
@ -146,7 +147,7 @@ namespace
generateWater(std::back_inserter(water), 1, random); generateWater(std::back_inserter(water), 1, random);
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water), RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
{generateHeightfield(random)}, {generateFlatHeightfield(random)}, {}); {generateHeightfield(random)}, {generateFlatHeightfield(random)}, {});
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)}; return Key {AgentBounds {agentShapeType, agentHalfExtents}, tilePosition, std::move(recastMesh)};
} }
constexpr std::size_t trianglesPerTile = 239; constexpr std::size_t trianglesPerTile = 239;
@ -165,7 +166,7 @@ namespace
while (true) while (true)
{ {
Key key = generateKey(trianglesPerTile, random); Key key = generateKey(trianglesPerTile, random);
cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
std::make_unique<PreparedNavMeshData>()); std::make_unique<PreparedNavMeshData>());
*out++ = std::move(key); *out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize; const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
@ -188,7 +189,7 @@ namespace
while (state.KeepRunning()) while (state.KeepRunning())
{ {
const auto& key = keys[n++ % keys.size()]; const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh); const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
benchmark::DoNotOptimize(result); benchmark::DoNotOptimize(result);
} }
} }
@ -216,7 +217,7 @@ namespace
while (state.KeepRunning()) while (state.KeepRunning())
{ {
const auto& key = keys[n++ % keys.size()]; const auto& key = keys[n++ % keys.size()];
const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, const auto result = cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
std::make_unique<PreparedNavMeshData>()); std::make_unique<PreparedNavMeshData>());
benchmark::DoNotOptimize(result); benchmark::DoNotOptimize(result);
} }

View file

@ -22,6 +22,7 @@
#include <components/vfs/registerarchives.hpp> #include <components/vfs/registerarchives.hpp>
#include <components/esm3/readerscache.hpp> #include <components/esm3/readerscache.hpp>
#include <components/platform/platform.hpp> #include <components/platform/platform.hpp>
#include <components/detournavigator/agentbounds.hpp>
#include <osg/Vec3f> #include <osg/Vec3f>
@ -173,7 +174,9 @@ namespace NavMeshTool
Settings::Manager settings; Settings::Manager settings;
settings.load(config); settings.load(config);
const DetourNavigator::CollisionShapeType agentCollisionShape = DetourNavigator::defaultCollisionShapeType;
const osg::Vec3f agentHalfExtents = Settings::Manager::getVector3("default actor pathfind half extents", "Game"); const osg::Vec3f agentHalfExtents = Settings::Manager::getVector3("default actor pathfind half extents", "Game");
const DetourNavigator::AgentBounds agentBounds {agentCollisionShape, agentHalfExtents};
const std::uint64_t maxDbFileSize = static_cast<std::uint64_t>(Settings::Manager::getInt64("max navmeshdb file size", "Navigator")); const std::uint64_t maxDbFileSize = static_cast<std::uint64_t>(Settings::Manager::getInt64("max navmeshdb file size", "Navigator"));
const std::string dbPath = (config.getUserDataPath() / "navmesh.db").string(); const std::string dbPath = (config.getUserDataPath() / "navmesh.db").string();
@ -201,7 +204,7 @@ namespace NavMeshTool
WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager, WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager,
esmData, processInteriorCells, writeBinaryLog); esmData, processInteriorCells, writeBinaryLog);
const Status status = generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, const Status status = generateAllNavMeshTiles(agentBounds, navigatorSettings, threadsNumber,
removeUnusedTiles, writeBinaryLog, cellsData, std::move(db)); removeUnusedTiles, writeBinaryLog, cellsData, std::move(db));
switch (status) switch (status)

View file

@ -32,6 +32,7 @@ namespace NavMeshTool
{ {
namespace namespace
{ {
using DetourNavigator::AgentBounds;
using DetourNavigator::GenerateNavMeshTile; using DetourNavigator::GenerateNavMeshTile;
using DetourNavigator::NavMeshDb; using DetourNavigator::NavMeshDb;
using DetourNavigator::NavMeshTileInfo; using DetourNavigator::NavMeshTileInfo;
@ -250,7 +251,7 @@ namespace NavMeshTool
}; };
} }
Status generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings, Status generateAllNavMeshTiles(const AgentBounds& agentBounds, const Settings& settings,
std::size_t threadsNumber, bool removeUnusedTiles, bool writeBinaryLog, WorldspaceData& data, std::size_t threadsNumber, bool removeUnusedTiles, bool writeBinaryLog, WorldspaceData& data,
NavMeshDb&& db) NavMeshDb&& db)
{ {
@ -291,7 +292,7 @@ namespace NavMeshTool
input->mWorldspace, input->mWorldspace,
tilePosition, tilePosition,
RecastMeshProvider(input->mTileCachedRecastMeshManager), RecastMeshProvider(input->mTileCachedRecastMeshManager),
agentHalfExtents, agentBounds,
settings, settings,
navMeshTileConsumer navMeshTileConsumer
)); ));

View file

@ -9,6 +9,7 @@ namespace DetourNavigator
{ {
class NavMeshDb; class NavMeshDb;
struct Settings; struct Settings;
struct AgentBounds;
} }
namespace NavMeshTool namespace NavMeshTool
@ -22,7 +23,7 @@ namespace NavMeshTool
NotEnoughSpace, NotEnoughSpace,
}; };
Status generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const DetourNavigator::Settings& settings, Status generateAllNavMeshTiles(const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Settings& settings,
std::size_t threadsNumber, bool removeUnusedTiles, bool writeBinaryLog, WorldspaceData& cellsData, std::size_t threadsNumber, bool removeUnusedTiles, bool writeBinaryLog, WorldspaceData& cellsData,
DetourNavigator::NavMeshDb&& db); DetourNavigator::NavMeshDb&& db);
} }

View file

@ -79,6 +79,7 @@ namespace MWMechanics
namespace DetourNavigator namespace DetourNavigator
{ {
struct Navigator; struct Navigator;
struct AgentBounds;
} }
namespace MWWorld namespace MWWorld
@ -644,14 +645,13 @@ namespace MWBase
virtual DetourNavigator::Navigator* getNavigator() const = 0; virtual DetourNavigator::Navigator* getNavigator() const = 0;
virtual void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, 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; const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const = 0;
virtual void removeActorPath(const MWWorld::ConstPtr& actor) const = 0; virtual void removeActorPath(const MWWorld::ConstPtr& actor) const = 0;
virtual void setNavMeshNumberToRender(const std::size_t value) = 0; virtual void setNavMeshNumberToRender(const std::size_t value) = 0;
/// Return physical half extents of the given actor to be used in pathfinding virtual DetourNavigator::AgentBounds getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const = 0;
virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const = 0;
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;

View file

@ -268,11 +268,11 @@ namespace MWMechanics
{ {
const MWBase::World* world = MWBase::Environment::get().getWorld(); const MWBase::World* world = MWBase::Environment::get().getWorld();
// Try to build path to the target. // Try to build path to the target.
const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto agentBounds = world->getPathfindingAgentBounds(actor);
const auto navigatorFlags = getNavigatorFlags(actor); const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor); const auto areaCosts = getAreaCosts(actor);
const auto pathGridGraph = getPathGridGraph(actor.getCell()); const auto pathGridGraph = getPathGridGraph(actor.getCell());
mPathFinder.buildPath(actor, vActorPos, vTargetPos, actor.getCell(), pathGridGraph, halfExtents, mPathFinder.buildPath(actor, vActorPos, vTargetPos, actor.getCell(), pathGridGraph, agentBounds,
navigatorFlags, areaCosts, storage.mAttackRange, PathType::Full); navigatorFlags, areaCosts, storage.mAttackRange, PathType::Full);
if (!mPathFinder.isPathConstructed()) if (!mPathFinder.isPathConstructed())
@ -280,12 +280,12 @@ namespace MWMechanics
// If there is no path, try to find a point on a line from the actor position to target projected // If there is no path, try to find a point on a line from the actor position to target projected
// on navmesh to attack the target from there. // on navmesh to attack the target from there.
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto hit = DetourNavigator::raycast(*navigator, halfExtents, vActorPos, vTargetPos, navigatorFlags); const auto hit = DetourNavigator::raycast(*navigator, agentBounds, vActorPos, vTargetPos, navigatorFlags);
if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack) if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack)
{ {
// If the point is close enough, try to find a path to that point. // If the point is close enough, try to find a path to that point.
mPathFinder.buildPath(actor, vActorPos, *hit, actor.getCell(), pathGridGraph, halfExtents, mPathFinder.buildPath(actor, vActorPos, *hit, actor.getCell(), pathGridGraph, agentBounds,
navigatorFlags, areaCosts, storage.mAttackRange, PathType::Full); navigatorFlags, areaCosts, storage.mAttackRange, PathType::Full);
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {

View file

@ -113,6 +113,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
const DetourNavigator::AgentBounds agentBounds = world->getPathfindingAgentBounds(actor);
/// Stops the actor when it gets too close to a unloaded cell /// Stops the actor when it gets too close to a unloaded cell
//... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range" setting value //... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range" setting value
@ -122,7 +123,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
{ {
actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
world->updateActorPath(actor, mPathFinder.getPath(), world->getPathfindingHalfExtents(actor), position, dest); world->updateActorPath(actor, mPathFinder.getPath(), agentBounds, position, dest);
return false; return false;
} }
@ -148,9 +149,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
{ {
if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path
{ {
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
mPathFinder.buildLimitedPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), mPathFinder.buildLimitedPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor), endTolerance, pathType); agentBounds, getNavigatorFlags(actor), getAreaCosts(actor), endTolerance, pathType);
mRotateOnTheRunChecks = 3; mRotateOnTheRunChecks = 3;
// give priority to go directly on target if there is minimal opportunity // give priority to go directly on target if there is minimal opportunity
@ -178,13 +178,13 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
} }
} }
const osg::Vec3f halfExtents = world->getHalfExtents(actor); const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration,
const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration, halfExtents); world->getHalfExtents(actor));
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE, mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE,
/*shortenIfAlmostStraight=*/smoothMovement, actorCanMoveByZ, /*shortenIfAlmostStraight=*/smoothMovement, actorCanMoveByZ,
halfExtents, getNavigatorFlags(actor)); agentBounds, getNavigatorFlags(actor));
if (isDestReached || mPathFinder.checkPathCompleted()) // if path is finished if (isDestReached || mPathFinder.checkPathCompleted()) // if path is finished
{ {
@ -197,7 +197,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
else if (mPathFinder.getPath().empty()) else if (mPathFinder.getPath().empty())
return false; return false;
world->updateActorPath(actor, mPathFinder.getPath(), world->getPathfindingHalfExtents(actor), position, dest); world->updateActorPath(actor, mPathFinder.getPath(), agentBounds, position, dest);
if (mRotateOnTheRunChecks == 0 if (mRotateOnTheRunChecks == 0
|| isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point || isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point

View file

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include <components/esm3/aisequence.hpp> #include <components/esm3/aisequence.hpp>
#include <components/detournavigator/agentbounds.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"

View file

@ -71,7 +71,7 @@ namespace MWMechanics
const auto position = actor.getRefData().getPosition().asVec3(); const auto position = actor.getRefData().getPosition().asVec3();
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor); const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(actor).mHalfExtents;
osg::Vec3f direction = destination - position; osg::Vec3f direction = destination - position;
direction.normalize(); direction.normalize();
const auto visibleDestination = ( const auto visibleDestination = (
@ -210,10 +210,10 @@ namespace MWMechanics
} }
else else
{ {
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); const auto agentBounds = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(actor);
constexpr float endTolerance = 0; constexpr float endTolerance = 0;
mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),
getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor), getPathGridGraph(actor.getCell()), agentBounds, getNavigatorFlags(actor), getAreaCosts(actor),
endTolerance, PathType::Full); endTolerance, PathType::Full);
} }
@ -345,7 +345,7 @@ namespace MWMechanics
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor); const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto agentBounds = world->getPathfindingAgentBounds(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto navigatorFlags = getNavigatorFlags(actor); const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor); const auto areaCosts = getAreaCosts(actor);
@ -358,7 +358,7 @@ namespace MWMechanics
if (!isWaterCreature && !isFlyingCreature) if (!isWaterCreature && !isFlyingCreature)
{ {
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance // findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
if (const auto destination = DetourNavigator::findRandomPointAroundCircle(*navigator, halfExtents, if (const auto destination = DetourNavigator::findRandomPointAroundCircle(*navigator, agentBounds,
mInitialActorPosition, wanderDistance, navigatorFlags, []() { mInitialActorPosition, wanderDistance, navigatorFlags, []() {
auto& prng = MWBase::Environment::get().getWorld()->getPrng(); auto& prng = MWBase::Environment::get().getWorld()->getPrng();
return Misc::Rng::rollProbability(prng); return Misc::Rng::rollProbability(prng);
@ -385,7 +385,7 @@ namespace MWMechanics
if (isWaterCreature || isFlyingCreature) if (isWaterCreature || isFlyingCreature)
mPathFinder.buildStraightPath(mDestination); mPathFinder.buildStraightPath(mDestination);
else else
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags, mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, agentBounds, navigatorFlags,
areaCosts, endTolerance, PathType::Full); areaCosts, endTolerance, PathType::Full);
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
@ -532,8 +532,8 @@ namespace MWMechanics
{ {
if (mUsePathgrid) if (mUsePathgrid)
{ {
const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); const auto agentBounds = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(actor);
mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), mPathFinder.buildPathByNavMeshToNextPoint(actor, agentBounds, getNavigatorFlags(actor),
getAreaCosts(actor)); getAreaCosts(actor));
} }

View file

@ -3,6 +3,7 @@
#include <array> #include <array>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/detournavigator/agentbounds.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
@ -80,7 +81,7 @@ namespace MWMechanics
std::vector<MWWorld::Ptr>* occupyingActors) std::vector<MWWorld::Ptr>* occupyingActors)
{ {
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor); const osg::Vec3f halfExtents = world->getPathfindingAgentBounds(actor).mHalfExtents;
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
if (ignorePlayer) if (ignorePlayer)
{ {

View file

@ -109,12 +109,12 @@ namespace
struct IsValidShortcut struct IsValidShortcut
{ {
const DetourNavigator::Navigator* mNavigator; const DetourNavigator::Navigator* mNavigator;
const osg::Vec3f mHalfExtents; const DetourNavigator::AgentBounds mAgentBounds;
const DetourNavigator::Flags mFlags; const DetourNavigator::Flags mFlags;
bool operator()(const osg::Vec3f& start, const osg::Vec3f& end) const bool operator()(const osg::Vec3f& start, const osg::Vec3f& end) const
{ {
const auto position = DetourNavigator::raycast(*mNavigator, mHalfExtents, start, end, mFlags); const auto position = DetourNavigator::raycast(*mNavigator, mAgentBounds, start, end, mFlags);
return position.has_value() && std::abs((position.value() - start).length2() - (end - start).length2()) <= 1; return position.has_value() && std::abs((position.value() - start).length2() - (end - start).length2()) <= 1;
} }
}; };
@ -307,8 +307,8 @@ namespace MWMechanics
} }
void PathFinder::update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance, void PathFinder::update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents, bool shortenIfAlmostStraight, bool canMoveByZ, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags) const DetourNavigator::Flags flags)
{ {
if (mPath.empty()) if (mPath.empty())
return; return;
@ -318,7 +318,7 @@ namespace MWMechanics
const IsValidShortcut isValidShortcut { const IsValidShortcut isValidShortcut {
MWBase::Environment::get().getWorld()->getNavigator(), MWBase::Environment::get().getWorld()->getNavigator(),
halfExtents, flags agentBounds, flags
}; };
if (shortenIfAlmostStraight) if (shortenIfAlmostStraight)
@ -375,13 +375,13 @@ namespace MWMechanics
} }
void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType) const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType)
{ {
mPath.clear(); mPath.clear();
// If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path
DetourNavigator::Status status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, DetourNavigator::Status status = buildPathByNavigatorImpl(actor, startPoint, endPoint, agentBounds, flags,
areaCosts, endTolerance, pathType, std::back_inserter(mPath)); areaCosts, endTolerance, pathType, std::back_inserter(mPath));
if (status != DetourNavigator::Status::Success) if (status != DetourNavigator::Status::Success)
@ -394,7 +394,7 @@ namespace MWMechanics
} }
void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void PathFinder::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 MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance,
PathType pathType) PathType pathType)
{ {
@ -405,7 +405,7 @@ namespace MWMechanics
if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor))
{ {
status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, status = buildPathByNavigatorImpl(actor, startPoint, endPoint, agentBounds, flags, areaCosts,
endTolerance, pathType, std::back_inserter(mPath)); endTolerance, pathType, std::back_inserter(mPath));
if (status != DetourNavigator::Status::Success) if (status != DetourNavigator::Status::Success)
mPath.clear(); mPath.clear();
@ -413,7 +413,7 @@ namespace MWMechanics
if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty() && (flags & DetourNavigator::Flag_usePathgrid) == 0) if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty() && (flags & DetourNavigator::Flag_usePathgrid) == 0)
{ {
status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, status = buildPathByNavigatorImpl(actor, startPoint, endPoint, agentBounds,
flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath)); flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath));
if (status != DetourNavigator::Status::Success) if (status != DetourNavigator::Status::Success)
mPath.clear(); mPath.clear();
@ -429,14 +429,14 @@ namespace MWMechanics
} }
DetourNavigator::Status PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, DetourNavigator::Status PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType,
std::back_insert_iterator<std::deque<osg::Vec3f>> out) std::back_insert_iterator<std::deque<osg::Vec3f>> out)
{ {
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor); const auto stepSize = getPathStepSize(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto status = DetourNavigator::findPath(*navigator, halfExtents, stepSize, const auto status = DetourNavigator::findPath(*navigator, agentBounds, stepSize,
startPoint, endPoint, flags, areaCosts, endTolerance, out); startPoint, endPoint, flags, areaCosts, endTolerance, out);
if (pathType == PathType::Partial && status == DetourNavigator::Status::PartialPath) if (pathType == PathType::Partial && status == DetourNavigator::Status::PartialPath)
@ -453,8 +453,9 @@ namespace MWMechanics
return status; return status;
} }
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts)
{ {
if (mPath.empty()) if (mPath.empty())
return; return;
@ -469,7 +470,7 @@ namespace MWMechanics
std::deque<osg::Vec3f> prePath; std::deque<osg::Vec3f> prePath;
auto prePathInserter = std::back_inserter(prePath); auto prePathInserter = std::back_inserter(prePath);
const float endTolerance = 0; const float endTolerance = 0;
const auto status = DetourNavigator::findPath(*navigator, halfExtents, stepSize, const auto status = DetourNavigator::findPath(*navigator, agentBounds, stepSize,
startPoint, mPath.front(), flags, areaCosts, endTolerance, prePathInserter); startPoint, mPath.front(), flags, areaCosts, endTolerance, prePathInserter);
if (status == DetourNavigator::Status::NavMeshNotFound) if (status == DetourNavigator::Status::NavMeshNotFound)
@ -494,9 +495,9 @@ namespace MWMechanics
} }
void PathFinder::buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void PathFinder::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 MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
PathType pathType) const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType)
{ {
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
const auto maxDistance = std::min( const auto maxDistance = std::min(
@ -506,9 +507,9 @@ namespace MWMechanics
const auto startToEnd = endPoint - startPoint; const auto startToEnd = endPoint - startPoint;
const auto distance = startToEnd.length(); const auto distance = startToEnd.length();
if (distance <= maxDistance) if (distance <= maxDistance)
return buildPath(actor, startPoint, endPoint, cell, pathgridGraph, halfExtents, flags, areaCosts, return buildPath(actor, startPoint, endPoint, cell, pathgridGraph, agentBounds, flags, areaCosts,
endTolerance, pathType); endTolerance, pathType);
const auto end = startPoint + startToEnd * maxDistance / distance; const auto end = startPoint + startToEnd * maxDistance / distance;
buildPath(actor, startPoint, end, cell, pathgridGraph, halfExtents, flags, areaCosts, endTolerance, pathType); buildPath(actor, startPoint, end, cell, pathgridGraph, agentBounds, flags, areaCosts, endTolerance, pathType);
} }
} }

View file

@ -18,6 +18,11 @@ namespace MWWorld
class Ptr; class Ptr;
} }
namespace DetourNavigator
{
struct AgentBounds;
}
namespace MWMechanics namespace MWMechanics
{ {
class PathgridGraph; class PathgridGraph;
@ -98,25 +103,26 @@ namespace MWMechanics
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType);
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, float endTolerance, const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance,
PathType pathType); PathType pathType);
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph,
const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType);
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
void buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, 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 MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance,
PathType pathType); PathType pathType);
/// Remove front point if exist and within tolerance /// Remove front point if exist and within tolerance
void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance, void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents, bool shortenIfAlmostStraight, bool canMoveByZ, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags); const DetourNavigator::Flags flags);
bool checkPathCompleted() const bool checkPathCompleted() const
@ -219,7 +225,7 @@ namespace MWMechanics
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out); const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
[[nodiscard]] DetourNavigator::Status buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, [[nodiscard]] DetourNavigator::Status buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor,
const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType, const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType,
std::back_insert_iterator<std::deque<osg::Vec3f>> out); std::back_insert_iterator<std::deque<osg::Vec3f>> out);
}; };

View file

@ -57,7 +57,18 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
} }
mShape = std::make_unique<btBoxShape>(Misc::Convert::toBullet(mOriginalHalfExtents)); mShape = std::make_unique<btBoxShape>(Misc::Convert::toBullet(mOriginalHalfExtents));
mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mOriginalHalfExtents.x() - mOriginalHalfExtents.y()) < 2.2;
if ((mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0)
&& std::fabs(mOriginalHalfExtents.x() - mOriginalHalfExtents.y()) < 2.2)
{
mRotationallyInvariant = true;
mCollisionShapeType = DetourNavigator::CollisionShapeType::Aabb;
}
else
{
mRotationallyInvariant = false;
mCollisionShapeType = DetourNavigator::CollisionShapeType::RotatingBox;
}
mConvexShape = static_cast<btConvexShape*>(mShape.get()); mConvexShape = static_cast<btConvexShape*>(mShape.get());
mConvexShape->setMargin(0.001); // make sure bullet isn't using the huge default convex shape margin of 0.04 mConvexShape->setMargin(0.001); // make sure bullet isn't using the huge default convex shape margin of 0.04

View file

@ -6,6 +6,8 @@
#include "ptrholder.hpp" #include "ptrholder.hpp"
#include <components/detournavigator/collisionshapetype.hpp>
#include <LinearMath/btTransform.h> #include <LinearMath/btTransform.h>
#include <osg/Vec3f> #include <osg/Vec3f>
#include <osg/Quat> #include <osg/Quat>
@ -156,6 +158,8 @@ namespace MWPhysics
void setActive(bool value) { mActive = value; } void setActive(bool value) { mActive = value; }
DetourNavigator::CollisionShapeType getCollisionShapeType() const { return mCollisionShapeType; }
private: private:
MWWorld::Ptr mStandingOnPtr; MWWorld::Ptr mStandingOnPtr;
/// Removes then re-adds the collision object to the dynamics world /// Removes then re-adds the collision object to the dynamics world
@ -171,6 +175,8 @@ namespace MWPhysics
bool mRotationallyInvariant; bool mRotationallyInvariant;
DetourNavigator::CollisionShapeType mCollisionShapeType;
std::unique_ptr<btCollisionShape> mShape; std::unique_ptr<btCollisionShape> mShape;
btConvexShape* mConvexShape; btConvexShape* mConvexShape;

View file

@ -38,7 +38,7 @@ namespace MWRender
} }
void ActorsPaths::update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, void ActorsPaths::update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end, const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end,
const DetourNavigator::Settings& settings) const DetourNavigator::Settings& settings)
{ {
if (!mEnabled) if (!mEnabled)
@ -48,7 +48,7 @@ namespace MWRender
if (group != mGroups.end()) if (group != mGroups.end())
mRootNode->removeChild(group->second.mNode); mRootNode->removeChild(group->second.mNode);
auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings.mRecast); auto newGroup = SceneUtil::createAgentPathGroup(path, agentBounds, start, end, settings.mRecast);
if (newGroup) if (newGroup)
{ {
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(newGroup, "debug"); MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(newGroup, "debug");

View file

@ -17,6 +17,7 @@ namespace osg
namespace DetourNavigator namespace DetourNavigator
{ {
struct Settings; struct Settings;
struct AgentBounds;
} }
namespace MWRender namespace MWRender
@ -30,7 +31,7 @@ namespace MWRender
bool toggle(); bool toggle();
void update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, void update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end, const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end,
const DetourNavigator::Settings& settings); const DetourNavigator::Settings& settings);
void remove(const MWWorld::ConstPtr& actor); void remove(const MWWorld::ConstPtr& actor);

View file

@ -1518,9 +1518,9 @@ namespace MWRender
} }
void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const
{ {
mActorsPaths->update(actor, path, halfExtents, start, end, mNavigator.getSettings()); mActorsPaths->update(actor, path, agentBounds, start, end, mNavigator.getSettings());
} }
void RenderingManager::removeActorPath(const MWWorld::ConstPtr& actor) const void RenderingManager::removeActorPath(const MWWorld::ConstPtr& actor) const

View file

@ -66,6 +66,7 @@ namespace DetourNavigator
{ {
struct Navigator; struct Navigator;
struct Settings; struct Settings;
struct AgentBounds;
} }
namespace MWWorld namespace MWWorld
@ -236,7 +237,7 @@ namespace MWRender
bool toggleBorders(); bool toggleBorders();
void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, 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; const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const;
void removeActorPath(const MWWorld::ConstPtr& actor) const; void removeActorPath(const MWWorld::ConstPtr& actor) const;

View file

@ -187,7 +187,7 @@ namespace
} }
else if (physics.getActor(ptr)) else if (physics.getActor(ptr))
{ {
navigator.addAgent(world.getPathfindingHalfExtents(ptr)); navigator.addAgent(world.getPathfindingAgentBounds(ptr));
} }
} }
@ -332,7 +332,7 @@ namespace MWWorld
} }
else if (mPhysics->getActor(ptr)) else if (mPhysics->getActor(ptr))
{ {
mNavigator.removeAgent(mWorld.getPathfindingHalfExtents(ptr)); mNavigator.removeAgent(mWorld.getPathfindingAgentBounds(ptr));
mRendering.removeActorPath(ptr); mRendering.removeActorPath(ptr);
mPhysics->remove(ptr); mPhysics->remove(ptr);
} }
@ -940,7 +940,7 @@ namespace MWWorld
} }
else if (mPhysics->getActor(ptr)) else if (mPhysics->getActor(ptr))
{ {
mNavigator.removeAgent(mWorld.getPathfindingHalfExtents(ptr)); mNavigator.removeAgent(mWorld.getPathfindingAgentBounds(ptr));
} }
mPhysics->remove(ptr); mPhysics->remove(ptr);
mRendering.removeObject (ptr); mRendering.removeObject (ptr);

View file

@ -1275,7 +1275,7 @@ namespace MWWorld
if (!force && scale == ptr.getCellRef().getScale()) if (!force && scale == ptr.getCellRef().getScale())
return; return;
if (mPhysics->getActor(ptr)) if (mPhysics->getActor(ptr))
mNavigator->removeAgent(getPathfindingHalfExtents(ptr)); mNavigator->removeAgent(getPathfindingAgentBounds(ptr));
ptr.getCellRef().setScale(scale); ptr.getCellRef().setScale(scale);
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
@ -1285,7 +1285,7 @@ namespace MWWorld
mWorldScene->updateObjectScale(ptr); mWorldScene->updateObjectScale(ptr);
if (mPhysics->getActor(ptr)) if (mPhysics->getActor(ptr))
mNavigator->addAgent(getPathfindingHalfExtents(ptr)); mNavigator->addAgent(getPathfindingAgentBounds(ptr));
else if (const auto object = mPhysics->getObject(ptr)) else if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(*object); updateNavigatorObject(*object);
} }
@ -2416,7 +2416,7 @@ namespace MWWorld
{ {
// Remove the old CharacterController // Remove the old CharacterController
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true); MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
mNavigator->removeAgent(getPathfindingHalfExtents(getPlayerConstPtr())); mNavigator->removeAgent(getPathfindingAgentBounds(getPlayerConstPtr()));
mPhysics->remove(getPlayerPtr()); mPhysics->remove(getPlayerPtr());
mRendering->removePlayer(getPlayerPtr()); mRendering->removePlayer(getPlayerPtr());
MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(getPlayerPtr()); MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(getPlayerPtr());
@ -2453,7 +2453,7 @@ namespace MWWorld
applyLoopingParticles(player); applyLoopingParticles(player);
mNavigator->addAgent(getPathfindingHalfExtents(getPlayerConstPtr())); mNavigator->addAgent(getPathfindingAgentBounds(getPlayerConstPtr()));
} }
World::RestPermitted World::canRest () const World::RestPermitted World::canRest () const
@ -3898,9 +3898,9 @@ namespace MWWorld
} }
void World::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, void World::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const
{ {
mRendering->updateActorPath(actor, path, halfExtents, start, end); mRendering->updateActorPath(actor, path, agentBounds, start, end);
} }
void World::removeActorPath(const MWWorld::ConstPtr& actor) const void World::removeActorPath(const MWWorld::ConstPtr& actor) const
@ -3913,12 +3913,13 @@ namespace MWWorld
mRendering->setNavMeshNumber(value); mRendering->setNavMeshNumber(value);
} }
osg::Vec3f World::getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const DetourNavigator::AgentBounds World::getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const
{ {
if (actor.isInCell() && actor.getCell()->isExterior()) const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
return mDefaultHalfExtents; // Using default half extents for better performance if (physicsActor == nullptr || (actor.isInCell() && actor.getCell()->isExterior()))
return DetourNavigator::AgentBounds {DetourNavigator::defaultCollisionShapeType, mDefaultHalfExtents};
else else
return getHalfExtents(actor); return DetourNavigator::AgentBounds {physicsActor->getCollisionShapeType(), physicsActor->getHalfExtents()};
} }
bool World::hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const bool World::hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const

View file

@ -727,14 +727,13 @@ namespace MWWorld
DetourNavigator::Navigator* getNavigator() const override; DetourNavigator::Navigator* getNavigator() const override;
void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path, 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 override; const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const override;
void removeActorPath(const MWWorld::ConstPtr& actor) const override; void removeActorPath(const MWWorld::ConstPtr& actor) const override;
void setNavMeshNumberToRender(const std::size_t value) override; void setNavMeshNumberToRender(const std::size_t value) override;
/// Return physical half extents of the given actor to be used in pathfinding DetourNavigator::AgentBounds getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const override;
osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const override;
bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;

View file

@ -50,7 +50,7 @@ namespace
Settings mSettings = makeSettings(); Settings mSettings = makeSettings();
TileCachedRecastMeshManager mRecastMeshManager {mSettings.mRecast}; TileCachedRecastMeshManager mRecastMeshManager {mSettings.mRecast};
OffMeshConnectionsManager mOffMeshConnectionsManager {mSettings.mRecast}; OffMeshConnectionsManager mOffMeshConnectionsManager {mSettings.mRecast};
const osg::Vec3f mAgentHalfExtents {29, 29, 66}; const AgentBounds mAgentBounds {CollisionShapeType::Aabb, {29, 29, 66}};
const TilePosition mPlayerTile {0, 0}; const TilePosition mPlayerTile {0, 0};
const std::string mWorldspace = "sys::default"; const std::string mWorldspace = "sys::default";
const btBoxShape mBox {btVector3(100, 100, 20)}; const btBoxShape mBox {btVector3(100, 100, 20)};
@ -76,7 +76,7 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0); EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
} }
@ -88,14 +88,14 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mGetCount, 1);
ASSERT_EQ(stats.mCache.mHitCount, 0); ASSERT_EQ(stats.mCache.mHitCount, 0);
} }
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
@ -111,14 +111,14 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::update}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::update}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mGetCount, 1);
ASSERT_EQ(stats.mCache.mHitCount, 0); ASSERT_EQ(stats.mCache.mHitCount, 0);
} }
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
@ -138,7 +138,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -146,10 +146,11 @@ namespace
ShapeId nextShapeId {1}; ShapeId nextShapeId {1};
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v, nextShapeId); }); [&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v, nextShapeId); });
const auto tile = dbPtr->findTile(mWorldspace, tilePosition, serialize(mSettings.mRecast, *recastMesh, objects)); const auto tile = dbPtr->findTile(mWorldspace, tilePosition,
serialize(mSettings.mRecast, mAgentBounds, *recastMesh, objects));
ASSERT_TRUE(tile.has_value()); ASSERT_TRUE(tile.has_value());
EXPECT_EQ(tile->mTileId, 1); EXPECT_EQ(tile->mTileId, 1);
EXPECT_EQ(tile->mVersion, mSettings.mNavMeshVersion); EXPECT_EQ(tile->mVersion, navMeshVersion);
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles)
@ -164,7 +165,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -172,7 +173,8 @@ namespace
ShapeId nextShapeId {1}; ShapeId nextShapeId {1};
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v, nextShapeId); }); [&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v, nextShapeId); });
const auto tile = dbPtr->findTile(mWorldspace, tilePosition, serialize(mSettings.mRecast, *recastMesh, objects)); const auto tile = dbPtr->findTile(mWorldspace, tilePosition,
serialize(mSettings.mRecast, mAgentBounds, *recastMesh, objects));
ASSERT_FALSE(tile.has_value()); ASSERT_FALSE(tile.has_value());
} }
@ -188,7 +190,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -207,7 +209,7 @@ namespace
std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())); std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()));
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
@ -217,7 +219,7 @@ namespace
ASSERT_EQ(stats.mDb->mGetTileCount, 1); ASSERT_EQ(stats.mDb->mGetTileCount, 1);
ASSERT_EQ(stats.mDbGetTileHits, 0); ASSERT_EQ(stats.mDbGetTileHits, 0);
} }
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
@ -236,12 +238,12 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTilesAdd {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTilesAdd {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTilesAdd); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTilesAdd);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0); ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
const std::map<TilePosition, ChangeType> changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}}; const std::map<TilePosition, ChangeType> changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}};
const TilePosition playerTile(100, 100); const TilePosition playerTile(100, 100);
updater.post(mAgentHalfExtents, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove); updater.post(mAgentBounds, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0); EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
} }
@ -261,7 +263,7 @@ namespace
for (int x = -5; x <= 5; ++x) for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y) for (int y = -5; y <= 5; ++y)
changedTiles.emplace(TilePosition {x, y}, ChangeType::add); changedTiles.emplace(TilePosition {x, y}, ChangeType::add);
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(mListener, WaitConditionType::allJobsDone);
updater.stop(); updater.stop();
const std::set<TilePosition> present { const std::set<TilePosition> present {
@ -276,7 +278,6 @@ namespace
TilePosition(0, 2), TilePosition(0, 2),
TilePosition(1, -1), TilePosition(1, -1),
TilePosition(1, 0), TilePosition(1, 0),
TilePosition(1, 1),
}; };
for (int x = -5; x <= 5; ++x) for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y) for (int y = -5; y <= 5; ++y)
@ -288,7 +289,8 @@ namespace
[&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v); }); [&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v); });
if (!objects.has_value()) if (!objects.has_value())
continue; continue;
EXPECT_EQ(dbPtr->findTile(mWorldspace, tilePosition, serialize(mSettings.mRecast, *recastMesh, *objects)).has_value(), EXPECT_EQ(dbPtr->findTile(mWorldspace, tilePosition,
serialize(mSettings.mRecast, mAgentBounds, *recastMesh, *objects)).has_value(),
present.find(tilePosition) != present.end()) present.find(tilePosition) != present.end())
<< tilePosition.x() << " " << tilePosition.y() << " present=" << (present.find(tilePosition) != present.end()); << tilePosition.x() << " " << tilePosition.y() << " present=" << (present.find(tilePosition) != present.end());
} }

View file

@ -42,7 +42,7 @@ namespace
std::unique_ptr<Navigator> mNavigator; std::unique_ptr<Navigator> mNavigator;
const osg::Vec3f mPlayerPosition; const osg::Vec3f mPlayerPosition;
const std::string mWorldspace; const std::string mWorldspace;
const osg::Vec3f mAgentHalfExtents; const AgentBounds mAgentBounds {CollisionShapeType::Aabb, {29, 29, 66}};
osg::Vec3f mStart; osg::Vec3f mStart;
osg::Vec3f mEnd; osg::Vec3f mEnd;
std::deque<osg::Vec3f> mPath; std::deque<osg::Vec3f> mPath;
@ -59,7 +59,6 @@ namespace
DetourNavigatorNavigatorTest() DetourNavigatorNavigatorTest()
: mPlayerPosition(256, 256, 0) : mPlayerPosition(256, 256, 0)
, mWorldspace("sys::default") , mWorldspace("sys::default")
, mAgentHalfExtents(29, 29, 66)
, mStart(52, 460, 1) , mStart(52, 460, 1)
, mEnd(460, 52, 1) , mEnd(460, 52, 1)
, mOut(mPath) , mOut(mPath)
@ -122,24 +121,24 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
{ {
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::NavMeshNotFound); Status::NavMeshNotFound);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>()); EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
} }
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
{ {
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent) TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent)
{ {
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->removeAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentBounds);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
@ -155,12 +154,12 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -204,12 +203,12 @@ namespace
CollisionShapeInstance compound(std::make_unique<btCompoundShape>()); CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -243,7 +242,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -288,13 +287,13 @@ namespace
CollisionShapeInstance compound(std::make_unique<btCompoundShape>()); CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -331,7 +330,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -382,13 +381,13 @@ namespace
CollisionShapeInstance heightfield2(makeSquareHeightfieldTerrainShape(heightfieldData2)); CollisionShapeInstance heightfield2(makeSquareHeightfieldTerrainShape(heightfieldData2));
heightfield2.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield2.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform);
mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -439,7 +438,7 @@ namespace
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2); const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addHeightfield(mCellPosition, cellSize1, surface1)); EXPECT_TRUE(mNavigator->addHeightfield(mCellPosition, cellSize1, surface1));
EXPECT_FALSE(mNavigator->addHeightfield(mCellPosition, cellSize2, surface2)); EXPECT_FALSE(mNavigator->addHeightfield(mCellPosition, cellSize2, surface2));
} }
@ -472,12 +471,12 @@ namespace
osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape)); osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform); mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -519,7 +518,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, 300); mNavigator->addWater(mCellPosition, cellSize, 300);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -530,7 +529,7 @@ namespace
mEnd.x() = 256; mEnd.x() = 256;
mEnd.z() = 300; mEnd.z() = 300;
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -567,7 +566,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, -25); mNavigator->addWater(mCellPosition, cellSize, -25);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -576,7 +575,7 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -613,7 +612,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25); mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -622,7 +621,7 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -659,7 +658,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, -25); mNavigator->addWater(mCellPosition, cellSize, -25);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -668,7 +667,7 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -703,7 +702,7 @@ namespace
CollisionShapeInstance heightfield(makeSquareHeightfieldTerrainShape(heightfieldData)); CollisionShapeInstance heightfield(makeSquareHeightfieldTerrainShape(heightfieldData));
heightfield.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -716,7 +715,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -757,7 +756,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -770,7 +769,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -812,14 +811,14 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
Misc::Rng::init(42); Misc::Rng::init(42);
const auto result = findRandomPointAroundCircle(*mNavigator, mAgentHalfExtents, mStart, 100.0, Flag_walk, const auto result = findRandomPointAroundCircle(*mNavigator, mAgentBounds, mStart, 100.0, Flag_walk,
[]() { return Misc::Rng::rollClosedProbability(); }); []() { return Misc::Rng::rollClosedProbability(); });
ASSERT_THAT(result, Optional(Vec3fEq(70.35845947265625, 335.592041015625, -2.6667339801788330078125))) ASSERT_THAT(result, Optional(Vec3fEq(70.35845947265625, 335.592041015625, -2.6667339801788330078125)))
@ -849,7 +848,7 @@ namespace
std::vector<CollisionShapeInstance<btBoxShape>> boxes; std::vector<CollisionShapeInstance<btBoxShape>> boxes;
std::generate_n(std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); }); std::generate_n(std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); });
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
@ -870,7 +869,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -905,7 +904,7 @@ namespace
std::vector<CollisionShapeInstance<btBoxShape>> shapes; std::vector<CollisionShapeInstance<btBoxShape>> shapes;
std::generate_n(std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); }); std::generate_n(std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); });
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
@ -950,14 +949,14 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
const osg::Vec3f start(57, 460, 1); const osg::Vec3f start(57, 460, 1);
const osg::Vec3f end(460, 57, 1); const osg::Vec3f end(460, 57, 1);
const auto result = raycast(*mNavigator, mAgentHalfExtents, start, end, Flag_walk); const auto result = raycast(*mNavigator, mAgentBounds, start, end, Flag_walk);
ASSERT_THAT(result, Optional(Vec3fEq(end.x(), end.y(), 1.95257937908172607421875))) ASSERT_THAT(result, Optional(Vec3fEq(end.x(), end.y(), 1.95257937908172607421875)))
<< (result ? *result : osg::Vec3f()); << (result ? *result : osg::Vec3f());
@ -979,7 +978,7 @@ namespace
const btVector3 oscillatingBoxShapePosition(288, 288, 400); const btVector3 oscillatingBoxShapePosition(288, 288, 400);
CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50))); CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform),
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition)); btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition));
@ -1013,12 +1012,12 @@ namespace
const HeightfieldPlane plane {100}; const HeightfieldPlane plane {100};
const int cellSize = mHeightfieldTileSize * 4; const int cellSize = mHeightfieldTileSize * 4;
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, plane); mNavigator->addHeightfield(mCellPosition, cellSize, plane);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -1063,13 +1062,13 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
new btBoxShape(btVector3(200, 200, 1000))); new btBoxShape(btVector3(200, 200, 1000)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::PartialPath); Status::PartialPath);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -1102,7 +1101,7 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
new btBoxShape(btVector3(100, 100, 1000))); new btBoxShape(btVector3(100, 100, 1000)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -1110,7 +1109,7 @@ namespace
const float endTolerance = 1000.0f; const float endTolerance = 1000.0f;
EXPECT_EQ(findPath(*mNavigator, mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
@ -1142,7 +1141,7 @@ namespace
const int cellSize2 = 200; const int cellSize2 = 200;
const float level2 = 2; const float level2 = 2;
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1)); EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1));
EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2)); EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2));
} }

View file

@ -95,7 +95,7 @@ namespace
struct DetourNavigatorNavMeshTilesCacheTest : Test struct DetourNavigatorNavMeshTilesCacheTest : Test
{ {
const osg::Vec3f mAgentHalfExtents {1, 2, 3}; const AgentBounds mAgentBounds {CollisionShapeType::Aabb, {1, 2, 3}};
const TilePosition mTilePosition {0, 0}; const TilePosition mTilePosition {0, 0};
const std::size_t mGeneration = 0; const std::size_t mGeneration = 0;
const std::size_t mRevision = 0; const std::size_t mRevision = 0;
@ -117,7 +117,7 @@ namespace
const std::size_t maxSize = 0; const std::size_t maxSize = 0;
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value)
@ -125,7 +125,7 @@ namespace
const std::size_t maxSize = 0; const std::size_t maxSize = 0;
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData))); EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)));
EXPECT_NE(mPreparedNavMeshData, nullptr); EXPECT_NE(mPreparedNavMeshData, nullptr);
} }
@ -136,7 +136,7 @@ namespace
const auto copy = clone(*mPreparedNavMeshData); const auto copy = clone(*mPreparedNavMeshData);
ASSERT_EQ(*mPreparedNavMeshData, *copy); ASSERT_EQ(*mPreparedNavMeshData, *copy);
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
ASSERT_TRUE(result); ASSERT_TRUE(result);
EXPECT_EQ(result.get(), *copy); EXPECT_EQ(result.get(), *copy);
} }
@ -148,9 +148,9 @@ namespace
auto copy = clone(*mPreparedNavMeshData); auto copy = clone(*mPreparedNavMeshData);
const auto sameCopy = clone(*mPreparedNavMeshData); const auto sameCopy = clone(*mPreparedNavMeshData);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_EQ(mPreparedNavMeshData, nullptr); EXPECT_EQ(mPreparedNavMeshData, nullptr);
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(copy)); const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(copy));
ASSERT_TRUE(result); ASSERT_TRUE(result);
EXPECT_EQ(result.get(), *sameCopy); EXPECT_EQ(result.get(), *sameCopy);
} }
@ -161,8 +161,8 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const auto copy = clone(*mPreparedNavMeshData); const auto copy = clone(*mPreparedNavMeshData);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); const auto result = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(result); ASSERT_TRUE(result);
EXPECT_EQ(result.get(), *copy); EXPECT_EQ(result.get(), *copy);
} }
@ -171,10 +171,10 @@ namespace
{ {
const std::size_t maxSize = 1; const std::size_t maxSize = 1;
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1}; const AgentBounds absentAgentBounds {CollisionShapeType::Aabb, {1, 1, 1}};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_FALSE(cache.get(absentAgentBounds, mTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value)
@ -183,8 +183,8 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const TilePosition unexistentTilePosition {1, 1}; const TilePosition unexistentTilePosition {1, 1};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, unexistentTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value)
@ -194,8 +194,8 @@ namespace
const std::vector<CellWater> water(1, CellWater {osg::Vec2i(), Water {1, 0.0f}}); const std::vector<CellWater> water(1, CellWater {osg::Vec2i(), Water {1, 0.0f}});
const RecastMesh unexistentRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources); const RecastMesh unexistentRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, unexistentRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value)
@ -208,12 +208,12 @@ namespace
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto copy = clone(*anotherPreparedNavMeshData); const auto copy = clone(*anotherPreparedNavMeshData);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, const auto result = cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherPreparedNavMeshData)); std::move(anotherPreparedNavMeshData));
ASSERT_TRUE(result); ASSERT_TRUE(result);
EXPECT_EQ(result.get(), *copy); EXPECT_EQ(result.get(), *copy);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value)
@ -225,9 +225,9 @@ namespace
const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources); const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto value = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData)); std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherPreparedNavMeshData))); std::move(anotherPreparedNavMeshData)));
} }
@ -247,17 +247,17 @@ namespace
mHeightfields, mFlatHeightfields, mSources); mHeightfields, mFlatHeightfields, mSources);
auto mostRecentlySetData = makePeparedNavMeshData(3); auto mostRecentlySetData = makePeparedNavMeshData(3);
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, leastRecentlySetRecastMesh,
std::move(leastRecentlySetData))); std::move(leastRecentlySetData)));
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, mostRecentlySetRecastMesh,
std::move(mostRecentlySetData))); std::move(mostRecentlySetData)));
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData)); std::move(mPreparedNavMeshData));
EXPECT_EQ(result.get(), *copy); EXPECT_EQ(result.get(), *copy);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, leastRecentlySetRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mostRecentlySetRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value)
@ -277,28 +277,28 @@ namespace
auto mostRecentlyUsedData = makePeparedNavMeshData(3); auto mostRecentlyUsedData = makePeparedNavMeshData(3);
const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData); const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData);
cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData)); cache.set(mAgentBounds, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData));
cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData)); cache.set(mAgentBounds, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData));
{ {
const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh); const auto value = cache.get(mAgentBounds, mTilePosition, leastRecentlyUsedRecastMesh);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_EQ(value.get(), *leastRecentlyUsedCopy); ASSERT_EQ(value.get(), *leastRecentlyUsedCopy);
} }
{ {
const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh); const auto value = cache.get(mAgentBounds, mTilePosition, mostRecentlyUsedRecastMesh);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_EQ(value.get(), *mostRecentlyUsedCopy); ASSERT_EQ(value.get(), *mostRecentlyUsedCopy);
} }
const auto copy = clone(*mPreparedNavMeshData); const auto copy = clone(*mPreparedNavMeshData);
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData)); std::move(mPreparedNavMeshData));
EXPECT_EQ(result.get(), *copy); EXPECT_EQ(result.get(), *copy);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh)); EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, leastRecentlyUsedRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mostRecentlyUsedRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size)
@ -311,9 +311,9 @@ namespace
mHeightfields, mFlatHeightfields, mSources); mHeightfields, mFlatHeightfields, mSources);
auto tooLargeData = makePeparedNavMeshData(10); auto tooLargeData = makePeparedNavMeshData(10);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData))); EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items) TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items)
@ -331,15 +331,15 @@ namespace
mHeightfields, mFlatHeightfields, mSources); mHeightfields, mFlatHeightfields, mSources);
auto tooLargeData = makePeparedNavMeshData(10); auto tooLargeData = makePeparedNavMeshData(10);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto value = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData)); std::move(mPreparedNavMeshData));
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherData))); std::move(anotherData)));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, tooLargeRecastMesh,
std::move(tooLargeData))); std::move(tooLargeData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, anotherRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available) TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available)
@ -351,14 +351,14 @@ namespace
const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources); const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
auto anotherData = makePeparedNavMeshData(3); auto anotherData = makePeparedNavMeshData(3);
const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); const auto firstCopy = cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
ASSERT_TRUE(firstCopy); ASSERT_TRUE(firstCopy);
{ {
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); const auto secondCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(secondCopy); ASSERT_TRUE(secondCopy);
} }
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh, std::move(anotherData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
} }
TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available) TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available)
@ -370,14 +370,14 @@ namespace
const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources); const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
auto anotherData = makePeparedNavMeshData(3); auto anotherData = makePeparedNavMeshData(3);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); const auto firstCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(firstCopy); ASSERT_TRUE(firstCopy);
{ {
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); const auto secondCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(secondCopy); ASSERT_TRUE(secondCopy);
} }
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh, std::move(anotherData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
} }
} }

View file

@ -0,0 +1,34 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_AGENTBOUNDS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_AGENTBOUNDS_H
#include "collisionshapetype.hpp"
#include <osg/Vec3f>
#include <tuple>
namespace DetourNavigator
{
struct AgentBounds
{
CollisionShapeType mShapeType;
osg::Vec3f mHalfExtents;
};
inline auto tie(const AgentBounds& value)
{
return std::tie(value.mShapeType, value.mHalfExtents);
}
inline bool operator==(const AgentBounds& lhs, const AgentBounds& rhs)
{
return tie(lhs) == tie(rhs);
}
inline bool operator<(const AgentBounds& lhs, const AgentBounds& rhs)
{
return tie(lhs) < tie(rhs);
}
}
#endif

View file

@ -32,12 +32,12 @@ namespace DetourNavigator
} }
int getMinDistanceTo(const TilePosition& position, int maxDistance, int getMinDistanceTo(const TilePosition& position, int maxDistance,
const std::set<std::tuple<osg::Vec3f, TilePosition>>& pushedTiles, const std::set<std::tuple<AgentBounds, TilePosition>>& pushedTiles,
const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles) const std::set<std::tuple<AgentBounds, TilePosition>>& presentTiles)
{ {
int result = maxDistance; int result = maxDistance;
for (const auto& [halfExtents, tile] : pushedTiles) for (const auto& [agentBounds, tile] : pushedTiles)
if (presentTiles.find(std::tie(halfExtents, tile)) == presentTiles.end()) if (presentTiles.find(std::tie(agentBounds, tile)) == presentTiles.end())
result = std::min(result, getManhattanDistance(position, tile)); result = std::min(result, getManhattanDistance(position, tile));
return result; return result;
} }
@ -84,14 +84,14 @@ namespace DetourNavigator
auto getAgentAndTile(const Job& job) noexcept auto getAgentAndTile(const Job& job) noexcept
{ {
return std::make_tuple(job.mAgentHalfExtents, job.mChangedTile); return std::make_tuple(job.mAgentBounds, job.mChangedTile);
} }
std::unique_ptr<DbWorker> makeDbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr<NavMeshDb>&& db, const Settings& settings) std::unique_ptr<DbWorker> makeDbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr<NavMeshDb>&& db, const Settings& settings)
{ {
if (db == nullptr) if (db == nullptr)
return nullptr; return nullptr;
return std::make_unique<DbWorker>(updater, std::move(db), TileVersion(settings.mNavMeshVersion), return std::make_unique<DbWorker>(updater, std::move(db), TileVersion(navMeshVersion),
settings.mRecast, settings.mWriteToNavMeshDb); settings.mRecast, settings.mWriteToNavMeshDb);
} }
@ -112,11 +112,11 @@ namespace DetourNavigator
} }
} }
Job::Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem, Job::Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer, std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::chrono::steady_clock::time_point processTime) std::chrono::steady_clock::time_point processTime)
: mId(getNextJobId()) : mId(getNextJobId())
, mAgentHalfExtents(agentHalfExtents) , mAgentBounds(agentBounds)
, mNavMeshCacheItem(std::move(navMeshCacheItem)) , mNavMeshCacheItem(std::move(navMeshCacheItem))
, mWorldspace(worldspace) , mWorldspace(worldspace)
, mChangedTile(changedTile) , mChangedTile(changedTile)
@ -145,7 +145,7 @@ namespace DetourNavigator
stop(); stop();
} }
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& navMeshCacheItem, void AsyncNavMeshUpdater::post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem,
const TilePosition& playerTile, std::string_view worldspace, const TilePosition& playerTile, std::string_view worldspace,
const std::map<TilePosition, ChangeType>& changedTiles) const std::map<TilePosition, ChangeType>& changedTiles)
{ {
@ -169,16 +169,16 @@ namespace DetourNavigator
for (const auto& [changedTile, changeType] : changedTiles) for (const auto& [changedTile, changeType] : changedTiles)
{ {
if (mPushed.emplace(agentHalfExtents, changedTile).second) if (mPushed.emplace(agentBounds, changedTile).second)
{ {
const auto processTime = changeType == ChangeType::update const auto processTime = changeType == ChangeType::update
? mLastUpdates[std::tie(agentHalfExtents, changedTile)] + mSettings.get().mMinUpdateInterval ? mLastUpdates[std::tie(agentBounds, changedTile)] + mSettings.get().mMinUpdateInterval
: std::chrono::steady_clock::time_point(); : std::chrono::steady_clock::time_point();
const JobIt it = mJobs.emplace(mJobs.end(), agentHalfExtents, navMeshCacheItem, worldspace, const JobIt it = mJobs.emplace(mJobs.end(), agentBounds, navMeshCacheItem, worldspace,
changedTile, changeType, getManhattanDistance(changedTile, playerTile), processTime); changedTile, changeType, getManhattanDistance(changedTile, playerTile), processTime);
Log(Debug::Debug) << "Post job " << it->mId << " for agent=(" << it->mAgentHalfExtents << ")" Log(Debug::Debug) << "Post job " << it->mId << " for agent=(" << it->mAgentBounds << ")"
<< " changedTile=(" << it->mChangedTile << ")"; << " changedTile=(" << it->mChangedTile << ")";
if (playerTileChanged) if (playerTileChanged)
@ -342,7 +342,7 @@ namespace DetourNavigator
switch (status) switch (status)
{ {
case JobStatus::Done: case JobStatus::Done:
unlockTile(job->mAgentHalfExtents, job->mChangedTile); unlockTile(job->mAgentBounds, job->mChangedTile);
if (job->mGeneratedNavMeshData != nullptr) if (job->mGeneratedNavMeshData != nullptr)
mDbWorker->enqueueJob(job); mDbWorker->enqueueJob(job);
else else
@ -419,7 +419,7 @@ namespace DetourNavigator
return JobStatus::Done; return JobStatus::Done;
} }
NavMeshTilesCache::Value cachedNavMeshData = mNavMeshTilesCache.get(job.mAgentHalfExtents, job.mChangedTile, *recastMesh); NavMeshTilesCache::Value cachedNavMeshData = mNavMeshTilesCache.get(job.mAgentBounds, job.mChangedTile, *recastMesh);
std::unique_ptr<PreparedNavMeshData> preparedNavMeshData; std::unique_ptr<PreparedNavMeshData> preparedNavMeshData;
const PreparedNavMeshData* preparedNavMeshDataPtr = nullptr; const PreparedNavMeshData* preparedNavMeshDataPtr = nullptr;
@ -435,7 +435,7 @@ namespace DetourNavigator
return JobStatus::MemoryCacheMiss; return JobStatus::MemoryCacheMiss;
} }
preparedNavMeshData = prepareNavMeshTileData(*recastMesh, job.mChangedTile, job.mAgentHalfExtents, mSettings.get().mRecast); preparedNavMeshData = prepareNavMeshTileData(*recastMesh, job.mChangedTile, job.mAgentBounds, mSettings.get().mRecast);
if (preparedNavMeshData == nullptr) if (preparedNavMeshData == nullptr)
{ {
@ -450,7 +450,7 @@ namespace DetourNavigator
} }
else else
{ {
cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentHalfExtents, job.mChangedTile, cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentBounds, job.mChangedTile,
*recastMesh, std::move(preparedNavMeshData)); *recastMesh, std::move(preparedNavMeshData));
preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get(); preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get();
} }
@ -459,7 +459,7 @@ namespace DetourNavigator
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile); const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
const UpdateNavMeshStatus status = navMeshCacheItem.lock()->updateTile(job.mChangedTile, std::move(cachedNavMeshData), const UpdateNavMeshStatus status = navMeshCacheItem.lock()->updateTile(job.mChangedTile, std::move(cachedNavMeshData),
makeNavMeshTileData(*preparedNavMeshDataPtr, offMeshConnections, job.mAgentHalfExtents, job.mChangedTile, mSettings.get().mRecast)); makeNavMeshTileData(*preparedNavMeshDataPtr, offMeshConnections, job.mAgentBounds, job.mChangedTile, mSettings.get().mRecast));
return handleUpdateNavMeshStatus(status, job, navMeshCacheItem, *recastMesh); return handleUpdateNavMeshStatus(status, job, navMeshCacheItem, *recastMesh);
} }
@ -471,7 +471,7 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> preparedNavMeshData; std::unique_ptr<PreparedNavMeshData> preparedNavMeshData;
bool generatedNavMeshData = false; bool generatedNavMeshData = false;
if (job.mCachedTileData.has_value() && job.mCachedTileData->mVersion == mSettings.get().mNavMeshVersion) if (job.mCachedTileData.has_value() && job.mCachedTileData->mVersion == navMeshVersion)
{ {
preparedNavMeshData = std::make_unique<PreparedNavMeshData>(); preparedNavMeshData = std::make_unique<PreparedNavMeshData>();
if (deserialize(job.mCachedTileData->mData, *preparedNavMeshData)) if (deserialize(job.mCachedTileData->mData, *preparedNavMeshData))
@ -482,7 +482,7 @@ namespace DetourNavigator
if (preparedNavMeshData == nullptr) if (preparedNavMeshData == nullptr)
{ {
preparedNavMeshData = prepareNavMeshTileData(*job.mRecastMesh, job.mChangedTile, job.mAgentHalfExtents, mSettings.get().mRecast); preparedNavMeshData = prepareNavMeshTileData(*job.mRecastMesh, job.mChangedTile, job.mAgentBounds, mSettings.get().mRecast);
generatedNavMeshData = true; generatedNavMeshData = true;
} }
@ -493,14 +493,14 @@ namespace DetourNavigator
return JobStatus::Done; return JobStatus::Done;
} }
auto cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentHalfExtents, job.mChangedTile, *job.mRecastMesh, auto cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentBounds, job.mChangedTile, *job.mRecastMesh,
std::move(preparedNavMeshData)); std::move(preparedNavMeshData));
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile); const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
const PreparedNavMeshData* preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get(); const PreparedNavMeshData* preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get();
const UpdateNavMeshStatus status = navMeshCacheItem.lock()->updateTile(job.mChangedTile, std::move(cachedNavMeshData), const UpdateNavMeshStatus status = navMeshCacheItem.lock()->updateTile(job.mChangedTile, std::move(cachedNavMeshData),
makeNavMeshTileData(*preparedNavMeshDataPtr, offMeshConnections, job.mAgentHalfExtents, job.mChangedTile, mSettings.get().mRecast)); makeNavMeshTileData(*preparedNavMeshDataPtr, offMeshConnections, job.mAgentBounds, job.mChangedTile, mSettings.get().mRecast));
const JobStatus result = handleUpdateNavMeshStatus(status, job, navMeshCacheItem, *job.mRecastMesh); const JobStatus result = handleUpdateNavMeshStatus(status, job, navMeshCacheItem, *job.mRecastMesh);
@ -522,12 +522,12 @@ namespace DetourNavigator
if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost) if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost)
{ {
const std::scoped_lock lock(mMutex); const std::scoped_lock lock(mMutex);
mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); mPresentTiles.erase(std::make_tuple(job.mAgentBounds, job.mChangedTile));
} }
else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored) else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored)
{ {
const std::scoped_lock lock(mMutex); const std::scoped_lock lock(mMutex);
mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); mPresentTiles.insert(std::make_tuple(job.mAgentBounds, job.mChangedTile));
} }
writeDebugFiles(job, &recastMesh); writeDebugFiles(job, &recastMesh);
@ -564,7 +564,7 @@ namespace DetourNavigator
if (job->mRecastMesh != nullptr) if (job->mRecastMesh != nullptr)
return job; return job;
if (!lockTile(job->mAgentHalfExtents, job->mChangedTile)) if (!lockTile(job->mAgentBounds, job->mChangedTile))
{ {
Log(Debug::Debug) << "Failed to lock tile by " << job->mId; Log(Debug::Debug) << "Failed to lock tile by " << job->mId;
++job->mTryNumber; ++job->mTryNumber;
@ -604,14 +604,14 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::repost(JobIt job) void AsyncNavMeshUpdater::repost(JobIt job)
{ {
unlockTile(job->mAgentHalfExtents, job->mChangedTile); unlockTile(job->mAgentBounds, job->mChangedTile);
if (mShouldStop || job->mTryNumber > 2) if (mShouldStop || job->mTryNumber > 2)
return; return;
const std::lock_guard<std::mutex> lock(mMutex); const std::lock_guard<std::mutex> lock(mMutex);
if (mPushed.emplace(job->mAgentHalfExtents, job->mChangedTile).second) if (mPushed.emplace(job->mAgentBounds, job->mChangedTile).second)
{ {
++job->mTryNumber; ++job->mTryNumber;
insertPrioritizedJob(job, mWaiting); insertPrioritizedJob(job, mWaiting);
@ -622,17 +622,17 @@ namespace DetourNavigator
mJobs.erase(job); mJobs.erase(job);
} }
bool AsyncNavMeshUpdater::lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile) bool AsyncNavMeshUpdater::lockTile(const AgentBounds& agentBounds, const TilePosition& changedTile)
{ {
Log(Debug::Debug) << "Locking tile agent=(" << agentHalfExtents << ") changedTile=(" << changedTile << ")"; Log(Debug::Debug) << "Locking tile agent=" << agentBounds << " changedTile=(" << changedTile << ")";
return mProcessingTiles.lock()->emplace(agentHalfExtents, changedTile).second; return mProcessingTiles.lock()->emplace(agentBounds, changedTile).second;
} }
void AsyncNavMeshUpdater::unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile) void AsyncNavMeshUpdater::unlockTile(const AgentBounds& agentBounds, const TilePosition& changedTile)
{ {
auto locked = mProcessingTiles.lock(); auto locked = mProcessingTiles.lock();
locked->erase(std::tie(agentHalfExtents, changedTile)); locked->erase(std::tie(agentBounds, changedTile));
Log(Debug::Debug) << "Unlocked tile agent=(" << agentHalfExtents << ") changedTile=(" << changedTile << ")"; Log(Debug::Debug) << "Unlocked tile agent=" << agentBounds << " changedTile=(" << changedTile << ")";
if (locked->empty()) if (locked->empty())
mProcessed.notify_all(); mProcessed.notify_all();
} }
@ -819,7 +819,7 @@ namespace DetourNavigator
{ {
const auto objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(), const auto objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(),
[&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); }); [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); });
job->mInput = serialize(mRecastSettings, *job->mRecastMesh, objects); job->mInput = serialize(mRecastSettings, job->mAgentBounds, *job->mRecastMesh, objects);
} }
else else
{ {
@ -827,7 +827,7 @@ namespace DetourNavigator
[&] (const MeshSource& v) { return resolveMeshSource(*mDb, v); }); [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v); });
if (!objects.has_value()) if (!objects.has_value())
return; return;
job->mInput = serialize(mRecastSettings, *job->mRecastMesh, *objects); job->mInput = serialize(mRecastSettings, job->mAgentBounds, *job->mRecastMesh, *objects);
} }
} }
@ -850,7 +850,7 @@ namespace DetourNavigator
Log(Debug::Debug) << "Serializing input for job " << job->mId; Log(Debug::Debug) << "Serializing input for job " << job->mId;
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(), const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(),
[&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); }); [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); });
job->mInput = serialize(mRecastSettings, *job->mRecastMesh, objects); job->mInput = serialize(mRecastSettings, job->mAgentBounds, *job->mRecastMesh, objects);
} }
if (const auto& cachedTileData = job->mCachedTileData) if (const auto& cachedTileData = job->mCachedTileData)

View file

@ -9,6 +9,7 @@
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include "navmeshdb.hpp" #include "navmeshdb.hpp"
#include "changetype.hpp" #include "changetype.hpp"
#include "agentbounds.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
@ -42,7 +43,7 @@ namespace DetourNavigator
struct Job struct Job
{ {
const std::size_t mId; const std::size_t mId;
const osg::Vec3f mAgentHalfExtents; const AgentBounds mAgentBounds;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem; const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
const std::string mWorldspace; const std::string mWorldspace;
const TilePosition mChangedTile; const TilePosition mChangedTile;
@ -57,7 +58,7 @@ namespace DetourNavigator
std::optional<TileData> mCachedTileData; std::optional<TileData> mCachedTileData;
std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData; std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData;
Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem, Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer, std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::chrono::steady_clock::time_point processTime); std::chrono::steady_clock::time_point processTime);
}; };
@ -166,7 +167,7 @@ namespace DetourNavigator
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db); OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db);
~AsyncNavMeshUpdater(); ~AsyncNavMeshUpdater();
void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& navMeshCacheItem, void post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem,
const TilePosition& playerTile, std::string_view worldspace, const TilePosition& playerTile, std::string_view worldspace,
const std::map<TilePosition, ChangeType>& changedTiles); const std::map<TilePosition, ChangeType>& changedTiles);
@ -191,12 +192,12 @@ namespace DetourNavigator
std::condition_variable mProcessed; std::condition_variable mProcessed;
std::list<Job> mJobs; std::list<Job> mJobs;
std::deque<JobIt> mWaiting; std::deque<JobIt> mWaiting;
std::set<std::tuple<osg::Vec3f, TilePosition>> mPushed; std::set<std::tuple<AgentBounds, TilePosition>> mPushed;
Misc::ScopeGuarded<TilePosition> mPlayerTile; Misc::ScopeGuarded<TilePosition> mPlayerTile;
NavMeshTilesCache mNavMeshTilesCache; NavMeshTilesCache mNavMeshTilesCache;
Misc::ScopeGuarded<std::set<std::tuple<osg::Vec3f, TilePosition>>> mProcessingTiles; Misc::ScopeGuarded<std::set<std::tuple<AgentBounds, TilePosition>>> mProcessingTiles;
std::map<std::tuple<osg::Vec3f, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates; std::map<std::tuple<AgentBounds, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates;
std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles; std::set<std::tuple<AgentBounds, TilePosition>> mPresentTiles;
std::vector<std::thread> mThreads; std::vector<std::thread> mThreads;
std::unique_ptr<DbWorker> mDbWorker; std::unique_ptr<DbWorker> mDbWorker;
std::atomic_size_t mDbGetTileHits {0}; std::atomic_size_t mDbGetTileHits {0};
@ -220,9 +221,9 @@ namespace DetourNavigator
void repost(JobIt job); void repost(JobIt job);
bool lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); bool lockTile(const AgentBounds& agentBounds, const TilePosition& changedTile);
void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); void unlockTile(const AgentBounds& agentBounds, const TilePosition& changedTile);
inline std::size_t getTotalJobs() const; inline std::size_t getTotalJobs() const;

View file

@ -0,0 +1,17 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_COLLISIONSHAPETYPE_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_COLLISIONSHAPETYPE_H
#include <cstdint>
namespace DetourNavigator
{
enum class CollisionShapeType : std::uint8_t
{
Aabb = 0,
RotatingBox = 1,
};
inline constexpr CollisionShapeType defaultCollisionShapeType = CollisionShapeType::Aabb;
}
#endif

View file

@ -4,6 +4,7 @@
#include "tilebounds.hpp" #include "tilebounds.hpp"
#include "status.hpp" #include "status.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "agentbounds.hpp"
#include <osg/io_utils> #include <osg/io_utils>
@ -69,6 +70,21 @@ namespace DetourNavigator
return s << ", .mOriginalSize=" << v.mOriginalSize << "}"; return s << ", .mOriginalSize=" << v.mOriginalSize << "}";
} }
inline std::ostream& operator<<(std::ostream& s, CollisionShapeType v)
{
switch (v)
{
case CollisionShapeType::Aabb: return s << "AgentShapeType::Aabb";
case CollisionShapeType::RotatingBox: return s << "AgentShapeType::RotatingBox";
}
return s << "AgentShapeType::" << static_cast<std::underlying_type_t<CollisionShapeType>>(v);
}
inline std::ostream& operator<<(std::ostream& s, const AgentBounds& v)
{
return s << "AgentBounds {" << v.mShapeType << ", " << v.mHalfExtents << "}";
}
class RecastMesh; class RecastMesh;
struct RecastSettings; struct RecastSettings;

View file

@ -38,12 +38,12 @@ namespace DetourNavigator
} }
GenerateNavMeshTile::GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition, GenerateNavMeshTile::GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition,
RecastMeshProvider recastMeshProvider, const osg::Vec3f& agentHalfExtents, RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds,
const DetourNavigator::Settings& settings, std::weak_ptr<NavMeshTileConsumer> consumer) const DetourNavigator::Settings& settings, std::weak_ptr<NavMeshTileConsumer> consumer)
: mWorldspace(std::move(worldspace)) : mWorldspace(std::move(worldspace))
, mTilePosition(tilePosition) , mTilePosition(tilePosition)
, mRecastMeshProvider(recastMeshProvider) , mRecastMeshProvider(recastMeshProvider)
, mAgentHalfExtents(agentHalfExtents) , mAgentBounds(agentBounds)
, mSettings(settings) , mSettings(settings)
, mConsumer(std::move(consumer)) {} , mConsumer(std::move(consumer)) {}
@ -70,25 +70,25 @@ namespace DetourNavigator
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (const MeshSource& v) { return consumer->resolveMeshSource(v); }); [&] (const MeshSource& v) { return consumer->resolveMeshSource(v); });
std::vector<std::byte> input = serialize(mSettings.mRecast, *recastMesh, objects); std::vector<std::byte> input = serialize(mSettings.mRecast, mAgentBounds, *recastMesh, objects);
const std::optional<NavMeshTileInfo> info = consumer->find(mWorldspace, mTilePosition, input); const std::optional<NavMeshTileInfo> info = consumer->find(mWorldspace, mTilePosition, input);
if (info.has_value() && info->mVersion == mSettings.mNavMeshVersion) if (info.has_value() && info->mVersion == navMeshVersion)
{ {
consumer->identity(mWorldspace, mTilePosition, info->mTileId); consumer->identity(mWorldspace, mTilePosition, info->mTileId);
ignore.mConsumer = nullptr; ignore.mConsumer = nullptr;
return; return;
} }
const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentHalfExtents, mSettings.mRecast); const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentBounds, mSettings.mRecast);
if (data == nullptr) if (data == nullptr)
return; return;
if (info.has_value()) if (info.has_value())
consumer->update(mWorldspace, mTilePosition, info->mTileId, mSettings.mNavMeshVersion, *data); consumer->update(mWorldspace, mTilePosition, info->mTileId, navMeshVersion, *data);
else else
consumer->insert(mWorldspace, mTilePosition, mSettings.mNavMeshVersion, input, *data); consumer->insert(mWorldspace, mTilePosition, navMeshVersion, input, *data);
ignore.mConsumer = nullptr; ignore.mConsumer = nullptr;
} }

View file

@ -3,6 +3,7 @@
#include "recastmeshprovider.hpp" #include "recastmeshprovider.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include "agentbounds.hpp"
#include <components/sceneutil/workqueue.hpp> #include <components/sceneutil/workqueue.hpp>
@ -57,7 +58,7 @@ namespace DetourNavigator
{ {
public: public:
GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition, GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition,
RecastMeshProvider recastMeshProvider, const osg::Vec3f& agentHalfExtents, const Settings& settings, RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds, const Settings& settings,
std::weak_ptr<NavMeshTileConsumer> consumer); std::weak_ptr<NavMeshTileConsumer> consumer);
void doWork() final; void doWork() final;
@ -66,7 +67,7 @@ namespace DetourNavigator
const std::string mWorldspace; const std::string mWorldspace;
const TilePosition mTilePosition; const TilePosition mTilePosition;
const RecastMeshProvider mRecastMeshProvider; const RecastMeshProvider mRecastMeshProvider;
const osg::Vec3f mAgentHalfExtents; const AgentBounds mAgentBounds;
const Settings& mSettings; const Settings& mSettings;
std::weak_ptr<NavMeshTileConsumer> mConsumer; std::weak_ptr<NavMeshTileConsumer> mConsumer;

View file

@ -101,9 +101,9 @@ namespace
return result; return result;
} }
float getHeight(const RecastSettings& settings,const osg::Vec3f& agentHalfExtents) float getHeight(const RecastSettings& settings,const AgentBounds& agentBounds)
{ {
return getAgentHeight(agentHalfExtents) * settings.mRecastScaleFactor; return getAgentHeight(agentBounds) * settings.mRecastScaleFactor;
} }
float getMaxClimb(const RecastSettings& settings) float getMaxClimb(const RecastSettings& settings)
@ -111,14 +111,14 @@ namespace
return settings.mMaxClimb * settings.mRecastScaleFactor; return settings.mMaxClimb * settings.mRecastScaleFactor;
} }
float getRadius(const RecastSettings& settings, const osg::Vec3f& agentHalfExtents) float getRadius(const RecastSettings& settings, const AgentBounds& agentBounds)
{ {
return getAgentRadius(agentHalfExtents) * settings.mRecastScaleFactor; return getAgentRadius(agentBounds) * settings.mRecastScaleFactor;
} }
float getSwimLevel(const RecastSettings& settings, const float waterLevel, const float agentHalfExtentsZ) float getSwimLevel(const RecastSettings& settings, const float waterLevel, const float agentHalfExtentsZ)
{ {
return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ;; return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ;
} }
struct RecastParams struct RecastParams
@ -131,13 +131,13 @@ namespace
int mWalkableRadius = 0; int mWalkableRadius = 0;
}; };
RecastParams makeRecastParams(const RecastSettings& settings, const osg::Vec3f& agentHalfExtents) RecastParams makeRecastParams(const RecastSettings& settings, const AgentBounds& agentBounds)
{ {
RecastParams result; RecastParams result;
result.mWalkableHeight = static_cast<int>(std::ceil(getHeight(settings, agentHalfExtents) / settings.mCellHeight)); result.mWalkableHeight = static_cast<int>(std::ceil(getHeight(settings, agentBounds) / settings.mCellHeight));
result.mWalkableClimb = static_cast<int>(std::floor(getMaxClimb(settings) / settings.mCellHeight)); result.mWalkableClimb = static_cast<int>(std::floor(getMaxClimb(settings) / settings.mCellHeight));
result.mWalkableRadius = static_cast<int>(std::ceil(getRadius(settings, agentHalfExtents) / settings.mCellSize)); result.mWalkableRadius = static_cast<int>(std::ceil(getRadius(settings, agentBounds) / settings.mCellSize));
result.mMaxEdgeLen = static_cast<int>(std::round(static_cast<float>(settings.mMaxEdgeLen) / settings.mCellSize)); result.mMaxEdgeLen = static_cast<int>(std::round(static_cast<float>(settings.mMaxEdgeLen) / settings.mCellSize));
result.mSampleDist = settings.mDetailSampleDist < 0.9f ? 0 : settings.mCellSize * settings.mDetailSampleDist; result.mSampleDist = settings.mDetailSampleDist < 0.9f ? 0 : settings.mCellSize * settings.mDetailSampleDist;
result.mSampleMaxError = settings.mCellHeight * settings.mDetailSampleMaxError; result.mSampleMaxError = settings.mCellHeight * settings.mDetailSampleMaxError;
@ -227,7 +227,7 @@ namespace
); );
} }
bool rasterizeTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const std::vector<CellWater>& water, bool rasterizeTriangles(rcContext& context, float agentHalfExtentsZ, const std::vector<CellWater>& water,
const RecastSettings& settings, const RecastParams& params, const TileBounds& realTileBounds, rcHeightfield& solid) const RecastSettings& settings, const RecastParams& params, const TileBounds& realTileBounds, rcHeightfield& solid)
{ {
for (const CellWater& cellWater : water) for (const CellWater& cellWater : water)
@ -237,7 +237,7 @@ namespace
{ {
const Rectangle rectangle { const Rectangle rectangle {
toNavMeshCoordinates(settings, *intersection), toNavMeshCoordinates(settings, *intersection),
toNavMeshCoordinates(settings, getSwimLevel(settings, cellWater.mWater.mLevel, agentHalfExtents.z())) toNavMeshCoordinates(settings, getSwimLevel(settings, cellWater.mWater.mLevel, agentHalfExtentsZ))
}; };
if (!rasterizeTriangles(context, rectangle, AreaType_water, params, solid)) if (!rasterizeTriangles(context, rectangle, AreaType_water, params, solid))
return false; return false;
@ -277,12 +277,12 @@ namespace
return true; return true;
} }
bool rasterizeTriangles(rcContext& context, const TilePosition& tilePosition, const osg::Vec3f& agentHalfExtents, bool rasterizeTriangles(rcContext& context, const TilePosition& tilePosition, float agentHalfExtentsZ,
const RecastMesh& recastMesh, const RecastSettings& settings, const RecastParams& params, rcHeightfield& solid) const RecastMesh& recastMesh, const RecastSettings& settings, const RecastParams& params, rcHeightfield& solid)
{ {
const TileBounds realTileBounds = makeRealTileBoundsWithBorder(settings, tilePosition); const TileBounds realTileBounds = makeRealTileBoundsWithBorder(settings, tilePosition);
return rasterizeTriangles(context, recastMesh.getMesh(), settings, params, solid) return rasterizeTriangles(context, recastMesh.getMesh(), settings, params, solid)
&& rasterizeTriangles(context, agentHalfExtents, recastMesh.getWater(), settings, params, realTileBounds, solid) && rasterizeTriangles(context, agentHalfExtentsZ, recastMesh.getWater(), settings, params, realTileBounds, solid)
&& rasterizeTriangles(context, recastMesh.getHeightfields(), settings, params, solid) && rasterizeTriangles(context, recastMesh.getHeightfields(), settings, params, solid)
&& rasterizeTriangles(context, realTileBounds, recastMesh.getFlatHeightfields(), settings, params, solid); && rasterizeTriangles(context, realTileBounds, recastMesh.getFlatHeightfields(), settings, params, solid);
} }
@ -389,7 +389,7 @@ namespace
return power; return power;
} }
std::pair<float, float> getBoundsByZ(const RecastMesh& recastMesh, const osg::Vec3f& agentHalfExtents, const RecastSettings& settings) std::pair<float, float> getBoundsByZ(const RecastMesh& recastMesh, float agentHalfExtentsZ, const RecastSettings& settings)
{ {
float minZ = 0; float minZ = 0;
float maxZ = 0; float maxZ = 0;
@ -403,7 +403,7 @@ namespace
for (const CellWater& water : recastMesh.getWater()) for (const CellWater& water : recastMesh.getWater())
{ {
const float swimLevel = getSwimLevel(settings, water.mWater.mLevel, agentHalfExtents.z()); const float swimLevel = getSwimLevel(settings, water.mWater.mLevel, agentHalfExtentsZ);
minZ = std::min(minZ, swimLevel); minZ = std::min(minZ, swimLevel);
maxZ = std::max(maxZ, swimLevel); maxZ = std::max(maxZ, swimLevel);
} }
@ -431,19 +431,19 @@ namespace
namespace DetourNavigator namespace DetourNavigator
{ {
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh,
const TilePosition& tilePosition, const osg::Vec3f& agentHalfExtents, const RecastSettings& settings) const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings)
{ {
rcContext context; rcContext context;
const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentHalfExtents, settings); const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentBounds.mHalfExtents.z(), settings);
rcHeightfield solid; rcHeightfield solid;
initHeightfield(context, tilePosition, toNavMeshCoordinates(settings, minZ), initHeightfield(context, tilePosition, toNavMeshCoordinates(settings, minZ),
toNavMeshCoordinates(settings, maxZ), settings, solid); toNavMeshCoordinates(settings, maxZ), settings, solid);
const RecastParams params = makeRecastParams(settings, agentHalfExtents); const RecastParams params = makeRecastParams(settings, agentBounds);
if (!rasterizeTriangles(context, tilePosition, agentHalfExtents, recastMesh, settings, params, solid)) if (!rasterizeTriangles(context, tilePosition, agentBounds.mHalfExtents.z(), recastMesh, settings, params, solid))
return nullptr; return nullptr;
rcFilterLowHangingWalkableObstacles(&context, params.mWalkableClimb, solid); rcFilterLowHangingWalkableObstacles(&context, params.mWalkableClimb, solid);
@ -462,11 +462,11 @@ namespace DetourNavigator
} }
NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data,
const std::vector<OffMeshConnection>& offMeshConnections, const osg::Vec3f& agentHalfExtents, const std::vector<OffMeshConnection>& offMeshConnections, const AgentBounds& agentBounds,
const TilePosition& tile, const RecastSettings& settings) const TilePosition& tile, const RecastSettings& settings)
{ {
const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); const auto offMeshConVerts = getOffMeshVerts(offMeshConnections);
const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentBounds));
const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0); const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0);
const std::vector<unsigned char> offMeshConAreas = getOffMeshConAreas(offMeshConnections); const std::vector<unsigned char> offMeshConAreas = getOffMeshConAreas(offMeshConnections);
const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections); const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections);
@ -491,8 +491,8 @@ namespace DetourNavigator
params.offMeshConFlags = offMeshConFlags.data(); params.offMeshConFlags = offMeshConFlags.data();
params.offMeshConUserID = nullptr; params.offMeshConUserID = nullptr;
params.offMeshConCount = static_cast<int>(offMeshConnections.size()); params.offMeshConCount = static_cast<int>(offMeshConnections.size());
params.walkableHeight = getHeight(settings, agentHalfExtents); params.walkableHeight = getHeight(settings, agentBounds);
params.walkableRadius = getRadius(settings, agentHalfExtents); params.walkableRadius = getRadius(settings, agentBounds);
params.walkableClimb = getMaxClimb(settings); params.walkableClimb = getMaxClimb(settings);
rcVcopy(params.bmin, data.mPolyMesh.bmin); rcVcopy(params.bmin, data.mPolyMesh.bmin);
rcVcopy(params.bmax, data.mPolyMesh.bmax); rcVcopy(params.bmax, data.mPolyMesh.bmax);

View file

@ -51,10 +51,10 @@ namespace DetourNavigator
} }
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh,
const TilePosition& tilePosition, const osg::Vec3f& agentHalfExtents, const RecastSettings& settings); const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings);
NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data,
const std::vector<OffMeshConnection>& offMeshConnections, const osg::Vec3f& agentHalfExtents, const std::vector<OffMeshConnection>& offMeshConnections, const AgentBounds& agentBounds,
const TilePosition& tile, const RecastSettings& settings); const TilePosition& tile, const RecastSettings& settings);
NavMeshPtr makeEmptyNavMesh(const Settings& settings); NavMeshPtr makeEmptyNavMesh(const Settings& settings);

View file

@ -26,6 +26,7 @@ namespace Loading
namespace DetourNavigator namespace DetourNavigator
{ {
struct Settings; struct Settings;
struct AgentBounds;
struct ObjectShapes struct ObjectShapes
{ {
@ -66,16 +67,16 @@ namespace DetourNavigator
/** /**
* @brief addAgent should be called for each agent even if all of them has same half extents. * @brief addAgent should be called for each agent even if all of them has same half extents.
* @param agentHalfExtents allows to setup bounding cylinder for each agent, for each different half extents * @param agentBounds allows to setup bounding cylinder for each agent, for each different half extents
* there is different navmesh. * there is different navmesh.
*/ */
virtual void addAgent(const osg::Vec3f& agentHalfExtents) = 0; virtual void addAgent(const AgentBounds& agentBounds) = 0;
/** /**
* @brief removeAgent should be called for each agent even if all of them has same half extents * @brief removeAgent should be called for each agent even if all of them has same half extents
* @param agentHalfExtents allows determine which agent to remove * @param agentBounds allows determine which agent to remove
*/ */
virtual void removeAgent(const osg::Vec3f& agentHalfExtents) = 0; virtual void removeAgent(const AgentBounds& agentBounds) = 0;
/** /**
* @brief setWorldspace should be called before adding object from new worldspace * @brief setWorldspace should be called before adding object from new worldspace
@ -185,13 +186,13 @@ namespace DetourNavigator
* @brief getNavMesh returns navmesh for specific agent half extents * @brief getNavMesh returns navmesh for specific agent half extents
* @return navmesh * @return navmesh
*/ */
virtual SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const = 0; virtual SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const = 0;
/** /**
* @brief getNavMeshes returns all current navmeshes * @brief getNavMeshes returns all current navmeshes
* @return map of agent half extents to navmesh * @return map of agent half extents to navmesh
*/ */
virtual std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const = 0; virtual std::map<AgentBounds, SharedNavMeshCacheItem> getNavMeshes() const = 0;
virtual const Settings& getSettings() const = 0; virtual const Settings& getSettings() const = 0;

View file

@ -16,17 +16,17 @@ namespace DetourNavigator
{ {
} }
void NavigatorImpl::addAgent(const osg::Vec3f& agentHalfExtents) void NavigatorImpl::addAgent(const AgentBounds& agentBounds)
{ {
if(agentHalfExtents.length2() <= 0) if(agentBounds.mHalfExtents.length2() <= 0)
return; return;
++mAgents[agentHalfExtents]; ++mAgents[agentBounds];
mNavMeshManager.addAgent(agentHalfExtents); mNavMeshManager.addAgent(agentBounds);
} }
void NavigatorImpl::removeAgent(const osg::Vec3f& agentHalfExtents) void NavigatorImpl::removeAgent(const AgentBounds& agentBounds)
{ {
const auto it = mAgents.find(agentHalfExtents); const auto it = mAgents.find(agentBounds);
if (it == mAgents.end()) if (it == mAgents.end())
return; return;
if (it->second > 0) if (it->second > 0)
@ -178,12 +178,12 @@ namespace DetourNavigator
mNavMeshManager.wait(listener, waitConditionType); mNavMeshManager.wait(listener, waitConditionType);
} }
SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const AgentBounds& agentBounds) const
{ {
return mNavMeshManager.getNavMesh(agentHalfExtents); return mNavMeshManager.getNavMesh(agentBounds);
} }
std::map<osg::Vec3f, SharedNavMeshCacheItem> NavigatorImpl::getNavMeshes() const std::map<AgentBounds, SharedNavMeshCacheItem> NavigatorImpl::getNavMeshes() const
{ {
return mNavMeshManager.getNavMeshes(); return mNavMeshManager.getNavMeshes();
} }

View file

@ -18,9 +18,9 @@ namespace DetourNavigator
*/ */
explicit NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db); explicit NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db);
void addAgent(const osg::Vec3f& agentHalfExtents) override; void addAgent(const AgentBounds& agentBounds) override;
void removeAgent(const osg::Vec3f& agentHalfExtents) override; void removeAgent(const AgentBounds& agentBounds) override;
void setWorldspace(std::string_view worldspace) override; void setWorldspace(std::string_view worldspace) override;
@ -56,9 +56,9 @@ namespace DetourNavigator
void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override;
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const override;
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override; std::map<AgentBounds, SharedNavMeshCacheItem> getNavMeshes() const override;
const Settings& getSettings() const override; const Settings& getSettings() const override;
@ -73,7 +73,7 @@ namespace DetourNavigator
NavMeshManager mNavMeshManager; NavMeshManager mNavMeshManager;
bool mUpdatesEnabled; bool mUpdatesEnabled;
std::optional<TilePosition> mLastPlayerPosition; std::optional<TilePosition> mLastPlayerPosition;
std::map<osg::Vec3f, std::size_t> mAgents; std::map<AgentBounds, std::size_t> mAgents;
std::unordered_map<ObjectId, ObjectId> mAvoidIds; std::unordered_map<ObjectId, ObjectId> mAvoidIds;
std::unordered_map<ObjectId, ObjectId> mWaterIds; std::unordered_map<ObjectId, ObjectId> mWaterIds;

View file

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORSTUB_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORSTUB_H
#include "navigator.hpp" #include "navigator.hpp"
#include "settings.hpp"
namespace Loading namespace Loading
{ {
@ -15,9 +16,9 @@ namespace DetourNavigator
public: public:
NavigatorStub() = default; NavigatorStub() = default;
void addAgent(const osg::Vec3f& /*agentHalfExtents*/) override {} void addAgent(const AgentBounds& /*agentBounds*/) override {}
void removeAgent(const osg::Vec3f& /*agentHalfExtents*/) override {} void removeAgent(const AgentBounds& /*agentBounds*/) override {}
void setWorldspace(std::string_view /*worldspace*/) override {} void setWorldspace(std::string_view /*worldspace*/) override {}
@ -80,14 +81,14 @@ namespace DetourNavigator
void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {}
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override
{ {
return mEmptyNavMeshCacheItem; return mEmptyNavMeshCacheItem;
} }
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override std::map<AgentBounds, SharedNavMeshCacheItem> getNavMeshes() const override
{ {
return std::map<osg::Vec3f, SharedNavMeshCacheItem>(); return {};
} }
const Settings& getSettings() const override const Settings& getSettings() const override

View file

@ -5,30 +5,30 @@
namespace DetourNavigator namespace DetourNavigator
{ {
std::optional<osg::Vec3f> findRandomPointAroundCircle(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, std::optional<osg::Vec3f> findRandomPointAroundCircle(const Navigator& navigator, const AgentBounds& agentBounds,
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, float(*prng)()) const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, float(*prng)())
{ {
const auto navMesh = navigator.getNavMesh(agentHalfExtents); const auto navMesh = navigator.getNavMesh(agentBounds);
if (!navMesh) if (!navMesh)
return std::nullopt; return std::nullopt;
const auto& settings = navigator.getSettings(); const auto& settings = navigator.getSettings();
const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(), const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(),
toNavMeshCoordinates(settings.mRecast, agentHalfExtents), toNavMeshCoordinates(settings.mRecast, start), toNavMeshCoordinates(settings.mRecast, agentBounds.mHalfExtents), toNavMeshCoordinates(settings.mRecast, start),
toNavMeshCoordinates(settings.mRecast, maxRadius), includeFlags, settings.mDetour, prng); toNavMeshCoordinates(settings.mRecast, maxRadius), includeFlags, settings.mDetour, prng);
if (!result) if (!result)
return std::nullopt; return std::nullopt;
return std::optional<osg::Vec3f>(fromNavMeshCoordinates(settings.mRecast, *result)); return std::optional<osg::Vec3f>(fromNavMeshCoordinates(settings.mRecast, *result));
} }
std::optional<osg::Vec3f> raycast(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, std::optional<osg::Vec3f> raycast(const Navigator& navigator, const AgentBounds& agentBounds, const osg::Vec3f& start,
const osg::Vec3f& end, const Flags includeFlags) const osg::Vec3f& end, const Flags includeFlags)
{ {
const auto navMesh = navigator.getNavMesh(agentHalfExtents); const auto navMesh = navigator.getNavMesh(agentBounds);
if (navMesh == nullptr) if (navMesh == nullptr)
return std::nullopt; return std::nullopt;
const auto& settings = navigator.getSettings(); const auto& settings = navigator.getSettings();
const auto result = DetourNavigator::raycast(navMesh->lockConst()->getImpl(), const auto result = DetourNavigator::raycast(navMesh->lockConst()->getImpl(),
toNavMeshCoordinates(settings.mRecast, agentHalfExtents), toNavMeshCoordinates(settings.mRecast, start), toNavMeshCoordinates(settings.mRecast, agentBounds.mHalfExtents), toNavMeshCoordinates(settings.mRecast, start),
toNavMeshCoordinates(settings.mRecast, end), includeFlags, settings.mDetour); toNavMeshCoordinates(settings.mRecast, end), includeFlags, settings.mDetour);
if (!result) if (!result)
return std::nullopt; return std::nullopt;

View file

@ -12,7 +12,7 @@ namespace DetourNavigator
{ {
/** /**
* @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through. * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through.
* @param agentHalfExtents allows to find navmesh for given actor. * @param agentBounds allows to find navmesh for given actor.
* @param start path from given point. * @param start path from given point.
* @param end path at given point. * @param end path at given point.
* @param includeFlags setup allowed surfaces for actor to walk. * @param includeFlags setup allowed surfaces for actor to walk.
@ -22,8 +22,8 @@ namespace DetourNavigator
* Equal to out if no path is found. * Equal to out if no path is found.
*/ */
template <class OutputIterator> template <class OutputIterator>
inline Status findPath(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, inline Status findPath(const Navigator& navigator, const AgentBounds& agentBounds, const float stepSize,
const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts, const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
float endTolerance, OutputIterator& out) float endTolerance, OutputIterator& out)
{ {
static_assert( static_assert(
@ -33,35 +33,35 @@ namespace DetourNavigator
>::value, >::value,
"out is not an OutputIterator" "out is not an OutputIterator"
); );
const auto navMesh = navigator.getNavMesh(agentHalfExtents); const auto navMesh = navigator.getNavMesh(agentBounds);
if (navMesh == nullptr) if (navMesh == nullptr)
return Status::NavMeshNotFound; return Status::NavMeshNotFound;
const auto settings = navigator.getSettings(); const auto settings = navigator.getSettings();
return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings.mRecast, agentHalfExtents), return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings.mRecast, agentBounds.mHalfExtents),
toNavMeshCoordinates(settings.mRecast, stepSize), toNavMeshCoordinates(settings.mRecast, start), toNavMeshCoordinates(settings.mRecast, stepSize), toNavMeshCoordinates(settings.mRecast, start),
toNavMeshCoordinates(settings.mRecast, end), includeFlags, areaCosts, settings, endTolerance, out); toNavMeshCoordinates(settings.mRecast, end), includeFlags, areaCosts, settings, endTolerance, out);
} }
/** /**
* @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location. * @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.
* @param agentHalfExtents allows to find navmesh for given actor. * @param agentBounds allows to find navmesh for given actor.
* @param start path from given point. * @param start path from given point.
* @param maxRadius limit maximum distance from start. * @param maxRadius limit maximum distance from start.
* @param includeFlags setup allowed surfaces for actor to walk. * @param includeFlags setup allowed surfaces for actor to walk.
* @return not empty optional with position if point is found and empty optional if point is not found. * @return not empty optional with position if point is found and empty optional if point is not found.
*/ */
std::optional<osg::Vec3f> findRandomPointAroundCircle(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, std::optional<osg::Vec3f> findRandomPointAroundCircle(const Navigator& navigator, const AgentBounds& agentBounds,
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, float(*prng)()); const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, float(*prng)());
/** /**
* @brief raycast finds farest navmesh point from start on a line from start to end that has path from start. * @brief raycast finds farest navmesh point from start on a line from start to end that has path from start.
* @param agentHalfExtents allows to find navmesh for given actor. * @param agentBounds allows to find navmesh for given actor.
* @param start of the line * @param start of the line
* @param end of the line * @param end of the line
* @param includeFlags setup allowed surfaces for actor to walk. * @param includeFlags setup allowed surfaces for actor to walk.
* @return not empty optional with position if point is found and empty optional if point is not found. * @return not empty optional with position if point is found and empty optional if point is not found.
*/ */
std::optional<osg::Vec3f> raycast(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, std::optional<osg::Vec3f> raycast(const Navigator& navigator, const AgentBounds& agentBounds, const osg::Vec3f& start,
const osg::Vec3f& end, const Flags includeFlags); const osg::Vec3f& end, const Flags includeFlags);
} }

View file

@ -151,27 +151,27 @@ namespace DetourNavigator
return true; return true;
} }
void NavMeshManager::addAgent(const osg::Vec3f& agentHalfExtents) void NavMeshManager::addAgent(const AgentBounds& agentBounds)
{ {
auto cached = mCache.find(agentHalfExtents); auto cached = mCache.find(agentBounds);
if (cached != mCache.end()) if (cached != mCache.end())
return; return;
mCache.insert(std::make_pair(agentHalfExtents, mCache.insert(std::make_pair(agentBounds,
std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter))); std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));
Log(Debug::Debug) << "cache add for agent=" << agentHalfExtents; Log(Debug::Debug) << "cache add for agent=" << agentBounds;
} }
bool NavMeshManager::reset(const osg::Vec3f& agentHalfExtents) bool NavMeshManager::reset(const AgentBounds& agentBounds)
{ {
const auto it = mCache.find(agentHalfExtents); const auto it = mCache.find(agentBounds);
if (it == mCache.end()) if (it == mCache.end())
return true; return true;
if (!resetIfUnique(it->second)) if (!resetIfUnique(it->second))
return false; return false;
mCache.erase(agentHalfExtents); mCache.erase(agentBounds);
mChangedTiles.erase(agentHalfExtents); mChangedTiles.erase(agentBounds);
mPlayerTile.erase(agentHalfExtents); mPlayerTile.erase(agentBounds);
mLastRecastMeshManagerRevision.erase(agentHalfExtents); mLastRecastMeshManagerRevision.erase(agentBounds);
return true; return true;
} }
@ -195,28 +195,28 @@ namespace DetourNavigator
addChangedTile(tile, ChangeType::update); addChangedTile(tile, ChangeType::update);
} }
void NavMeshManager::update(const osg::Vec3f& playerPosition, const osg::Vec3f& agentHalfExtents) void NavMeshManager::update(const osg::Vec3f& playerPosition, const AgentBounds& agentBounds)
{ {
const auto playerTile = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition)); const auto playerTile = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition));
auto& lastRevision = mLastRecastMeshManagerRevision[agentHalfExtents]; auto& lastRevision = mLastRecastMeshManagerRevision[agentBounds];
auto lastPlayerTile = mPlayerTile.find(agentHalfExtents); auto lastPlayerTile = mPlayerTile.find(agentBounds);
if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end() if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end()
&& lastPlayerTile->second == playerTile) && lastPlayerTile->second == playerTile)
return; return;
lastRevision = mRecastMeshManager.getRevision(); lastRevision = mRecastMeshManager.getRevision();
if (lastPlayerTile == mPlayerTile.end()) if (lastPlayerTile == mPlayerTile.end())
lastPlayerTile = mPlayerTile.insert(std::make_pair(agentHalfExtents, playerTile)).first; lastPlayerTile = mPlayerTile.insert(std::make_pair(agentBounds, playerTile)).first;
else else
lastPlayerTile->second = playerTile; lastPlayerTile->second = playerTile;
std::map<TilePosition, ChangeType> tilesToPost; std::map<TilePosition, ChangeType> tilesToPost;
const auto cached = getCached(agentHalfExtents); const auto cached = getCached(agentBounds);
if (!cached) if (!cached)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Agent with half extents is not found: " << agentHalfExtents; stream << "Agent with half extents is not found: " << agentBounds;
throw InvalidArgument(stream.str()); throw InvalidArgument(stream.str());
} }
const auto changedTiles = mChangedTiles.find(agentHalfExtents); const auto changedTiles = mChangedTiles.find(agentBounds);
{ {
const auto locked = cached->lockConst(); const auto locked = cached->lockConst();
const auto& navMesh = locked->getImpl(); const auto& navMesh = locked->getImpl();
@ -247,10 +247,10 @@ namespace DetourNavigator
recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0}); recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0});
}); });
} }
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost); mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost);
if (changedTiles != mChangedTiles.end()) if (changedTiles != mChangedTiles.end())
changedTiles->second.clear(); changedTiles->second.clear();
Log(Debug::Debug) << "Cache update posted for agent=" << agentHalfExtents << Log(Debug::Debug) << "Cache update posted for agent=" << agentBounds <<
" playerTile=" << lastPlayerTile->second << " playerTile=" << lastPlayerTile->second <<
" recastMeshManagerRevision=" << lastRevision; " recastMeshManagerRevision=" << lastRevision;
} }
@ -260,12 +260,12 @@ namespace DetourNavigator
mAsyncNavMeshUpdater.wait(listener, waitConditionType); mAsyncNavMeshUpdater.wait(listener, waitConditionType);
} }
SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const SharedNavMeshCacheItem NavMeshManager::getNavMesh(const AgentBounds& agentBounds) const
{ {
return getCached(agentHalfExtents); return getCached(agentBounds);
} }
std::map<osg::Vec3f, SharedNavMeshCacheItem> NavMeshManager::getNavMeshes() const std::map<AgentBounds, SharedNavMeshCacheItem> NavMeshManager::getNavMeshes() const
{ {
return mCache; return mCache;
} }
@ -319,9 +319,9 @@ namespace DetourNavigator
} }
} }
SharedNavMeshCacheItem NavMeshManager::getCached(const osg::Vec3f& agentHalfExtents) const SharedNavMeshCacheItem NavMeshManager::getCached(const AgentBounds& agentBounds) const
{ {
const auto cached = mCache.find(agentHalfExtents); const auto cached = mCache.find(agentBounds);
if (cached != mCache.end()) if (cached != mCache.end())
return cached->second; return cached->second;
return SharedNavMeshCacheItem(); return SharedNavMeshCacheItem();

View file

@ -7,7 +7,7 @@
#include "recastmeshtiles.hpp" #include "recastmeshtiles.hpp"
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include "heightfieldshape.hpp" #include "heightfieldshape.hpp"
#include "agentbounds.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
@ -35,7 +35,7 @@ namespace DetourNavigator
bool removeObject(const ObjectId id); bool removeObject(const ObjectId id);
void addAgent(const osg::Vec3f& agentHalfExtents); void addAgent(const AgentBounds& agentBounds);
bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level);
@ -45,19 +45,19 @@ namespace DetourNavigator
bool removeHeightfield(const osg::Vec2i& cellPosition); bool removeHeightfield(const osg::Vec2i& cellPosition);
bool reset(const osg::Vec3f& agentHalfExtents); bool reset(const AgentBounds& agentBounds);
void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType); void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType);
void removeOffMeshConnections(const ObjectId id); void removeOffMeshConnections(const ObjectId id);
void update(const osg::Vec3f& playerPosition, const osg::Vec3f& agentHalfExtents); void update(const osg::Vec3f& playerPosition, const AgentBounds& agentBounds);
void wait(Loading::Listener& listener, WaitConditionType waitConditionType); void wait(Loading::Listener& listener, WaitConditionType waitConditionType);
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const;
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const; std::map<AgentBounds, SharedNavMeshCacheItem> getNavMeshes() const;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
@ -69,11 +69,11 @@ namespace DetourNavigator
TileCachedRecastMeshManager mRecastMeshManager; TileCachedRecastMeshManager mRecastMeshManager;
OffMeshConnectionsManager mOffMeshConnectionsManager; OffMeshConnectionsManager mOffMeshConnectionsManager;
AsyncNavMeshUpdater mAsyncNavMeshUpdater; AsyncNavMeshUpdater mAsyncNavMeshUpdater;
std::map<osg::Vec3f, SharedNavMeshCacheItem> mCache; std::map<AgentBounds, SharedNavMeshCacheItem> mCache;
std::map<osg::Vec3f, std::map<TilePosition, ChangeType>> mChangedTiles; std::map<AgentBounds, std::map<TilePosition, ChangeType>> mChangedTiles;
std::size_t mGenerationCounter = 0; std::size_t mGenerationCounter = 0;
std::map<osg::Vec3f, TilePosition> mPlayerTile; std::map<AgentBounds, TilePosition> mPlayerTile;
std::map<osg::Vec3f, std::size_t> mLastRecastMeshManagerRevision; std::map<AgentBounds, std::size_t> mLastRecastMeshManagerRevision;
void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType); void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType);
@ -81,7 +81,7 @@ namespace DetourNavigator
void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType); void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType);
SharedNavMeshCacheItem getCached(const osg::Vec3f& agentHalfExtents) const; SharedNavMeshCacheItem getCached(const AgentBounds& agentBounds) const;
}; };
} }

View file

@ -10,14 +10,14 @@ namespace DetourNavigator
: mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0), : mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0),
mHitCount(0), mGetCount(0) {} mHitCount(0), mGetCount(0) {}
NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshTilesCache::Value NavMeshTilesCache::get(const AgentBounds& agentBounds, const TilePosition& changedTile,
const RecastMesh& recastMesh) const RecastMesh& recastMesh)
{ {
const std::lock_guard<std::mutex> lock(mMutex); const std::lock_guard<std::mutex> lock(mMutex);
++mGetCount; ++mGetCount;
const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, recastMesh)); const auto tile = mValues.find(std::make_tuple(agentBounds, changedTile, recastMesh));
if (tile == mValues.end()) if (tile == mValues.end())
return Value(); return Value();
@ -28,7 +28,7 @@ namespace DetourNavigator
return Value(*this, tile->second); return Value(*this, tile->second);
} }
NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshTilesCache::Value NavMeshTilesCache::set(const AgentBounds& agentBounds, const TilePosition& changedTile,
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value) const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value)
{ {
const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh) const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh)
@ -45,8 +45,8 @@ namespace DetourNavigator
RecastMeshData key {recastMesh.getMesh(), recastMesh.getWater(), RecastMeshData key {recastMesh.getMesh(), recastMesh.getWater(),
recastMesh.getHeightfields(), recastMesh.getFlatHeightfields()}; recastMesh.getHeightfields(), recastMesh.getFlatHeightfields()};
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize); const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentBounds, changedTile, std::move(key), itemSize);
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator); const auto emplaced = mValues.emplace(std::make_tuple(agentBounds, changedTile, std::cref(iterator->mRecastMeshData)), iterator);
if (!emplaced.second) if (!emplaced.second)
{ {
@ -92,7 +92,7 @@ namespace DetourNavigator
{ {
const auto& item = mFreeItems.back(); const auto& item = mFreeItems.back();
const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, std::cref(item.mRecastMeshData))); const auto value = mValues.find(std::make_tuple(item.mAgentBounds, item.mChangedTile, std::cref(item.mRecastMeshData)));
if (value == mValues.end()) if (value == mValues.end())
return; return;

View file

@ -4,6 +4,7 @@
#include "preparednavmeshdata.hpp" #include "preparednavmeshdata.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include "agentbounds.hpp"
#include <atomic> #include <atomic>
#include <map> #include <map>
@ -52,16 +53,16 @@ namespace DetourNavigator
struct Item struct Item
{ {
std::atomic<std::int64_t> mUseCount; std::atomic<std::int64_t> mUseCount;
osg::Vec3f mAgentHalfExtents; AgentBounds mAgentBounds;
TilePosition mChangedTile; TilePosition mChangedTile;
RecastMeshData mRecastMeshData; RecastMeshData mRecastMeshData;
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData; std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData;
std::size_t mSize; std::size_t mSize;
Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, Item(const AgentBounds& agentBounds, const TilePosition& changedTile,
RecastMeshData&& recastMeshData, std::size_t size) RecastMeshData&& recastMeshData, std::size_t size)
: mUseCount(0) : mUseCount(0)
, mAgentHalfExtents(agentHalfExtents) , mAgentBounds(agentBounds)
, mChangedTile(changedTile) , mChangedTile(changedTile)
, mRecastMeshData(std::move(recastMeshData)) , mRecastMeshData(std::move(recastMeshData))
, mSize(size) , mSize(size)
@ -136,10 +137,10 @@ namespace DetourNavigator
NavMeshTilesCache(const std::size_t maxNavMeshDataSize); NavMeshTilesCache(const std::size_t maxNavMeshDataSize);
Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, Value get(const AgentBounds& agentBounds, const TilePosition& changedTile,
const RecastMesh& recastMesh); const RecastMesh& recastMesh);
Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, Value set(const AgentBounds& agentBounds, const TilePosition& changedTile,
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value); const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value);
Stats getStats() const; Stats getStats() const;
@ -153,7 +154,7 @@ namespace DetourNavigator
std::size_t mGetCount; std::size_t mGetCount;
std::list<Item> mBusyItems; std::list<Item> mBusyItems;
std::list<Item> mFreeItems; std::list<Item> mFreeItems;
std::map<std::tuple<osg::Vec3f, TilePosition, std::reference_wrapper<const RecastMeshData>>, ItemIterator, std::less<>> mValues; std::map<std::tuple<AgentBounds, TilePosition, std::reference_wrapper<const RecastMeshData>>, ItemIterator, std::less<>> mValues;
void removeLeastRecentlyUsed(); void removeLeastRecentlyUsed();

View file

@ -1,20 +1,32 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H
#include "agentbounds.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
#include <cassert>
#include <cmath> #include <cmath>
#include <algorithm>
namespace DetourNavigator namespace DetourNavigator
{ {
inline float getAgentHeight(const osg::Vec3f& agentHalfExtents) inline float getAgentHeight(const AgentBounds& agentBounds)
{ {
return 2.0f * agentHalfExtents.z(); return 2.0f * agentBounds.mHalfExtents.z();
} }
inline float getAgentRadius(const osg::Vec3f& agentHalfExtents) inline float getAgentRadius(const AgentBounds& agentBounds)
{ {
return std::max(agentHalfExtents.x(), agentHalfExtents.y()) * std::sqrt(2); switch (agentBounds.mShapeType)
{
case CollisionShapeType::Aabb:
return std::max(agentBounds.mHalfExtents.x(), agentBounds.mHalfExtents.y()) * std::sqrt(2);
case CollisionShapeType::RotatingBox:
return agentBounds.mHalfExtents.x();
}
assert(false && "Unsupported agent shape type");
return 0;
} }
} }

View file

@ -5,6 +5,7 @@
#include "recast.hpp" #include "recast.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "agentbounds.hpp"
#include <components/serialization/binaryreader.hpp> #include <components/serialization/binaryreader.hpp>
#include <components/serialization/binarywriter.hpp> #include <components/serialization/binarywriter.hpp>
@ -136,12 +137,13 @@ namespace
} }
template <class Visitor> template <class Visitor>
void operator()(Visitor&& visitor, const RecastSettings& settings, const RecastMesh& recastMesh, void operator()(Visitor&& visitor, const RecastSettings& settings, const AgentBounds& agentBounds,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects) const const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects) const
{ {
visitor(*this, DetourNavigator::recastMeshMagic); visitor(*this, DetourNavigator::recastMeshMagic);
visitor(*this, DetourNavigator::recastMeshVersion); visitor(*this, DetourNavigator::recastMeshVersion);
visitor(*this, settings); visitor(*this, settings);
visitor(*this, agentBounds);
visitor(*this, recastMesh); visitor(*this, recastMesh);
visitor(*this, dbRefGeometryObjects); visitor(*this, dbRefGeometryObjects);
} }
@ -228,21 +230,28 @@ namespace
visitor(*this, value.mPolyMesh); visitor(*this, value.mPolyMesh);
visitor(*this, value.mPolyMeshDetail); visitor(*this, value.mPolyMeshDetail);
} }
template <class Visitor>
void operator()(Visitor&& visitor, const AgentBounds& value) const
{
visitor(*this, value.mShapeType);
visitor(*this, value.mHalfExtents);
}
}; };
} }
} // namespace DetourNavigator } // namespace DetourNavigator
namespace DetourNavigator namespace DetourNavigator
{ {
std::vector<std::byte> serialize(const RecastSettings& settings, const RecastMesh& recastMesh, std::vector<std::byte> serialize(const RecastSettings& settings, const AgentBounds& agentBounds,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects) const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects)
{ {
constexpr Format<Serialization::Mode::Write> format; constexpr Format<Serialization::Mode::Write> format;
Serialization::SizeAccumulator sizeAccumulator; Serialization::SizeAccumulator sizeAccumulator;
format(sizeAccumulator, settings, recastMesh, dbRefGeometryObjects); format(sizeAccumulator, settings, agentBounds, recastMesh, dbRefGeometryObjects);
std::vector<std::byte> result(sizeAccumulator.value()); std::vector<std::byte> result(sizeAccumulator.value());
format(Serialization::BinaryWriter(result.data(), result.data() + result.size()), format(Serialization::BinaryWriter(result.data(), result.data() + result.size()),
settings, recastMesh, dbRefGeometryObjects); settings, agentBounds, recastMesh, dbRefGeometryObjects);
return result; return result;
} }

View file

@ -11,15 +11,16 @@ namespace DetourNavigator
struct DbRefGeometryObject; struct DbRefGeometryObject;
struct PreparedNavMeshData; struct PreparedNavMeshData;
struct RecastSettings; struct RecastSettings;
struct AgentBounds;
constexpr char recastMeshMagic[] = {'r', 'c', 's', 't'}; constexpr char recastMeshMagic[] = {'r', 'c', 's', 't'};
constexpr std::uint32_t recastMeshVersion = 1; constexpr std::uint32_t recastMeshVersion = 2;
constexpr char preparedNavMeshDataMagic[] = {'p', 'n', 'a', 'v'}; constexpr char preparedNavMeshDataMagic[] = {'p', 'n', 'a', 'v'};
constexpr std::uint32_t preparedNavMeshDataVersion = 1; constexpr std::uint32_t preparedNavMeshDataVersion = 1;
std::vector<std::byte> serialize(const RecastSettings& settings, const RecastMesh& value, std::vector<std::byte> serialize(const RecastSettings& settings, const AgentBounds& agentBounds,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects); const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects);
std::vector<std::byte> serialize(const PreparedNavMeshData& value); std::vector<std::byte> serialize(const PreparedNavMeshData& value);

View file

@ -61,7 +61,6 @@ namespace DetourNavigator
result.mEnableRecastMeshFileNameRevision = ::Settings::Manager::getBool("enable recast mesh file name revision", "Navigator"); result.mEnableRecastMeshFileNameRevision = ::Settings::Manager::getBool("enable recast mesh file name revision", "Navigator");
result.mEnableNavMeshFileNameRevision = ::Settings::Manager::getBool("enable nav mesh file name revision", "Navigator"); result.mEnableNavMeshFileNameRevision = ::Settings::Manager::getBool("enable nav mesh file name revision", "Navigator");
result.mMinUpdateInterval = std::chrono::milliseconds(::Settings::Manager::getInt("min update interval ms", "Navigator")); result.mMinUpdateInterval = std::chrono::milliseconds(::Settings::Manager::getInt("min update interval ms", "Navigator"));
result.mNavMeshVersion = ::Settings::Manager::getInt("nav mesh version", "Navigator");
result.mEnableNavMeshDiskCache = ::Settings::Manager::getBool("enable nav mesh disk cache", "Navigator"); result.mEnableNavMeshDiskCache = ::Settings::Manager::getBool("enable nav mesh disk cache", "Navigator");
result.mWriteToNavMeshDb = ::Settings::Manager::getBool("write to navmeshdb", "Navigator"); result.mWriteToNavMeshDb = ::Settings::Manager::getBool("write to navmeshdb", "Navigator");
result.mMaxDbFileSize = static_cast<std::uint64_t>(::Settings::Manager::getInt64("max navmeshdb file size", "Navigator")); result.mMaxDbFileSize = static_cast<std::uint64_t>(::Settings::Manager::getInt64("max navmeshdb file size", "Navigator"));

View file

@ -50,10 +50,11 @@ namespace DetourNavigator
std::string mRecastMeshPathPrefix; std::string mRecastMeshPathPrefix;
std::string mNavMeshPathPrefix; std::string mNavMeshPathPrefix;
std::chrono::milliseconds mMinUpdateInterval; std::chrono::milliseconds mMinUpdateInterval;
std::int64_t mNavMeshVersion = 0;
std::uint64_t mMaxDbFileSize = 0; std::uint64_t mMaxDbFileSize = 0;
}; };
inline constexpr std::int64_t navMeshVersion = 2;
RecastSettings makeRecastSettingsFromSettingsManager(); RecastSettings makeRecastSettingsFromSettingsManager();
DetourSettings makeDetourSettingsFromSettingsManager(); DetourSettings makeDetourSettingsFromSettingsManager();

View file

@ -37,7 +37,7 @@ namespace
namespace SceneUtil namespace SceneUtil
{ {
osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path, osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end, const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end,
const DetourNavigator::RecastSettings& settings) const DetourNavigator::RecastSettings& settings)
{ {
using namespace DetourNavigator; using namespace DetourNavigator;
@ -46,8 +46,8 @@ namespace SceneUtil
DebugDraw debugDraw(*group, DebugDraw::makeStateSet(), osg::Vec3f(0, 0, 0), 1); DebugDraw debugDraw(*group, DebugDraw::makeStateSet(), osg::Vec3f(0, 0, 0), 1);
const auto agentRadius = DetourNavigator::getAgentRadius(halfExtents); const auto agentRadius = DetourNavigator::getAgentRadius(agentBounds);
const auto agentHeight = DetourNavigator::getAgentHeight(halfExtents); const auto agentHeight = DetourNavigator::getAgentHeight(agentBounds);
const auto agentClimb = settings.mMaxClimb; const auto agentClimb = settings.mMaxClimb;
const auto startColor = duRGBA(128, 25, 0, 192); const auto startColor = duRGBA(128, 25, 0, 192);
const auto endColor = duRGBA(51, 102, 0, 129); const auto endColor = duRGBA(51, 102, 0, 129);

View file

@ -14,12 +14,13 @@ namespace osg
namespace DetourNavigator namespace DetourNavigator
{ {
struct RecastSettings; struct RecastSettings;
struct AgentBounds;
} }
namespace SceneUtil namespace SceneUtil
{ {
osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path, osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end, const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end,
const DetourNavigator::RecastSettings& settings); const DetourNavigator::RecastSettings& settings);
} }

View file

@ -943,10 +943,6 @@ min update interval ms = 250
# Distance is measured in the number of tiles and can be only an integer value. # Distance is measured in the number of tiles and can be only an integer value.
wait until min distance to player = 5 wait until min distance to player = 5
# Version of navigation mesh generation algorithm.
# Should be increased each time there is a difference between output of makeNavMeshTileData function for the same input.
nav mesh version = 1
# Use navigation mesh cache stored on disk (true, false) # Use navigation mesh cache stored on disk (true, false)
enable nav mesh disk cache = true enable nav mesh disk cache = true