diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 5130488e3e..1797e31720 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -414,5 +414,8 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: if (actorClass.canWalk(actor)) result |= DetourNavigator::Flag_walk; + if (actorClass.isBipedal(actor) && getTypeId() != TypeIdWander) + result |= DetourNavigator::Flag_openDoor; + return result; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 2f6c0330f3..f68f1008c0 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -29,6 +29,7 @@ #include "../mwphysics/actor.hpp" #include "../mwphysics/object.hpp" #include "../mwphysics/heightfield.hpp" +#include "../mwphysics/convert.hpp" #include "player.hpp" #include "localscripts.hpp" @@ -40,25 +41,45 @@ namespace { + osg::Quat makeActorOsgQuat(const ESM::Position& position) + { + return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1)); + } - void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, bool inverseRotationOrder) + osg::Quat makeInversedOrderObjectOsgQuat(const ESM::Position& position) + { + const float xr = position.rot[0]; + const float yr = position.rot[1]; + const float zr = position.rot[2]; + + return osg::Quat(xr, osg::Vec3(-1, 0, 0)) + * osg::Quat(yr, osg::Vec3(0, -1, 0)) + * osg::Quat(zr, osg::Vec3(0, 0, -1)); + } + + osg::Quat makeObjectOsgQuat(const ESM::Position& position) + { + const float xr = position.rot[0]; + const float yr = position.rot[1]; + const float zr = position.rot[2]; + + return osg::Quat(zr, osg::Vec3(0, 0, -1)) + * osg::Quat(yr, osg::Vec3(0, -1, 0)) + * osg::Quat(xr, osg::Vec3(-1, 0, 0)); + } + + void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, const bool inverseRotationOrder) { if (!ptr.getRefData().getBaseNode()) return; - osg::Quat worldRotQuat(ptr.getRefData().getPosition().rot[2], osg::Vec3(0,0,-1)); - if (!ptr.getClass().isActor()) - { - float xr = ptr.getRefData().getPosition().rot[0]; - float yr = ptr.getRefData().getPosition().rot[1]; - if (!inverseRotationOrder) - worldRotQuat = worldRotQuat * osg::Quat(yr, osg::Vec3(0,-1,0)) * - osg::Quat(xr, osg::Vec3(-1,0,0)); - else - worldRotQuat = osg::Quat(xr, osg::Vec3(-1,0,0)) * osg::Quat(yr, osg::Vec3(0,-1,0)) * worldRotQuat; - } - - rendering.rotateObject(ptr, worldRotQuat); + rendering.rotateObject(ptr, + ptr.getClass().isActor() + ? makeActorOsgQuat(ptr.getRefData().getPosition()) + : (inverseRotationOrder + ? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition()) + : makeObjectOsgQuat(ptr.getRefData().getPosition())) + ); } void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, @@ -84,23 +105,6 @@ namespace ptr.getClass().insertObject (ptr, model, physics); - if (const auto object = physics.getObject(ptr)) - { - const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); - const DetourNavigator::ObjectShapes shapes { - *object->getShapeInstance()->getCollisionShape(), - object->getShapeInstance()->getAvoidCollisionShape() - }; - navigator->addObject(reinterpret_cast(object), shapes, - object->getCollisionObject()->getWorldTransform()); - } - else if (const auto actor = physics.getActor(ptr)) - { - const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); - const auto playerHalfExtents = physics.getHalfExtents(MWBase::Environment::get().getWorld()->getPlayerPtr()); - navigator->addAgent(playerHalfExtents); - } - if (useAnim) MWBase::Environment::get().getMechanicsManager()->add(ptr); @@ -111,6 +115,71 @@ namespace MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); } + void addObject(const MWWorld::Ptr& ptr, const MWPhysics::PhysicsSystem& physics, DetourNavigator::Navigator& navigator) + { + if (const auto object = physics.getObject(ptr)) + { + if (ptr.getClass().isDoor() && !ptr.getCellRef().getTeleport()) + { + const auto shape = object->getShapeInstance()->getCollisionShape(); + + btVector3 aabbMin; + btVector3 aabbMax; + shape->getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + + const auto center = (aabbMax + aabbMin) * 0.5f; + + const auto distanceFromDoor = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 0.5f; + const auto toPoint = aabbMax.x() - aabbMin.x() < aabbMax.y() - aabbMin.y() + ? btVector3(distanceFromDoor, 0, 0) + : btVector3(0, distanceFromDoor, 0); + + const auto& transform = object->getCollisionObject()->getWorldTransform(); + const btTransform closedDoorTransform( + MWPhysics::toBullet(makeObjectOsgQuat(ptr.getCellRef().getPosition())), + transform.getOrigin() + ); + + const auto start = DetourNavigator::makeOsgVec3f(closedDoorTransform(center + toPoint)); + const auto startPoint = physics.castRay(start, start - osg::Vec3f(0, 0, 1000), ptr, {}, + MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water); + const auto connectionStart = startPoint.mHit ? startPoint.mHitPos : start; + + const auto end = DetourNavigator::makeOsgVec3f(closedDoorTransform(center - toPoint)); + const auto endPoint = physics.castRay(end, end - osg::Vec3f(0, 0, 1000), ptr, {}, + MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water); + const auto connectionEnd = endPoint.mHit ? endPoint.mHitPos : end; + + navigator.addObject( + reinterpret_cast(object), + DetourNavigator::DoorShapes( + *shape, + object->getShapeInstance()->getAvoidCollisionShape(), + connectionStart, + connectionEnd + ), + transform + ); + } + else + { + navigator.addObject( + reinterpret_cast(object), + DetourNavigator::ObjectShapes { + *object->getShapeInstance()->getCollisionShape(), + object->getShapeInstance()->getAvoidCollisionShape() + }, + object->getCollisionObject()->getWorldTransform() + ); + } + } + else if (const auto actor = physics.getActor(ptr)) + { + const auto playerHalfExtents = physics.getHalfExtents(MWBase::Environment::get().getWorld()->getPlayerPtr()); + navigator.addAgent(playerHalfExtents); + } + } + void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering, bool inverseRotationOrder) { @@ -137,24 +206,19 @@ namespace MWWorld::CellStore& mCell; bool mRescale; Loading::Listener& mLoadingListener; - MWPhysics::PhysicsSystem& mPhysics; - MWRender::RenderingManager& mRendering; std::vector mToInsert; - InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, - MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering); + InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener); bool operator() (const MWWorld::Ptr& ptr); - void insert(); + + template + void insert(AddObject&& addObject); }; - InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, bool rescale, - Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) - : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), - mPhysics (physics), - mRendering (rendering) + InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener) + : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener) {} bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) @@ -165,7 +229,8 @@ namespace return true; } - void InsertVisitor::insert() + template + void InsertVisitor::insert(AddObject&& addObject) { for (std::vector::iterator it = mToInsert.begin(); it != mToInsert.end(); ++it) { @@ -182,7 +247,7 @@ namespace { try { - addObject(ptr, mPhysics, mRendering); + addObject(ptr); } catch (const std::exception& e) { @@ -577,8 +642,9 @@ namespace MWWorld mLastPlayerPos = pos.asVec3(); } - Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) - : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) + Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, + DetourNavigator::Navigator& navigator) + : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) @@ -706,9 +772,10 @@ namespace MWWorld void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { - InsertVisitor insertVisitor (cell, rescale, *loadingListener, *mPhysics, mRendering); + InsertVisitor insertVisitor (cell, rescale, *loadingListener); cell.forEach (insertVisitor); - insertVisitor.insert(); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order AdjustPositionVisitor adjustPosVisitor; @@ -720,6 +787,7 @@ namespace MWWorld try { addObject(ptr, *mPhysics, mRendering); + addObject(ptr, *mPhysics, mNavigator); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index c8578fc354..00f5f98b83 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -30,6 +30,7 @@ namespace Loading namespace DetourNavigator { + class Navigator; class Water; } @@ -63,6 +64,7 @@ namespace MWWorld bool mCellChanged; MWPhysics::PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; + DetourNavigator::Navigator& mNavigator; std::unique_ptr mPreloader; float mPreloadTimer; int mHalfGridSize; @@ -91,7 +93,8 @@ namespace MWWorld public: - Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics); + Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, + DetourNavigator::Navigator& navigator); ~Scene(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b467a687aa..bfa6b5124a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -235,7 +235,7 @@ namespace MWWorld mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mFallback, mStore)); - mWorldScene.reset(new Scene(*mRendering.get(), mPhysics.get())); + mWorldScene.reset(new Scene(*mRendering.get(), mPhysics.get(), *mNavigator)); } void World::fillGlobalVariables() diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index bea6ffa57c..168ec8f417 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -46,9 +46,11 @@ namespace DetourNavigator return stream << "unknown"; } - AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager) + AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager, + OffMeshConnectionsManager& offMeshConnectionsManager) : mSettings(settings) , mRecastMeshManager(recastMeshManager) + , mOffMeshConnectionsManager(offMeshConnectionsManager) , mShouldStop() , mThread([&] { process(); }) { @@ -124,9 +126,10 @@ namespace DetourNavigator const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile); const auto playerTile = getPlayerTile(); + const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile); const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile, - mSettings, *job.mNavMeshCacheItem); + offMeshConnections, mSettings, *job.mNavMeshCacheItem); const auto finish = std::chrono::steady_clock::now(); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index bba788c3c4..d2d72a290a 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H #include "navmeshcacheitem.hpp" +#include "offmeshconnectionsmanager.hpp" #include "tilecachedrecastmeshmanager.hpp" #include "tileposition.hpp" @@ -32,7 +33,8 @@ namespace DetourNavigator class AsyncNavMeshUpdater { public: - AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager); + AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager, + OffMeshConnectionsManager& offMeshConnectionsManager); ~AsyncNavMeshUpdater(); void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& mNavMeshCacheItem, @@ -58,6 +60,7 @@ namespace DetourNavigator std::reference_wrapper mSettings; std::reference_wrapper mRecastMeshManager; + std::reference_wrapper mOffMeshConnectionsManager; std::atomic_bool mShouldStop; std::mutex mMutex; std::condition_variable mHasJob; diff --git a/components/detournavigator/flags.hpp b/components/detournavigator/flags.hpp index 38c0f13f6a..f52c0faf5a 100644 --- a/components/detournavigator/flags.hpp +++ b/components/detournavigator/flags.hpp @@ -10,6 +10,7 @@ namespace DetourNavigator Flag_none = 0, Flag_walk = 1 << 0, Flag_swim = 1 << 1, + Flag_openDoor = 1 << 2, }; } diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 71e8881683..07f2225046 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -100,9 +100,31 @@ namespace } } + std::vector getOffMeshVerts(const std::vector& connections) + { + std::vector result; + + result.reserve(connections.size() * 6); + + const auto add = [&] (const osg::Vec3f& v) + { + result.push_back(v.x()); + result.push_back(v.y()); + result.push_back(v.z()); + }; + + for (const auto& v : connections) + { + add(v.mStart); + add(v.mEnd); + } + + return result; + } + NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, - const int tileX, const int tileY, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, - const Settings& settings) + const std::vector& offMeshConnections, const int tileX, const int tileY, + const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) { rcContext context; rcConfig config; @@ -280,6 +302,12 @@ namespace polyMesh.flags[i] = Flag_swim; } + const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); + const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); + const std::vector offMeshConDir(offMeshConnections.size(), DT_OFFMESH_CON_BIDIR); + const std::vector offMeshConAreas(offMeshConnections.size(), AreaType_ground); + const std::vector offMeshConFlags(offMeshConnections.size(), Flag_openDoor); + dtNavMeshCreateParams params; params.verts = polyMesh.verts; params.vertCount = polyMesh.nverts; @@ -293,13 +321,13 @@ namespace params.detailVertsCount = polyMeshDetail.nverts; params.detailTris = polyMeshDetail.tris; params.detailTriCount = polyMeshDetail.ntris; - params.offMeshConVerts = nullptr; - params.offMeshConRad = nullptr; - params.offMeshConDir = nullptr; - params.offMeshConAreas = nullptr; - params.offMeshConFlags = nullptr; + params.offMeshConVerts = offMeshConVerts.data(); + params.offMeshConRad = offMeshConRad.data(); + params.offMeshConDir = offMeshConDir.data(); + params.offMeshConAreas = offMeshConAreas.data(); + params.offMeshConFlags = offMeshConFlags.data(); params.offMeshConUserID = nullptr; - params.offMeshConCount = 0; + params.offMeshConCount = static_cast(offMeshConnections.size()); params.walkableHeight = getHeight(settings, agentHalfExtents); params.walkableRadius = getRadius(settings, agentHalfExtents); params.walkableClimb = getMaxClimb(settings); @@ -358,7 +386,8 @@ namespace DetourNavigator } UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, - const TilePosition& changedTile, const TilePosition& playerTile, const Settings& settings, + const TilePosition& changedTile, const TilePosition& playerTile, + const std::vector& offMeshConnections, const Settings& settings, NavMeshCacheItem& navMeshCacheItem) { log("update NavMesh with mutiple tiles:", @@ -419,7 +448,7 @@ namespace DetourNavigator const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y()); const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y()); - auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, x, y, + auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, x, y, tileBorderMin, tileBorderMax, settings); if (!navMeshData.mValue) diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 7a60152d71..f9c304ee99 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H +#include "offmeshconnectionsmanager.hpp" #include "settings.hpp" #include "navmeshcacheitem.hpp" #include "tileposition.hpp" @@ -47,7 +48,8 @@ namespace DetourNavigator NavMeshPtr makeEmptyNavMesh(const Settings& settings); UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, - const TilePosition& changedTile, const TilePosition& playerTile, const Settings& settings, + const TilePosition& changedTile, const TilePosition& playerTile, + const std::vector& offMeshConnections, const Settings& settings, NavMeshCacheItem& navMeshCacheItem); } diff --git a/components/detournavigator/navigator.cpp b/components/detournavigator/navigator.cpp index 359a9a316e..1c232a17c5 100644 --- a/components/detournavigator/navigator.cpp +++ b/components/detournavigator/navigator.cpp @@ -47,6 +47,20 @@ namespace DetourNavigator return result; } + bool Navigator::addObject(std::size_t id, const DoorShapes& shapes, const btTransform& transform) + { + if (addObject(id, static_cast(shapes), transform)) + { + mNavMeshManager.addOffMeshConnection( + id, + toNavMeshCoordinates(mSettings, shapes.mConnectionStart), + toNavMeshCoordinates(mSettings, shapes.mConnectionEnd) + ); + return true; + } + return false; + } + bool Navigator::updateObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform) { return mNavMeshManager.updateObject(id, shape, transform, AreaType_ground); @@ -67,6 +81,11 @@ namespace DetourNavigator return result; } + bool Navigator::updateObject(std::size_t id, const DoorShapes& shapes, const btTransform& transform) + { + return updateObject(id, static_cast(shapes), transform); + } + bool Navigator::removeObject(std::size_t id) { bool result = mNavMeshManager.removeObject(id); @@ -76,6 +95,7 @@ namespace DetourNavigator const auto water = mWaterIds.find(id); if (water != mWaterIds.end()) result = mNavMeshManager.removeObject(water->second) || result; + mNavMeshManager.removeOffMeshConnection(id); return result; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 3e60d72e02..e33f7c4abf 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -19,6 +19,19 @@ namespace DetourNavigator {} }; + struct DoorShapes : ObjectShapes + { + osg::Vec3f mConnectionStart; + osg::Vec3f mConnectionEnd; + + DoorShapes(const btCollisionShape& shape, const btCollisionShape* avoid, + const osg::Vec3f& connectionStart,const osg::Vec3f& connectionEnd) + : ObjectShapes(shape, avoid) + , mConnectionStart(connectionStart) + , mConnectionEnd(connectionEnd) + {} + }; + class Navigator { public: @@ -32,10 +45,14 @@ namespace DetourNavigator bool addObject(std::size_t id, const ObjectShapes& shapes, const btTransform& transform); + bool addObject(std::size_t id, const DoorShapes& shapes, const btTransform& transform); + bool updateObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); bool updateObject(std::size_t id, const ObjectShapes& shapes, const btTransform& transform); + bool updateObject(std::size_t id, const DoorShapes& shapes, const btTransform& transform); + bool removeObject(std::size_t id); bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level, diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index bfca44f794..07df5c7af3 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -28,7 +28,8 @@ namespace DetourNavigator NavMeshManager::NavMeshManager(const Settings& settings) : mSettings(settings) , mRecastMeshManager(settings) - , mAsyncNavMeshUpdater(settings, mRecastMeshManager) + , mOffMeshConnectionsManager(settings) + , mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager) {} bool NavMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform, @@ -90,6 +91,34 @@ namespace DetourNavigator mCache.erase(agentHalfExtents); } + void NavMeshManager::addOffMeshConnection(std::size_t id, const osg::Vec3f& start, const osg::Vec3f& end) + { + if (!mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end})) + return; + + const auto startTilePosition = getTilePosition(mSettings, start); + const auto endTilePosition = getTilePosition(mSettings, end); + + addChangedTile(startTilePosition, ChangeType::add); + + if (startTilePosition != endTilePosition) + addChangedTile(endTilePosition, ChangeType::add); + } + + void NavMeshManager::removeOffMeshConnection(std::size_t id) + { + if (const auto connection = mOffMeshConnectionsManager.remove(id)) + { + const auto startTilePosition = getTilePosition(mSettings, connection->mStart); + const auto endTilePosition = getTilePosition(mSettings, connection->mEnd); + + addChangedTile(startTilePosition, ChangeType::remove); + + if (startTilePosition != endTilePosition) + addChangedTile(endTilePosition, ChangeType::remove); + } + } + void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) { const auto playerTile = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition)); diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 6636402bf0..acec44eb9e 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -3,6 +3,7 @@ #include "asyncnavmeshupdater.hpp" #include "cachedrecastmeshmanager.hpp" +#include "offmeshconnectionsmanager.hpp" #include "sharednavmesh.hpp" #include @@ -37,6 +38,10 @@ namespace DetourNavigator void reset(const osg::Vec3f& agentHalfExtents); + void addOffMeshConnection(std::size_t id, const osg::Vec3f& start, const osg::Vec3f& end); + + void removeOffMeshConnection(std::size_t id); + void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); void wait(); @@ -48,6 +53,7 @@ namespace DetourNavigator private: const Settings& mSettings; TileCachedRecastMeshManager mRecastMeshManager; + OffMeshConnectionsManager mOffMeshConnectionsManager; std::map> mCache; std::map> mChangedTiles; AsyncNavMeshUpdater mAsyncNavMeshUpdater; diff --git a/components/detournavigator/offmeshconnectionsmanager.hpp b/components/detournavigator/offmeshconnectionsmanager.hpp new file mode 100644 index 0000000000..0b8f2010b9 --- /dev/null +++ b/components/detournavigator/offmeshconnectionsmanager.hpp @@ -0,0 +1,112 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTIONSMANAGER_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTIONSMANAGER_H + +#include "settings.hpp" +#include "settingsutils.hpp" +#include "tileposition.hpp" + +#include + +#include + +#include +#include +#include +#include +#include + +namespace DetourNavigator +{ + struct OffMeshConnection + { + osg::Vec3f mStart; + osg::Vec3f mEnd; + }; + + class OffMeshConnectionsManager + { + public: + OffMeshConnectionsManager(const Settings& settings) + : mSettings(settings) + {} + + bool add(const std::size_t id, const OffMeshConnection& value) + { + const std::lock_guard lock(mMutex); + + if (!mValuesById.insert(std::make_pair(id, value)).second) + return false; + + const auto startTilePosition = getTilePosition(mSettings, value.mStart); + const auto endTilePosition = getTilePosition(mSettings, value.mEnd); + + mValuesByTilePosition[startTilePosition].insert(id); + + if (startTilePosition != endTilePosition) + mValuesByTilePosition[endTilePosition].insert(id); + + return true; + } + + boost::optional remove(const std::size_t id) + { + const std::lock_guard lock(mMutex); + + const auto itById = mValuesById.find(id); + + if (itById == mValuesById.end()) + return boost::none; + + const auto result = itById->second; + + mValuesById.erase(itById); + + const auto startTilePosition = getTilePosition(mSettings, result.mStart); + const auto endTilePosition = getTilePosition(mSettings, result.mEnd); + + removeByTilePosition(startTilePosition, id); + + if (startTilePosition != endTilePosition) + removeByTilePosition(endTilePosition, id); + + return result; + } + + std::vector get(const TilePosition& tilePosition) + { + std::vector result; + + const std::lock_guard lock(mMutex); + + const auto itByTilePosition = mValuesByTilePosition.find(tilePosition); + + if (itByTilePosition == mValuesByTilePosition.end()) + return result; + + std::for_each(itByTilePosition->second.begin(), itByTilePosition->second.end(), + [&] (const std::size_t v) + { + const auto itById = mValuesById.find(v); + if (itById != mValuesById.end()) + result.push_back(itById->second); + }); + + return result; + } + + private: + const Settings& mSettings; + std::mutex mMutex; + std::unordered_map mValuesById; + std::map> mValuesByTilePosition; + + void removeByTilePosition(const TilePosition& tilePosition, const std::size_t id) + { + const auto it = mValuesByTilePosition.find(tilePosition); + if (it != mValuesByTilePosition.end()) + it->second.erase(id); + } + }; +} + +#endif