2018-03-13 22:49:08 +00:00
|
|
|
#include "asyncnavmeshupdater.hpp"
|
|
|
|
#include "debug.hpp"
|
|
|
|
#include "makenavmesh.hpp"
|
|
|
|
#include "settings.hpp"
|
|
|
|
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
2018-04-01 17:24:02 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using DetourNavigator::TilePosition;
|
|
|
|
|
|
|
|
int getDistance(const TilePosition& lhs, const TilePosition& rhs)
|
|
|
|
{
|
|
|
|
return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> makePriority(const TilePosition& changedTile, const TilePosition& playerTile)
|
|
|
|
{
|
|
|
|
return std::make_pair(getDistance(changedTile, playerTile), getDistance(changedTile, TilePosition {0, 0}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-13 22:49:08 +00:00
|
|
|
namespace DetourNavigator
|
|
|
|
{
|
2018-04-21 12:27:47 +00:00
|
|
|
static std::ostream& operator <<(std::ostream& stream, UpdateNavMeshStatus value)
|
|
|
|
{
|
|
|
|
switch (value)
|
|
|
|
{
|
|
|
|
case UpdateNavMeshStatus::ignore:
|
|
|
|
return stream << "ignore";
|
|
|
|
case UpdateNavMeshStatus::removed:
|
|
|
|
return stream << "removed";
|
|
|
|
case UpdateNavMeshStatus::add:
|
|
|
|
return stream << "add";
|
|
|
|
case UpdateNavMeshStatus::replaced:
|
|
|
|
return stream << "replaced";
|
|
|
|
}
|
|
|
|
return stream << "unknown";
|
|
|
|
}
|
|
|
|
|
2018-03-13 22:49:08 +00:00
|
|
|
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings)
|
2018-04-01 00:44:16 +00:00
|
|
|
: mSettings(std::cref(settings))
|
2018-03-13 22:49:08 +00:00
|
|
|
, mShouldStop()
|
|
|
|
, mThread([&] { process(); })
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncNavMeshUpdater::~AsyncNavMeshUpdater()
|
|
|
|
{
|
|
|
|
mShouldStop = true;
|
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
2018-04-01 17:24:02 +00:00
|
|
|
mJobs = decltype(mJobs)();
|
2018-03-13 22:49:08 +00:00
|
|
|
mHasJob.notify_all();
|
|
|
|
lock.unlock();
|
|
|
|
mThread.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr<RecastMesh>& recastMesh,
|
2018-04-01 17:24:02 +00:00
|
|
|
const std::shared_ptr<NavMeshCacheItem>& navMeshCacheItem, const TilePosition& playerTile,
|
|
|
|
const std::set<TilePosition>& changedTiles)
|
2018-03-13 22:49:08 +00:00
|
|
|
{
|
2018-04-04 00:20:48 +00:00
|
|
|
setRecastMesh(recastMesh);
|
|
|
|
|
|
|
|
if (changedTiles.empty())
|
|
|
|
return;
|
|
|
|
|
2018-03-13 22:49:08 +00:00
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
2018-04-04 00:20:48 +00:00
|
|
|
|
2018-04-01 17:24:02 +00:00
|
|
|
for (const auto& changedTile : changedTiles)
|
2018-04-04 00:20:48 +00:00
|
|
|
mJobs.push(Job {agentHalfExtents, navMeshCacheItem, changedTile, makePriority(changedTile, playerTile)});
|
|
|
|
|
2018-03-13 22:49:08 +00:00
|
|
|
mHasJob.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncNavMeshUpdater::wait()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
mDone.wait(lock, [&] { return mJobs.empty(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncNavMeshUpdater::process() throw()
|
|
|
|
{
|
|
|
|
log("start process jobs");
|
|
|
|
while (!mShouldStop)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (const auto job = getNextJob())
|
|
|
|
processJob(*job);
|
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
|
|
|
DetourNavigator::log("AsyncNavMeshUpdater::process exception: ", e.what());
|
|
|
|
::Log(Debug::Error) << "Exception while process navmesh updated job: " << e.what();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log("stop process jobs");
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncNavMeshUpdater::processJob(const Job& job)
|
|
|
|
{
|
2018-04-01 00:44:16 +00:00
|
|
|
log("process job for agent=", job.mAgentHalfExtents);
|
2018-03-13 22:49:08 +00:00
|
|
|
|
|
|
|
const auto start = std::chrono::steady_clock::now();
|
|
|
|
|
2018-04-04 00:20:48 +00:00
|
|
|
const auto recastMesh = getRecastMesh();
|
|
|
|
|
2018-04-21 12:27:47 +00:00
|
|
|
const auto status = updateNavMesh(job.mAgentHalfExtents, *recastMesh, job.mChangedTile, mSettings,
|
|
|
|
*job.mNavMeshCacheItem);
|
2018-04-07 13:11:23 +00:00
|
|
|
|
2018-03-13 22:49:08 +00:00
|
|
|
const auto finish = std::chrono::steady_clock::now();
|
|
|
|
|
2018-04-04 00:20:48 +00:00
|
|
|
writeDebugFiles(job, *recastMesh);
|
2018-03-13 22:49:08 +00:00
|
|
|
|
|
|
|
using FloatMs = std::chrono::duration<float, std::milli>;
|
|
|
|
|
2018-04-21 12:27:47 +00:00
|
|
|
log("cache updated for agent=", job.mAgentHalfExtents, " status=", status,
|
2018-03-13 22:49:08 +00:00
|
|
|
" time=", std::chrono::duration_cast<FloatMs>(finish - start).count(), "ms");
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
if (mJobs.empty())
|
|
|
|
mHasJob.wait_for(lock, std::chrono::milliseconds(10));
|
|
|
|
if (mJobs.empty())
|
|
|
|
{
|
|
|
|
mDone.notify_all();
|
|
|
|
return boost::none;
|
|
|
|
}
|
|
|
|
log("got ", mJobs.size(), " jobs");
|
2018-04-01 17:24:02 +00:00
|
|
|
const auto job = mJobs.top();
|
|
|
|
mJobs.pop();
|
2018-03-13 22:49:08 +00:00
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
2018-04-04 00:20:48 +00:00
|
|
|
void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const
|
2018-03-13 22:49:08 +00:00
|
|
|
{
|
2018-04-01 15:09:43 +00:00
|
|
|
std::string revision;
|
2018-04-02 06:55:12 +00:00
|
|
|
std::string recastMeshRevision;
|
|
|
|
std::string navMeshRevision;
|
|
|
|
if ((mSettings.get().mEnableWriteNavMeshToFile || mSettings.get().mEnableWriteRecastMeshToFile)
|
|
|
|
&& (mSettings.get().mEnableRecastMeshFileNameRevision || mSettings.get().mEnableNavMeshFileNameRevision))
|
|
|
|
{
|
|
|
|
revision = "." + std::to_string((std::chrono::steady_clock::now()
|
2018-04-01 15:09:43 +00:00
|
|
|
- std::chrono::steady_clock::time_point()).count());
|
2018-04-02 06:55:12 +00:00
|
|
|
if (mSettings.get().mEnableRecastMeshFileNameRevision)
|
|
|
|
recastMeshRevision = revision;
|
|
|
|
if (mSettings.get().mEnableNavMeshFileNameRevision)
|
|
|
|
navMeshRevision = revision;
|
|
|
|
}
|
2018-04-01 15:09:43 +00:00
|
|
|
if (mSettings.get().mEnableWriteRecastMeshToFile)
|
2018-04-04 00:20:48 +00:00
|
|
|
writeToFile(recastMesh, mSettings.get().mRecastMeshPathPrefix, recastMeshRevision);
|
2018-04-01 15:09:43 +00:00
|
|
|
if (mSettings.get().mEnableWriteNavMeshToFile)
|
2018-04-02 06:55:12 +00:00
|
|
|
writeToFile(*job.mNavMeshCacheItem->mValue.lock(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
2018-03-13 22:49:08 +00:00
|
|
|
}
|
2018-04-04 00:20:48 +00:00
|
|
|
|
|
|
|
std::shared_ptr<RecastMesh> AsyncNavMeshUpdater::getRecastMesh()
|
|
|
|
{
|
|
|
|
const std::lock_guard<std::mutex> lock(mRecastMeshMutex);
|
|
|
|
return mRecastMesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncNavMeshUpdater::setRecastMesh(const std::shared_ptr<RecastMesh>& value)
|
|
|
|
{
|
|
|
|
const std::lock_guard<std::mutex> lock(mRecastMeshMutex);
|
|
|
|
mRecastMesh = value;
|
|
|
|
}
|
2018-03-13 22:49:08 +00:00
|
|
|
}
|