From b360deaec3b58f0e71b3a89604d77e04ce5c27a0 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 25 Jul 2019 17:46:28 +0400 Subject: [PATCH] Store raw data about fog of war instead of RGBA images (bug #5108) --- CHANGELOG.md | 1 + apps/openmw/mwgui/mapwindow.cpp | 1 + apps/openmw/mwrender/localmap.cpp | 93 ++++++------------------------- apps/openmw/mwrender/localmap.hpp | 3 - components/esm/fogstate.cpp | 81 +++++++++++++++++++++++++++ components/esm/fogstate.hpp | 12 ++++ components/esm/savedgame.cpp | 2 +- 7 files changed, 113 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 811a276f13..c534d992a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,6 +132,7 @@ Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5105: NPCs start combat with werewolves from any distance Bug #5106: Still can jump even when encumbered + Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5110: ModRegion with a redundant numerical argument breaks script execution Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5113: Unknown alchemy question mark not centered diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 02d2be5770..84b2e765b0 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -209,6 +209,7 @@ namespace MWGui MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); fog->setDepth(Local_FogLayer); + fog->setColour(MyGUI::Colour(0, 0, 0)); map->setNeedMouseFocus(false); fog->setNeedMouseFocus(false); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index f4a54eb982..1a8366e03a 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -9,8 +9,6 @@ #include #include -#include - #include #include #include @@ -18,7 +16,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -541,11 +538,10 @@ bool LocalMap::isPositionExplored (float nX, float nY, int x, int y) nX = std::max(0.f, std::min(1.f, nX)); nY = std::max(0.f, std::min(1.f, nY)); - int texU = static_cast((sFogOfWarResolution - 1) * nX); - int texV = static_cast((sFogOfWarResolution - 1) * nY); + int texU = static_cast((ESM::FogTexture::sFogOfWarResolution - 1) * nX); + int texV = static_cast((ESM::FogTexture::sFogOfWarResolution - 1) * nY); - uint32_t clr = ((const uint32_t*)segment.mFogOfWarImage->data())[texV * sFogOfWarResolution + texU]; - uint8_t alpha = (clr >> 24); + uint8_t alpha = ((const uint8_t*)segment.mFogOfWarImage->data())[texV * ESM::FogTexture::sFogOfWarResolution + texU]; return alpha < 200; } @@ -580,9 +576,9 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient } // explore radius (squared) - const float exploreRadius = 0.17f * (sFogOfWarResolution-1); // explore radius from 0 to sFogOfWarResolution-1 + const float exploreRadius = 0.17f * (ESM::FogTexture::sFogOfWarResolution-1); // explore radius from 0 to sFogOfWarResolution-1 const float sqrExploreRadius = square(exploreRadius); - const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space) + const float exploreRadiusUV = exploreRadius / ESM::FogTexture::sFogOfWarResolution; // explore radius from 0 to 1 (UV space) // change the affected fog of war textures (in a 3x3 grid around the player) for (int mx = -mCellDistance; mx<=mCellDistance; ++mx) @@ -611,23 +607,21 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient if (!segment.mFogOfWarImage || !segment.mMapTexture) continue; - uint32_t* data = (uint32_t*)segment.mFogOfWarImage->data(); + uint8_t* data = (uint8_t*)segment.mFogOfWarImage->data(); bool changed = false; - for (int texV = 0; texV> 24); + uint8_t alpha = *data; alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); - uint32_t val = (uint32_t) (alpha << 24); - if ( *data != val) + if ( *data != alpha) { - *data = val; + *data = alpha; changed = true; } @@ -670,15 +664,7 @@ void LocalMap::MapSegment::createFogOfWarTexture() void LocalMap::MapSegment::initFogOfWar() { - mFogOfWarImage = new osg::Image; - // Assign a PixelBufferObject for asynchronous transfer of data to the GPU - mFogOfWarImage->setPixelBufferObject(new osg::PixelBufferObject); - mFogOfWarImage->allocateImage(sFogOfWarResolution, sFogOfWarResolution, 1, GL_RGBA, GL_UNSIGNED_BYTE); - assert(mFogOfWarImage->isDataContiguous()); - std::vector data; - data.resize(sFogOfWarResolution*sFogOfWarResolution, 0xff000000); - - memcpy(mFogOfWarImage->data(), &data[0], data.size()*4); + mFogOfWarImage = ESM::FogState::initFogOfWar(); createFogOfWarTexture(); mFogOfWarTexture->setImage(mFogOfWarImage); @@ -686,34 +672,9 @@ void LocalMap::MapSegment::initFogOfWar() void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) { - const std::vector& data = esm.mImageData; - if (data.empty()) - { - initFogOfWar(); - return; - } - - // TODO: deprecate tga and use raw data instead - - osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); - if (!readerwriter) - { - Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter" ; - return; - } - - Files::IMemStream in(&data[0], data.size()); - - osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in); - if (!result.success()) - { - Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status(); + mFogOfWarImage = ESM::FogState::loadFogOfWar(esm); + if (!mFogOfWarImage) return; - } - - mFogOfWarImage = result.getImage(); - mFogOfWarImage->flipVertical(); - mFogOfWarImage->dirty(); createFogOfWarTexture(); mFogOfWarTexture->setImage(mFogOfWarImage); @@ -725,27 +686,7 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const if (!mFogOfWarImage) return; - std::ostringstream ostream; - - osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); - if (!readerwriter) - { - Log(Debug::Error) << "Error: Unable to write fog, can't find a tga ReaderWriter"; - return; - } - - // extra flips are unfortunate, but required for compatibility with older versions - mFogOfWarImage->flipVertical(); - osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mFogOfWarImage, ostream); - if (!result.success()) - { - Log(Debug::Error) << "Error: Unable to write fog: " << result.message() << " code " << result.status(); - return; - } - mFogOfWarImage->flipVertical(); - - std::string data = ostream.str(); - fog.mImageData = std::vector(data.begin(), data.end()); + ESM::FogState::saveFogOfWar(mFogOfWarImage, fog); } } diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 83a975aeda..370d939f0a 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -136,9 +136,6 @@ namespace MWRender int mMapResolution; - // the dynamic texture is a bottleneck, so don't set this too high - static const int sFogOfWarResolution = 32; - // size of a map segment (for exteriors, 1 cell) float mMapWorldSize; diff --git a/components/esm/fogstate.cpp b/components/esm/fogstate.cpp index 18235066d4..391a94887a 100644 --- a/components/esm/fogstate.cpp +++ b/components/esm/fogstate.cpp @@ -1,7 +1,13 @@ #include "fogstate.hpp" +#include + +#include +#include + #include "esmreader.hpp" #include "esmwriter.hpp" +#include "savedgame.hpp" void ESM::FogState::load (ESMReader &esm) { @@ -18,6 +24,10 @@ void ESM::FogState::load (ESMReader &esm) size_t imageSize = esm.getSubSize()-sizeof(int)*2; tex.mImageData.resize(imageSize); esm.getExact(&tex.mImageData[0], imageSize); + + if (esm.getFormat() < 6) + convertFogOfWar(tex.mImageData); + mFogTextures.push_back(tex); } } @@ -38,3 +48,74 @@ void ESM::FogState::save (ESMWriter &esm, bool interiorCell) const esm.endRecord("FTEX"); } } + +osg::Image* ESM::FogState::initFogOfWar() +{ + osg::Image* fogOfWarImage = new osg::Image; + // Assign a PixelBufferObject for asynchronous transfer of data to the GPU + fogOfWarImage->setPixelBufferObject(new osg::PixelBufferObject); + fogOfWarImage->allocateImage(FogTexture::sFogOfWarResolution, FogTexture::sFogOfWarResolution, 1, GL_ALPHA, GL_UNSIGNED_BYTE); + assert(fogOfWarImage->isDataContiguous()); + std::fill(fogOfWarImage->data(), fogOfWarImage->data() + FogTexture::sFogOfWarResolution*FogTexture::sFogOfWarResolution, 0xff); + + return fogOfWarImage; +} + +void ESM::FogState::saveFogOfWar(osg::Image* fogImage, ESM::FogTexture &fog) +{ + if (!fogImage) + return; + + assert (fogImage->isDataContiguous()); + fog.mImageData.assign(fogImage->data(), fogImage->data() + FogTexture::sFogOfWarResolution*FogTexture::sFogOfWarResolution); +} + +osg::Image* ESM::FogState::loadFogOfWar(const ESM::FogTexture &esm) +{ + osg::Image* fogImage = initFogOfWar(); + const std::vector& data = esm.mImageData; + if (data.empty()) + { + return fogImage; + } + + std::copy(data.begin(), data.end(), fogImage->data()); + fogImage->dirty(); + + return fogImage; +} + +void ESM::FogState::convertFogOfWar(std::vector& imageData) +{ + if (imageData.empty()) + { + return; + } + + osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); + if (!readerwriter) + { + Log(Debug::Error) << "Error: Unable to load fog, can't find a TGA ReaderWriter"; + return; + } + + Files::IMemStream in(&imageData[0], imageData.size()); + + osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in); + if (!result.success()) + { + Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status(); + return; + } + std::ostringstream ostream; + + osg::Image* resultImage = result.getImage(); + resultImage->flipVertical(); + + unsigned char* oldFogData = resultImage->data(); + for (int i=0;i(str.begin(), str.end()); +} diff --git a/components/esm/fogstate.hpp b/components/esm/fogstate.hpp index 4a5619e518..9c274cab2c 100644 --- a/components/esm/fogstate.hpp +++ b/components/esm/fogstate.hpp @@ -3,6 +3,11 @@ #include +namespace osg +{ + class Image; +} + namespace ESM { class ESMReader; @@ -12,6 +17,8 @@ namespace ESM { int mX, mY; // Only used for interior cells std::vector mImageData; + // the dynamic texture is a bottleneck, so don't set this too high + static const int sFogOfWarResolution = 32; }; // format 0, saved games only @@ -32,6 +39,11 @@ namespace ESM void load (ESMReader &esm); void save (ESMWriter &esm, bool interiorCell) const; + + static void saveFogOfWar(osg::Image* fogImage, ESM::FogTexture &fog); + static osg::Image* loadFogOfWar(const ESM::FogTexture &esm); + static void convertFogOfWar(std::vector& imageData); + static osg::Image* initFogOfWar(); }; } diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index ea9fef4fbf..633f7c9fd0 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 5; +int ESM::SavedGame::sCurrentFormat = 6; void ESM::SavedGame::load (ESMReader &esm) {