1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 09:35:28 +00:00

Merge branch 'navmesh_agent_bounds' into 'master'

Support different agent collision shape type for pathfinding

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

View File

@ -13,7 +13,7 @@ namespace
struct Key
{
osg::Vec3f mAgentHalfExtents;
AgentBounds mAgentBounds;
TilePosition mTilePosition;
RecastMesh mRecastMesh;
};
@ -137,6 +137,7 @@ namespace
template <class 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 TilePosition tilePosition = generateVec2i(10000, 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);
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
{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;
@ -165,7 +166,7 @@ namespace
while (true)
{
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>());
*out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
@ -188,7 +189,7 @@ namespace
while (state.KeepRunning())
{
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);
}
}
@ -216,7 +217,7 @@ namespace
while (state.KeepRunning())
{
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>());
benchmark::DoNotOptimize(result);
}

View File

@ -22,6 +22,7 @@
#include <components/vfs/registerarchives.hpp>
#include <components/esm3/readerscache.hpp>
#include <components/platform/platform.hpp>
#include <components/detournavigator/agentbounds.hpp>
#include <osg/Vec3f>
@ -173,7 +174,9 @@ namespace NavMeshTool
Settings::Manager settings;
settings.load(config);
const DetourNavigator::CollisionShapeType agentCollisionShape = DetourNavigator::defaultCollisionShapeType;
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::string dbPath = (config.getUserDataPath() / "navmesh.db").string();
@ -201,7 +204,7 @@ namespace NavMeshTool
WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager,
esmData, processInteriorCells, writeBinaryLog);
const Status status = generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber,
const Status status = generateAllNavMeshTiles(agentBounds, navigatorSettings, threadsNumber,
removeUnusedTiles, writeBinaryLog, cellsData, std::move(db));
switch (status)

View File

@ -32,6 +32,7 @@ namespace NavMeshTool
{
namespace
{
using DetourNavigator::AgentBounds;
using DetourNavigator::GenerateNavMeshTile;
using DetourNavigator::NavMeshDb;
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,
NavMeshDb&& db)
{
@ -291,7 +292,7 @@ namespace NavMeshTool
input->mWorldspace,
tilePosition,
RecastMeshProvider(input->mTileCachedRecastMeshManager),
agentHalfExtents,
agentBounds,
settings,
navMeshTileConsumer
));

View File

@ -9,6 +9,7 @@ namespace DetourNavigator
{
class NavMeshDb;
struct Settings;
struct AgentBounds;
}
namespace NavMeshTool
@ -22,7 +23,7 @@ namespace NavMeshTool
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,
DetourNavigator::NavMeshDb&& db);
}

View File

