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

Support different agent collision shape type for pathfinding

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

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

View file

@ -13,7 +13,7 @@ namespace
struct Key
{
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