1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-03 15:39:41 +00:00

Add navmeshtool flag to remove unused tiles from navmesh disk cache

* Remove tiles outside processing range. Useful when new content profile map
  has different bounds.
* Remove ignored tiles. For a case when content profile maps have intersection
  but there is no more data for navmesh.
* Remove older tiles at the same worldspace position. If navmesh tile data has
  changed with new content, the old ones unlikely to be used.
* Vacuum the database when there are modifications. SQLite leaves empty pages
  in the file on database modification. Vacuum cleans up unused pages reducing
  the file size.
This commit is contained in:
elsid 2022-02-18 21:35:09 +01:00
parent 67741402b5
commit ab1a6e034e
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
10 changed files with 323 additions and 34 deletions

View file

@ -83,6 +83,9 @@ namespace NavMeshTool
("process-interior-cells", bpo::value<bool>()->implicit_value(true) ("process-interior-cells", bpo::value<bool>()->implicit_value(true)
->default_value(false), "build navmesh for interior cells") ->default_value(false), "build navmesh for interior cells")
("remove-unused-tiles", bpo::value<bool>()->implicit_value(true)
->default_value(false), "remove tiles from cache that will not be used with current content profile")
; ;
Files::ConfigurationManager::addCommonOptions(result); Files::ConfigurationManager::addCommonOptions(result);
@ -141,6 +144,7 @@ namespace NavMeshTool
} }
const bool processInteriorCells = variables["process-interior-cells"].as<bool>(); const bool processInteriorCells = variables["process-interior-cells"].as<bool>();
const bool removeUnusedTiles = variables["remove-unused-tiles"].as<bool>();
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap); Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
@ -177,7 +181,8 @@ namespace NavMeshTool
WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager, WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager,
esmData, processInteriorCells); esmData, processInteriorCells);
generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, cellsData, std::move(db)); generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, removeUnusedTiles,
cellsData, std::move(db));
Log(Debug::Info) << "Done"; Log(Debug::Info) << "Done";

View file

@ -41,6 +41,7 @@ namespace NavMeshTool
using DetourNavigator::TileId; using DetourNavigator::TileId;
using DetourNavigator::TilePosition; using DetourNavigator::TilePosition;
using DetourNavigator::TileVersion; using DetourNavigator::TileVersion;
using DetourNavigator::TilesPositionsRange;
using Sqlite3::Transaction; using Sqlite3::Transaction;
void logGeneratedTiles(std::size_t provided, std::size_t expected) void logGeneratedTiles(std::size_t provided, std::size_t expected)
@ -63,8 +64,9 @@ namespace NavMeshTool
public: public:
std::atomic_size_t mExpected {0}; std::atomic_size_t mExpected {0};
explicit NavMeshTileConsumer(NavMeshDb&& db) explicit NavMeshTileConsumer(NavMeshDb&& db, bool removeUnusedTiles)
: mDb(std::move(db)) : mDb(std::move(db))
, mRemoveUnusedTiles(removeUnusedTiles)
, mTransaction(mDb.startTransaction()) , mTransaction(mDb.startTransaction())
, mNextTileId(mDb.getMaxTileId() + 1) , mNextTileId(mDb.getMaxTileId() + 1)
, mNextShapeId(mDb.getMaxShapeId() + 1) , mNextShapeId(mDb.getMaxShapeId() + 1)
@ -76,6 +78,12 @@ namespace NavMeshTool
std::size_t getUpdated() const { return mUpdated.load(); } std::size_t getUpdated() const { return mUpdated.load(); }
std::size_t getDeleted() const
{
const std::lock_guard lock(mMutex);
return mDeleted;
}
std::int64_t resolveMeshSource(const MeshSource& source) override std::int64_t resolveMeshSource(const MeshSource& source) override
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
@ -97,11 +105,34 @@ namespace NavMeshTool
return result; return result;
} }
void ignore() override { report(); } void ignore(std::string_view worldspace, const TilePosition& tilePosition) override
void insert(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t version,
const std::vector<std::byte>& input, PreparedNavMeshData& data) override
{ {
if (mRemoveUnusedTiles)
{
std::lock_guard lock(mMutex);
mDeleted += static_cast<std::size_t>(mDb.deleteTilesAt(worldspace, tilePosition));
}
report();
}
void identity(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId) override
{
if (mRemoveUnusedTiles)
{
std::lock_guard lock(mMutex);
mDeleted += static_cast<std::size_t>(mDb.deleteTilesAtExcept(worldspace, tilePosition, TileId {tileId}));
}
report();
}
void insert(std::string_view worldspace, const TilePosition& tilePosition,
std::int64_t version, const std::vector<std::byte>& input, PreparedNavMeshData& data) override
{
if (mRemoveUnusedTiles)
{
std::lock_guard lock(mMutex);
mDeleted += static_cast<std::size_t>(mDb.deleteTilesAt(worldspace, tilePosition));
}
data.mUserId = static_cast<unsigned>(mNextTileId); data.mUserId = static_cast<unsigned>(mNextTileId);
{ {
std::lock_guard lock(mMutex); std::lock_guard lock(mMutex);
@ -112,11 +143,14 @@ namespace NavMeshTool
report(); report();
} }
void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override void update(std::string_view worldspace, const TilePosition& tilePosition,
std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override
{ {
data.mUserId = static_cast<unsigned>(tileId); data.mUserId = static_cast<unsigned>(tileId);
{ {
std::lock_guard lock(mMutex); std::lock_guard lock(mMutex);
if (mRemoveUnusedTiles)
mDeleted += static_cast<std::size_t>(mDb.deleteTilesAtExcept(worldspace, tilePosition, TileId {tileId}));
mDb.updateTile(TileId {tileId}, TileVersion {version}, serialize(data)); mDb.updateTile(TileId {tileId}, TileVersion {version}, serialize(data));
} }
++mUpdated; ++mUpdated;
@ -141,49 +175,66 @@ namespace NavMeshTool
void commit() { mTransaction.commit(); } void commit() { mTransaction.commit(); }
void vacuum() { mDb.vacuum(); }
void removeTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range)
{
const std::lock_guard lock(mMutex);
mTransaction.commit();
Log(Debug::Info) << "Removing tiles outside processed range for worldspace \"" << worldspace << "\"...";
mDeleted += static_cast<std::size_t>(mDb.deleteTilesOutsideRange(worldspace, range));
mTransaction = mDb.startTransaction();
}
private: private:
std::atomic_size_t mProvided {0}; std::atomic_size_t mProvided {0};
std::atomic_size_t mInserted {0}; std::atomic_size_t mInserted {0};
std::atomic_size_t mUpdated {0}; std::atomic_size_t mUpdated {0};
std::mutex mMutex; std::size_t mDeleted = 0;
mutable std::mutex mMutex;
NavMeshDb mDb; NavMeshDb mDb;
const bool mRemoveUnusedTiles;
Transaction mTransaction; Transaction mTransaction;
TileId mNextTileId; TileId mNextTileId;
std::condition_variable mHasTile; std::condition_variable mHasTile;
Misc::ProgressReporter<LogGeneratedTiles> mReporter; Misc::ProgressReporter<LogGeneratedTiles> mReporter;
ShapeId mNextShapeId; ShapeId mNextShapeId;
std::mutex mReportMutex;
void report() void report()
{ {
mReporter(mProvided + 1, mExpected); const std::size_t provided = mProvided.fetch_add(1, std::memory_order_relaxed) + 1;
++mProvided; mReporter(provided, mExpected);
mHasTile.notify_one(); mHasTile.notify_one();
} }
}; };
} }
void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings, void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings,
const std::size_t threadsNumber, WorldspaceData& data, NavMeshDb&& db) std::size_t threadsNumber, bool removeUnusedTiles, WorldspaceData& data, NavMeshDb&& db)
{ {
Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers..."; Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers...";
SceneUtil::WorkQueue workQueue(threadsNumber); SceneUtil::WorkQueue workQueue(threadsNumber);
auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db)); auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db), removeUnusedTiles);
std::size_t tiles = 0; std::size_t tiles = 0;
std::mt19937_64 random; std::mt19937_64 random;
for (const std::unique_ptr<WorldspaceNavMeshInput>& input : data.mNavMeshInputs) for (const std::unique_ptr<WorldspaceNavMeshInput>& input : data.mNavMeshInputs)
{ {
const auto range = DetourNavigator::makeTilesPositionsRange(
Misc::Convert::toOsgXY(input->mAabb.m_min),
Misc::Convert::toOsgXY(input->mAabb.m_max),
settings.mRecast
);
if (removeUnusedTiles)
navMeshTileConsumer->removeTilesOutsideRange(input->mWorldspace, range);
std::vector<TilePosition> worldspaceTiles; std::vector<TilePosition> worldspaceTiles;
DetourNavigator::getTilesPositions( DetourNavigator::getTilesPositions(range,
DetourNavigator::makeTilesPositionsRange( [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); });
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(); tiles += worldspaceTiles.size();
@ -205,8 +256,19 @@ namespace NavMeshTool
navMeshTileConsumer->wait(); navMeshTileConsumer->wait();
navMeshTileConsumer->commit(); navMeshTileConsumer->commit();
const auto inserted = navMeshTileConsumer->getInserted();
const auto updated = navMeshTileConsumer->getUpdated();
const auto deleted = navMeshTileConsumer->getDeleted();
Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, " Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, "
<< navMeshTileConsumer->getInserted() << " are inserted and " << inserted << " are inserted, "
<< navMeshTileConsumer->getUpdated() << " updated"; << updated << " updated and "
<< deleted << " deleted";
if (inserted + updated + deleted > 0)
{
Log(Debug::Info) << "Vacuuming the database...";
navMeshTileConsumer->vacuum();
}
} }
} }

View file

@ -16,7 +16,8 @@ namespace NavMeshTool
struct WorldspaceData; struct WorldspaceData;
void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const DetourNavigator::Settings& settings, void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const DetourNavigator::Settings& settings,
const std::size_t threadsNumber, WorldspaceData& cellsData, DetourNavigator::NavMeshDb&& db); std::size_t threadsNumber, bool removeUnusedTiles, WorldspaceData& cellsData,
DetourNavigator::NavMeshDb&& db);
} }
#endif #endif

View file

@ -109,4 +109,61 @@ namespace
EXPECT_THROW(mDb.insertTile(tileId, worldspace, tilePosition, version, input, data), std::runtime_error); EXPECT_THROW(mDb.insertTile(tileId, worldspace, tilePosition, version, input, data), std::runtime_error);
EXPECT_NO_THROW(insertTile(TileId {54}, version)); EXPECT_NO_THROW(insertTile(TileId {54}, version));
} }
TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_should_remove_all_tiles_with_given_worldspace_and_position)
{
const TileVersion version {1};
const std::string worldspace = "sys::default";
const TilePosition tilePosition {3, 4};
const std::vector<std::byte> input1 = generateData();
const std::vector<std::byte> input2 = generateData();
const std::vector<std::byte> data = generateData();
ASSERT_EQ(mDb.insertTile(TileId {53}, worldspace, tilePosition, version, input1, data), 1);
ASSERT_EQ(mDb.insertTile(TileId {54}, worldspace, tilePosition, version, input2, data), 1);
ASSERT_EQ(mDb.deleteTilesAt(worldspace, tilePosition), 2);
EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, input1).has_value());
EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, input2).has_value());
}
TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_except_should_leave_tile_with_given_id)
{
const TileId leftTileId {53};
const TileId removedTileId {54};
const TileVersion version {1};
const std::string worldspace = "sys::default";
const TilePosition tilePosition {3, 4};
const std::vector<std::byte> leftInput = generateData();
const std::vector<std::byte> removedInput = generateData();
const std::vector<std::byte> data = generateData();
ASSERT_EQ(mDb.insertTile(leftTileId, worldspace, tilePosition, version, leftInput, data), 1);
ASSERT_EQ(mDb.insertTile(removedTileId, worldspace, tilePosition, version, removedInput, data), 1);
ASSERT_EQ(mDb.deleteTilesAtExcept(worldspace, tilePosition, leftTileId), 1);
const auto left = mDb.findTile(worldspace, tilePosition, leftInput);
ASSERT_TRUE(left.has_value());
EXPECT_EQ(left->mTileId, leftTileId);
EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, removedInput).has_value());
}
TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_outside_range_should_leave_tiles_inside_given_rectangle)
{
TileId tileId {1};
const TileVersion version {1};
const std::string worldspace = "sys::default";
const std::vector<std::byte> input = generateData();
const std::vector<std::byte> data = generateData();
for (int x = -2; x <= 2; ++x)
{
for (int y = -2; y <= 2; ++y)
{
ASSERT_EQ(mDb.insertTile(tileId, worldspace, TilePosition {x, y}, version, input, data), 1);
++tileId.t;
}
}
const TilesPositionsRange range {TilePosition {-1, -1}, TilePosition {2, 2}};
ASSERT_EQ(mDb.deleteTilesOutsideRange(worldspace, range), 16);
for (int x = -2; x <= 2; ++x)
for (int y = -2; y <= 2; ++y)
ASSERT_EQ(mDb.findTile(worldspace, TilePosition {x, y}, input).has_value(),
-1 <= x && x <= 1 && -1 <= y && y <= 1) << "x=" << x << " y=" << y;
}
} }

