mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-03 13:43:06 +00:00
Merge branch 'navmeshtool_worldspaces' into 'master'
Somewhat reduce navmeshtool peak memory usage See merge request OpenMW/openmw!5076
This commit is contained in:
commit
9efef817dc
6 changed files with 304 additions and 146 deletions
|
|
@ -24,6 +24,7 @@
|
|||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/niffilemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
|
|
@ -39,9 +40,9 @@
|
|||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -119,6 +120,10 @@ namespace NavMeshTool
|
|||
addOption("write-binary-log", bpo::value<bool>()->implicit_value(true)->default_value(false),
|
||||
"write progress in binary messages to be consumed by the launcher");
|
||||
|
||||
addOption("worldspace-filter", bpo::value<std::string>()->default_value(".*"),
|
||||
"Regular expression to filter in specified worldspaces in modified ECMAScript grammar (see "
|
||||
"https://en.cppreference.com/w/cpp/regex/ecmascript.html)");
|
||||
|
||||
Files::ConfigurationManager::addCommonOptions(result);
|
||||
|
||||
return result;
|
||||
|
|
@ -180,6 +185,8 @@ namespace NavMeshTool
|
|||
const bool removeUnusedTiles = variables["remove-unused-tiles"].as<bool>();
|
||||
const bool writeBinaryLog = variables["write-binary-log"].as<bool>();
|
||||
|
||||
const std::regex worldspaceFilter(variables["worldspace-filter"].as<std::string>());
|
||||
|
||||
#ifdef WIN32
|
||||
if (writeBinaryLog)
|
||||
_setmode(_fileno(stderr), _O_BINARY);
|
||||
|
|
@ -229,11 +236,42 @@ namespace NavMeshTool
|
|||
navigatorSettings.mRecast.mSwimHeightScale
|
||||
= EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat();
|
||||
|
||||
WorldspaceData cellsData = gatherWorldspaceData(
|
||||
navigatorSettings, readers, vfs, bulletShapeManager, esmData, processInteriorCells, writeBinaryLog);
|
||||
const std::unordered_map<ESM::RefId, std::vector<std::size_t>> worldspaceCells
|
||||
= collectWorldspaceCells(esmData, processInteriorCells, worldspaceFilter);
|
||||
|
||||
const Status status = generateAllNavMeshTiles(agentBounds, navigatorSettings, threadsNumber,
|
||||
removeUnusedTiles, writeBinaryLog, cellsData, std::move(db));
|
||||
Status status = Status::Ok;
|
||||
bool needVacuum = false;
|
||||
std::size_t count = 0;
|
||||
|
||||
SceneUtil::WorkQueue workQueue(threadsNumber);
|
||||
|
||||
Log(Debug::Info) << "Using " << threadsNumber << " parallel workers...";
|
||||
|
||||
for (const auto& [worldspace, cells] : worldspaceCells)
|
||||
{
|
||||
const WorldspaceData worldspaceData = gatherWorldspaceData(
|
||||
navigatorSettings, readers, vfs, bulletShapeManager, esmData, writeBinaryLog, worldspace, cells);
|
||||
|
||||
const Result result = generateAllNavMeshTiles(
|
||||
agentBounds, navigatorSettings, removeUnusedTiles, writeBinaryLog, worldspaceData, db, workQueue);
|
||||
|
||||
++count;
|
||||
|
||||
Log(Debug::Info) << "Processed worldspace (" << count << "/" << worldspaceCells.size() << ") "
|
||||
<< worldspace;
|
||||
|
||||
status = result.mStatus;
|
||||
needVacuum = needVacuum || result.mNeedVacuum;
|
||||
|
||||
if (status != Status::Ok)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == Status::Ok && needVacuum)
|
||||
{
|
||||
Log(Debug::Info) << "Vacuuming the database...";
|
||||
db.vacuum();
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include <cstddef>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace NavMeshTool
|
||||
|
|
@ -78,8 +77,8 @@ namespace NavMeshTool
|
|||
public:
|
||||
std::atomic_size_t mExpected{ 0 };
|
||||
|
||||
explicit NavMeshTileConsumer(NavMeshDb&& db, bool removeUnusedTiles, bool writeBinaryLog)
|
||||
: mDb(std::move(db))
|
||||
explicit NavMeshTileConsumer(NavMeshDb& db, bool removeUnusedTiles, bool writeBinaryLog)
|
||||
: mDb(db)
|
||||
, mRemoveUnusedTiles(removeUnusedTiles)
|
||||
, mWriteBinaryLog(writeBinaryLog)
|
||||
, mTransaction(mDb.startTransaction(Sqlite3::TransactionMode::Immediate))
|
||||
|
|
@ -211,12 +210,6 @@ namespace NavMeshTool
|
|||
mTransaction.commit();
|
||||
}
|
||||
|
||||
void vacuum()
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
mDb.vacuum();
|
||||
}
|
||||
|
||||
void removeTilesOutsideRange(ESM::RefId worldspace, const TilesPositionsRange& range)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
|
|
@ -233,7 +226,7 @@ namespace NavMeshTool
|
|||
std::size_t mDeleted = 0;
|
||||
Status mStatus = Status::Ok;
|
||||
mutable std::mutex mMutex;
|
||||
NavMeshDb mDb;
|
||||
NavMeshDb& mDb;
|
||||
const bool mRemoveUnusedTiles;
|
||||
const bool mWriteBinaryLog;
|
||||
Transaction mTransaction;
|
||||
|
|
@ -254,45 +247,39 @@ namespace NavMeshTool
|
|||
};
|
||||
}
|
||||
|
||||
Status generateAllNavMeshTiles(const AgentBounds& agentBounds, const Settings& settings, std::size_t threadsNumber,
|
||||
bool removeUnusedTiles, bool writeBinaryLog, WorldspaceData& data, NavMeshDb&& db)
|
||||
Result generateAllNavMeshTiles(const AgentBounds& agentBounds, const Settings& settings, bool removeUnusedTiles,
|
||||
bool writeBinaryLog, const WorldspaceData& data, NavMeshDb& db, SceneUtil::WorkQueue& workQueue)
|
||||
{
|
||||
Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers...";
|
||||
Log(Debug::Info) << "Generating navmesh tiles for " << data.mWorldspace << " worldspace...";
|
||||
|
||||
SceneUtil::WorkQueue workQueue(threadsNumber);
|
||||
auto navMeshTileConsumer
|
||||
= std::make_shared<NavMeshTileConsumer>(std::move(db), removeUnusedTiles, writeBinaryLog);
|
||||
std::size_t tiles = 0;
|
||||
std::mt19937_64 random;
|
||||
auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(db, removeUnusedTiles, writeBinaryLog);
|
||||
|
||||
const auto range = DetourNavigator::makeTilesPositionsRange(
|
||||
Misc::Convert::toOsgXY(data.mAabb.m_min), Misc::Convert::toOsgXY(data.mAabb.m_max), settings.mRecast);
|
||||
|
||||
if (removeUnusedTiles)
|
||||
navMeshTileConsumer->removeTilesOutsideRange(data.mWorldspace, range);
|
||||
|
||||
std::vector<TilePosition> worldspaceTiles = data.mTiles;
|
||||
|
||||
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;
|
||||
|
||||
DetourNavigator::getTilesPositions(
|
||||
range, [&](const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); });
|
||||
|
||||
tiles += worldspaceTiles.size();
|
||||
const std::size_t tiles = worldspaceTiles.size();
|
||||
|
||||
if (writeBinaryLog)
|
||||
serializeToStderr(ExpectedTiles{ static_cast<std::uint64_t>(tiles) });
|
||||
|
||||
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), agentBounds, settings,
|
||||
navMeshTileConsumer));
|
||||
}
|
||||
|
||||
{
|
||||
std::mt19937_64 random;
|
||||
std::shuffle(worldspaceTiles.begin(), worldspaceTiles.end(), random);
|
||||
}
|
||||
|
||||
for (const TilePosition& tilePosition : worldspaceTiles)
|
||||
workQueue.addWorkItem(new GenerateNavMeshTile(data.mWorldspace, tilePosition,
|
||||
RecastMeshProvider(*data.mTileCachedRecastMeshManager), agentBounds, settings, navMeshTileConsumer));
|
||||
|
||||
const Status status = navMeshTileConsumer->wait();
|
||||
if (status == Status::Ok)
|
||||
navMeshTileConsumer->commit();
|
||||
|
|
@ -304,12 +291,9 @@ namespace NavMeshTool
|
|||
Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, " << inserted
|
||||
<< " are inserted, " << updated << " updated and " << deleted << " deleted";
|
||||
|
||||
if (inserted + updated + deleted > 0)
|
||||
{
|
||||
Log(Debug::Info) << "Vacuuming the database...";
|
||||
navMeshTileConsumer->vacuum();
|
||||
}
|
||||
|
||||
return status;
|
||||
return Result{
|
||||
.mStatus = status,
|
||||
.mNeedVacuum = inserted + updated + deleted > 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef OPENMW_NAVMESHTOOL_NAVMESH_H
|
||||
#define OPENMW_NAVMESHTOOL_NAVMESH_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class NavMeshDb;
|
||||
|
|
@ -10,6 +8,11 @@ namespace DetourNavigator
|
|||
struct AgentBounds;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class WorkQueue;
|
||||
}
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
struct WorldspaceData;
|
||||
|
|
@ -21,9 +24,15 @@ namespace NavMeshTool
|
|||
NotEnoughSpace,
|
||||
};
|
||||
|
||||
Status generateAllNavMeshTiles(const DetourNavigator::AgentBounds& agentBounds,
|
||||
const DetourNavigator::Settings& settings, std::size_t threadsNumber, bool removeUnusedTiles,
|
||||
bool writeBinaryLog, WorldspaceData& cellsData, DetourNavigator::NavMeshDb&& db);
|
||||
struct Result
|
||||
{
|
||||
Status mStatus;
|
||||
bool mNeedVacuum;
|
||||
};
|
||||
|
||||
Result generateAllNavMeshTiles(const DetourNavigator::AgentBounds& agentBounds,
|
||||
const DetourNavigator::Settings& settings, bool removeUnusedTiles, bool writeBinaryLog,
|
||||
const WorldspaceData& data, DetourNavigator::NavMeshDb& db, SceneUtil::WorkQueue& workQueue);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ namespace NavMeshTool
|
|||
using DetourNavigator::HeightfieldSurface;
|
||||
using DetourNavigator::ObjectId;
|
||||
using DetourNavigator::ObjectTransform;
|
||||
using DetourNavigator::TilesPositionsRange;
|
||||
|
||||
struct CellRef
|
||||
{
|
||||
|
|
@ -71,6 +72,25 @@ namespace NavMeshTool
|
|||
}
|
||||
};
|
||||
|
||||
struct AddedCellRef
|
||||
{
|
||||
std::string mCell;
|
||||
CellRef mCellRef;
|
||||
TilesPositionsRange mRange;
|
||||
};
|
||||
|
||||
struct WriteArray
|
||||
{
|
||||
const float (&mValue)[3];
|
||||
|
||||
friend inline std::ostream& operator<<(std::ostream& stream, const WriteArray& value)
|
||||
{
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
stream << value.mValue[i] << ", ";
|
||||
return stream << value.mValue[2];
|
||||
}
|
||||
};
|
||||
|
||||
ESM::RecNameInts getType(const EsmLoader::EsmData& esmData, const ESM::RefId& refId)
|
||||
{
|
||||
const auto it = std::lower_bound(
|
||||
|
|
@ -119,7 +139,7 @@ namespace NavMeshTool
|
|||
|
||||
Log(Debug::Debug) << "Prepared " << cellRefs.size() << " unique cell refs";
|
||||
|
||||
for (CellRef& cellRef : cellRefs)
|
||||
for (const CellRef& cellRef : cellRefs)
|
||||
{
|
||||
VFS::Path::Normalized model(getModel(esmData, cellRef.mRefId, cellRef.mType));
|
||||
if (model.empty())
|
||||
|
|
@ -153,7 +173,7 @@ namespace NavMeshTool
|
|||
case ESM::REC_CONT:
|
||||
case ESM::REC_DOOR:
|
||||
case ESM::REC_STAT:
|
||||
f(BulletObject(std::move(shapeInstance), cellRef.mPos, cellRef.mScale));
|
||||
f(BulletObject(std::move(shapeInstance), cellRef.mPos, cellRef.mScale), cellRef);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -232,30 +252,104 @@ namespace NavMeshTool
|
|||
<< " fileHash=" << Misc::StringUtils::toHex(shape.getInstance()->mFileHash);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void detectDisconnectedTileGroups(ESM::RefId worldspace,
|
||||
const std::map<osg::Vec2i, DetourNavigator::ChangeType>& changedTiles,
|
||||
std::span<const AddedCellRef> cellRefs)
|
||||
{
|
||||
if (changedTiles.empty())
|
||||
return;
|
||||
|
||||
std::deque<osg::Vec2i> queue;
|
||||
std::map<osg::Vec2i, std::size_t> positionToComponent;
|
||||
std::size_t componentIndex = 0;
|
||||
|
||||
for (const auto& [initial, changeType] : changedTiles)
|
||||
{
|
||||
if (positionToComponent.contains(initial))
|
||||
continue;
|
||||
|
||||
queue.push_back(initial);
|
||||
positionToComponent.emplace(initial, componentIndex);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
const osg::Vec2i position = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
for (int x = position.x() - 1; x <= position.x() + 1; ++x)
|
||||
{
|
||||
for (int y = position.y() - 1; y <= position.y() + 1; ++y)
|
||||
{
|
||||
const osg::Vec2i candidate(x, y);
|
||||
|
||||
if (candidate == position)
|
||||
continue;
|
||||
|
||||
if (!changedTiles.contains(candidate))
|
||||
continue;
|
||||
|
||||
const auto it = positionToComponent.find(candidate);
|
||||
|
||||
if (it != positionToComponent.end())
|
||||
continue;
|
||||
|
||||
queue.push_back(candidate);
|
||||
positionToComponent.emplace_hint(it, candidate, componentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++componentIndex;
|
||||
}
|
||||
|
||||
if (componentIndex <= 1)
|
||||
return;
|
||||
|
||||
Log(Debug::Warning) << "Found " << componentIndex << " disconnected tile groups";
|
||||
|
||||
std::vector<std::size_t> cellRefsPerComponent(componentIndex);
|
||||
|
||||
for (const AddedCellRef& v : cellRefs)
|
||||
++cellRefsPerComponent[positionToComponent.at(v.mRange.mBegin)];
|
||||
|
||||
const std::size_t largestComponent
|
||||
= std::max_element(cellRefsPerComponent.begin(), cellRefsPerComponent.end())
|
||||
- cellRefsPerComponent.begin();
|
||||
|
||||
for (const AddedCellRef& v : cellRefs)
|
||||
{
|
||||
const std::size_t component = positionToComponent.at(v.mRange.mBegin);
|
||||
|
||||
if (component == largestComponent)
|
||||
continue;
|
||||
|
||||
Log(Debug::Warning) << "CellRef belongs to not largest disconnected tile group:"
|
||||
<< " worldspace=" << worldspace << " cell=\"" << v.mCell << "\""
|
||||
<< " ref_num=" << v.mCellRef.mRefNum << " ref_id=" << v.mCellRef.mRefId
|
||||
<< " scale=" << v.mCellRef.mScale << " pos=" << WriteArray{ v.mCellRef.mPos.pos }
|
||||
<< " rot=" << WriteArray{ v.mCellRef.mPos.rot }
|
||||
<< " begin_tile=" << v.mRange.mBegin.x() << ", " << v.mRange.mBegin.y()
|
||||
<< " end_tile=" << v.mRange.mEnd.x() << ", " << v.mRange.mEnd.y()
|
||||
<< " component=" << component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorldspaceNavMeshInput::WorldspaceNavMeshInput(
|
||||
ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings)
|
||||
WorldspaceData::WorldspaceData(ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings)
|
||||
: mWorldspace(worldspace)
|
||||
, mTileCachedRecastMeshManager(settings)
|
||||
, mTileCachedRecastMeshManager(std::make_unique<TileCachedRecastMeshManager>(settings))
|
||||
{
|
||||
mAabb.m_min = btVector3(0, 0, 0);
|
||||
mAabb.m_max = btVector3(0, 0, 0);
|
||||
}
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, ESM::ReadersCache& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells, bool writeBinaryLog)
|
||||
std::unordered_map<ESM::RefId, std::vector<std::size_t>> collectWorldspaceCells(
|
||||
const EsmLoader::EsmData& esmData, bool processInteriorCells, const std::regex& worldspaceFilter)
|
||||
{
|
||||
Log(Debug::Info) << "Processing " << esmData.mCells.size() << " cells...";
|
||||
Log(Debug::Info) << "Collecting worldspaces from " << esmData.mCells.size() << " cells...";
|
||||
|
||||
std::unordered_map<ESM::RefId, std::unique_ptr<WorldspaceNavMeshInput>> navMeshInputs;
|
||||
WorldspaceData data;
|
||||
|
||||
std::size_t objectsCounter = 0;
|
||||
|
||||
if (writeBinaryLog)
|
||||
serializeToStderr(ExpectedCells{ static_cast<std::uint64_t>(esmData.mCells.size()) });
|
||||
std::unordered_map<ESM::RefId, std::vector<std::size_t>> result;
|
||||
|
||||
for (std::size_t i = 0; i < esmData.mCells.size(); ++i)
|
||||
{
|
||||
|
|
@ -264,34 +358,62 @@ namespace NavMeshTool
|
|||
|
||||
if (!exterior && !processInteriorCells)
|
||||
{
|
||||
if (writeBinaryLog)
|
||||
serializeToStderr(ProcessedCells{ static_cast<std::uint64_t>(i + 1) });
|
||||
Log(Debug::Info) << "Skipped interior"
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") \""
|
||||
<< cell.getDescription() << "\"";
|
||||
Log(Debug::Verbose) << "Skipped interior"
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") \""
|
||||
<< cell.getDescription() << "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
const ESM::RefId cellWorldspace = cell.isExterior() ? ESM::Cell::sDefaultWorldspaceId : cell.mId;
|
||||
|
||||
if (!std::regex_match(cellWorldspace.toString(), worldspaceFilter))
|
||||
{
|
||||
Log(Debug::Verbose) << "Skipped filtered out"
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") \""
|
||||
<< cell.getDescription() << "\" from " << cellWorldspace << " worldspace";
|
||||
continue;
|
||||
}
|
||||
|
||||
result[cellWorldspace].push_back(i);
|
||||
|
||||
Log(Debug::Info) << "Collected " << (exterior ? "exterior" : "interior") << " cell (" << (i + 1) << "/"
|
||||
<< esmData.mCells.size() << ") " << cell.getDescription();
|
||||
}
|
||||
|
||||
Log(Debug::Info) << "Collected " << result.size() << " worldspaces";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, ESM::ReadersCache& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool writeBinaryLog, ESM::RefId worldspace, std::span<const std::size_t> cells)
|
||||
{
|
||||
Log(Debug::Info) << "Processing " << cells.size() << " cells from worldspace " << worldspace << "...";
|
||||
|
||||
if (writeBinaryLog)
|
||||
serializeToStderr(ExpectedCells{ static_cast<std::uint64_t>(cells.size()) });
|
||||
|
||||
WorldspaceData data(worldspace, settings.mRecast);
|
||||
TileCachedRecastMeshManager& manager = *data.mTileCachedRecastMeshManager;
|
||||
|
||||
const auto guard = manager.makeUpdateGuard();
|
||||
|
||||
manager.setWorldspace(worldspace, guard.get());
|
||||
|
||||
std::size_t objectsCounter = 0;
|
||||
std::vector<AddedCellRef> addedCellRefs;
|
||||
|
||||
for (std::size_t i = 0; i < cells.size(); ++i)
|
||||
{
|
||||
const ESM::Cell& cell = esmData.mCells[cells[i]];
|
||||
const bool exterior = cell.isExterior();
|
||||
|
||||
Log(Debug::Debug) << "Processing " << (exterior ? "exterior" : "interior") << " cell (" << (i + 1) << "/"
|
||||
<< esmData.mCells.size() << ") \"" << cell.getDescription() << "\"";
|
||||
<< cells.size() << ") \"" << cell.getDescription() << "\"";
|
||||
|
||||
const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY);
|
||||
const std::size_t cellObjectsBegin = data.mObjects.size();
|
||||
const ESM::RefId cellWorldspace = cell.isExterior() ? ESM::Cell::sDefaultWorldspaceId : cell.mId;
|
||||
WorldspaceNavMeshInput& navMeshInput = [&]() -> WorldspaceNavMeshInput& {
|
||||
auto it = navMeshInputs.find(cellWorldspace);
|
||||
if (it == navMeshInputs.end())
|
||||
{
|
||||
it = navMeshInputs
|
||||
.emplace(cellWorldspace,
|
||||
std::make_unique<WorldspaceNavMeshInput>(cellWorldspace, settings.mRecast))
|
||||
.first;
|
||||
it->second->mTileCachedRecastMeshManager.setWorldspace(cellWorldspace, nullptr);
|
||||
}
|
||||
return *it->second;
|
||||
}();
|
||||
|
||||
const auto guard = navMeshInput.mTileCachedRecastMeshManager.makeUpdateGuard();
|
||||
|
||||
if (exterior)
|
||||
{
|
||||
|
|
@ -301,69 +423,75 @@ namespace NavMeshTool
|
|||
= makeHeightfieldShape(it == esmData.mLands.end() ? std::optional<ESM::Land>() : *it, cellPosition,
|
||||
data.mHeightfields, data.mLandData);
|
||||
|
||||
mergeOrAssign(
|
||||
getAabb(cellPosition, minHeight, maxHeight), navMeshInput.mAabb, navMeshInput.mAabbInitialized);
|
||||
mergeOrAssign(getAabb(cellPosition, minHeight, maxHeight), data.mAabb, data.mAabbInitialized);
|
||||
|
||||
navMeshInput.mTileCachedRecastMeshManager.addHeightfield(
|
||||
cellPosition, ESM::Land::REAL_SIZE, heightfieldShape, guard.get());
|
||||
manager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape, guard.get());
|
||||
|
||||
navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1, guard.get());
|
||||
manager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1, guard.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((cell.mData.mFlags & ESM::Cell::HasWater) != 0)
|
||||
navMeshInput.mTileCachedRecastMeshManager.addWater(
|
||||
cellPosition, std::numeric_limits<int>::max(), cell.mWater, guard.get());
|
||||
manager.addWater(cellPosition, std::numeric_limits<int>::max(), cell.mWater, guard.get());
|
||||
}
|
||||
|
||||
forEachObject(cell, esmData, vfs, bulletShapeManager, readers, [&](BulletObject object) {
|
||||
if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None)
|
||||
return;
|
||||
forEachObject(
|
||||
cell, esmData, vfs, bulletShapeManager, readers, [&](BulletObject object, const CellRef& cellRef) {
|
||||
if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None)
|
||||
return;
|
||||
|
||||
const btTransform& transform = object.getCollisionObject().getWorldTransform();
|
||||
const btAABB aabb = BulletHelpers::getAabb(*object.getCollisionObject().getCollisionShape(), transform);
|
||||
mergeOrAssign(aabb, navMeshInput.mAabb, navMeshInput.mAabbInitialized);
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
navMeshInput.mAabb.merge(BulletHelpers::getAabb(*avoid, transform));
|
||||
const btTransform& transform = object.getCollisionObject().getWorldTransform();
|
||||
const btAABB aabb
|
||||
= BulletHelpers::getAabb(*object.getCollisionObject().getCollisionShape(), transform);
|
||||
mergeOrAssign(aabb, data.mAabb, data.mAabbInitialized);
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
data.mAabb.merge(BulletHelpers::getAabb(*avoid, transform));
|
||||
|
||||
const ObjectId objectId(++objectsCounter);
|
||||
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(),
|
||||
object.getObjectTransform());
|
||||
const ObjectId objectId(++objectsCounter);
|
||||
const CollisionShape shape(object.getShapeInstance(),
|
||||
*object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
|
||||
|
||||
if (!navMeshInput.mTileCachedRecastMeshManager.addObject(
|
||||
objectId, shape, transform, DetourNavigator::AreaType_ground, guard.get()))
|
||||
throw std::logic_error(
|
||||
makeAddObjectErrorMessage(objectId, DetourNavigator::AreaType_ground, shape));
|
||||
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
{
|
||||
const ObjectId avoidObjectId(++objectsCounter);
|
||||
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
|
||||
if (!navMeshInput.mTileCachedRecastMeshManager.addObject(
|
||||
avoidObjectId, avoidShape, transform, DetourNavigator::AreaType_null, guard.get()))
|
||||
if (!manager.addObject(objectId, shape, transform, DetourNavigator::AreaType_ground, guard.get()))
|
||||
throw std::logic_error(
|
||||
makeAddObjectErrorMessage(avoidObjectId, DetourNavigator::AreaType_null, avoidShape));
|
||||
}
|
||||
makeAddObjectErrorMessage(objectId, DetourNavigator::AreaType_ground, shape));
|
||||
|
||||
data.mObjects.emplace_back(std::move(object));
|
||||
});
|
||||
addedCellRefs.push_back(AddedCellRef{
|
||||
.mCell = cell.getDescription(),
|
||||
.mCellRef = cellRef,
|
||||
.mRange = makeTilesPositionsRange(shape.getShape(), transform, settings.mRecast),
|
||||
});
|
||||
|
||||
const auto cellDescription = cell.getDescription();
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
{
|
||||
const ObjectId avoidObjectId(++objectsCounter);
|
||||
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
|
||||
if (!manager.addObject(
|
||||
avoidObjectId, avoidShape, transform, DetourNavigator::AreaType_null, guard.get()))
|
||||
throw std::logic_error(
|
||||
makeAddObjectErrorMessage(avoidObjectId, DetourNavigator::AreaType_null, avoidShape));
|
||||
}
|
||||
|
||||
data.mObjects.emplace_back(std::move(object));
|
||||
});
|
||||
|
||||
if (writeBinaryLog)
|
||||
serializeToStderr(ProcessedCells{ static_cast<std::uint64_t>(i + 1) });
|
||||
|
||||
Log(Debug::Info) << "Processed " << (exterior ? "exterior" : "interior") << " cell (" << (i + 1) << "/"
|
||||
<< esmData.mCells.size() << ") " << cellDescription << " with "
|
||||
<< cells.size() << ") " << cell.getDescription() << " with "
|
||||
<< (data.mObjects.size() - cellObjectsBegin) << " objects";
|
||||
}
|
||||
|
||||
data.mNavMeshInputs.reserve(navMeshInputs.size());
|
||||
std::transform(navMeshInputs.begin(), navMeshInputs.end(), std::back_inserter(data.mNavMeshInputs),
|
||||
[](auto& v) { return std::move(v.second); });
|
||||
const std::map<osg::Vec2i, DetourNavigator::ChangeType> changedTiles = manager.takeChangedTiles(guard.get());
|
||||
|
||||
Log(Debug::Info) << "Processed " << esmData.mCells.size() << " cells, added " << data.mObjects.size()
|
||||
<< " objects and " << data.mHeightfields.size() << " height fields";
|
||||
if (worldspace != ESM::Cell::sDefaultWorldspaceId)
|
||||
detectDisconnectedTileGroups(worldspace, changedTiles, addedCellRefs);
|
||||
|
||||
data.mTiles.reserve(changedTiles.size());
|
||||
std::ranges::transform(changedTiles, std::back_inserter(data.mTiles), [](const auto& v) { return v.first; });
|
||||
|
||||
Log(Debug::Info) << "Processed " << cells.size() << " cells, added " << data.mObjects.size() << " objects and "
|
||||
<< data.mHeightfields.size() << " height fields";
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
#include <LinearMath/btVector3.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
|
|
@ -46,16 +47,6 @@ namespace NavMeshTool
|
|||
using DetourNavigator::ObjectTransform;
|
||||
using DetourNavigator::TileCachedRecastMeshManager;
|
||||
|
||||
struct WorldspaceNavMeshInput
|
||||
{
|
||||
ESM::RefId mWorldspace;
|
||||
TileCachedRecastMeshManager mTileCachedRecastMeshManager;
|
||||
btAABB mAabb;
|
||||
bool mAabbInitialized = false;
|
||||
|
||||
explicit WorldspaceNavMeshInput(ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings);
|
||||
};
|
||||
|
||||
class BulletObject
|
||||
{
|
||||
public:
|
||||
|
|
@ -82,15 +73,24 @@ namespace NavMeshTool
|
|||
|
||||
struct WorldspaceData
|
||||
{
|
||||
std::vector<std::unique_ptr<WorldspaceNavMeshInput>> mNavMeshInputs;
|
||||
ESM::RefId mWorldspace;
|
||||
std::unique_ptr<TileCachedRecastMeshManager> mTileCachedRecastMeshManager;
|
||||
btAABB mAabb;
|
||||
bool mAabbInitialized = false;
|
||||
std::vector<DetourNavigator::TilePosition> mTiles;
|
||||
std::vector<BulletObject> mObjects;
|
||||
std::vector<std::unique_ptr<ESM::Land::LandData>> mLandData;
|
||||
std::vector<std::vector<float>> mHeightfields;
|
||||
|
||||
WorldspaceData(ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings);
|
||||
};
|
||||
|
||||
std::unordered_map<ESM::RefId, std::vector<std::size_t>> collectWorldspaceCells(
|
||||
const EsmLoader::EsmData& esmData, bool processInteriorCells, const std::regex& worldspaceFilter);
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, ESM::ReadersCache& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells, bool writeBinaryLog);
|
||||
bool writeBinaryLog, ESM::RefId worldspace, std::span<const std::size_t> cells);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue