1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-01-25 08:30:54 +00:00

Refactors MapExtractor to use engine options

This commit is contained in:
Diject 2025-12-30 18:49:08 +03:00
parent d27a9143c8
commit e8d844e7e5
11 changed files with 97 additions and 150 deletions

View file

@ -375,9 +375,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mExportFonts(false)
, mRandomSeed(0)
, mNewGame(false)
, mExtractMaps(false)
, mCfgMgr(configurationManager)
, mGlMaxTextureImageUnits(0)
, mOverwriteMaps(false)
{
#if SDL_VERSION_ATLEAST(2, 24, 0)
SDL_SetHint(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, "1");
@ -836,7 +836,8 @@ void OMW::Engine::prepareEngine()
// Create the world
mWorld = std::make_unique<MWWorld::World>(
mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath());
mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath(),
mWorldMapOutput, mLocalMapOutput, mOverwriteMaps);
mEnvironment.setWorld(*mWorld);
mEnvironment.setWorldModel(mWorld->getWorldModel());
mEnvironment.setESMStore(mWorld->getStore());
@ -1158,7 +1159,7 @@ void OMW::Engine::setLocalMapOutput(const std::string& path)
mLocalMapOutput = path;
}
void OMW::Engine::setExtractMaps(bool extract)
void OMW::Engine::setOverwriteMaps(bool overwrite)
{
mExtractMaps = extract;
mOverwriteMaps = overwrite;
}

View file

@ -185,7 +185,7 @@ namespace OMW
std::string mWorldMapOutput;
std::string mLocalMapOutput;
bool mExtractMaps;
bool mOverwriteMaps;
Files::ConfigurationManager& mCfgMgr;
int mGlMaxTextureImageUnits;
@ -272,6 +272,8 @@ namespace OMW
void setLocalMapOutput(const std::string& path);
void setOverwriteMaps(bool overwrite);
void setExtractMaps(bool extract);
void setRecastMaxLogLevel(Debug::Level value) { mMaxRecastLogLevel = value; }

View file

@ -167,17 +167,26 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati
std::string worldMapOutput = variables["world-map-output"].as<std::string>();
std::string localMapOutput = variables["local-map-output"].as<std::string>();
bool extractMaps = variables["extract-maps"].as<bool>();
if (worldMapOutput.empty() && extractMaps)
auto removeQuotes = [](std::string& str) {
if (str.size() >= 2 && ((str.front() == '"' && str.back() == '"') || (str.front() == '\'' && str.back() == '\'')))
{
str = str.substr(1, str.size() - 2);
}
};
removeQuotes(worldMapOutput);
removeQuotes(localMapOutput);
if (worldMapOutput.empty())
worldMapOutput = Files::pathToUnicodeString(std::filesystem::current_path() / "textures" / "advanced_world_map" / "custom");
if (localMapOutput.empty() && extractMaps)
if (localMapOutput.empty())
localMapOutput = Files::pathToUnicodeString(std::filesystem::current_path() / "textures" / "advanced_world_map" / "local");
engine.setWorldMapOutput(worldMapOutput);
engine.setLocalMapOutput(localMapOutput);
engine.setExtractMaps(extractMaps);
engine.setOverwriteMaps(variables["overwrite-maps"].as<bool>());
return true;
}

View file

