mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 18:39:40 +00:00
Merge branch 'navmesh_debug_async_scene' into 'master'
Prepare navmesh scene asynchronously See merge request OpenMW/openmw!1788
This commit is contained in:
commit
f63cd44f52
3 changed files with 235 additions and 39 deletions
|
@ -6,6 +6,8 @@
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/detournavigator/navmeshcacheitem.hpp>
|
#include <components/detournavigator/navmeshcacheitem.hpp>
|
||||||
#include <components/sceneutil/detourdebugdraw.hpp>
|
#include <components/sceneutil/detourdebugdraw.hpp>
|
||||||
|
#include <components/sceneutil/workqueue.hpp>
|
||||||
|
#include <components/detournavigator/settings.hpp>
|
||||||
|
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/StateSet>
|
#include <osg/StateSet>
|
||||||
|
@ -17,8 +19,137 @@
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
NavMesh::NavMesh(const osg::ref_ptr<osg::Group>& root, bool enabled)
|
struct NavMesh::LessByTilePosition
|
||||||
|
{
|
||||||
|
bool operator()(const DetourNavigator::TilePosition& lhs,
|
||||||
|
const std::pair<DetourNavigator::TilePosition, DetourNavigator::Version>& rhs) const
|
||||||
|
{
|
||||||
|
return lhs < rhs.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const std::pair<DetourNavigator::TilePosition, DetourNavigator::Version>& lhs,
|
||||||
|
const DetourNavigator::TilePosition& rhs) const
|
||||||
|
{
|
||||||
|
return lhs.first < rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NavMesh::CreateNavMeshTileGroups final : SceneUtil::WorkItem
|
||||||
|
{
|
||||||
|
std::size_t mId;
|
||||||
|
DetourNavigator::Version mVersion;
|
||||||
|
const std::weak_ptr<DetourNavigator::GuardedNavMeshCacheItem> mNavMesh;
|
||||||
|
const osg::ref_ptr<osg::StateSet> mGroupStateSet;
|
||||||
|
const osg::ref_ptr<osg::StateSet> mDebugDrawStateSet;
|
||||||
|
const DetourNavigator::Settings mSettings;
|
||||||
|
std::map<DetourNavigator::TilePosition, Tile> mTiles;
|
||||||
|
std::atomic_bool mAborted {false};
|
||||||
|
std::mutex mMutex;
|
||||||
|
bool mStarted = false;
|
||||||
|
std::vector<std::pair<DetourNavigator::TilePosition, Tile>> mUpdatedTiles;
|
||||||
|
std::vector<DetourNavigator::TilePosition> mRemovedTiles;
|
||||||
|
|
||||||
|
explicit CreateNavMeshTileGroups(std::size_t id, DetourNavigator::Version version,
|
||||||
|
std::weak_ptr<DetourNavigator::GuardedNavMeshCacheItem> navMesh,
|
||||||
|
const osg::ref_ptr<osg::StateSet>& groupStateSet, const osg::ref_ptr<osg::StateSet>& debugDrawStateSet,
|
||||||
|
const DetourNavigator::Settings& settings, const std::map<DetourNavigator::TilePosition, Tile>& tiles)
|
||||||
|
: mId(id)
|
||||||
|
, mVersion(version)
|
||||||
|
, mNavMesh(navMesh)
|
||||||
|
, mGroupStateSet(groupStateSet)
|
||||||
|
, mDebugDrawStateSet(debugDrawStateSet)
|
||||||
|
, mSettings(settings)
|
||||||
|
, mTiles(tiles)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void doWork() final
|
||||||
|
{
|
||||||
|
using DetourNavigator::TilePosition;
|
||||||
|
using DetourNavigator::Version;
|
||||||
|
|
||||||
|
const std::lock_guard lock(mMutex);
|
||||||
|
mStarted = true;
|
||||||
|
|
||||||
|
if (mAborted.load(std::memory_order_acquire))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto navMeshPtr = mNavMesh.lock();
|
||||||
|
if (navMeshPtr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<std::pair<DetourNavigator::TilePosition, Version>> existingTiles;
|
||||||
|
|
||||||
|
navMeshPtr->lockConst()->forEachUsedTile([&] (const TilePosition& position, const Version& version, const dtMeshTile& /*meshTile*/)
|
||||||
|
{
|
||||||
|
existingTiles.emplace_back(position, version);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mAborted.load(std::memory_order_acquire))
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::sort(existingTiles.begin(), existingTiles.end());
|
||||||
|
|
||||||
|
std::vector<DetourNavigator::TilePosition> removedTiles;
|
||||||
|
|
||||||
|
for (const auto& [position, tile] : mTiles)
|
||||||
|
if (!std::binary_search(existingTiles.begin(), existingTiles.end(), position, LessByTilePosition {}))
|
||||||
|
removedTiles.push_back(position);
|
||||||
|
|
||||||
|
std::vector<std::pair<TilePosition, Tile>> updatedTiles;
|
||||||
|
|
||||||
|
for (const auto& [position, version] : existingTiles)
|
||||||
|
{
|
||||||
|
const auto it = mTiles.find(position);
|
||||||
|
if (it != mTiles.end() && it->second.mGroup != nullptr && it->second.mVersion == version)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> group;
|
||||||
|
{
|
||||||
|
const auto navMesh = navMeshPtr->lockConst();
|
||||||
|
const dtMeshTile* meshTile = DetourNavigator::getTile(navMesh->getImpl(), position);
|
||||||
|
if (meshTile == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (mAborted.load(std::memory_order_acquire))
|
||||||
|
return;
|
||||||
|
|
||||||
|
group = SceneUtil::createNavMeshTileGroup(navMesh->getImpl(), *meshTile, mSettings, mGroupStateSet, mDebugDrawStateSet);
|
||||||
|
}
|
||||||
|
if (group == nullptr)
|
||||||
|
{
|
||||||
|
removedTiles.push_back(position);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug");
|
||||||
|
group->setNodeMask(Mask_Debug);
|
||||||
|
updatedTiles.emplace_back(position, Tile {version, std::move(group)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAborted.load(std::memory_order_acquire))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mUpdatedTiles = std::move(updatedTiles);
|
||||||
|
mRemovedTiles = std::move(removedTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void abort() final
|
||||||
|
{
|
||||||
|
mAborted.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NavMesh::DeallocateCreateNavMeshTileGroups final : SceneUtil::WorkItem
|
||||||
|
{
|
||||||
|
osg::ref_ptr<NavMesh::CreateNavMeshTileGroups> mWorkItem;
|
||||||
|
|
||||||
|
explicit DeallocateCreateNavMeshTileGroups(osg::ref_ptr<NavMesh::CreateNavMeshTileGroups>&& workItem)
|
||||||
|
: mWorkItem(std::move(workItem)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
NavMesh::NavMesh(const osg::ref_ptr<osg::Group>& root, const osg::ref_ptr<SceneUtil::WorkQueue>& workQueue, bool enabled)
|
||||||
: mRootNode(root)
|
: mRootNode(root)
|
||||||
|
, mWorkQueue(workQueue)
|
||||||
, mGroupStateSet(SceneUtil::makeNavMeshTileStateSet())
|
, mGroupStateSet(SceneUtil::makeNavMeshTileStateSet())
|
||||||
, mDebugDrawStateSet(SceneUtil::DebugDraw::makeStateSet())
|
, mDebugDrawStateSet(SceneUtil::DebugDraw::makeStateSet())
|
||||||
, mEnabled(enabled)
|
, mEnabled(enabled)
|
||||||
|
@ -30,6 +161,8 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
if (mEnabled)
|
if (mEnabled)
|
||||||
disable();
|
disable();
|
||||||
|
for (const auto& workItem : mWorkItems)
|
||||||
|
workItem->abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavMesh::toggle()
|
bool NavMesh::toggle()
|
||||||
|
@ -42,13 +175,69 @@ namespace MWRender
|
||||||
return mEnabled;
|
return mEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMesh::update(const DetourNavigator::NavMeshCacheItem& navMesh, std::size_t id,
|
void NavMesh::update(const std::shared_ptr<DetourNavigator::GuardedNavMeshCacheItem>& navMesh, std::size_t id,
|
||||||
const DetourNavigator::Settings& settings)
|
const DetourNavigator::Settings& settings)
|
||||||
{
|
{
|
||||||
using DetourNavigator::TilePosition;
|
using DetourNavigator::TilePosition;
|
||||||
using DetourNavigator::Version;
|
using DetourNavigator::Version;
|
||||||
|
|
||||||
if (!mEnabled || (!mTiles.empty() && mId == id && mVersion == navMesh.getVersion()))
|
if (!mEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::pair<std::size_t, Version> lastest {0, Version {}};
|
||||||
|
osg::ref_ptr<CreateNavMeshTileGroups> latestCandidate;
|
||||||
|
for (auto it = mWorkItems.begin(); it != mWorkItems.end();)
|
||||||
|
{
|
||||||
|
if (!(*it)->isDone())
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::pair<std::size_t, Version> order {(*it)->mId, (*it)->mVersion};
|
||||||
|
if (lastest < order)
|
||||||
|
{
|
||||||
|
lastest = order;
|
||||||
|
std::swap(latestCandidate, *it);
|
||||||
|
}
|
||||||
|
if (*it != nullptr)
|
||||||
|
mWorkQueue->addWorkItem(new DeallocateCreateNavMeshTileGroups(std::move(*it)));
|
||||||
|
it = mWorkItems.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestCandidate != nullptr)
|
||||||
|
{
|
||||||
|
for (const TilePosition& position : latestCandidate->mRemovedTiles)
|
||||||
|
{
|
||||||
|
const auto it = mTiles.find(position);
|
||||||
|
if (it == mTiles.end())
|
||||||
|
continue;
|
||||||
|
mRootNode->removeChild(it->second.mGroup);
|
||||||
|
mTiles.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [position, tile] : latestCandidate->mUpdatedTiles)
|
||||||
|
{
|
||||||
|
const auto it = mTiles.find(position);
|
||||||
|
if (it == mTiles.end())
|
||||||
|
{
|
||||||
|
mRootNode->addChild(tile.mGroup);
|
||||||
|
mTiles.emplace_hint(it, position, std::move(tile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRootNode->replaceChild(it->second.mGroup, tile.mGroup);
|
||||||
|
std::swap(it->second, tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mWorkQueue->addWorkItem(new DeallocateCreateNavMeshTileGroups(std::move(latestCandidate)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto version = navMesh->lock()->getVersion();
|
||||||
|
|
||||||
|
if (!mTiles.empty() && mId == id && mVersion == version)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mId != id)
|
if (mId != id)
|
||||||
|
@ -57,40 +246,35 @@ namespace MWRender
|
||||||
mId = id;
|
mId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
mVersion = navMesh.getVersion();
|
mVersion = version;
|
||||||
|
|
||||||
std::vector<TilePosition> updated;
|
for (auto& workItem : mWorkItems)
|
||||||
navMesh.forEachUsedTile([&] (const TilePosition& position, const Version& version, const dtMeshTile& meshTile)
|
|
||||||
{
|
{
|
||||||
updated.push_back(position);
|
const std::unique_lock lock(workItem->mMutex, std::try_to_lock);
|
||||||
Tile& tile = mTiles[position];
|
|
||||||
if (tile.mGroup != nullptr && tile.mVersion == version)
|
if (!lock.owns_lock())
|
||||||
return;
|
continue;
|
||||||
if (tile.mGroup != nullptr)
|
|
||||||
mRootNode->removeChild(tile.mGroup);
|
if (workItem->mStarted)
|
||||||
tile.mGroup = SceneUtil::createNavMeshTileGroup(navMesh.getImpl(), meshTile, settings,
|
continue;
|
||||||
mGroupStateSet, mDebugDrawStateSet);
|
|
||||||
if (tile.mGroup == nullptr)
|
workItem->mId = id;
|
||||||
return;
|
workItem->mVersion = version;
|
||||||
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(tile.mGroup, "debug");
|
workItem->mTiles = mTiles;
|
||||||
tile.mGroup->setNodeMask(Mask_Debug);
|
|
||||||
mRootNode->addChild(tile.mGroup);
|
return;
|
||||||
});
|
|
||||||
std::sort(updated.begin(), updated.end());
|
|
||||||
for (auto it = mTiles.begin(); it != mTiles.end();)
|
|
||||||
{
|
|
||||||
if (!std::binary_search(updated.begin(), updated.end(), it->first))
|
|
||||||
{
|
|
||||||
mRootNode->removeChild(it->second.mGroup);
|
|
||||||
it = mTiles.erase(it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<CreateNavMeshTileGroups> workItem = new CreateNavMeshTileGroups(id, version, navMesh, mGroupStateSet, mDebugDrawStateSet, settings, mTiles);
|
||||||
|
mWorkQueue->addWorkItem(workItem);
|
||||||
|
mWorkItems.push_back(std::move(workItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMesh::reset()
|
void NavMesh::reset()
|
||||||
{
|
{
|
||||||
|
for (auto& workItem : mWorkItems)
|
||||||
|
workItem->abort();
|
||||||
|
mWorkItems.clear();
|
||||||
for (auto& [position, tile] : mTiles)
|
for (auto& [position, tile] : mTiles)
|
||||||
mRootNode->removeChild(tile.mGroup);
|
mRootNode->removeChild(tile.mGroup);
|
||||||
mTiles.clear();
|
mTiles.clear();
|
||||||
|
@ -98,15 +282,12 @@ namespace MWRender
|
||||||
|
|
||||||
void NavMesh::enable()
|
void NavMesh::enable()
|
||||||
{
|
{
|
||||||
for (const auto& [position, tile] : mTiles)
|
|
||||||
mRootNode->addChild(tile.mGroup);
|
|
||||||
mEnabled = true;
|
mEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMesh::disable()
|
void NavMesh::disable()
|
||||||
{
|
{
|
||||||
for (const auto& [position, tile] : mTiles)
|
reset();
|
||||||
mRootNode->removeChild(tile.mGroup);
|
|
||||||
mEnabled = false;
|
mEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
|
|
||||||
#include <components/detournavigator/version.hpp>
|
#include <components/detournavigator/version.hpp>
|
||||||
#include <components/detournavigator/tileposition.hpp>
|
#include <components/detournavigator/tileposition.hpp>
|
||||||
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class dtNavMesh;
|
class dtNavMesh;
|
||||||
|
|
||||||
|
@ -24,18 +27,24 @@ namespace DetourNavigator
|
||||||
struct Settings;
|
struct Settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
class WorkQueue;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class NavMesh
|
class NavMesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NavMesh(const osg::ref_ptr<osg::Group>& root, bool enabled);
|
explicit NavMesh(const osg::ref_ptr<osg::Group>& root, const osg::ref_ptr<SceneUtil::WorkQueue>& workQueue,
|
||||||
|
bool enabled);
|
||||||
~NavMesh();
|
~NavMesh();
|
||||||
|
|
||||||
bool toggle();
|
bool toggle();
|
||||||
|
|
||||||
void update(const DetourNavigator::NavMeshCacheItem& navMesh, std::size_t id,
|
void update(const std::shared_ptr<Misc::ScopeGuarded<DetourNavigator::NavMeshCacheItem>>& navMesh,
|
||||||
const DetourNavigator::Settings& settings);
|
std::size_t id, const DetourNavigator::Settings& settings);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -55,13 +64,19 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::Group> mGroup;
|
osg::ref_ptr<osg::Group> mGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LessByTilePosition;
|
||||||
|
struct CreateNavMeshTileGroups;
|
||||||
|
struct DeallocateCreateNavMeshTileGroups;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> mRootNode;
|
osg::ref_ptr<osg::Group> mRootNode;
|
||||||
|
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
||||||
osg::ref_ptr<osg::StateSet> mGroupStateSet;
|
osg::ref_ptr<osg::StateSet> mGroupStateSet;
|
||||||
osg::ref_ptr<osg::StateSet> mDebugDrawStateSet;
|
osg::ref_ptr<osg::StateSet> mDebugDrawStateSet;
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
std::size_t mId;
|
std::size_t mId;
|
||||||
DetourNavigator::Version mVersion;
|
DetourNavigator::Version mVersion;
|
||||||
std::map<DetourNavigator::TilePosition, Tile> mTiles;
|
std::map<DetourNavigator::TilePosition, Tile> mTiles;
|
||||||
|
std::vector<osg::ref_ptr<CreateNavMeshTileGroups>> mWorkItems;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ namespace MWRender
|
||||||
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
|
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
|
||||||
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
|
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
|
||||||
|
|
||||||
mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
|
mNavMesh.reset(new NavMesh(mRootNode, mWorkQueue, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
|
||||||
mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
|
mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
|
||||||
mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
|
mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
|
||||||
mPathgrid.reset(new Pathgrid(mRootNode));
|
mPathgrid.reset(new Pathgrid(mRootNode));
|
||||||
|
@ -1395,7 +1395,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mNavMesh->update(*it->second->lockConst(), mNavMeshNumber, mNavigator.getSettings());
|
mNavMesh->update(it->second, mNavMeshNumber, mNavigator.getSettings());
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue