mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-25 00:30:54 +00:00
Add local map texture extraction for starting cell
This commit is contained in:
parent
c613c5decc
commit
3dd3d34543
4 changed files with 452 additions and 37 deletions
|
|
@ -960,20 +960,6 @@ void OMW::Engine::go()
|
|||
|
||||
prepareEngine();
|
||||
|
||||
if (mExtractMaps)
|
||||
{
|
||||
Log(Debug::Info) << "Starting map extraction mode...";
|
||||
|
||||
mStateManager->newGame(true);
|
||||
|
||||
MapExtractor extractor(*mWorld, mWorldMapOutput, mLocalMapOutput);
|
||||
extractor.extractWorldMap();
|
||||
extractor.extractLocalMaps();
|
||||
|
||||
Log(Debug::Info) << "Map extraction complete. Exiting...";
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto* statsFile = _wgetenv(L"OPENMW_OSG_STATS_FILE");
|
||||
#else
|
||||
|
|
@ -1008,6 +994,25 @@ void OMW::Engine::go()
|
|||
if (stats.is_open())
|
||||
Resource::collectStatistics(*mViewer);
|
||||
|
||||
|
||||
// Map extractor
|
||||
if (mExtractMaps)
|
||||
{
|
||||
Log(Debug::Info) << "Starting map extraction mode...";
|
||||
|
||||
mStateManager->newGame(true);
|
||||
|
||||
Log(Debug::Info) << "Starting map extraction...";
|
||||
|
||||
MapExtractor extractor(*mWorld, mViewer.get(), mWorldMapOutput, mLocalMapOutput);
|
||||
extractor.extractWorldMap();
|
||||
extractor.extractLocalMaps();
|
||||
|
||||
Log(Debug::Info) << "Map extraction complete. Exiting...";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Start the game
|
||||
if (!mSaveGameFile.empty())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
#include "mapextractor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
|
||||
#include <osg/ComputeBoundsVisitor>
|
||||
#include <osg/Group>
|
||||
#include <osg/Image>
|
||||
#include <osg/Texture2D>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
|
|
@ -16,19 +21,23 @@
|
|||
#include <components/settings/values.hpp>
|
||||
|
||||
#include "mwbase/environment.hpp"
|
||||
#include "mwbase/mechanicsmanager.hpp"
|
||||
#include "mwbase/world.hpp"
|
||||
#include "mwrender/globalmap.hpp"
|
||||
#include "mwrender/localmap.hpp"
|
||||
#include "mwrender/renderingmanager.hpp"
|
||||
#include "mwrender/vismask.hpp"
|
||||
#include "mwworld/cellstore.hpp"
|
||||
#include "mwworld/esmstore.hpp"
|
||||
#include "mwworld/worldimp.hpp"
|
||||
#include "mwworld/worldmodel.hpp"
|
||||
|
||||
namespace OMW
|
||||
{
|
||||
MapExtractor::MapExtractor(
|
||||
MWWorld::World& world, const std::string& worldMapOutput, const std::string& localMapOutput)
|
||||
MapExtractor::MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer,
|
||||
const std::string& worldMapOutput, const std::string& localMapOutput)
|
||||
: mWorld(world)
|
||||
, mViewer(viewer)
|
||||
, mWorldMapOutputDir(worldMapOutput)
|
||||
, mLocalMapOutputDir(localMapOutput)
|
||||
{
|
||||
|
|
@ -37,14 +46,43 @@ namespace OMW
|
|||
|
||||
// Create GlobalMap and LocalMap instances
|
||||
MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager();
|
||||
if (renderingManager)
|
||||
if (!renderingManager)
|
||||
{
|
||||
osg::Group* root = renderingManager->getLightRoot()->getParent(0)->asGroup();
|
||||
SceneUtil::WorkQueue* workQueue = renderingManager->getWorkQueue();
|
||||
Log(Debug::Error) << "RenderingManager is null in MapExtractor constructor";
|
||||
throw std::runtime_error("RenderingManager is null");
|
||||
}
|
||||
|
||||
osg::Group* lightRoot = renderingManager->getLightRoot();
|
||||
if (!lightRoot)
|
||||
{
|
||||
Log(Debug::Error) << "LightRoot is null in MapExtractor constructor";
|
||||
throw std::runtime_error("LightRoot is null");
|
||||
}
|
||||
|
||||
osg::Group* root = lightRoot->getParent(0) ? lightRoot->getParent(0)->asGroup() : nullptr;
|
||||
if (!root)
|
||||
{
|
||||
Log(Debug::Error) << "Root node is null in MapExtractor constructor";
|
||||
throw std::runtime_error("Root node is null");
|
||||
}
|
||||
|
||||
SceneUtil::WorkQueue* workQueue = renderingManager->getWorkQueue();
|
||||
if (!workQueue)
|
||||
{
|
||||
Log(Debug::Error) << "WorkQueue is null in MapExtractor constructor";
|
||||
throw std::runtime_error("WorkQueue is null");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mGlobalMap = std::make_unique<MWRender::GlobalMap>(root, workQueue);
|
||||
mLocalMap = std::make_unique<MWRender::LocalMap>(root);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Failed to create map objects: " << e.what();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
MapExtractor::~MapExtractor() = default;
|
||||
|
|
@ -56,7 +94,7 @@ namespace OMW
|
|||
if (!mGlobalMap)
|
||||
{
|
||||
Log(Debug::Error) << "Global map not initialized";
|
||||
return;
|
||||
throw std::runtime_error("Global map not initialized");
|
||||
}
|
||||
|
||||
// Temporarily set cell size to 32 pixels for extraction
|
||||
|
|
@ -149,44 +187,375 @@ namespace OMW
|
|||
{
|
||||
Log(Debug::Info) << "Extracting local maps...";
|
||||
|
||||
saveLocalMapTextures();
|
||||
setupExtractionMode();
|
||||
extractExteriorLocalMaps();
|
||||
extractInteriorLocalMaps();
|
||||
restoreNormalMode();
|
||||
|
||||
Log(Debug::Info) << "Local map extraction complete";
|
||||
}
|
||||
|
||||
void MapExtractor::saveLocalMapTextures()
|
||||
void MapExtractor::setupExtractionMode()
|
||||
{
|
||||
mWorld.toggleCollisionMode();
|
||||
MWBase::Environment::get().getMechanicsManager()->toggleAI();
|
||||
mWorld.toggleScripts();
|
||||
mWorld.toggleGodMode();
|
||||
}
|
||||
|
||||
void MapExtractor::restoreNormalMode()
|
||||
{
|
||||
if (!mWorld.getGodModeState())
|
||||
mWorld.toggleGodMode();
|
||||
if (!mWorld.getScriptsEnabled())
|
||||
mWorld.toggleScripts();
|
||||
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
MWBase::Environment::get().getMechanicsManager()->toggleAI();
|
||||
}
|
||||
|
||||
void MapExtractor::extractExteriorLocalMaps()
|
||||
{
|
||||
if (!mLocalMap)
|
||||
{
|
||||
Log(Debug::Error) << "Local map not initialized";
|
||||
return;
|
||||
throw std::runtime_error("Local map not initialized");
|
||||
}
|
||||
|
||||
const MWWorld::ESMStore& store = mWorld.getStore();
|
||||
int count = 0;
|
||||
|
||||
MWWorld::Store<ESM::Cell>::iterator it = store.get<ESM::Cell>().extBegin();
|
||||
for (; it != store.get<ESM::Cell>().extEnd(); ++it)
|
||||
// Get currently active cells
|
||||
MWWorld::Scene* scene = &mWorld.getWorldScene();
|
||||
if (!scene)
|
||||
{
|
||||
int x = it->getGridX();
|
||||
int y = it->getGridY();
|
||||
Log(Debug::Error) << "Scene not available";
|
||||
throw std::runtime_error("Scene not available");
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> texture = mLocalMap->getMapTexture(x, y);
|
||||
if (!texture || !texture->getImage())
|
||||
const auto& activeCells = scene->getActiveCells();
|
||||
Log(Debug::Info) << "Processing " << activeCells.size() << " currently active cells...";
|
||||
|
||||
int count = 0;
|
||||
int skipped = 0;
|
||||
|
||||
for (const MWWorld::CellStore* cellStore : activeCells)
|
||||
{
|
||||
if (!cellStore->getCell()->isExterior())
|
||||
continue;
|
||||
|
||||
int x = cellStore->getCell()->getGridX();
|
||||
int y = cellStore->getCell()->getGridY();
|
||||
|
||||
Log(Debug::Info) << "Processing active cell (" << x << "," << y << ")";
|
||||
|
||||
// Request map generation for this cell
|
||||
Log(Debug::Verbose) << "Requesting map for cell (" << x << "," << y << ")";
|
||||
mLocalMap->requestMap(const_cast<MWWorld::CellStore*>(cellStore));
|
||||
Log(Debug::Verbose) << "Map requested for cell (" << x << "," << y << ")";
|
||||
|
||||
// CRITICAL: LocalMap::requestMap() creates RTT cameras that render asynchronously.
|
||||
// We must run the render loop to actually execute the RTT rendering before we can
|
||||
// access the resulting textures.
|
||||
|
||||
MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager();
|
||||
if (renderingManager && mViewer)
|
||||
{
|
||||
Log(Debug::Verbose) << "Starting render loop for cell (" << x << "," << y << ")";
|
||||
|
||||
// Phase 1: Setup (let RTT cameras initialize)
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
// Phase 2: Main rendering (RTT cameras execute)
|
||||
for (int i = 0; i < 60; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
// Phase 3: Finalization (ensure GPU->CPU transfer completes)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
Log(Debug::Verbose) << "Render loop completed for cell (" << x << "," << y << ")";
|
||||
}
|
||||
|
||||
// Clean up RTT cameras before trying to access textures
|
||||
mLocalMap->cleanupCameras();
|
||||
|
||||
// Now try to get the texture
|
||||
Log(Debug::Verbose) << "Getting texture for cell (" << x << "," << y << ")";
|
||||
osg::ref_ptr<osg::Texture2D> texture = mLocalMap->getMapTexture(x, y);
|
||||
|
||||
if (!texture)
|
||||
{
|
||||
Log(Debug::Warning) << "No texture for cell (" << x << "," << y << ")";
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the image from the texture (should be set by LocalMapRenderToTexture)
|
||||
osg::Image* image = texture->getImage();
|
||||
|
||||
if (!image)
|
||||
{
|
||||
Log(Debug::Warning) << "Texture for cell (" << x << "," << y << ") has no image data attached";
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (image->s() == 0 || image->t() == 0)
|
||||
{
|
||||
Log(Debug::Warning) << "Empty image for cell (" << x << "," << y << ")";
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Log(Debug::Info) << "Got image size: " << image->s() << "x" << image->t()
|
||||
<< " for cell (" << x << "," << y << ")";
|
||||
|
||||
osg::ref_ptr<osg::Image> outputImage = new osg::Image(*image, osg::CopyOp::DEEP_COPY_ALL);
|
||||
|
||||
if (outputImage->s() != 256 || outputImage->t() != 256)
|
||||
{
|
||||
osg::ref_ptr<osg::Image> resized = new osg::Image;
|
||||
resized->allocateImage(256, 256, 1, outputImage->getPixelFormat(), outputImage->getDataType());
|
||||
outputImage->scaleImage(256, 256, 1);
|
||||
outputImage = resized;
|
||||
}
|
||||
|
||||
std::ostringstream filename;
|
||||
filename << "(" << x << "," << y << ").png";
|
||||
std::filesystem::path outputPath = mLocalMapOutputDir / filename.str();
|
||||
|
||||
if (osgDB::writeImageFile(*texture->getImage(), outputPath.string()))
|
||||
if (osgDB::writeImageFile(*outputImage, outputPath.string()))
|
||||
{
|
||||
count++;
|
||||
if (count % 100 == 0)
|
||||
Log(Debug::Info) << "Saved " << count << " local map textures...";
|
||||
Log(Debug::Info) << "Saved local map texture for cell (" << x << "," << y << ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Warning) << "Failed to write texture for cell (" << x << "," << y << ")";
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
Log(Debug::Info) << "Saved " << count << " exterior local map textures";
|
||||
if (skipped > 0)
|
||||
Log(Debug::Warning) << "Skipped " << skipped << " cells without valid textures";
|
||||
}
|
||||
|
||||
void MapExtractor::extractInteriorLocalMaps()
|
||||
{
|
||||
if (!mLocalMap)
|
||||
{
|
||||
Log(Debug::Error) << "Local map not initialized";
|
||||
throw std::runtime_error("Local map not initialized");
|
||||
}
|
||||
|
||||
// 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 active interior cells...";
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (const MWWorld::CellStore* cellStore : activeCells)
|
||||
{
|
||||
if (cellStore->getCell()->isExterior())
|
||||
continue;
|
||||
|
||||
ESM::RefId cellId = cellStore->getCell()->getId();
|
||||
std::string cellName(cellStore->getCell()->getNameId());
|
||||
|
||||
Log(Debug::Info) << "Processing active interior cell: " << cellName;
|
||||
|
||||
// Request map generation for this cell
|
||||
mLocalMap->requestMap(const_cast<MWWorld::CellStore*>(cellStore));
|
||||
|
||||
// CRITICAL: LocalMap::requestMap() creates RTT cameras that render asynchronously.
|
||||
// We must run the render loop to actually execute the RTT rendering before we can
|
||||
// access the resulting textures.
|
||||
|
||||
MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager();
|
||||
if (renderingManager && mViewer)
|
||||
{
|
||||
Log(Debug::Verbose) << "Starting render loop for interior: " << cellName;
|
||||
|
||||
// Phase 1: Setup (let RTT cameras initialize)
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
// Phase 2: Main rendering (RTT cameras execute)
|
||||
for (int i = 0; i < 60; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
// Phase 3: Finalization (ensure GPU->CPU transfer completes)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
renderingManager->update(0.016f, false);
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
Log(Debug::Verbose) << "Render loop completed for interior: " << cellName;
|
||||
}
|
||||
|
||||
// Clean up RTT cameras before trying to access textures
|
||||
mLocalMap->cleanupCameras();
|
||||
|
||||
saveInteriorCellTextures(cellId, cellName);
|
||||
count++;
|
||||
}
|
||||
|
||||
Log(Debug::Info) << "Saved " << count << " interior local map textures";
|
||||
}
|
||||
|
||||
void MapExtractor::saveInteriorCellTextures(const ESM::RefId& cellId, const std::string& cellName)
|
||||
{
|
||||
MyGUI::IntRect grid = mLocalMap->getInteriorGrid();
|
||||
|
||||
std::string lowerCaseId = cellId.toDebugString();
|
||||
std::transform(lowerCaseId.begin(), lowerCaseId.end(), lowerCaseId.begin(), ::tolower);
|
||||
|
||||
int segmentsX = grid.width() + 1;
|
||||
int segmentsY = grid.height() + 1;
|
||||
|
||||
if (segmentsX <= 0 || segmentsY <= 0)
|
||||
return;
|
||||
|
||||
int totalWidth = segmentsX * 256;
|
||||
int totalHeight = segmentsY * 256;
|
||||
|
||||
osg::ref_ptr<osg::Image> combinedImage = new osg::Image;
|
||||
combinedImage->allocateImage(totalWidth, totalHeight, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
||||
|
||||
unsigned char* data = combinedImage->data();
|
||||
memset(data, 0, totalWidth * totalHeight * 3);
|
||||
|
||||
for (int x = grid.left; x < grid.right; ++x)
|
||||
{
|
||||
for (int y = grid.top; y < grid.bottom; ++y)
|
||||
{
|
||||
osg::ref_ptr<osg::Texture2D> texture = mLocalMap->getMapTexture(x, y);
|
||||
if (!texture || !texture->getImage())
|
||||
continue;
|
||||
|
||||
osg::Image* segmentImage = texture->getImage();
|
||||
int segWidth = segmentImage->s();
|
||||
int segHeight = segmentImage->t();
|
||||
|
||||
int destX = (x - grid.left) * 256;
|
||||
int destY = (y - grid.top) * 256;
|
||||
|
||||
for (int sy = 0; sy < std::min(segHeight, 256); ++sy)
|
||||
{
|
||||
for (int sx = 0; sx < std::min(segWidth, 256); ++sx)
|
||||
{
|
||||
unsigned char* srcPixel = segmentImage->data(sx, sy);
|
||||
int dx = destX + sx;
|
||||
int dy = destY + sy;
|
||||
|
||||
if (dx < totalWidth && dy < totalHeight)
|
||||
{
|
||||
unsigned char* destPixel = data + ((dy * totalWidth + dx) * 3);
|
||||
destPixel[0] = srcPixel[0];
|
||||
destPixel[1] = srcPixel[1];
|
||||
destPixel[2] = srcPixel[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path texturePath = mLocalMapOutputDir / (lowerCaseId + ".png");
|
||||
osgDB::writeImageFile(*combinedImage, texturePath.string());
|
||||
|
||||
saveInteriorMapInfo(cellId, lowerCaseId, segmentsX, segmentsY);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
float nA = 0.0f;
|
||||
float mX = 0.0f;
|
||||
float mY = 0.0f;
|
||||
|
||||
MWWorld::ConstPtr northmarker = cell->searchConst(ESM::RefId::stringRefId("northmarker"));
|
||||
if (!northmarker.isEmpty())
|
||||
{
|
||||
osg::Quat orient(-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, 1));
|
||||
osg::Vec3f dir = orient * osg::Vec3f(0, 1, 0);
|
||||
nA = std::atan2(dir.x(), dir.y());
|
||||
}
|
||||
|
||||
osg::BoundingBox bounds;
|
||||
osg::ComputeBoundsVisitor computeBoundsVisitor;
|
||||
computeBoundsVisitor.setTraversalMask(MWRender::Mask_Scene | MWRender::Mask_Terrain |
|
||||
MWRender::Mask_Object | MWRender::Mask_Static);
|
||||
|
||||
MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager();
|
||||
if (renderingManager && renderingManager->getLightRoot())
|
||||
{
|
||||
renderingManager->getLightRoot()->accept(computeBoundsVisitor);
|
||||
bounds = computeBoundsVisitor.getBoundingBox();
|
||||
}
|
||||
|
||||
osg::Vec2f center(bounds.center().x(), bounds.center().y());
|
||||
osg::Vec2f min(bounds.xMin(), bounds.yMin());
|
||||
|
||||
const float mapWorldSize = Constants::CellSizeInUnits;
|
||||
|
||||
mX = ((0 - center.x()) * std::cos(nA) - (0 - center.y()) * std::sin(nA) + center.x() - min.x()) / mapWorldSize * 256.0f * 2.0f;
|
||||
mY = ((0 - center.x()) * std::sin(nA) + (0 - center.y()) * std::cos(nA) + center.y() - min.y()) / mapWorldSize * 256.0f * 2.0f;
|
||||
|
||||
std::filesystem::path yamlPath = mLocalMapOutputDir / (lowerCaseId + ".yaml");
|
||||
std::ofstream file(yamlPath);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
Log(Debug::Error) << "Failed to create interior map info file: " << yamlPath;
|
||||
return;
|
||||
}
|
||||
|
||||
file << "nA: " << nA << "\n";
|
||||
file << "mX: " << mX << "\n";
|
||||
file << "mY: " << mY << "\n";
|
||||
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,18 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/refid.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Group;
|
||||
}
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class WorkQueue;
|
||||
|
|
@ -31,7 +38,8 @@ namespace OMW
|
|||
class MapExtractor
|
||||
{
|
||||
public:
|
||||
MapExtractor(MWWorld::World& world, const std::string& worldMapOutput, const std::string& localMapOutput);
|
||||
MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer, const std::string& worldMapOutput,
|
||||
const std::string& localMapOutput);
|
||||
~MapExtractor();
|
||||
|
||||
void extractWorldMap();
|
||||
|
|
@ -39,6 +47,7 @@ namespace OMW
|
|||
|
||||
private:
|
||||
MWWorld::World& mWorld;
|
||||
osgViewer::Viewer* mViewer;
|
||||
std::filesystem::path mWorldMapOutputDir;
|
||||
std::filesystem::path mLocalMapOutputDir;
|
||||
|
||||
|
|
@ -47,7 +56,16 @@ namespace OMW
|
|||
|
||||
void saveWorldMapTexture();
|
||||
void saveWorldMapInfo();
|
||||
void saveLocalMapTextures();
|
||||
|
||||
void setupExtractionMode();
|
||||
void restoreNormalMode();
|
||||
void extractExteriorLocalMaps();
|
||||
void extractInteriorLocalMaps();
|
||||
void loadCellAndWait(int x, int y);
|
||||
void loadInteriorCellAndWait(const std::string& cellName);
|
||||
void saveInteriorCellTextures(const ESM::RefId& cellId, const std::string& cellName);
|
||||
void saveInteriorMapInfo(const ESM::RefId& cellId, const std::string& lowerCaseId,
|
||||
int segmentsX, int segmentsY);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -774,6 +774,29 @@ namespace MWRender
|
|||
|
||||
camera->addChild(lightSource);
|
||||
camera->addChild(mSceneRoot);
|
||||
|
||||
// CRITICAL FIX: Setup both texture and image for CPU-side access (needed by mapextractor)
|
||||
// First attach texture for normal rendering (GPU-side)
|
||||
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
|
||||
texture->setTextureSize(camera->getViewport()->width(), camera->getViewport()->height());
|
||||
texture->setInternalFormat(GL_RGB);
|
||||
texture->setSourceFormat(GL_RGB);
|
||||
texture->setSourceType(GL_UNSIGNED_BYTE);
|
||||
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, texture.get());
|
||||
|
||||
// Then attach Image for CPU-side readback
|
||||
// OSG will automatically copy rendered pixels to this Image
|
||||
osg::ref_ptr<osg::Image> image = new osg::Image();
|
||||
image->setPixelFormat(GL_RGB);
|
||||
image->setDataType(GL_UNSIGNED_BYTE);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, image.get());
|
||||
|
||||
// Also set the Image on the texture so mapextractor can retrieve it via texture->getImage()
|
||||
texture->setImage(image.get());
|
||||
}
|
||||
|
||||
void CameraLocalUpdateCallback::operator()(LocalMapRenderToTexture* node, osg::NodeVisitor* nv)
|
||||
|
|
|
|||
Loading…
Reference in a new issue