View file

@ -25,12 +25,14 @@ namespace DetourNavigator
{ {
struct Ignore struct Ignore
{ {
std::string_view mWorldspace;
const TilePosition& mTilePosition;
std::shared_ptr<NavMeshTileConsumer> mConsumer; std::shared_ptr<NavMeshTileConsumer> mConsumer;
~Ignore() noexcept ~Ignore() noexcept
{ {
if (mConsumer != nullptr) if (mConsumer != nullptr)
mConsumer->ignore(); mConsumer->ignore(mWorldspace, mTilePosition);
} }
}; };
} }
@ -59,7 +61,7 @@ namespace DetourNavigator
try try
{ {
Ignore ignore {consumer}; Ignore ignore {mWorldspace, mTilePosition, consumer};
const std::shared_ptr<RecastMesh> recastMesh = mRecastMeshProvider.getMesh(mWorldspace, mTilePosition); const std::shared_ptr<RecastMesh> recastMesh = mRecastMeshProvider.getMesh(mWorldspace, mTilePosition);
@ -72,7 +74,11 @@ namespace DetourNavigator
const std::optional<NavMeshTileInfo> info = consumer->find(mWorldspace, mTilePosition, input); const std::optional<NavMeshTileInfo> info = consumer->find(mWorldspace, mTilePosition, input);
if (info.has_value() && info->mVersion == mSettings.mNavMeshVersion) if (info.has_value() && info->mVersion == mSettings.mNavMeshVersion)
{
consumer->identity(mWorldspace, mTilePosition, info->mTileId);
ignore.mConsumer = nullptr;
return; return;
}
const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentHalfExtents, mSettings.mRecast); const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentHalfExtents, mSettings.mRecast);
@ -80,7 +86,7 @@ namespace DetourNavigator
return; return;
if (info.has_value()) if (info.has_value())
consumer->update(info->mTileId, mSettings.mNavMeshVersion, *data); consumer->update(mWorldspace, mTilePosition, info->mTileId, mSettings.mNavMeshVersion, *data);
else else
consumer->insert(mWorldspace, mTilePosition, mSettings.mNavMeshVersion, input, *data); consumer->insert(mWorldspace, mTilePosition, mSettings.mNavMeshVersion, input, *data);

View file

@ -39,12 +39,16 @@ namespace DetourNavigator
virtual std::optional<NavMeshTileInfo> find(std::string_view worldspace, const TilePosition& tilePosition, virtual std::optional<NavMeshTileInfo> find(std::string_view worldspace, const TilePosition& tilePosition,
const std::vector<std::byte>& input) = 0; const std::vector<std::byte>& input) = 0;
virtual void ignore() = 0; virtual void ignore(std::string_view worldspace, const TilePosition& tilePosition) = 0;
virtual void identity(std::string_view worldspace, const TilePosition& tilePosition,
std::int64_t tileId) = 0;
virtual void insert(std::string_view worldspace, const TilePosition& tilePosition, virtual void insert(std::string_view worldspace, const TilePosition& tilePosition,
std::int64_t version, const std::vector<std::byte>& input, PreparedNavMeshData& data) = 0; std::int64_t version, const std::vector<std::byte>& input, PreparedNavMeshData& data) = 0;
virtual void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) = 0; virtual void update(std::string_view worldspace, const TilePosition& tilePosition,
std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) = 0;
}; };
class GenerateNavMeshTile final : public SceneUtil::WorkItem class GenerateNavMeshTile final : public SceneUtil::WorkItem

View file

@ -3,6 +3,7 @@
#include "tilebounds.hpp" #include "tilebounds.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include "tilespositionsrange.hpp"
class btVector3; class btVector3;
class btTransform; class btTransform;
@ -17,12 +18,6 @@ namespace DetourNavigator
{ {
struct RecastSettings; struct RecastSettings;
struct TilesPositionsRange
{
TilePosition mBegin;
TilePosition mEnd;
};
TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin,
const osg::Vec2f& aabbMax, const RecastSettings& settings); const osg::Vec2f& aabbMax, const RecastSettings& settings);

View file

@ -34,6 +34,9 @@ namespace DetourNavigator
CREATE UNIQUE INDEX IF NOT EXISTS index_unique_tiles_by_worldspace_and_tile_position_and_input CREATE UNIQUE INDEX IF NOT EXISTS index_unique_tiles_by_worldspace_and_tile_position_and_input
ON tiles (worldspace, tile_position_x, tile_position_y, input); ON tiles (worldspace, tile_position_x, tile_position_y, input);
CREATE INDEX IF NOT EXISTS index_tiles_by_worldspace_and_tile_position
ON tiles (worldspace, tile_position_x, tile_position_y);
CREATE TABLE IF NOT EXISTS shapes ( CREATE TABLE IF NOT EXISTS shapes (
shape_id INTEGER PRIMARY KEY, shape_id INTEGER PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -82,6 +85,31 @@ namespace DetourNavigator
WHERE tile_id = :tile_id WHERE tile_id = :tile_id
)"; )";
constexpr std::string_view deleteTilesAtQuery = R"(
DELETE FROM tiles
WHERE worldspace = :worldspace
AND tile_position_x = :tile_position_x
AND tile_position_y = :tile_position_y
)";
constexpr std::string_view deleteTilesAtExceptQuery = R"(
DELETE FROM tiles
WHERE worldspace = :worldspace
AND tile_position_x = :tile_position_x
AND tile_position_y = :tile_position_y
AND tile_id != :exclude_tile_id
)";
constexpr std::string_view deleteTilesOutsideRangeQuery = R"(
DELETE FROM tiles
WHERE worldspace = :worldspace
AND ( tile_position_x < :begin_tile_position_x
OR tile_position_y < :begin_tile_position_y
OR tile_position_x >= :end_tile_position_x
OR tile_position_y >= :end_tile_position_y
)
)";
constexpr std::string_view getMaxShapeIdQuery = R"( constexpr std::string_view getMaxShapeIdQuery = R"(
SELECT max(shape_id) FROM shapes SELECT max(shape_id) FROM shapes
)"; )";
@ -98,6 +126,10 @@ namespace DetourNavigator
INSERT INTO shapes ( shape_id, name, type, hash) INSERT INTO shapes ( shape_id, name, type, hash)
VALUES (:shape_id, :name, :type, :hash) VALUES (:shape_id, :name, :type, :hash)
)"; )";
constexpr std::string_view vacuumQuery = R"(
VACUUM;
)";
} }
std::ostream& operator<<(std::ostream& stream, ShapeType value) std::ostream& operator<<(std::ostream& stream, ShapeType value)
@ -117,9 +149,13 @@ namespace DetourNavigator
, mGetTileData(*mDb, DbQueries::GetTileData {}) , mGetTileData(*mDb, DbQueries::GetTileData {})
, mInsertTile(*mDb, DbQueries::InsertTile {}) , mInsertTile(*mDb, DbQueries::InsertTile {})
, mUpdateTile(*mDb, DbQueries::UpdateTile {}) , mUpdateTile(*mDb, DbQueries::UpdateTile {})
, mDeleteTilesAt(*mDb, DbQueries::DeleteTilesAt {})
, mDeleteTilesAtExcept(*mDb, DbQueries::DeleteTilesAtExcept {})
, mDeleteTilesOutsideRange(*mDb, DbQueries::DeleteTilesOutsideRange {})
, mGetMaxShapeId(*mDb, DbQueries::GetMaxShapeId {}) , mGetMaxShapeId(*mDb, DbQueries::GetMaxShapeId {})
, mFindShapeId(*mDb, DbQueries::FindShapeId {}) , mFindShapeId(*mDb, DbQueries::FindShapeId {})
, mInsertShape(*mDb, DbQueries::InsertShape {}) , mInsertShape(*mDb, DbQueries::InsertShape {})
, mVacuum(*mDb, DbQueries::Vacuum {})
{ {
} }
@ -172,6 +208,21 @@ namespace DetourNavigator
return execute(*mDb, mUpdateTile, tileId, version, compressedData); return execute(*mDb, mUpdateTile, tileId, version, compressedData);
} }
int NavMeshDb::deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition)
{
return execute(*mDb, mDeleteTilesAt, worldspace, tilePosition);
}
int NavMeshDb::deleteTilesAtExcept(std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId)
{
return execute(*mDb, mDeleteTilesAtExcept, worldspace, tilePosition, excludeTileId);
}
int NavMeshDb::deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range)
{
return execute(*mDb, mDeleteTilesOutsideRange, worldspace, range);
}
ShapeId NavMeshDb::getMaxShapeId() ShapeId NavMeshDb::getMaxShapeId()
{ {
ShapeId shapeId {0}; ShapeId shapeId {0};
@ -194,6 +245,11 @@ namespace DetourNavigator
return execute(*mDb, mInsertShape, shapeId, name, type, hash); return execute(*mDb, mInsertShape, shapeId, name, type, hash);
} }
void NavMeshDb::vacuum()
{
execute(*mDb, mVacuum);
}
namespace DbQueries namespace DbQueries
{ {
std::string_view GetMaxTileId::text() noexcept std::string_view GetMaxTileId::text() noexcept
@ -260,6 +316,48 @@ namespace DetourNavigator
Sqlite3::bindParameter(db, statement, ":data", data); Sqlite3::bindParameter(db, statement, ":data", data);
} }
std::string_view DeleteTilesAt::text() noexcept
{
return deleteTilesAtQuery;
}
void DeleteTilesAt::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilePosition& tilePosition)
{
Sqlite3::bindParameter(db, statement, ":worldspace", worldspace);
Sqlite3::bindParameter(db, statement, ":tile_position_x", tilePosition.x());
Sqlite3::bindParameter(db, statement, ":tile_position_y", tilePosition.y());
}
std::string_view DeleteTilesAtExcept::text() noexcept
{
return deleteTilesAtExceptQuery;
}
void DeleteTilesAtExcept::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilePosition& tilePosition, TileId excludeTileId)
{
Sqlite3::bindParameter(db, statement, ":worldspace", worldspace);
Sqlite3::bindParameter(db, statement, ":tile_position_x", tilePosition.x());
Sqlite3::bindParameter(db, statement, ":tile_position_y", tilePosition.y());
Sqlite3::bindParameter(db, statement, ":exclude_tile_id", excludeTileId);
}
std::string_view DeleteTilesOutsideRange::text() noexcept
{
return deleteTilesOutsideRangeQuery;
}
void DeleteTilesOutsideRange::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilesPositionsRange& range)
{
Sqlite3::bindParameter(db, statement, ":worldspace", worldspace);
Sqlite3::bindParameter(db, statement, ":begin_tile_position_x", range.mBegin.x());
Sqlite3::bindParameter(db, statement, ":begin_tile_position_y", range.mBegin.y());
Sqlite3::bindParameter(db, statement, ":end_tile_position_x", range.mEnd.x());
Sqlite3::bindParameter(db, statement, ":end_tile_position_y", range.mEnd.y());
}
std::string_view GetMaxShapeId::text() noexcept std::string_view GetMaxShapeId::text() noexcept
{ {
return getMaxShapeIdQuery; return getMaxShapeIdQuery;
@ -291,5 +389,10 @@ namespace DetourNavigator
Sqlite3::bindParameter(db, statement, ":type", static_cast<int>(type)); Sqlite3::bindParameter(db, statement, ":type", static_cast<int>(type));
Sqlite3::bindParameter(db, statement, ":hash", hash); Sqlite3::bindParameter(db, statement, ":hash", hash);
} }
std::string_view Vacuum::text() noexcept
{
return vacuumQuery;
}
} }
} }

View file

@ -3,6 +3,8 @@
#include "tileposition.hpp" #include "tileposition.hpp"
#include <components/detournavigator/tilespositionsrange.hpp>
#include <components/sqlite3/db.hpp> #include <components/sqlite3/db.hpp>
#include <components/sqlite3/statement.hpp> #include <components/sqlite3/statement.hpp>
#include <components/sqlite3/transaction.hpp> #include <components/sqlite3/transaction.hpp>
@ -89,6 +91,27 @@ namespace DetourNavigator
const std::vector<std::byte>& data); const std::vector<std::byte>& data);
}; };
struct DeleteTilesAt
{
static std::string_view text() noexcept;
static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilePosition& tilePosition);
};
struct DeleteTilesAtExcept
{
static std::string_view text() noexcept;
static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilePosition& tilePosition, TileId excludeTileId);
};
struct DeleteTilesOutsideRange
{
static std::string_view text() noexcept;
static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace,
const TilesPositionsRange& range);
};
struct GetMaxShapeId struct GetMaxShapeId
{ {
static std::string_view text() noexcept; static std::string_view text() noexcept;
@ -108,6 +131,12 @@ namespace DetourNavigator
static void bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, std::string_view name, static void bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, std::string_view name,
ShapeType type, const Sqlite3::ConstBlob& hash); ShapeType type, const Sqlite3::ConstBlob& hash);
}; };
struct Vacuum
{
static std::string_view text() noexcept;
static void bind(sqlite3&, sqlite3_stmt&) {}
};
} }
class NavMeshDb class NavMeshDb
@ -130,12 +159,20 @@ namespace DetourNavigator
int updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data); int updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data);
int deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition);
int deleteTilesAtExcept(std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId);
int deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range);
ShapeId getMaxShapeId(); ShapeId getMaxShapeId();
std::optional<ShapeId> findShapeId(std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); std::optional<ShapeId> findShapeId(std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash);
int insertShape(ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); int insertShape(ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash);
void vacuum();
private: private:
Sqlite3::Db mDb; Sqlite3::Db mDb;
Sqlite3::Statement<DbQueries::GetMaxTileId> mGetMaxTileId; Sqlite3::Statement<DbQueries::GetMaxTileId> mGetMaxTileId;
@ -143,9 +180,13 @@ namespace DetourNavigator
Sqlite3::Statement<DbQueries::GetTileData> mGetTileData; Sqlite3::Statement<DbQueries::GetTileData> mGetTileData;
Sqlite3::Statement<DbQueries::InsertTile> mInsertTile; Sqlite3::Statement<DbQueries::InsertTile> mInsertTile;
Sqlite3::Statement<DbQueries::UpdateTile> mUpdateTile; Sqlite3::Statement<DbQueries::UpdateTile> mUpdateTile;
Sqlite3::Statement<DbQueries::DeleteTilesAt> mDeleteTilesAt;
Sqlite3::Statement<DbQueries::DeleteTilesAtExcept> mDeleteTilesAtExcept;
Sqlite3::Statement<DbQueries::DeleteTilesOutsideRange> mDeleteTilesOutsideRange;
Sqlite3::Statement<DbQueries::GetMaxShapeId> mGetMaxShapeId; Sqlite3::Statement<DbQueries::GetMaxShapeId> mGetMaxShapeId;
Sqlite3::Statement<DbQueries::FindShapeId> mFindShapeId; Sqlite3::Statement<DbQueries::FindShapeId> mFindShapeId;
Sqlite3::Statement<DbQueries::InsertShape> mInsertShape; Sqlite3::Statement<DbQueries::InsertShape> mInsertShape;
Sqlite3::Statement<DbQueries::Vacuum> mVacuum;
}; };
} }

View file

@ -0,0 +1,15 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILESPOSITIONSRANGE_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILESPOSITIONSRANGE_H
#include "tileposition.hpp"
namespace DetourNavigator
{
struct TilesPositionsRange
{
TilePosition mBegin;
TilePosition mEnd;
};
}
#endif