mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 01:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			211 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "navmesh.hpp"
 | 
						|
 | 
						|
#include "worldspacedata.hpp"
 | 
						|
 | 
						|
#include <components/debug/debuglog.hpp>
 | 
						|
#include <components/detournavigator/generatenavmeshtile.hpp>
 | 
						|
#include <components/detournavigator/gettilespositions.hpp>
 | 
						|
#include <components/detournavigator/navmeshdb.hpp>
 | 
						|
#include <components/detournavigator/navmeshdbutils.hpp>
 | 
						|
#include <components/detournavigator/preparednavmeshdata.hpp>
 | 
						|
#include <components/detournavigator/recastmesh.hpp>
 | 
						|
#include <components/detournavigator/recastmeshprovider.hpp>
 | 
						|
#include <components/detournavigator/serialization.hpp>
 | 
						|
#include <components/detournavigator/tileposition.hpp>
 | 
						|
#include <components/misc/progressreporter.hpp>
 | 
						|
#include <components/sceneutil/workqueue.hpp>
 | 
						|
#include <components/sqlite3/transaction.hpp>
 | 
						|
 | 
						|
#include <osg/Vec3f>
 | 
						|
 | 
						|
#include <atomic>
 | 
						|
#include <chrono>
 | 
						|
#include <cstddef>
 | 
						|
#include <utility>
 | 
						|
#include <vector>
 | 
						|
#include <random>
 | 
						|
 | 
						|
namespace NavMeshTool
 | 
						|
{
 | 
						|
    namespace
 | 
						|
    {
 | 
						|
        using DetourNavigator::GenerateNavMeshTile;
 | 
						|
        using DetourNavigator::NavMeshDb;
 | 
						|
        using DetourNavigator::NavMeshTileInfo;
 | 
						|
        using DetourNavigator::PreparedNavMeshData;
 | 
						|
        using DetourNavigator::RecastMeshProvider;
 | 
						|
        using DetourNavigator::MeshSource;
 | 
						|
        using DetourNavigator::Settings;
 | 
						|
        using DetourNavigator::ShapeId;
 | 
						|
        using DetourNavigator::TileId;
 | 
						|
        using DetourNavigator::TilePosition;
 | 
						|
        using DetourNavigator::TileVersion;
 | 
						|
        using Sqlite3::Transaction;
 | 
						|
 | 
						|
        void logGeneratedTiles(std::size_t provided, std::size_t expected)
 | 
						|
        {
 | 
						|
            Log(Debug::Info) << provided << "/" << expected << " ("
 | 
						|
                << (static_cast<double>(provided) / static_cast<double>(expected) * 100)
 | 
						|
                << "%) navmesh tiles are generated";
 | 
						|
        }
 | 
						|
 | 
						|
        struct LogGeneratedTiles
 | 
						|
        {
 | 
						|
            void operator()(std::size_t provided, std::size_t expected) const
 | 
						|
            {
 | 
						|
                logGeneratedTiles(provided, expected);
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        class NavMeshTileConsumer final : public DetourNavigator::NavMeshTileConsumer
 | 
						|
        {
 | 
						|
        public:
 | 
						|
            std::atomic_size_t mExpected {0};
 | 
						|
 | 
						|
            explicit NavMeshTileConsumer(NavMeshDb&& db)
 | 
						|
                : mDb(std::move(db))
 | 
						|
                , mTransaction(mDb.startTransaction())
 | 
						|
                , mNextTileId(mDb.getMaxTileId() + 1)
 | 
						|
                , mNextShapeId(mDb.getMaxShapeId() + 1)
 | 
						|
            {}
 | 
						|
 | 
						|
            std::size_t getProvided() const { return mProvided.load(); }
 | 
						|
 | 
						|
            std::size_t getInserted() const { return mInserted.load(); }
 | 
						|
 | 
						|
            std::size_t getUpdated() const { return mUpdated.load(); }
 | 
						|
 | 
						|
            std::int64_t resolveMeshSource(const MeshSource& source) override
 | 
						|
            {
 | 
						|
                const std::lock_guard lock(mMutex);
 | 
						|
                return DetourNavigator::resolveMeshSource(mDb, source, mNextShapeId);
 | 
						|
            }
 | 
						|
 | 
						|
            std::optional<NavMeshTileInfo> find(const std::string& worldspace, const TilePosition &tilePosition,
 | 
						|
                const std::vector<std::byte> &input) override
 | 
						|
            {
 | 
						|
                std::optional<NavMeshTileInfo> result;
 | 
						|
                std::lock_guard lock(mMutex);
 | 
						|
                if (const auto tile = mDb.findTile(worldspace, tilePosition, input))
 | 
						|
                {
 | 
						|
                    NavMeshTileInfo info;
 | 
						|
                    info.mTileId = tile->mTileId;
 | 
						|
                    info.mVersion = tile->mVersion;
 | 
						|
                    result.emplace(info);
 | 
						|
                }
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
 | 
						|
            void ignore() override { report(); }
 | 
						|
 | 
						|
            void insert(const std::string& worldspace, const TilePosition& tilePosition, std::int64_t version,
 | 
						|
                const std::vector<std::byte>& input, PreparedNavMeshData& data) override
 | 
						|
            {
 | 
						|
                data.mUserId = static_cast<unsigned>(mNextTileId);
 | 
						|
                {
 | 
						|
                    std::lock_guard lock(mMutex);
 | 
						|
                    mDb.insertTile(mNextTileId, worldspace, tilePosition, TileVersion {version}, input, serialize(data));
 | 
						|
                    ++mNextTileId.t;
 | 
						|
                }
 | 
						|
                ++mInserted;
 | 
						|
                report();
 | 
						|
            }
 | 
						|
 | 
						|
            void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override
 | 
						|
            {
 | 
						|
                data.mUserId = static_cast<unsigned>(tileId);
 | 
						|
                {
 | 
						|
                    std::lock_guard lock(mMutex);
 | 
						|
                    mDb.updateTile(TileId {tileId}, TileVersion {version}, serialize(data));
 | 
						|
                }
 | 
						|
                ++mUpdated;
 | 
						|
                report();
 | 
						|
            }
 | 
						|
 | 
						|
            void wait()
 | 
						|
            {
 | 
						|
                constexpr std::size_t tilesPerTransaction = 3000;
 | 
						|
                std::unique_lock lock(mMutex);
 | 
						|
                while (mProvided < mExpected)
 | 
						|
                {
 | 
						|
                    mHasTile.wait(lock);
 | 
						|
                    if (mProvided % tilesPerTransaction == 0)
 | 
						|
                    {
 | 
						|
                        mTransaction.commit();
 | 
						|
                        mTransaction = mDb.startTransaction();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                logGeneratedTiles(mProvided, mExpected);
 | 
						|
            }
 | 
						|
 | 
						|
            void commit() { mTransaction.commit(); }
 | 
						|
 | 
						|
        private:
 | 
						|
            std::atomic_size_t mProvided {0};
 | 
						|
            std::atomic_size_t mInserted {0};
 | 
						|
            std::atomic_size_t mUpdated {0};
 | 
						|
            std::mutex mMutex;
 | 
						|
            NavMeshDb mDb;
 | 
						|
            Transaction mTransaction;
 | 
						|
            TileId mNextTileId;
 | 
						|
            std::condition_variable mHasTile;
 | 
						|
            Misc::ProgressReporter<LogGeneratedTiles> mReporter;
 | 
						|
            ShapeId mNextShapeId;
 | 
						|
 | 
						|
            void report()
 | 
						|
            {
 | 
						|
                mReporter(mProvided + 1, mExpected);
 | 
						|
                ++mProvided;
 | 
						|
                mHasTile.notify_one();
 | 
						|
            }
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings,
 | 
						|
        const std::size_t threadsNumber, WorldspaceData& data, NavMeshDb&& db)
 | 
						|
    {
 | 
						|
        Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers...";
 | 
						|
 | 
						|
        SceneUtil::WorkQueue workQueue(threadsNumber);
 | 
						|
        auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db));
 | 
						|
        std::size_t tiles = 0;
 | 
						|
        std::mt19937_64 random;
 | 
						|
 | 
						|
        for (const std::unique_ptr<WorldspaceNavMeshInput>& input : data.mNavMeshInputs)
 | 
						|
        {
 | 
						|
            std::vector<TilePosition> worldspaceTiles;
 | 
						|
 | 
						|
            DetourNavigator::getTilesPositions(
 | 
						|
                DetourNavigator::makeTilesPositionsRange(
 | 
						|
                    Misc::Convert::toOsgXY(input->mAabb.m_min),
 | 
						|
                    Misc::Convert::toOsgXY(input->mAabb.m_max),
 | 
						|
                    settings.mRecast
 | 
						|
                ),
 | 
						|
                [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); }
 | 
						|
            );
 | 
						|
 | 
						|
            tiles += worldspaceTiles.size();
 | 
						|
 | 
						|
            navMeshTileConsumer->mExpected = tiles;
 | 
						|
 | 
						|
            std::shuffle(worldspaceTiles.begin(), worldspaceTiles.end(), random);
 | 
						|
 | 
						|
            for (const TilePosition& tilePosition : worldspaceTiles)
 | 
						|
                workQueue.addWorkItem(new GenerateNavMeshTile(
 | 
						|
                    input->mWorldspace,
 | 
						|
                    tilePosition,
 | 
						|
                    RecastMeshProvider(input->mTileCachedRecastMeshManager),
 | 
						|
                    agentHalfExtents,
 | 
						|
                    settings,
 | 
						|
                    navMeshTileConsumer
 | 
						|
                ));
 | 
						|
        }
 | 
						|
 | 
						|
        navMeshTileConsumer->wait();
 | 
						|
        navMeshTileConsumer->commit();
 | 
						|
 | 
						|
        Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, "
 | 
						|
            << navMeshTileConsumer->getInserted() << " are inserted and "
 | 
						|
            << navMeshTileConsumer->getUpdated() << " updated";
 | 
						|
    }
 | 
						|
}
 |