From 1a12c453d60cbbbcc8b50e1823b9dc0164b1549f Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 17 Jun 2022 00:28:44 +0200 Subject: [PATCH] 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. --- .../detournavigator/navmeshtilescache.cpp | 11 ++- apps/navmeshtool/main.cpp | 5 +- apps/navmeshtool/navmesh.cpp | 5 +- apps/navmeshtool/navmesh.hpp | 3 +- apps/openmw/mwbase/world.hpp | 6 +- apps/openmw/mwmechanics/aicombat.cpp | 8 +- apps/openmw/mwmechanics/aipackage.cpp | 14 +-- apps/openmw/mwmechanics/aitravel.cpp | 1 + apps/openmw/mwmechanics/aiwander.cpp | 16 +-- apps/openmw/mwmechanics/obstacle.cpp | 3 +- apps/openmw/mwmechanics/pathfinding.cpp | 41 ++++---- apps/openmw/mwmechanics/pathfinding.hpp | 24 +++-- apps/openmw/mwphysics/actor.cpp | 13 ++- apps/openmw/mwphysics/actor.hpp | 6 ++ apps/openmw/mwrender/actorspaths.cpp | 4 +- apps/openmw/mwrender/actorspaths.hpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/renderingmanager.hpp | 3 +- apps/openmw/mwworld/scene.cpp | 6 +- apps/openmw/mwworld/worldimp.cpp | 21 ++-- apps/openmw/mwworld/worldimp.hpp | 5 +- .../detournavigator/asyncnavmeshupdater.cpp | 40 ++++---- .../detournavigator/navigator.cpp | 97 +++++++++--------- .../detournavigator/navmeshtilescache.cpp | 98 +++++++++---------- components/detournavigator/agentbounds.hpp | 34 +++++++ .../detournavigator/asyncnavmeshupdater.cpp | 72 +++++++------- .../detournavigator/asyncnavmeshupdater.hpp | 19 ++-- .../detournavigator/collisionshapetype.hpp | 17 ++++ components/detournavigator/debug.hpp | 16 +++ .../detournavigator/generatenavmeshtile.cpp | 14 +-- .../detournavigator/generatenavmeshtile.hpp | 5 +- components/detournavigator/makenavmesh.cpp | 44 ++++----- components/detournavigator/makenavmesh.hpp | 4 +- components/detournavigator/navigator.hpp | 13 +-- components/detournavigator/navigatorimpl.cpp | 18 ++-- components/detournavigator/navigatorimpl.hpp | 10 +- components/detournavigator/navigatorstub.hpp | 11 ++- components/detournavigator/navigatorutils.cpp | 12 +-- components/detournavigator/navigatorutils.hpp | 18 ++-- components/detournavigator/navmeshmanager.cpp | 48 ++++----- components/detournavigator/navmeshmanager.hpp | 22 ++--- .../detournavigator/navmeshtilescache.cpp | 12 +-- .../detournavigator/navmeshtilescache.hpp | 13 +-- components/detournavigator/recastparams.hpp | 20 +++- components/detournavigator/serialization.cpp | 21 ++-- components/detournavigator/serialization.hpp | 7 +- components/detournavigator/settings.cpp | 1 - components/detournavigator/settings.hpp | 3 +- components/sceneutil/agentpath.cpp | 6 +- components/sceneutil/agentpath.hpp | 3 +- files/settings-default.cfg | 4 - 51 files changed, 515 insertions(+), 389 deletions(-) create mode 100644 components/detournavigator/agentbounds.hpp create mode 100644 components/detournavigator/collisionshapetype.hpp diff --git a/apps/benchmarks/detournavigator/navmeshtilescache.cpp b/apps/benchmarks/detournavigator/navmeshtilescache.cpp index e8a4be1e46..976f7a46e1 100644 --- a/apps/benchmarks/detournavigator/navmeshtilescache.cpp +++ b/apps/benchmarks/detournavigator/navmeshtilescache.cpp @@ -13,7 +13,7 @@ namespace struct Key { - osg::Vec3f mAgentHalfExtents; + AgentBounds mAgentBounds; TilePosition mTilePosition; RecastMesh mRecastMesh; }; @@ -137,6 +137,7 @@ namespace template 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(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()); *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()); benchmark::DoNotOptimize(result); } diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index c1f8a8e90a..ea0046b1ae 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -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(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) diff --git a/apps/navmeshtool/navmesh.cpp b/apps/navmeshtool/navmesh.cpp index a51d0bbc81..053809eca4 100644 --- a/apps/navmeshtool/navmesh.cpp +++ b/apps/navmeshtool/navmesh.cpp @@ -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 )); diff --git a/apps/navmeshtool/navmesh.hpp b/apps/navmeshtool/navmesh.hpp index fa340b5de7..f0199ea1c4 100644 --- a/apps/navmeshtool/navmesh.hpp +++ b/apps/navmeshtool/navmesh.hpp @@ -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); } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b20ae33d63..2c96a07851 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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& 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; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 38bd9ed4b5..5bb03bb751 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -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()) { diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 24bfe41165..906894dd9c 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -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 diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 378099e7d1..14b416ac61 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d7f2026b82..0bd424785b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -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)); } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 3d64eae862..0f73890eb7 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -80,7 +81,7 @@ namespace MWMechanics std::vector* 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) { diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1cc4526193..4f4c35bb40 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -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> 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 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); } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 17b355682a..c07b085e5a 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -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> 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> out); }; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 455237dfc8..64e38559f6 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -57,7 +57,18 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic } mShape = std::make_unique(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(mShape.get()); mConvexShape->setMargin(0.001); // make sure bullet isn't using the huge default convex shape margin of 0.04 diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 2b67f265c6..322ad74d7c 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -6,6 +6,8 @@ #include "ptrholder.hpp" +#include + #include #include #include @@ -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 mShape; btConvexShape* mConvexShape; diff --git a/apps/openmw/mwrender/actorspaths.cpp b/apps/openmw/mwrender/actorspaths.cpp index ae2d6bccb8..45b35df4b9 100644 --- a/apps/openmw/mwrender/actorspaths.cpp +++ b/apps/openmw/mwrender/actorspaths.cpp @@ -38,7 +38,7 @@ namespace MWRender } void ActorsPaths::update(const MWWorld::ConstPtr& actor, const std::deque& 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"); diff --git a/apps/openmw/mwrender/actorspaths.hpp b/apps/openmw/mwrender/actorspaths.hpp index d6cec9357b..304d5c09b3 100644 --- a/apps/openmw/mwrender/actorspaths.hpp +++ b/apps/openmw/mwrender/actorspaths.hpp @@ -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& 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); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e639880b37..214e6cf731 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1518,9 +1518,9 @@ namespace MWRender } void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque& 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 diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 12f5bb073d..0424d20b23 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -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& 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; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 24b4786759..57eec9d2dd 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -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); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bec6a9064a..d2470fd678 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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& 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 diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 50aff408d5..c4024cec29 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -727,14 +727,13 @@ namespace MWWorld DetourNavigator::Navigator* getNavigator() const override; void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque& 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; diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index db8cfb83dc..71a833cc26 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -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(makeEmptyNavMesh(mSettings), 1); const std::map 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(makeEmptyNavMesh(mSettings), 1); const std::map 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(makeEmptyNavMesh(mSettings), 1); const std::map 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(makeEmptyNavMesh(mSettings), 1); const TilePosition tilePosition {0, 0}; const std::map 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 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(makeEmptyNavMesh(mSettings), 1); const TilePosition tilePosition {0, 0}; const std::map 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 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(makeEmptyNavMesh(mSettings), 1); const TilePosition tilePosition {0, 0}; const std::map 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(":memory:", std::numeric_limits::max())); const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map 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(makeEmptyNavMesh(mSettings), 1); const std::map 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 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 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()); } diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index d8df4e00ae..f49f3cdc74 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -42,7 +42,7 @@ namespace std::unique_ptr 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 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()); } 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()); 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()); 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 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::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> boxes; std::generate_n(std::back_inserter(boxes), 100, [] { return std::make_unique(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> shapes; std::generate_n(std::back_inserter(shapes), 100, [] { return std::make_unique(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(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)); } diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index cbd68e0fe1..0180373e10 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -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 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)); } } diff --git a/components/detournavigator/agentbounds.hpp b/components/detournavigator/agentbounds.hpp new file mode 100644 index 0000000000..b45a2fa6cc --- /dev/null +++ b/components/detournavigator/agentbounds.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_AGENTBOUNDS_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_AGENTBOUNDS_H + +#include "collisionshapetype.hpp" + +#include + +#include + +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 diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 22086c67ea..b9424e406a 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -32,12 +32,12 @@ namespace DetourNavigator } int getMinDistanceTo(const TilePosition& position, int maxDistance, - const std::set>& pushedTiles, - const std::set>& presentTiles) + const std::set>& pushedTiles, + const std::set>& 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 makeDbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr&& db, const Settings& settings) { if (db == nullptr) return nullptr; - return std::make_unique(updater, std::move(db), TileVersion(settings.mNavMeshVersion), + return std::make_unique(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 navMeshCacheItem, + Job::Job(const AgentBounds& agentBounds, std::weak_ptr 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& 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; 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; 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(); 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 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 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) diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index ec8c849ca8..d0850f0249 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -9,6 +9,7 @@ #include "waitconditiontype.hpp" #include "navmeshdb.hpp" #include "changetype.hpp" +#include "agentbounds.hpp" #include @@ -42,7 +43,7 @@ namespace DetourNavigator struct Job { const std::size_t mId; - const osg::Vec3f mAgentHalfExtents; + const AgentBounds mAgentBounds; const std::weak_ptr mNavMeshCacheItem; const std::string mWorldspace; const TilePosition mChangedTile; @@ -57,7 +58,7 @@ namespace DetourNavigator std::optional mCachedTileData; std::unique_ptr mGeneratedNavMeshData; - Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr navMeshCacheItem, + Job(const AgentBounds& agentBounds, std::weak_ptr 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&& 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& changedTiles); @@ -191,12 +192,12 @@ namespace DetourNavigator std::condition_variable mProcessed; std::list mJobs; std::deque mWaiting; - std::set> mPushed; + std::set> mPushed; Misc::ScopeGuarded mPlayerTile; NavMeshTilesCache mNavMeshTilesCache; - Misc::ScopeGuarded>> mProcessingTiles; - std::map, std::chrono::steady_clock::time_point> mLastUpdates; - std::set> mPresentTiles; + Misc::ScopeGuarded>> mProcessingTiles; + std::map, std::chrono::steady_clock::time_point> mLastUpdates; + std::set> mPresentTiles; std::vector mThreads; std::unique_ptr 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; diff --git a/components/detournavigator/collisionshapetype.hpp b/components/detournavigator/collisionshapetype.hpp new file mode 100644 index 0000000000..3964840ecf --- /dev/null +++ b/components/detournavigator/collisionshapetype.hpp @@ -0,0 +1,17 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_COLLISIONSHAPETYPE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_COLLISIONSHAPETYPE_H + +#include + +namespace DetourNavigator +{ + enum class CollisionShapeType : std::uint8_t + { + Aabb = 0, + RotatingBox = 1, + }; + + inline constexpr CollisionShapeType defaultCollisionShapeType = CollisionShapeType::Aabb; +} + +#endif diff --git a/components/detournavigator/debug.hpp b/components/detournavigator/debug.hpp index ce1e2d2023..091e49821c 100644 --- a/components/detournavigator/debug.hpp +++ b/components/detournavigator/debug.hpp @@ -4,6 +4,7 @@ #include "tilebounds.hpp" #include "status.hpp" #include "recastmesh.hpp" +#include "agentbounds.hpp" #include @@ -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>(v); + } + + inline std::ostream& operator<<(std::ostream& s, const AgentBounds& v) + { + return s << "AgentBounds {" << v.mShapeType << ", " << v.mHalfExtents << "}"; + } + class RecastMesh; struct RecastSettings; diff --git a/components/detournavigator/generatenavmeshtile.cpp b/components/detournavigator/generatenavmeshtile.cpp index 3475b4ee8a..41b60d4a75 100644 --- a/components/detournavigator/generatenavmeshtile.cpp +++ b/components/detournavigator/generatenavmeshtile.cpp @@ -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 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 objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), [&] (const MeshSource& v) { return consumer->resolveMeshSource(v); }); - std::vector input = serialize(mSettings.mRecast, *recastMesh, objects); + std::vector input = serialize(mSettings.mRecast, mAgentBounds, *recastMesh, objects); const std::optional 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; } diff --git a/components/detournavigator/generatenavmeshtile.hpp b/components/detournavigator/generatenavmeshtile.hpp index ecdf1c8bc0..0fc7341ff1 100644 --- a/components/detournavigator/generatenavmeshtile.hpp +++ b/components/detournavigator/generatenavmeshtile.hpp @@ -3,6 +3,7 @@ #include "recastmeshprovider.hpp" #include "tileposition.hpp" +#include "agentbounds.hpp" #include @@ -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 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 mConsumer; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 97c5377558..3b26ad696f 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -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(std::ceil(getHeight(settings, agentHalfExtents) / settings.mCellHeight)); + result.mWalkableHeight = static_cast(std::ceil(getHeight(settings, agentBounds) / settings.mCellHeight)); result.mWalkableClimb = static_cast(std::floor(getMaxClimb(settings) / settings.mCellHeight)); - result.mWalkableRadius = static_cast(std::ceil(getRadius(settings, agentHalfExtents) / settings.mCellSize)); + result.mWalkableRadius = static_cast(std::ceil(getRadius(settings, agentBounds) / settings.mCellSize)); result.mMaxEdgeLen = static_cast(std::round(static_cast(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& water, + bool rasterizeTriangles(rcContext& context, float agentHalfExtentsZ, const std::vector& 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 getBoundsByZ(const RecastMesh& recastMesh, const osg::Vec3f& agentHalfExtents, const RecastSettings& settings) + std::pair 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 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& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const std::vector& offMeshConnections, const AgentBounds& agentBounds, const TilePosition& tile, const RecastSettings& settings) { const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); - const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); + const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentBounds)); const std::vector offMeshConDir(offMeshConnections.size(), 0); const std::vector offMeshConAreas = getOffMeshConAreas(offMeshConnections); const std::vector offMeshConFlags = getOffMeshFlags(offMeshConnections); @@ -491,8 +491,8 @@ namespace DetourNavigator params.offMeshConFlags = offMeshConFlags.data(); params.offMeshConUserID = nullptr; params.offMeshConCount = static_cast(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); diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 14919ab134..b890c5fcff 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -51,10 +51,10 @@ namespace DetourNavigator } std::unique_ptr 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& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const std::vector& offMeshConnections, const AgentBounds& agentBounds, const TilePosition& tile, const RecastSettings& settings); NavMeshPtr makeEmptyNavMesh(const Settings& settings); diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 65f7c43fc1..df2b202cdd 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -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 getNavMeshes() const = 0; + virtual std::map getNavMeshes() const = 0; virtual const Settings& getSettings() const = 0; diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 42cd8bd9d1..e1b2df55ed 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -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 NavigatorImpl::getNavMeshes() const + std::map NavigatorImpl::getNavMeshes() const { return mNavMeshManager.getNavMeshes(); } diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 58c4c6c05d..f7b2d6d8c8 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -18,9 +18,9 @@ namespace DetourNavigator */ explicit NavigatorImpl(const Settings& settings, std::unique_ptr&& 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 getNavMeshes() const override; + std::map getNavMeshes() const override; const Settings& getSettings() const override; @@ -73,7 +73,7 @@ namespace DetourNavigator NavMeshManager mNavMeshManager; bool mUpdatesEnabled; std::optional mLastPlayerPosition; - std::map mAgents; + std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 466ff5c912..1eb1860217 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -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 getNavMeshes() const override + std::map getNavMeshes() const override { - return std::map(); + return {}; } const Settings& getSettings() const override diff --git a/components/detournavigator/navigatorutils.cpp b/components/detournavigator/navigatorutils.cpp index a64343905d..450fab6a6d 100644 --- a/components/detournavigator/navigatorutils.cpp +++ b/components/detournavigator/navigatorutils.cpp @@ -5,30 +5,30 @@ namespace DetourNavigator { - std::optional findRandomPointAroundCircle(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, + std::optional 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(fromNavMeshCoordinates(settings.mRecast, *result)); } - std::optional raycast(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, + std::optional 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; diff --git a/components/detournavigator/navigatorutils.hpp b/components/detournavigator/navigatorutils.hpp index 8bd1499897..44db7b40fe 100644 --- a/components/detournavigator/navigatorutils.hpp +++ b/components/detournavigator/navigatorutils.hpp @@ -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 - 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 findRandomPointAroundCircle(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, + std::optional 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 raycast(const Navigator& navigator, const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, + std::optional raycast(const Navigator& navigator, const AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags); } diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index bbaf010807..ccab20a5aa 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -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(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 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 NavMeshManager::getNavMeshes() const + std::map 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(); diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index dbe45b684f..d4a5a58122 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -7,7 +7,7 @@ #include "recastmeshtiles.hpp" #include "waitconditiontype.hpp" #include "heightfieldshape.hpp" - +#include "agentbounds.hpp" #include @@ -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 getNavMeshes() const; + std::map 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 mCache; - std::map> mChangedTiles; + std::map mCache; + std::map> mChangedTiles; std::size_t mGenerationCounter = 0; - std::map mPlayerTile; - std::map mLastRecastMeshManagerRevision; + std::map mPlayerTile; + std::map 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; }; } diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index bbda8a3179..6938ff3650 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -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 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&& 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; diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index e7e0b6c7a8..572f7fdb4b 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -4,6 +4,7 @@ #include "preparednavmeshdata.hpp" #include "recastmesh.hpp" #include "tileposition.hpp" +#include "agentbounds.hpp" #include #include @@ -52,16 +53,16 @@ namespace DetourNavigator struct Item { std::atomic mUseCount; - osg::Vec3f mAgentHalfExtents; + AgentBounds mAgentBounds; TilePosition mChangedTile; RecastMeshData mRecastMeshData; std::unique_ptr 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&& value); Stats getStats() const; @@ -153,7 +154,7 @@ namespace DetourNavigator std::size_t mGetCount; std::list mBusyItems; std::list mFreeItems; - std::map>, ItemIterator, std::less<>> mValues; + std::map>, ItemIterator, std::less<>> mValues; void removeLeastRecentlyUsed(); diff --git a/components/detournavigator/recastparams.hpp b/components/detournavigator/recastparams.hpp index 7e07d2c085..e765623431 100644 --- a/components/detournavigator/recastparams.hpp +++ b/components/detournavigator/recastparams.hpp @@ -1,20 +1,32 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTPARAMS_H +#include "agentbounds.hpp" + #include +#include #include +#include 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; } } diff --git a/components/detournavigator/serialization.cpp b/components/detournavigator/serialization.cpp index 50b014acb1..a0a097ab0b 100644 --- a/components/detournavigator/serialization.cpp +++ b/components/detournavigator/serialization.cpp @@ -5,6 +5,7 @@ #include "recast.hpp" #include "recastmesh.hpp" #include "settings.hpp" +#include "agentbounds.hpp" #include #include @@ -136,12 +137,13 @@ namespace } template - void operator()(Visitor&& visitor, const RecastSettings& settings, const RecastMesh& recastMesh, - const std::vector& dbRefGeometryObjects) const + void operator()(Visitor&& visitor, const RecastSettings& settings, const AgentBounds& agentBounds, + const RecastMesh& recastMesh, const std::vector& 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 + void operator()(Visitor&& visitor, const AgentBounds& value) const + { + visitor(*this, value.mShapeType); + visitor(*this, value.mHalfExtents); + } }; } } // namespace DetourNavigator namespace DetourNavigator { - std::vector serialize(const RecastSettings& settings, const RecastMesh& recastMesh, - const std::vector& dbRefGeometryObjects) + std::vector serialize(const RecastSettings& settings, const AgentBounds& agentBounds, + const RecastMesh& recastMesh, const std::vector& dbRefGeometryObjects) { constexpr Format format; Serialization::SizeAccumulator sizeAccumulator; - format(sizeAccumulator, settings, recastMesh, dbRefGeometryObjects); + format(sizeAccumulator, settings, agentBounds, recastMesh, dbRefGeometryObjects); std::vector result(sizeAccumulator.value()); format(Serialization::BinaryWriter(result.data(), result.data() + result.size()), - settings, recastMesh, dbRefGeometryObjects); + settings, agentBounds, recastMesh, dbRefGeometryObjects); return result; } diff --git a/components/detournavigator/serialization.hpp b/components/detournavigator/serialization.hpp index 194e50a994..93022df97e 100644 --- a/components/detournavigator/serialization.hpp +++ b/components/detournavigator/serialization.hpp @@ -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 serialize(const RecastSettings& settings, const RecastMesh& value, - const std::vector& dbRefGeometryObjects); + std::vector serialize(const RecastSettings& settings, const AgentBounds& agentBounds, + const RecastMesh& recastMesh, const std::vector& dbRefGeometryObjects); std::vector serialize(const PreparedNavMeshData& value); diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 040a0a7b29..178a1215a5 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -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(::Settings::Manager::getInt64("max navmeshdb file size", "Navigator")); diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index b107a6eb85..67e0a5a958 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -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(); diff --git a/components/sceneutil/agentpath.cpp b/components/sceneutil/agentpath.cpp index 8d90475a82..e1fa9fe3c8 100644 --- a/components/sceneutil/agentpath.cpp +++ b/components/sceneutil/agentpath.cpp @@ -37,7 +37,7 @@ namespace namespace SceneUtil { osg::ref_ptr createAgentPathGroup(const std::deque& 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); diff --git a/components/sceneutil/agentpath.hpp b/components/sceneutil/agentpath.hpp index 1194fa512a..53a41e02d8 100644 --- a/components/sceneutil/agentpath.hpp +++ b/components/sceneutil/agentpath.hpp @@ -14,12 +14,13 @@ namespace osg namespace DetourNavigator { struct RecastSettings; + struct AgentBounds; } namespace SceneUtil { osg::ref_ptr createAgentPathGroup(const std::deque& 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); } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 807f00b41f..2675004907 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -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