@ -79,6 +79,7 @@ namespace MWMechanics
namespace DetourNavigator
{
struct Navigator;
struct AgentBounds;
}
namespace MWWorld
@ -644,14 +645,13 @@ namespace MWBase
virtual DetourNavigator::Navigator* getNavigator() const = 0;
virtual void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const = 0;
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 setNavMeshNumberToRender(const std::size_t value) = 0;
/// Return physical half extents of the given actor to be used in pathfinding
virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const = 0;
virtual DetourNavigator::AgentBounds getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) 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();
// 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 areaCosts = getAreaCosts(actor);
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);
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
// on navmesh to attack the target from there.
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 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);
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
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
//... 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[1] = 0;
world->updateActorPath(actor, mPathFinder.getPath(), world->getPathfindingHalfExtents(actor), position, dest);
world->updateActorPath(actor, mPathFinder.getPath(), agentBounds, position, dest);
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
{
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
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;
// 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, halfExtents);
const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration,
world->getHalfExtents(actor));
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE,
/*shortenIfAlmostStraight=*/smoothMovement, actorCanMoveByZ,
halfExtents, getNavigatorFlags(actor));
agentBounds, getNavigatorFlags(actor));
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())
return false;
world->updateActorPath(actor, mPathFinder.getPath(), world->getPathfindingHalfExtents(actor), position, dest);
world->updateActorPath(actor, mPathFinder.getPath(), agentBounds, position, dest);
if (mRotateOnTheRunChecks == 0
|| isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point

View File

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

View File

@ -71,7 +71,7 @@ namespace MWMechanics
const auto position = actor.getRefData().getPosition().asVec3();
const bool isWaterCreature = actor.getClass().isPureWaterCreature(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;
direction.normalize();
const auto visibleDestination = (
@ -210,10 +210,10 @@ namespace MWMechanics
}
else
{
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
const auto agentBounds = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(actor);
constexpr float endTolerance = 0;
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);
}
@ -345,7 +345,7 @@ namespace MWMechanics
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
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 navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
@ -358,7 +358,7 @@ namespace MWMechanics
if (!isWaterCreature && !isFlyingCreature)
{
// 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, []() {
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
return Misc::Rng::rollProbability(prng);
@ -385,7 +385,7 @@ namespace MWMechanics
if (isWaterCreature || isFlyingCreature)
mPathFinder.buildStraightPath(mDestination);
else
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags,
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, agentBounds, navigatorFlags,
areaCosts, endTolerance, PathType::Full);
if (mPathFinder.isPathConstructed())
@ -532,8 +532,8 @@ namespace MWMechanics
{
if (mUsePathgrid)
{
const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor),
const auto agentBounds = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(actor);
mPathFinder.buildPathByNavMeshToNextPoint(actor, agentBounds, getNavigatorFlags(actor),
getAreaCosts(actor));
}

View File

@ -3,6 +3,7 @@
#include <array>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/detournavigator/agentbounds.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
@ -80,7 +81,7 @@ namespace MWMechanics
std::vector<MWWorld::Ptr>* occupyingActors)
{
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()));
if (ignorePlayer)
{

View File

@ -109,12 +109,12 @@ namespace
struct IsValidShortcut
{
const DetourNavigator::Navigator* mNavigator;
const osg::Vec3f mHalfExtents;
const DetourNavigator::AgentBounds mAgentBounds;
const DetourNavigator::Flags mFlags;
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;
}
};
@ -307,8 +307,8 @@ namespace MWMechanics
}
void PathFinder::update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags)
bool shortenIfAlmostStraight, bool canMoveByZ, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags)
{
if (mPath.empty())
return;
@ -318,7 +318,7 @@ namespace MWMechanics
const IsValidShortcut isValidShortcut {
MWBase::Environment::get().getWorld()->getNavigator(),
halfExtents, flags
agentBounds, flags
};
if (shortenIfAlmostStraight)
@ -375,13 +375,13 @@ namespace MWMechanics
}
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)
{
mPath.clear();
// 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));
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,
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,
PathType pathType)
{
@ -405,7 +405,7 @@ namespace MWMechanics
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));
if (status != DetourNavigator::Status::Success)
mPath.clear();
@ -413,7 +413,7 @@ namespace MWMechanics
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));
if (status != DetourNavigator::Status::Success)
mPath.clear();
@ -429,14 +429,14 @@ namespace MWMechanics
}
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,
std::back_insert_iterator<std::deque<osg::Vec3f>> out)
{
const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor);
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);
if (pathType == PathType::Partial && status == DetourNavigator::Status::PartialPath)
@ -453,8 +453,9 @@ namespace MWMechanics
return status;
}
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor,
const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts)
{
if (mPath.empty())
return;
@ -469,7 +470,7 @@ namespace MWMechanics
std::deque<osg::Vec3f> prePath;
auto prePathInserter = std::back_inserter(prePath);
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);
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,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance,
PathType pathType)
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph,
const DetourNavigator::AgentBounds& agentBounds, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts, float endTolerance, PathType pathType)
{
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
const auto maxDistance = std::min(
@ -506,9 +507,9 @@ namespace MWMechanics
const auto startToEnd = endPoint - startPoint;
const auto distance = startToEnd.length();
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);
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;
}
namespace DetourNavigator
{
struct AgentBounds;
}
namespace MWMechanics
{
class PathgridGraph;
@ -98,25 +103,26 @@ namespace MWMechanics
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
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 osg::Vec3f& endPoint, const DetourNavigator::AgentBounds& agentBounds,
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts, float endTolerance,
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);
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,
PathType pathType);
/// Remove front point if exist and within tolerance
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);
bool checkPathCompleted() const
@ -219,7 +225,7 @@ namespace MWMechanics
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
[[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,
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));
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->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 <components/detournavigator/collisionshapetype.hpp>
#include <LinearMath/btTransform.h>
#include <osg/Vec3f>
#include <osg/Quat>
@ -156,6 +158,8 @@ namespace MWPhysics
void setActive(bool value) { mActive = value; }
DetourNavigator::CollisionShapeType getCollisionShapeType() const { return mCollisionShapeType; }
private:
MWWorld::Ptr mStandingOnPtr;
/// Removes then re-adds the collision object to the dynamics world
@ -171,6 +175,8 @@ namespace MWPhysics
bool mRotationallyInvariant;
DetourNavigator::CollisionShapeType mCollisionShapeType;
std::unique_ptr<btCollisionShape> mShape;
btConvexShape* mConvexShape;

View File

@ -38,7 +38,7 @@ namespace MWRender
}
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)
{
if (!mEnabled)
@ -48,7 +48,7 @@ namespace MWRender
if (group != mGroups.end())
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)
{
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(newGroup, "debug");

View File

@ -17,6 +17,7 @@ namespace osg
namespace DetourNavigator
{
struct Settings;
struct AgentBounds;
}
namespace MWRender
@ -30,7 +31,7 @@ namespace MWRender
bool toggle();
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);
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,
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

View File

@ -66,6 +66,7 @@ namespace DetourNavigator
{
struct Navigator;
struct Settings;
struct AgentBounds;
}
namespace MWWorld
@ -236,7 +237,7 @@ namespace MWRender
bool toggleBorders();
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;

View File

@ -187,7 +187,7 @@ namespace
}
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))
{
mNavigator.removeAgent(mWorld.getPathfindingHalfExtents(ptr));
mNavigator.removeAgent(mWorld.getPathfindingAgentBounds(ptr));
mRendering.removeActorPath(ptr);
mPhysics->remove(ptr);
}
@ -940,7 +940,7 @@ namespace MWWorld
}
else if (mPhysics->getActor(ptr))
{
mNavigator.removeAgent(mWorld.getPathfindingHalfExtents(ptr));
mNavigator.removeAgent(mWorld.getPathfindingAgentBounds(ptr));
}
mPhysics->remove(ptr);
mRendering.removeObject (ptr);

View File