@ -36,14 +36,14 @@
namespace OMW
{
MapExtractor::MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer,
MWBase::WindowManager* windowManager, const std::string& worldMapOutput, const std::string& localMapOutput)
: mWorld(world)
, mViewer(viewer)
, mWindowManager(windowManager)
, mWorldMapOutputDir(worldMapOutput)
MapExtractor::MapExtractor(const std::string& worldMapOutput, const std::string& localMapOutput,
bool forceOverwrite, MWRender::RenderingManager* renderingManager, const MWWorld::ESMStore* store)
: mWorldMapOutputDir(worldMapOutput)
, mLocalMapOutputDir(localMapOutput)
, mRenderingManager(renderingManager)
, mStore(store)
, mLocalMap(nullptr)
, mForceOverwrite(forceOverwrite)
{
// Only create directories if paths are not empty
if (!mWorldMapOutputDir.empty())
@ -52,14 +52,13 @@ namespace OMW
std::filesystem::create_directories(mLocalMapOutputDir);
// Create GlobalMap instance
MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager();
if (!renderingManager)
if (!mRenderingManager)
{
Log(Debug::Error) << "RenderingManager is null in MapExtractor constructor";
throw std::runtime_error("RenderingManager is null");
}
osg::Group* lightRoot = renderingManager->getLightRoot();
osg::Group* lightRoot = mRenderingManager->getLightRoot();
if (!lightRoot)
{
Log(Debug::Error) << "LightRoot is null in MapExtractor constructor";
@ -73,7 +72,7 @@ namespace OMW
throw std::runtime_error("Root node is null");
}
SceneUtil::WorkQueue* workQueue = renderingManager->getWorkQueue();
SceneUtil::WorkQueue* workQueue = mRenderingManager->getWorkQueue();
if (!workQueue)
{
Log(Debug::Error) << "WorkQueue is null in MapExtractor constructor";
@ -83,8 +82,7 @@ namespace OMW
try
{
mGlobalMap = std::make_unique<MWRender::GlobalMap>(root, workQueue);
// Get LocalMap from WindowManager - it will be set after initUI is called
// For now just set to nullptr
// LocalMap will be set later via setLocalMap() method
mLocalMap = nullptr;
}
catch (const std::exception& e)
@ -157,14 +155,13 @@ namespace OMW
int width = mGlobalMap->getWidth();
int height = mGlobalMap->getHeight();
const MWWorld::ESMStore& store = mWorld.getStore();
int minX = std::numeric_limits<int>::max();
int maxX = std::numeric_limits<int>::min();
int minY = std::numeric_limits<int>::max();
int maxY = std::numeric_limits<int>::min();
MWWorld::Store<ESM::Cell>::iterator it = store.get<ESM::Cell>().extBegin();
for (; it != store.get<ESM::Cell>().extEnd(); ++it)
MWWorld::Store<ESM::Cell>::iterator it = mStore->get<ESM::Cell>().extBegin();
for (; it != mStore->get<ESM::Cell>().extEnd(); ++it)
{
if (it->getGridX() < minX)
minX = it->getGridX();
@ -201,7 +198,7 @@ namespace OMW
Log(Debug::Info) << "Saved world map info: " << infoPath;
}
void MapExtractor::extractLocalMaps(bool forceOverwrite)
void MapExtractor::extractLocalMaps(const std::vector<const MWWorld::CellStore*>& activeCells)
{
Log(Debug::Info) << "Extracting active local maps...";
@ -214,28 +211,20 @@ namespace OMW
// Create output directory if it doesn't exist
std::filesystem::create_directories(mLocalMapOutputDir);
// Get LocalMap from WindowManager now that UI is initialized
if (mWindowManager)
{
mLocalMap = mWindowManager->getLocalMapRender();
}
if (!mLocalMap)
{
Log(Debug::Error) << "Local map not initialized - cannot extract local maps";
Log(Debug::Error) << "Make sure the game is fully loaded before calling extractLocalMaps";
return;
}
Log(Debug::Info) << "LocalMap instance is available, starting extraction";
mForceOverwrite = forceOverwrite;
mFramesToWait = 10; // Wait 10 frames before checking (increased from 3)
startExtraction(forceOverwrite);
startExtraction(activeCells);
}
void MapExtractor::startExtraction(bool forceOverwrite)
void MapExtractor::startExtraction(const std::vector<const MWWorld::CellStore*>& activeCells)
{
// Enable extraction mode to prevent automatic camera cleanup
if (mLocalMap)
@ -243,15 +232,6 @@ namespace OMW
mLocalMap->setExtractionMode(true);
}
// Get currently active cells
MWWorld::Scene* scene = &mWorld.getWorldScene();
if (!scene)
{
Log(Debug::Error) << "Scene not available";
throw std::runtime_error("Scene not available");
}
const auto& activeCells = scene->getActiveCells();
Log(Debug::Info) << "Processing " << activeCells.size() << " currently active cells...";
mPendingExtractions.clear();
@ -267,7 +247,7 @@ namespace OMW
filename << "(" << x << "," << y << ").png";
std::filesystem::path outputPath = mLocalMapOutputDir / filename.str();
if (!forceOverwrite && std::filesystem::exists(outputPath))
if (!mForceOverwrite && std::filesystem::exists(outputPath))
{
Log(Debug::Info) << "Skipping cell (" << x << "," << y << ") - file already exists";
continue;
@ -302,7 +282,7 @@ namespace OMW
std::filesystem::path texturePath = mLocalMapOutputDir / (lowerCaseId + ".png");
std::filesystem::path yamlPath = mLocalMapOutputDir / (lowerCaseId + ".yaml");
if (!forceOverwrite && std::filesystem::exists(texturePath) && std::filesystem::exists(yamlPath))
if (!mForceOverwrite && std::filesystem::exists(yamlPath))
{
Log(Debug::Info) << "Skipping interior cell: " << cellName << " - files already exist";
continue;
@ -656,10 +636,6 @@ namespace OMW
void MapExtractor::saveInteriorMapInfo(const ESM::RefId& cellId, const std::string& lowerCaseId,
int segmentsX, int segmentsY)
{
MWWorld::CellStore* cell = mWorld.getWorldModel().findCell(cellId);
if (!cell)
return;
// Get the bounds, center and angle that LocalMap actually used for rendering
const osg::BoundingBox& bounds = mLocalMap->getInteriorBounds();
const osg::Vec2f& center = mLocalMap->getInteriorCenter();

View file

@ -28,12 +28,14 @@ namespace MWRender
{
class GlobalMap;
class LocalMap;
class RenderingManager;
}
namespace MWWorld
{
class World;
class CellStore;
class ESMStore;
}
namespace MWBase
@ -46,12 +48,14 @@ namespace OMW
class MapExtractor
{
public:
MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer, MWBase::WindowManager* windowManager,
const std::string& worldMapOutput, const std::string& localMapOutput);
MapExtractor(const std::string& worldMapOutput, const std::string& localMapOutput, bool forceOverwrite,
MWRender::RenderingManager* renderingManager, const MWWorld::ESMStore* store);
~MapExtractor();
void setLocalMap(MWRender::LocalMap* localMap) { mLocalMap = localMap; }
void extractWorldMap();
void extractLocalMaps(bool forceOverwrite = false);
void extractLocalMaps(const std::vector<const MWWorld::CellStore*>& activeCells);
// Called every frame to process pending extractions
void update();
@ -69,11 +73,10 @@ namespace OMW
std::function<void()> completionCallback;
};
MWWorld::World& mWorld;
osgViewer::Viewer* mViewer;
MWBase::WindowManager* mWindowManager;
std::filesystem::path mWorldMapOutputDir;
std::filesystem::path mLocalMapOutputDir;
MWRender::RenderingManager* mRenderingManager;
const MWWorld::ESMStore* mStore;
std::unique_ptr<MWRender::GlobalMap> mGlobalMap;
MWRender::LocalMap* mLocalMap;
@ -85,7 +88,7 @@ namespace OMW
void saveWorldMapTexture();
void saveWorldMapInfo();
void startExtraction(bool forceOverwrite);
void startExtraction(const std::vector<const MWWorld::CellStore*>& activeCells);
void processNextCell();
bool savePendingExtraction(const PendingExtraction& extraction);

View file

@ -613,11 +613,11 @@ namespace MWBase
virtual std::string getLocalMapOutputPath() const = 0;
///< Get the local map output path from options or default
virtual void extractWorldMap(const std::string& worldMapOutput) = 0;
///< Extract world map to the specified directory
virtual void extractWorldMap() = 0;
///< Extract world map using path from options or default
virtual void extractLocalMaps(const std::string& localMapOutput) = 0;
///< Extract local maps to the specified directory
virtual void extractLocalMaps() = 0;
///< Extract local maps using path from options or default
virtual bool isMapExtractionActive() const = 0;
///< Check if map extraction is currently in progress

View file

@ -247,22 +247,20 @@ namespace MWLua
api["vfx"] = initWorldVfxBindings(context);
api["extractWorldMap"] = [context, lua = context.mLua](sol::optional<std::string> worldMapOutput) {
api["extractWorldMap"] = [context, lua = context.mLua]() {
checkGameInitialized(lua);
context.mLuaManager->addAction(
[worldMapOutput] {
std::string path = worldMapOutput.value_or("");
MWBase::Environment::get().getWorld()->extractWorldMap(path);
[] {
MWBase::Environment::get().getWorld()->extractWorldMap();
},
"extractWorldMapAction");
};
api["extractLocalMaps"] = [context, lua = context.mLua](sol::optional<std::string> localMapOutput) {
api["extractLocalMaps"] = [context, lua = context.mLua]() {
checkGameInitialized(lua);
context.mLuaManager->addAction(
[localMapOutput] {
std::string path = localMapOutput.value_or("");
MWBase::Environment::get().getWorld()->extractLocalMaps(path);
[] {
MWBase::Environment::get().getWorld()->extractLocalMaps();
},
"extractLocalMapsAction");
};

View file

@ -251,7 +251,7 @@ namespace MWWorld
}
World::World(Resource::ResourceSystem* resourceSystem, int activationDistanceOverride, const std::string& startCell,
const std::filesystem::path& userDataPath)
const std::filesystem::path& userDataPath, const std::string& worldMapOutputPath, const std::string& localMapOutputPath, bool overwriteMaps)
: mResourceSystem(resourceSystem)
, mLocalScripts(mStore)
, mWorldModel(mStore, mReaders)
@ -263,6 +263,9 @@ namespace MWWorld
, mUserDataPath(userDataPath)
, mActivationDistanceOverride(activationDistanceOverride)
, mStartCell(startCell)
, mWorldMapOutputPath(worldMapOutputPath)
, mLocalMapOutputPath(localMapOutputPath)
, mOverwriteMaps(overwriteMaps)
, mSwimHeightScale(0.f)
, mDistanceToFocusObject(-1.f)
, mTeleportEnabled(true)
@ -3913,83 +3916,35 @@ namespace MWWorld
actor->setActive(value);
}
std::string World::getWorldMapOutputPath() const
void World::extractWorldMap()
{
// Try to get from Engine via environment, fallback to default
// Since we can't directly access Engine from World, we'll use a default path
// The actual path from options will be passed through the Lua API
return "./textures/advanced_world_map/custom";
}
std::string World::getLocalMapOutputPath() const
{
// Try to get from Engine via environment, fallback to default
// Since we can't directly access Engine from World, we'll use a default path
// The actual path from options will be passed through the Lua API
return "./textures/advanced_world_map/local";
}
void World::extractWorldMap(const std::string& worldMapOutput)
{
if (!mRendering)
if (!mMapExtractor)
{
throw std::runtime_error("Rendering manager is not initialized");
mMapExtractor = std::make_unique<OMW::MapExtractor>(
mWorldMapOutputPath, mLocalMapOutputPath, mOverwriteMaps, mRendering.get(), &mStore);
}
osgViewer::Viewer* viewer = mRendering->getViewer();
if (!viewer)
{
throw std::runtime_error("Viewer is not initialized");
}
// If extraction is already in progress, ignore the request
if (mMapExtractor)
{
Log(Debug::Warning) << "Map extraction is already in progress";
return;
}
std::string outputPath = worldMapOutput.empty() ? getWorldMapOutputPath() : worldMapOutput;
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
mMapExtractor = std::make_unique<OMW::MapExtractor>(*this, viewer, windowManager, outputPath, "");
Log(Debug::Info) << "Starting world map extraction to: " << outputPath;
mMapExtractor->extractWorldMap();
}
void World::extractLocalMaps(const std::string& localMapOutput)
void World::extractLocalMaps()
{
if (!mRendering)
if (!mMapExtractor)
{
throw std::runtime_error("Rendering manager is not initialized");
mMapExtractor = std::make_unique<OMW::MapExtractor>(
mWorldMapOutputPath, mLocalMapOutputPath, mOverwriteMaps, mRendering.get(), &mStore);
}
osgViewer::Viewer* viewer = mRendering->getViewer();
if (!viewer)
// Set LocalMap from WindowManager
if (auto* localMap = MWBase::Environment::get().getWindowManager()->getLocalMapRender())
{
throw std::runtime_error("Viewer is not initialized");
mMapExtractor->setLocalMap(localMap);
}
// If extraction is already in progress, ignore the request
if (mMapExtractor)
{
Log(Debug::Warning) << "Map extraction is already in progress";
return;
}
std::string outputPath = localMapOutput.empty() ? getLocalMapOutputPath() : localMapOutput;
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
mMapExtractor = std::make_unique<OMW::MapExtractor>(*this, viewer, windowManager, "", outputPath);
Log(Debug::Info) << "Starting local maps extraction to: " << outputPath;
mMapExtractor->extractLocalMaps(false);
Log(Debug::Info) << "Local maps extraction started, will complete during gameplay...";
const auto& activeCells = mWorldScene->getActiveCells();
std::vector<const MWWorld::CellStore*> cells(activeCells.begin(), activeCells.end());
mMapExtractor->extractLocalMaps(cells);
}
bool World::isMapExtractionActive() const
{
return mMapExtractor != nullptr;
return mMapExtractor && !mMapExtractor->isExtractionComplete();
}
}

View file

@ -121,6 +121,9 @@ namespace MWWorld
int mActivationDistanceOverride;
std::string mStartCell;
std::string mWorldMapOutputPath;
std::string mLocalMapOutputPath;
bool mOverwriteMaps;
float mSwimHeightScale;
@ -198,7 +201,7 @@ namespace MWWorld
void removeContainerScripts(const Ptr& reference) override;
World(Resource::ResourceSystem* resourceSystem, int activationDistanceOverride, const std::string& startCell,
const std::filesystem::path& userDataPath);
const std::filesystem::path& userDataPath, const std::string& worldMapOutputPath, const std::string& localMapOutputPath, bool overwriteMaps);
void loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles,
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder,
@ -684,11 +687,12 @@ namespace MWWorld
void setActorActive(const MWWorld::Ptr& ptr, bool value) override;
std::string getWorldMapOutputPath() const override;
std::string getLocalMapOutputPath() const override;
std::string getWorldMapOutputPath() const override { return mWorldMapOutputPath; }
std::string getLocalMapOutputPath() const override { return mLocalMapOutputPath; }
bool getOverwriteMaps() const { return mOverwriteMaps; }
void extractWorldMap(const std::string& worldMapOutput) override;
void extractLocalMaps(const std::string& localMapOutput) override;
void extractWorldMap() override;
void extractLocalMaps() override;
bool isMapExtractionActive() const override;
};
}

View file

@ -101,8 +101,8 @@ namespace OpenMW
addOption("local-map-output", bpo::value<std::string>()->default_value(""),
"directory to save local map textures (default: textures/advanced_world_map/local)");
addOption("extract-maps", bpo::value<bool>()->implicit_value(true)->default_value(true),
"extract world and local map textures and exit");
addOption("overwrite-maps", bpo::value<bool>()->implicit_value(true)->default_value(false),
"overwrite existing map files during extraction");
return desc;
}

