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

Support different agent collision shape type for pathfinding

Actors may have different collision shapes. Currently there are axis-aligned
bounding boxes and rotating bounding boxes. With AABB it's required to use
bounding cylinder for navmesh agent to avoid providing paths where actor can't
pass. But for rotating bounding boxes cylinder with diameter equal to the front
face width should be used to not reduce of available paths. For example rats
have rotating bounding box as collision shape because of the difference between
front and side faces width.

* Add agent bounds to navmesh tile db cache key. This is required to distinguish
  tiles for agents with different bounds.
* Increase navmesh version because navmesh tile db cache key and data has changed.
* Move navmesh version to the code to avoid misconfiguration by users.
* Fix all places where wrong half extents were used for pathfinding.
This commit is contained in:
elsid 2022-06-17 00:28:44 +02:00
parent 15c7ed774c
commit 1a12c453d6
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
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