@ -1275,7 +1275,7 @@ namespace MWWorld
if (!force && scale == ptr.getCellRef().getScale())
return;
if (mPhysics->getActor(ptr))
mNavigator->removeAgent(getPathfindingHalfExtents(ptr));
mNavigator->removeAgent(getPathfindingAgentBounds(ptr));
ptr.getCellRef().setScale(scale);
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
@ -1285,7 +1285,7 @@ namespace MWWorld
mWorldScene->updateObjectScale(ptr);
if (mPhysics->getActor(ptr))
mNavigator->addAgent(getPathfindingHalfExtents(ptr));
mNavigator->addAgent(getPathfindingAgentBounds(ptr));
else if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(*object);
}
@ -2416,7 +2416,7 @@ namespace MWWorld
{
// Remove the old CharacterController
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
mNavigator->removeAgent(getPathfindingHalfExtents(getPlayerConstPtr()));
mNavigator->removeAgent(getPathfindingAgentBounds(getPlayerConstPtr()));
mPhysics->remove(getPlayerPtr());
mRendering->removePlayer(getPlayerPtr());
MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(getPlayerPtr());
@ -2453,7 +2453,7 @@ namespace MWWorld
applyLoopingParticles(player);
mNavigator->addAgent(getPathfindingHalfExtents(getPlayerConstPtr()));
mNavigator->addAgent(getPathfindingAgentBounds(getPlayerConstPtr()));
}
World::RestPermitted World::canRest () const
@ -3898,9 +3898,9 @@ namespace MWWorld
}
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
@ -3913,12 +3913,13 @@ namespace MWWorld
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())
return mDefaultHalfExtents; // Using default half extents for better performance
const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
if (physicsActor == nullptr || (actor.isInCell() && actor.getCell()->isExterior()))
return DetourNavigator::AgentBounds {DetourNavigator::defaultCollisionShapeType, mDefaultHalfExtents};
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

View File

@ -727,14 +727,13 @@ namespace MWWorld
DetourNavigator::Navigator* getNavigator() const override;
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 setNavMeshNumberToRender(const std::size_t value) override;
/// Return physical half extents of the given actor to be used in pathfinding
osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const override;
DetourNavigator::AgentBounds getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) 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();
TileCachedRecastMeshManager mRecastMeshManager {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 std::string mWorldspace = "sys::default";
const btBoxShape mBox {btVector3(100, 100, 20)};
@ -76,7 +76,7 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
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);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
}
@ -88,14 +88,14 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
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);
{
const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1);
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);
{
const auto stats = updater.getStats();
@ -111,14 +111,14 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
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);
{
const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1);
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);
{
const auto stats = updater.getStats();
@ -138,7 +138,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0};
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.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -146,10 +146,11 @@ namespace
ShapeId nextShapeId {1};
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (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());
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)
@ -164,7 +165,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0};
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.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -172,7 +173,8 @@ namespace
ShapeId nextShapeId {1};
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (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());
}
@ -188,7 +190,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const TilePosition tilePosition {0, 0};
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.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
@ -207,7 +209,7 @@ namespace
std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()));
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
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);
{
const auto stats = updater.getStats();
@ -217,7 +219,7 @@ namespace
ASSERT_EQ(stats.mDb->mGetTileCount, 1);
ASSERT_EQ(stats.mDbGetTileHits, 0);
}
updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone);
{
const auto stats = updater.getStats();
@ -236,12 +238,12 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
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);
ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
const std::map<TilePosition, ChangeType> changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}};
const TilePosition playerTile(100, 100);
updater.post(mAgentHalfExtents, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove);
updater.post(mAgentBounds, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove);
updater.wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0);
}
@ -261,7 +263,7 @@ namespace
for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y)
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.stop();
const std::set<TilePosition> present {
@ -276,7 +278,6 @@ namespace
TilePosition(0, 2),
TilePosition(1, -1),
TilePosition(1, 0),
TilePosition(1, 1),
};
for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y)
@ -288,7 +289,8 @@ namespace
[&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v); });
if (!objects.has_value())
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())
<< tilePosition.x() << " " << tilePosition.y() << " present=" << (present.find(tilePosition) != present.end());
}

View File

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

View File

@ -95,7 +95,7 @@ namespace
struct DetourNavigatorNavMeshTilesCacheTest : Test
{
const osg::Vec3f mAgentHalfExtents {1, 2, 3};
const AgentBounds mAgentBounds {CollisionShapeType::Aabb, {1, 2, 3}};
const TilePosition mTilePosition {0, 0};
const std::size_t mGeneration = 0;
const std::size_t mRevision = 0;
@ -117,7 +117,7 @@ namespace
const std::size_t maxSize = 0;
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)
@ -125,7 +125,7 @@ namespace
const std::size_t maxSize = 0;
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);
}
@ -136,7 +136,7 @@ namespace
const auto copy = clone(*mPreparedNavMeshData);
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);
EXPECT_EQ(result.get(), *copy);
}
@ -148,9 +148,9 @@ namespace
auto copy = 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);
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);
EXPECT_EQ(result.get(), *sameCopy);
}
@ -161,8 +161,8 @@ namespace
NavMeshTilesCache cache(maxSize);
const auto copy = clone(*mPreparedNavMeshData);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(result);
EXPECT_EQ(result.get(), *copy);
}
@ -171,10 +171,10 @@ namespace
{
const std::size_t maxSize = 1;
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));
EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh));
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(absentAgentBounds, mTilePosition, mRecastMesh));
}
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value)
@ -183,8 +183,8 @@ namespace
NavMeshTilesCache cache(maxSize);
const TilePosition unexistentTilePosition {1, 1};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh));
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentBounds, unexistentTilePosition, mRecastMesh));
}
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 RecastMesh unexistentRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh));
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, unexistentRecastMesh));
}
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value)
@ -208,12 +208,12 @@ namespace
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto copy = clone(*anotherPreparedNavMeshData);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto result = cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherPreparedNavMeshData));
ASSERT_TRUE(result);
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)
@ -225,9 +225,9 @@ namespace
const RecastMesh anotherRecastMesh(mGeneration, mRevision, mMesh, water, mHeightfields, mFlatHeightfields, mSources);
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
const auto value = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherPreparedNavMeshData)));
}
@ -247,17 +247,17 @@ namespace
mHeightfields, mFlatHeightfields, mSources);
auto mostRecentlySetData = makePeparedNavMeshData(3);
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh,
ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, leastRecentlySetRecastMesh,
std::move(leastRecentlySetData)));
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh,
ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, mostRecentlySetRecastMesh,
std::move(mostRecentlySetData)));
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData));
EXPECT_EQ(result.get(), *copy);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh));
EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, leastRecentlySetRecastMesh));
EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mostRecentlySetRecastMesh));
}
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value)
@ -277,28 +277,28 @@ namespace
auto mostRecentlyUsedData = makePeparedNavMeshData(3);
const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData);
cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData));
cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData));
cache.set(mAgentBounds, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData));
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_EQ(value.get(), *leastRecentlyUsedCopy);
}
{
const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh);
const auto value = cache.get(mAgentBounds, mTilePosition, mostRecentlyUsedRecastMesh);
ASSERT_TRUE(value);
ASSERT_EQ(value.get(), *mostRecentlyUsedCopy);
}
const auto copy = clone(*mPreparedNavMeshData);
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
const auto result = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData));
EXPECT_EQ(result.get(), *copy);
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh));
EXPECT_FALSE(cache.get(mAgentBounds, mTilePosition, leastRecentlyUsedRecastMesh));
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)
@ -311,9 +311,9 @@ namespace
mHeightfields, mFlatHeightfields, mSources);
auto tooLargeData = makePeparedNavMeshData(10);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData)));
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)
@ -331,15 +331,15 @@ namespace
mHeightfields, mFlatHeightfields, mSources);
auto tooLargeData = makePeparedNavMeshData(10);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
const auto value = cache.set(mAgentBounds, mTilePosition, mRecastMesh,
std::move(mPreparedNavMeshData));
ASSERT_TRUE(value);
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
ASSERT_TRUE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh,
std::move(anotherData)));
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh,
EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, tooLargeRecastMesh,
std::move(tooLargeData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh));
EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, anotherRecastMesh));
}
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);
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);
{
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
const auto secondCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(secondCopy);
}
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh, std::move(anotherData)));
EXPECT_TRUE(cache.get(mAgentBounds, mTilePosition, mRecastMesh));
}
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);
auto anotherData = makePeparedNavMeshData(3);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
cache.set(mAgentBounds, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
const auto firstCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(firstCopy);
{
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
const auto secondCopy = cache.get(mAgentBounds, mTilePosition, mRecastMesh);
ASSERT_TRUE(secondCopy);
}
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData)));
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
EXPECT_FALSE(cache.set(mAgentBounds, mTilePosition, anotherRecastMesh, std::move(anotherData)));
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,
const std::set<std::tuple<osg::Vec3f, TilePosition>>& pushedTiles,
const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles)
const std::set<std::tuple<AgentBounds, TilePosition>>& pushedTiles,
const std::set<std::tuple<AgentBounds, TilePosition>>& presentTiles)
{
int result = maxDistance;
for (const auto& [halfExtents, tile] : pushedTiles)
if (presentTiles.find(std::tie(halfExtents, tile)) == presentTiles.end())
for (const auto& [agentBounds, tile] : pushedTiles)
if (presentTiles.find(std::tie(agentBounds, tile)) == presentTiles.end())
result = std::min(result, getManhattanDistance(position, tile));
return result;
}
@ -84,14 +84,14 @@ namespace DetourNavigator
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)
{
if (db == 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);
}
@ -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::chrono::steady_clock::time_point processTime)
: mId(getNextJobId())
, mAgentHalfExtents(agentHalfExtents)
, mAgentBounds(agentBounds)
, mNavMeshCacheItem(std::move(navMeshCacheItem))
, mWorldspace(worldspace)
, mChangedTile(changedTile)
@ -145,7 +145,7 @@ namespace DetourNavigator
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 std::map<TilePosition, ChangeType>& changedTiles)
{
@ -169,16 +169,16 @@ namespace DetourNavigator
for (const auto& [changedTile, changeType] : changedTiles)
{
if (mPushed.emplace(agentHalfExtents, changedTile).second)
if (mPushed.emplace(agentBounds, changedTile).second)
{
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();
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);
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 << ")";
if (playerTileChanged)
@ -342,7 +342,7 @@ namespace DetourNavigator
switch (status)
{
case JobStatus::Done:
unlockTile(job->mAgentHalfExtents, job->mChangedTile);
unlockTile(job->mAgentBounds, job->mChangedTile);
if (job->mGeneratedNavMeshData != nullptr)
mDbWorker->enqueueJob(job);
else
@ -419,7 +419,7 @@ namespace DetourNavigator
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;
const PreparedNavMeshData* preparedNavMeshDataPtr = nullptr;
@ -435,7 +435,7 @@ namespace DetourNavigator
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)
{
@ -450,7 +450,7 @@ namespace DetourNavigator
}
else
{
cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentHalfExtents, job.mChangedTile,
cachedNavMeshData = mNavMeshTilesCache.set(job.mAgentBounds, job.mChangedTile,
*recastMesh, std::move(preparedNavMeshData));
preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get();
}
@ -459,7 +459,7 @@ namespace DetourNavigator
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
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);
}
@ -471,7 +471,7 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> preparedNavMeshData;
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>();
if (deserialize(job.mCachedTileData->mData, *preparedNavMeshData))
@ -482,7 +482,7 @@ namespace DetourNavigator
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;
}
@ -493,14 +493,14 @@ namespace DetourNavigator
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));
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
const PreparedNavMeshData* preparedNavMeshDataPtr = cachedNavMeshData ? &cachedNavMeshData.get() : preparedNavMeshData.get();
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);
@ -522,12 +522,12 @@ namespace DetourNavigator
if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost)
{
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)
{
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);
@ -564,7 +564,7 @@ namespace DetourNavigator
if (job->mRecastMesh != nullptr)
return job;
if (!lockTile(job->mAgentHalfExtents, job->mChangedTile))
if (!lockTile(job->mAgentBounds, job->mChangedTile))
{
Log(Debug::Debug) << "Failed to lock tile by " << job->mId;
++job->mTryNumber;
@ -604,14 +604,14 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::repost(JobIt job)
{
unlockTile(job->mAgentHalfExtents, job->mChangedTile);
unlockTile(job->mAgentBounds, job->mChangedTile);
if (mShouldStop || job->mTryNumber > 2)
return;
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;
insertPrioritizedJob(job, mWaiting);
@ -622,17 +622,17 @@ namespace DetourNavigator
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 << ")";
return mProcessingTiles.lock()->emplace(agentHalfExtents, changedTile).second;
Log(Debug::Debug) << "Locking tile agent=" << agentBounds << " changedTile=(" << changedTile << ")";
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();
locked->erase(std::tie(agentHalfExtents, changedTile));
Log(Debug::Debug) << "Unlocked tile agent=(" << agentHalfExtents << ") changedTile=(" << changedTile << ")";
locked->erase(std::tie(agentBounds, changedTile));
Log(Debug::Debug) << "Unlocked tile agent=" << agentBounds << " changedTile=(" << changedTile << ")";
if (locked->empty())
mProcessed.notify_all();
}
@ -819,7 +819,7 @@ namespace DetourNavigator
{
const auto objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(),
[&] (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
{
@ -827,7 +827,7 @@ namespace DetourNavigator
[&] (const MeshSource& v) { return resolveMeshSource(*mDb, v); });
if (!objects.has_value())
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;
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(),
[&] (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)

View File

@ -9,6 +9,7 @@
#include "waitconditiontype.hpp"
#include "navmeshdb.hpp"
#include "changetype.hpp"
#include "agentbounds.hpp"
#include <osg/Vec3f>
@ -42,7 +43,7 @@ namespace DetourNavigator
struct Job
{
const std::size_t mId;
const osg::Vec3f mAgentHalfExtents;
const AgentBounds mAgentBounds;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
const std::string mWorldspace;
const TilePosition mChangedTile;
@ -57,7 +58,7 @@ namespace DetourNavigator
std::optional<TileData> mCachedTileData;
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::chrono::steady_clock::time_point processTime);
};
@ -166,7 +167,7 @@ namespace DetourNavigator
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db);
~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 std::map<TilePosition, ChangeType>& changedTiles);
@ -191,12 +192,12 @@ namespace DetourNavigator
std::condition_variable mProcessed;
std::list<Job> mJobs;
std::deque<JobIt> mWaiting;
std::set<std::tuple<osg::Vec3f, TilePosition>> mPushed;
std::set<std::tuple<AgentBounds, TilePosition>> mPushed;
Misc::ScopeGuarded<TilePosition> mPlayerTile;
NavMeshTilesCache mNavMeshTilesCache;
Misc::ScopeGuarded<std::set<std::tuple<osg::Vec3f, TilePosition>>> mProcessingTiles;
std::map<std::tuple<osg::Vec3f, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates;
std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles;
Misc::ScopeGuarded<std::set<std::tuple<AgentBounds, TilePosition>>> mProcessingTiles;
std::map<std::tuple<AgentBounds, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates;
std::set<std::tuple<AgentBounds, TilePosition>> mPresentTiles;
std::vector<std::thread> mThreads;
std::unique_ptr<DbWorker> mDbWorker;
std::atomic_size_t mDbGetTileHits {0};
@ -220,9 +221,9 @@ namespace DetourNavigator
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;

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 "status.hpp"
#include "recastmesh.hpp"
#include "agentbounds.hpp"
#include <osg/io_utils>
@ -69,6 +70,21 @@ namespace DetourNavigator
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;
struct RecastSettings;

View File

@ -38,12 +38,12 @@ namespace DetourNavigator
}
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)
: mWorldspace(std::move(worldspace))
, mTilePosition(tilePosition)
, mRecastMeshProvider(recastMeshProvider)
, mAgentHalfExtents(agentHalfExtents)
, mAgentBounds(agentBounds)
, mSettings(settings)
, mConsumer(std::move(consumer)) {}
@ -70,25 +70,25 @@ namespace DetourNavigator
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (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);
if (info.has_value() && info->mVersion == mSettings.mNavMeshVersion)
if (info.has_value() && info->mVersion == navMeshVersion)
{
consumer->identity(mWorldspace, mTilePosition, info->mTileId);
ignore.mConsumer = nullptr;
return;
}
const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentHalfExtents, mSettings.mRecast);
const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentBounds, mSettings.mRecast);
if (data == nullptr)
return;
if (info.has_value())
consumer->update(mWorldspace, mTilePosition, info->mTileId, mSettings.mNavMeshVersion, *data);
consumer->update(mWorldspace, mTilePosition, info->mTileId, navMeshVersion, *data);
else
consumer->insert(mWorldspace, mTilePosition, mSettings.mNavMeshVersion, input, *data);
consumer->insert(mWorldspace, mTilePosition, navMeshVersion, input, *data);
ignore.mConsumer = nullptr;
}

View File

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

View File

@ -101,9 +101,9 @@ namespace
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)
@ -111,14 +111,14 @@ namespace
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)
{
return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ;;
return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ;
}
struct RecastParams
@ -131,13 +131,13 @@ namespace
int mWalkableRadius = 0;
};
RecastParams makeRecastParams(const RecastSettings& settings, const osg::Vec3f& agentHalfExtents)
RecastParams makeRecastParams(const RecastSettings& settings, const AgentBounds& agentBounds)
{
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.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.mSampleDist = settings.mDetailSampleDist < 0.9f ? 0 : settings.mCellSize * settings.mDetailSampleDist;
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)
{
for (const CellWater& cellWater : water)
@ -237,7 +237,7 @@ namespace
{
const Rectangle rectangle {
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))
return false;
@ -277,12 +277,12 @@ namespace
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 TileBounds realTileBounds = makeRealTileBoundsWithBorder(settings, tilePosition);
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, realTileBounds, recastMesh.getFlatHeightfields(), settings, params, solid);
}
@ -389,7 +389,7 @@ namespace
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 maxZ = 0;
@ -403,7 +403,7 @@ namespace
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);
maxZ = std::max(maxZ, swimLevel);
}
@ -431,19 +431,19 @@ namespace
namespace DetourNavigator
{
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;
const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentHalfExtents, settings);
const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentBounds.mHalfExtents.z(), settings);
rcHeightfield solid;
initHeightfield(context, tilePosition, toNavMeshCoordinates(settings, minZ),
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;
rcFilterLowHangingWalkableObstacles(&context, params.mWalkableClimb, solid);
@ -462,11 +462,11 @@ namespace DetourNavigator
}
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 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> offMeshConAreas = getOffMeshConAreas(offMeshConnections);
const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections);
@ -491,8 +491,8 @@ namespace DetourNavigator
params.offMeshConFlags = offMeshConFlags.data();
params.offMeshConUserID = nullptr;
params.offMeshConCount = static_cast<int>(offMeshConnections.size());
params.walkableHeight = getHeight(settings, agentHalfExtents);
params.walkableRadius = getRadius(settings, agentHalfExtents);
params.walkableHeight = getHeight(settings, agentBounds);
params.walkableRadius = getRadius(settings, agentBounds);
params.walkableClimb = getMaxClimb(settings);
rcVcopy(params.bmin, data.mPolyMesh.bmin);
rcVcopy(params.bmax, data.mPolyMesh.bmax);

View File

@ -51,10 +51,10 @@ namespace DetourNavigator
}
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,
const std::vector<OffMeshConnection>& offMeshConnections, const osg::Vec3f& agentHalfExtents,
const std::vector<OffMeshConnection>& offMeshConnections, const AgentBounds& agentBounds,
const TilePosition& tile, const RecastSettings& settings);
NavMeshPtr makeEmptyNavMesh(const Settings& settings);

View File

@ -26,6 +26,7 @@ namespace Loading
namespace DetourNavigator
{
struct Settings;
struct AgentBounds;
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.
* @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.
*/
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
* @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
@ -185,13 +186,13 @@ namespace DetourNavigator
* @brief getNavMesh returns navmesh for specific agent half extents
* @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
* @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;

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;
++mAgents[agentHalfExtents];
mNavMeshManager.addAgent(agentHalfExtents);
++mAgents[agentBounds];
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())
return;
if (it->second > 0)
@ -178,12 +178,12 @@ namespace DetourNavigator
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();
}

View File

@ -18,9 +18,9 @@ namespace DetourNavigator
*/
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;
@ -56,9 +56,9 @@ namespace DetourNavigator
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;
@ -73,7 +73,7 @@ namespace DetourNavigator
NavMeshManager mNavMeshManager;
bool mUpdatesEnabled;
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> mWaterIds;

View File

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORSTUB_H
#include "navigator.hpp"
#include "settings.hpp"
namespace Loading
{
@ -15,9 +16,9 @@ namespace DetourNavigator
public:
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 {}
@ -80,14 +81,14 @@ namespace DetourNavigator
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;
}
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

View File

@ -5,30 +5,30 @@
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 auto navMesh = navigator.getNavMesh(agentHalfExtents);
const auto navMesh = navigator.getNavMesh(agentBounds);
if (!navMesh)
return std::nullopt;
const auto& settings = navigator.getSettings();
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);
if (!result)
return std::nullopt;
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 auto navMesh = navigator.getNavMesh(agentHalfExtents);
const auto navMesh = navigator.getNavMesh(agentBounds);
if (navMesh == nullptr)
return std::nullopt;
const auto& settings = navigator.getSettings();
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);
if (!result)
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.
* @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 end path at given point.
* @param includeFlags setup allowed surfaces for actor to walk.
@ -22,8 +22,8 @@ namespace DetourNavigator
* Equal to out if no path is found.
*/
template <class OutputIterator>
inline Status findPath(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts,
inline Status findPath(const Navigator& navigator, const AgentBounds& agentBounds, const float stepSize,
const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
float endTolerance, OutputIterator& out)
{
static_assert(
@ -33,35 +33,35 @@ namespace DetourNavigator
>::value,
"out is not an OutputIterator"
);
const auto navMesh = navigator.getNavMesh(agentHalfExtents);
const auto navMesh = navigator.getNavMesh(agentBounds);
if (navMesh == nullptr)
return Status::NavMeshNotFound;
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, end), includeFlags, areaCosts, settings, endTolerance, out);
}
/**
* @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 maxRadius limit maximum distance from start.
* @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.
*/
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)());
/**
* @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 end of the line
* @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.
*/
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);
}

View File

@ -151,27 +151,27 @@ namespace DetourNavigator
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())
return;
mCache.insert(std::make_pair(agentHalfExtents,
mCache.insert(std::make_pair(agentBounds,
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())
return true;
if (!resetIfUnique(it->second))
return false;
mCache.erase(agentHalfExtents);
mChangedTiles.erase(agentHalfExtents);
mPlayerTile.erase(agentHalfExtents);
mLastRecastMeshManagerRevision.erase(agentHalfExtents);
mCache.erase(agentBounds);
mChangedTiles.erase(agentBounds);
mPlayerTile.erase(agentBounds);
mLastRecastMeshManagerRevision.erase(agentBounds);
return true;
}
@ -195,28 +195,28 @@ namespace DetourNavigator
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));
auto& lastRevision = mLastRecastMeshManagerRevision[agentHalfExtents];
auto lastPlayerTile = mPlayerTile.find(agentHalfExtents);
auto& lastRevision = mLastRecastMeshManagerRevision[agentBounds];
auto lastPlayerTile = mPlayerTile.find(agentBounds);
if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end()
&& lastPlayerTile->second == playerTile)
return;
lastRevision = mRecastMeshManager.getRevision();
if (lastPlayerTile == mPlayerTile.end())
lastPlayerTile = mPlayerTile.insert(std::make_pair(agentHalfExtents, playerTile)).first;
lastPlayerTile = mPlayerTile.insert(std::make_pair(agentBounds, playerTile)).first;
else
lastPlayerTile->second = playerTile;
std::map<TilePosition, ChangeType> tilesToPost;
const auto cached = getCached(agentHalfExtents);
const auto cached = getCached(agentBounds);
if (!cached)
{
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());
}
const auto changedTiles = mChangedTiles.find(agentHalfExtents);
const auto changedTiles = mChangedTiles.find(agentBounds);
{
const auto locked = cached->lockConst();
const auto& navMesh = locked->getImpl();
@ -247,10 +247,10 @@ namespace DetourNavigator
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())
changedTiles->second.clear();
Log(Debug::Debug) << "Cache update posted for agent=" << agentHalfExtents <<
Log(Debug::Debug) << "Cache update posted for agent=" << agentBounds <<
" playerTile=" << lastPlayerTile->second <<
" recastMeshManagerRevision=" << lastRevision;
}
@ -260,12 +260,12 @@ namespace DetourNavigator
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;
}
@ -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())
return cached->second;
return SharedNavMeshCacheItem();

View File

@ -7,7 +7,7 @@
#include "recastmeshtiles.hpp"
#include "waitconditiontype.hpp"
#include "heightfieldshape.hpp"
#include "agentbounds.hpp"
#include <osg/Vec3f>
@ -35,7 +35,7 @@ namespace DetourNavigator
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);
@ -45,19 +45,19 @@ namespace DetourNavigator
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 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);
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;
@ -69,11 +69,11 @@ namespace DetourNavigator
TileCachedRecastMeshManager mRecastMeshManager;
OffMeshConnectionsManager mOffMeshConnectionsManager;
AsyncNavMeshUpdater mAsyncNavMeshUpdater;
std::map<osg::Vec3f, SharedNavMeshCacheItem> mCache;
std::map<osg::Vec3f, std::map<TilePosition, ChangeType>> mChangedTiles;
std::map<AgentBounds, SharedNavMeshCacheItem> mCache;
std::map<AgentBounds, std::map<TilePosition, ChangeType>> mChangedTiles;
std::size_t mGenerationCounter = 0;
std::map<osg::Vec3f, TilePosition> mPlayerTile;
std::map<osg::Vec3f, std::size_t> mLastRecastMeshManagerRevision;
std::map<AgentBounds, TilePosition> mPlayerTile;
std::map<AgentBounds, std::size_t> mLastRecastMeshManagerRevision;
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);
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),
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 std::lock_guard<std::mutex> lock(mMutex);
++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())
return Value();
@ -28,7 +28,7 @@ namespace DetourNavigator
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 auto itemSize = sizeof(RecastMesh) + getSize(recastMesh)
@ -45,8 +45,8 @@ namespace DetourNavigator
RecastMeshData key {recastMesh.getMesh(), recastMesh.getWater(),
recastMesh.getHeightfields(), recastMesh.getFlatHeightfields()};
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize);
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator);
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentBounds, changedTile, std::move(key), itemSize);
const auto emplaced = mValues.emplace(std::make_tuple(agentBounds, changedTile, std::cref(iterator->mRecastMeshData)), iterator);
if (!emplaced.second)
{
@ -92,7 +92,7 @@ namespace DetourNavigator
{
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())
return;

View File

@ -4,6 +4,7 @@
#include "preparednavmeshdata.hpp"
#include "recastmesh.hpp"
#include "tileposition.hpp"
#include "agentbounds.hpp"
#include <atomic>
#include <map>
@ -52,16 +53,16 @@ namespace DetourNavigator
struct Item
{
std::atomic<std::int64_t> mUseCount;
osg::Vec3f mAgentHalfExtents;
AgentBounds mAgentBounds;
TilePosition mChangedTile;
RecastMeshData mRecastMeshData;
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData;
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)
: mUseCount(0)
, mAgentHalfExtents(agentHalfExtents)
, mAgentBounds(agentBounds)
, mChangedTile(changedTile)
, mRecastMeshData(std::move(recastMeshData))
, mSize(size)
@ -136,10 +137,10 @@ namespace DetourNavigator
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);
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);
Stats getStats() const;
@ -153,7 +154,7 @@ namespace DetourNavigator
std::size_t mGetCount;
std::list<Item> mBusyItems;
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();

View File

@ -1,20 +1,32 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H
#include "agentbounds.hpp"
#include <osg/Vec3f>
#include <cassert>
#include <cmath>
#include <algorithm>
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 "recastmesh.hpp"
#include "settings.hpp"
#include "agentbounds.hpp"
#include <components/serialization/binaryreader.hpp>
#include <components/serialization/binarywriter.hpp>
@ -136,12 +137,13 @@ namespace
}
template <class Visitor>
void operator()(Visitor&& visitor, const RecastSettings& settings, const RecastMesh& recastMesh,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects) const
void operator()(Visitor&& visitor, const RecastSettings& settings, const AgentBounds& agentBounds,
const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects) const
{
visitor(*this, DetourNavigator::recastMeshMagic);
visitor(*this, DetourNavigator::recastMeshVersion);
visitor(*this, settings);
visitor(*this, agentBounds);
visitor(*this, recastMesh);
visitor(*this, dbRefGeometryObjects);
}
@ -228,21 +230,28 @@ namespace
visitor(*this, value.mPolyMesh);
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
{
std::vector<std::byte> serialize(const RecastSettings& settings, const RecastMesh& recastMesh,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects)
std::vector<std::byte> serialize(const RecastSettings& settings, const AgentBounds& agentBounds,
const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects)
{
constexpr Format<Serialization::Mode::Write> format;
Serialization::SizeAccumulator sizeAccumulator;
format(sizeAccumulator, settings, recastMesh, dbRefGeometryObjects);
format(sizeAccumulator, settings, agentBounds, recastMesh, dbRefGeometryObjects);
std::vector<std::byte> result(sizeAccumulator.value());
format(Serialization::BinaryWriter(result.data(), result.data() + result.size()),
settings, recastMesh, dbRefGeometryObjects);
settings, agentBounds, recastMesh, dbRefGeometryObjects);
return result;
}

View File

@ -11,15 +11,16 @@ namespace DetourNavigator
struct DbRefGeometryObject;
struct PreparedNavMeshData;
struct RecastSettings;
struct AgentBounds;
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 std::uint32_t preparedNavMeshDataVersion = 1;
std::vector<std::byte> serialize(const RecastSettings& settings, const RecastMesh& value,
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects);
std::vector<std::byte> serialize(const RecastSettings& settings, const AgentBounds& agentBounds,
const RecastMesh& recastMesh, const std::vector<DbRefGeometryObject>& dbRefGeometryObjects);
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.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.mNavMeshVersion = ::Settings::Manager::getInt("nav mesh version", "Navigator");
result.mEnableNavMeshDiskCache = ::Settings::Manager::getBool("enable nav mesh disk cache", "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"));

View File

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

View File

@ -37,7 +37,7 @@ namespace
namespace SceneUtil
{
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)
{
using namespace DetourNavigator;
@ -46,8 +46,8 @@ namespace SceneUtil
DebugDraw debugDraw(*group, DebugDraw::makeStateSet(), osg::Vec3f(0, 0, 0), 1);
const auto agentRadius = DetourNavigator::getAgentRadius(halfExtents);
const auto agentHeight = DetourNavigator::getAgentHeight(halfExtents);
const auto agentRadius = DetourNavigator::getAgentRadius(agentBounds);
const auto agentHeight = DetourNavigator::getAgentHeight(agentBounds);
const auto agentClimb = settings.mMaxClimb;
const auto startColor = duRGBA(128, 25, 0, 192);
const auto endColor = duRGBA(51, 102, 0, 129);

View File

@ -14,12 +14,13 @@ namespace osg
namespace DetourNavigator
{
struct RecastSettings;
struct AgentBounds;
}
namespace SceneUtil
{
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);
}

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.
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)
enable nav mesh disk cache = true