View file

@ -222,22 +222,21 @@
-- @param #number hours Number of hours to advance time
---
-- Extract world map to the specified directory.
-- Extract world map using path from --world-map-output option or default path.
-- This function generates a world map image and saves it as a PNG file.
-- If no path is provided (nil), uses the path from --world-map-output option or default "./textures/advanced_world_map/custom".
-- The output directory is determined by --world-map-output command line option,
-- or defaults to "./textures/advanced_world_map/custom" if not specified.
-- @function [parent=#world] extractWorldMap
-- @param #string worldMapOutput (optional) Directory path where world map will be saved
-- @usage world.extractWorldMap("./maps/world") -- Custom path
-- @usage world.extractWorldMap() -- Use default or option path
-- @usage world.extractWorldMap() -- Use path from option or default
---
-- Extract local maps to the specified directory.
-- Extract local maps using path from --local-map-output option or default path.
-- This function generates map images for all active cells and saves them as PNG files.
-- If no path is provided (nil), uses the path from --local-map-output option or default "./textures/advanced_world_map/local".
-- The output directory is determined by --local-map-output command line option,
-- or defaults to "./textures/advanced_world_map/local" if not specified.
-- By default, existing maps are not overwritten. Use --overwrite-maps option to force overwriting.
-- @function [parent=#world] extractLocalMaps
-- @param #string localMapOutput (optional) Directory path where local maps will be saved
-- @usage world.extractLocalMaps("./maps/local") -- Custom path
-- @usage world.extractLocalMaps() -- Use default or option path
-- @usage world.extractLocalMaps() -- Use path from option or default
---
-- Enable extraction mode for map generation.