From ca6baed3663ee81d84d63ef142f9ec17c6bb7971 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 3 Jun 2024 16:42:27 +0200 Subject: [PATCH 1/2] Fix LTEX overriding and allow deletion --- apps/opencs/view/render/terrainstorage.cpp | 4 +- apps/opencs/view/render/terrainstorage.hpp | 2 +- apps/openmw/mwrender/terrainstorage.cpp | 3 +- apps/openmw/mwrender/terrainstorage.hpp | 2 +- apps/openmw/mwworld/esmstore.cpp | 5 -- apps/openmw/mwworld/store.cpp | 76 ++++++------------- apps/openmw/mwworld/store.hpp | 18 ++--- apps/openmw_test_suite/mwworld/test_store.cpp | 6 +- components/esm3/loadltex.hpp | 3 +- components/esmterrain/storage.cpp | 4 +- components/esmterrain/storage.hpp | 2 +- 11 files changed, 41 insertions(+), 84 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 53e62d3969..ff9a8e09b1 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -42,14 +42,14 @@ namespace CSVRender land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); } - const ESM::LandTexture* TerrainStorage::getLandTexture(std::uint16_t index, int plugin) + const std::string* TerrainStorage::getLandTexture(std::uint16_t index, int plugin) { const int row = mData.getLandTextures().searchId( ESM::RefId::stringRefId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index))); if (row == -1) return nullptr; - return &mData.getLandTextures().getRecord(row).get(); + return &mData.getLandTextures().getRecord(row).get().mTexture; } void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height) diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 5775fbf3ff..f7a7f72201 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -38,7 +38,7 @@ namespace CSVRender std::array mAlteredHeight; osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) override; - const ESM::LandTexture* getLandTexture(std::uint16_t index, int plugin) override; + const std::string* getLandTexture(std::uint16_t index, int plugin) override; void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override; diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 757e743cfc..9776d7e632 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -105,9 +105,8 @@ namespace MWRender return mLandManager->getLand(cellLocation); } - const ESM::LandTexture* TerrainStorage::getLandTexture(std::uint16_t index, int plugin) + const std::string* TerrainStorage::getLandTexture(std::uint16_t index, int plugin) { - assert(plugin >= 0); // Saves don't contain textures const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); return esmStore.get().search(index, plugin); } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 6e695dd30f..731f396713 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -22,7 +22,7 @@ namespace MWRender ~TerrainStorage(); osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) override; - const ESM::LandTexture* getLandTexture(std::uint16_t index, int plugin) override; + const std::string* getLandTexture(std::uint16_t index, int plugin) override; bool hasData(ESM::ExteriorCellLocation cellLocation) override; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index a8b2beb003..15a687f4d7 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -391,11 +391,6 @@ namespace MWWorld if (listener != nullptr) listener->setProgressRange(::EsmLoader::fileProgress); - // Land texture loading needs to use a separate internal store for each plugin. - // We set the number of plugins here so we can properly verify if valid plugin - // indices are being passed to the LandTexture Store retrieval methods. - getWritable().resize(esm.getIndex() + 1); - // Loop through all records while (esm.hasMoreRecs()) { diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 6c617c1f77..a98f06a64a 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -379,83 +379,51 @@ namespace MWWorld // LandTexture //========================================================================= - Store::Store() {} - const ESM::LandTexture* Store::search(size_t index, size_t plugin) const - { - assert(plugin < mStatic.size()); - const LandTextureList& ltexl = mStatic[plugin]; + Store::Store() = default; - if (index >= ltexl.size()) + const std::string* Store::search(std::uint32_t index, int plugin) const + { + auto mapping = mMappings.find(PluginIndex{ plugin, index }); + if (mapping == mMappings.end()) return nullptr; - return <exl[index]; - } - const ESM::LandTexture* Store::find(size_t index, size_t plugin) const - { - const ESM::LandTexture* ptr = search(index, plugin); - if (ptr == nullptr) - { - const std::string msg = "Land texture with index " + std::to_string(index) + " not found"; - throw std::runtime_error(msg); - } - return ptr; - } - - void Store::resize(std::size_t num) - { - mStatic.resize(num); + auto texture = mTextures.find(mapping->second); + if (texture == mTextures.end()) + return nullptr; + return &texture->second; } size_t Store::getSize() const { - return mStatic.size(); - } - size_t Store::getSize(size_t plugin) const - { - assert(plugin < mStatic.size()); - return mStatic[plugin].size(); + return mTextures.size(); } + RecordId Store::load(ESM::ESMReader& esm) { + const int plugin = esm.getIndex(); + ESM::LandTexture lt; bool isDeleted = false; lt.load(esm, isDeleted); - // Replace texture for records with given ID and index from all plugins. - for (unsigned int i = 0; i < mStatic.size(); i++) + if (!isDeleted) { - ESM::LandTexture* tex = const_cast(search(lt.mIndex, i)); - if (tex) - { - if (tex->mId == lt.mId) - tex->mTexture = lt.mTexture; - } + mTextures[lt.mId] = std::move(lt.mTexture); + mMappings.emplace(PluginIndex{ plugin, lt.mIndex }, lt.mId); } - LandTextureList& ltexl = mStatic.back(); - if (lt.mIndex + 1 > (int)ltexl.size()) - ltexl.resize(lt.mIndex + 1); - - // Store it - auto idx = lt.mIndex; - ltexl[idx] = std::move(lt); - - return RecordId(ltexl[idx].mId, isDeleted); + return RecordId(lt.mId, isDeleted); } - Store::iterator Store::begin(size_t plugin) const + + bool Store::eraseStatic(const ESM::RefId& id) { - assert(plugin < mStatic.size()); - return mStatic[plugin].begin(); - } - Store::iterator Store::end(size_t plugin) const - { - assert(plugin < mStatic.size()); - return mStatic[plugin].end(); + mTextures.erase(id); + return true; } // Land //========================================================================= - Store::~Store() {} + Store::~Store() = default; size_t Store::getSize() const { return mStatic.size(); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 5cb0d6935c..91ac15885c 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -255,29 +255,21 @@ namespace MWWorld template <> class Store : public DynamicStore { - // For multiple ESM/ESP files we need one list per file. - typedef std::vector LandTextureList; - std::vector mStatic; + using PluginIndex = std::pair; // This is essentially a FormId + std::unordered_map mTextures; + std::map mMappings; public: Store(); - typedef std::vector::const_iterator iterator; - // Must be threadsafe! Called from terrain background loading threads. // Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased - const ESM::LandTexture* search(size_t index, size_t plugin) const; - const ESM::LandTexture* find(size_t index, size_t plugin) const; - - void resize(std::size_t num); + const std::string* search(std::uint32_t index, int plugin) const; size_t getSize() const override; - size_t getSize(size_t plugin) const; + bool eraseStatic(const ESM::RefId& id) override; RecordId load(ESM::ESMReader& esm) override; - - iterator begin(size_t plugin) const; - iterator end(size_t plugin) const; }; template <> diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index b63c0902ab..27010ef851 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -497,7 +497,11 @@ namespace const RecordType* result = nullptr; if constexpr (std::is_same_v) - result = esmStore.get().search(index, 0); + { + const std::string* texture = esmStore.get().search(index, 0); + ASSERT_NE(texture, nullptr); + return; + } else if constexpr (ESM::hasIndex) result = esmStore.get().search(index); else diff --git a/components/esm3/loadltex.hpp b/components/esm3/loadltex.hpp index fb95e8b9ed..7a5ff7d613 100644 --- a/components/esm3/loadltex.hpp +++ b/components/esm3/loadltex.hpp @@ -26,10 +26,9 @@ namespace ESM /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string_view getRecordType() { return "LandTexture"; } - // mId is merely a user friendly name for the texture in the editor. std::string mTexture; RefId mId; - int32_t mIndex; + uint32_t mIndex; void load(ESMReader& esm, bool& isDeleted); void save(ESMWriter& esm, bool isDeleted = false) const; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index a4ff3e6aaa..35ec814aa2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -372,9 +372,9 @@ namespace ESMTerrain if (id.first != 0) { // NB: All vtex ids are +1 compared to the ltex ids - const ESM::LandTexture* ltex = getLandTexture(id.first - 1, id.second); + const std::string* ltex = getLandTexture(id.first - 1, id.second); if (ltex) - texture = ltex->mTexture; + texture = *ltex; else { Log(Debug::Warning) << "Warning: Unable to find land texture index " << id.first - 1 << " in plugin " diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 6d388e6e8c..402f2147ab 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -73,7 +73,7 @@ namespace ESMTerrain // Not implemented in this class, because we need different Store implementations for game and editor virtual osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) = 0; - virtual const ESM::LandTexture* getLandTexture(std::uint16_t index, int plugin) = 0; + virtual const std::string* getLandTexture(std::uint16_t index, int plugin) = 0; /// Get bounds of the whole terrain in cell units void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override = 0; From 330f5de78d271ff3c162fe176c788f4ddf5e91fb Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 5 Jun 2024 21:48:07 +0200 Subject: [PATCH 2/2] Rename ot mStatic --- apps/openmw/mwworld/store.cpp | 10 +++++----- apps/openmw/mwworld/store.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index a98f06a64a..1e92df85ec 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -386,15 +386,15 @@ namespace MWWorld auto mapping = mMappings.find(PluginIndex{ plugin, index }); if (mapping == mMappings.end()) return nullptr; - auto texture = mTextures.find(mapping->second); - if (texture == mTextures.end()) + auto texture = mStatic.find(mapping->second); + if (texture == mStatic.end()) return nullptr; return &texture->second; } size_t Store::getSize() const { - return mTextures.size(); + return mStatic.size(); } RecordId Store::load(ESM::ESMReader& esm) @@ -408,7 +408,7 @@ namespace MWWorld if (!isDeleted) { - mTextures[lt.mId] = std::move(lt.mTexture); + mStatic[lt.mId] = std::move(lt.mTexture); mMappings.emplace(PluginIndex{ plugin, lt.mIndex }, lt.mId); } @@ -417,7 +417,7 @@ namespace MWWorld bool Store::eraseStatic(const ESM::RefId& id) { - mTextures.erase(id); + mStatic.erase(id); return true; } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 91ac15885c..5ce287c34e 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -256,7 +256,7 @@ namespace MWWorld class Store : public DynamicStore { using PluginIndex = std::pair; // This is essentially a FormId - std::unordered_map mTextures; + std::unordered_map mStatic; std::map mMappings; public: