diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 9adc43d315..3714ecef64 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -275,7 +275,7 @@ namespace MWGui if (!mInterior) { - ESM::ExteriorCellLocation cellPos = ESM::positionToCellIndex(worldX, worldY); + ESM::ExteriorCellLocation cellPos = ESM::positionToExteriorCellLocation(worldX, worldY); cellIndex.x() = cellPos.mX; cellIndex.y() = cellPos.mY; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index c83caa0dc5..2c3310b891 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -126,7 +126,7 @@ namespace MWGui std::string_view cellname = transport[i].mCellName; bool interior = true; const ESM::ExteriorCellLocation cellIndex - = ESM::positionToCellIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1]); + = ESM::positionToExteriorCellLocation(transport[i].mPos.pos[0], transport[i].mPos.pos[1]); if (cellname.empty()) { MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex); @@ -192,7 +192,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(1); - const ESM::ExteriorCellLocation posCell = ESM::positionToCellIndex(pos.pos[0], pos.pos[1]); + const ESM::ExteriorCellLocation posCell = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1]); ESM::RefId cellId = ESM::Cell::generateIdForCell(!interior, cellname, posCell.mX, posCell.mY); // Teleports any followers, too. diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 177bdc77b6..5e5c380469 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -67,7 +67,11 @@ namespace MWLua else cell = &wm->getCell(name); } - return &wm->getCellByPosition(pos, cell); + if (cell != nullptr && !cell->isExterior()) + return cell; + const ESM::RefId worldspace + = cell == nullptr ? ESM::Cell::sDefaultWorldspaceId : cell->getCell()->getWorldSpace(); + return &wm->getExterior(ESM::positionToExteriorCellLocation(pos.x(), pos.y(), worldspace)); } void teleportPlayer( diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index bf9fe14298..879cf85144 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -402,7 +402,7 @@ namespace MWScript if (store->isExterior()) { const ESM::ExteriorCellLocation cellIndex - = ESM::positionToCellIndex(x, y, store->getCell()->getWorldSpace()); + = ESM::positionToExteriorCellLocation(x, y, store->getCell()->getWorldSpace()); store = &worldModel->getExterior(cellIndex); } } @@ -418,7 +418,7 @@ namespace MWScript if (!isPlayer) return; const ESM::ExteriorCellLocation cellIndex - = ESM::positionToCellIndex(x, y, store->getCell()->getWorldSpace()); + = ESM::positionToExteriorCellLocation(x, y, store->getCell()->getWorldSpace()); store = &worldModel->getExterior(cellIndex); } if (store) @@ -469,7 +469,7 @@ namespace MWScript if (isPlayer) world->getPlayer().setTeleported(true); const ESM::ExteriorCellLocation cellIndex - = ESM::positionToCellIndex(x, y, ESM::Cell::sDefaultWorldspaceId); + = ESM::positionToExteriorCellLocation(x, y, ESM::Cell::sDefaultWorldspaceId); // another morrowind oddity: player will be moved to the exterior cell at this location, // non-player actors will move within the cell they are in. @@ -570,7 +570,7 @@ namespace MWScript if (player.getCell()->isExterior()) { const ESM::ExteriorCellLocation cellIndex - = ESM::positionToCellIndex(x, y, player.getCell()->getCell()->getWorldSpace()); + = ESM::positionToExteriorCellLocation(x, y, player.getCell()->getCell()->getWorldSpace()); store = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex); } else diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 3e81ba1c37..6b9494b24f 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -88,7 +88,7 @@ namespace MWWorld } else { - const auto cellPos = ESM::positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]); + const auto cellPos = ESM::positionToExteriorCellLocation(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]); return ESM::RefId::esm3ExteriorCell(cellPos.mX, cellPos.mY); } }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index c9be8c0a96..4036c378db 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1283,4 +1283,23 @@ namespace MWWorld return {}; } + Ptr CellStore::getPtr(ESM::RefId id) + { + if (mState == CellStore::State_Unloaded) + preload(); + + if (mState == CellStore::State_Preloaded) + { + if (!std::binary_search(mIds.begin(), mIds.end(), id)) + return Ptr(); + load(); + } + + Ptr ptr = search(id); + + if (!ptr.isEmpty() && isAccessible(ptr.getRefData(), ptr.getCellRef())) + return ptr; + + return Ptr(); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 47867a63ff..f0690e13a1 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -322,6 +322,8 @@ namespace MWWorld Ptr getMovedActor(int actorId) const; + Ptr getPtr(ESM::RefId id); + bool operator==(const CellStore& right) const; private: diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b116fbe633..5112fad252 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -521,7 +521,7 @@ namespace MWWorld if (distance <= maxDistance) return *currentGridCenter; } - ESM::ExteriorCellLocation cellPos = ESM::positionToCellIndex(pos.x(), pos.y(), worldspace); + ESM::ExteriorCellLocation cellPos = ESM::positionToExteriorCellLocation(pos.x(), pos.y(), worldspace); return { cellPos.mX, cellPos.mY }; } @@ -1283,7 +1283,8 @@ namespace MWWorld else { osg::Vec3f pos = dest.mPos.asVec3(); - const ESM::ExteriorCellLocation cellIndex = ESM::positionToCellIndex(pos.x(), pos.y(), extWorldspace); + const ESM::ExteriorCellLocation cellIndex + = ESM::positionToExteriorCellLocation(pos.x(), pos.y(), extWorldspace); preloadCell(mWorld.getWorldModel().getExterior(cellIndex), true); exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index ec984e030d..e2bd7cafb2 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1166,8 +1166,8 @@ namespace MWWorld const ESM4::Cell* cell = cells.find(ref.mParent); if (cell->isExterior() && (cell->mFlags & ESM4::Rec_Persistent)) { - const ESM4::Cell* actualCell - = cells.searchExterior(positionToCellIndex(ref.mPos.pos[0], ref.mPos.pos[1], cell->mParent)); + const ESM4::Cell* actualCell = cells.searchExterior( + positionToExteriorCellLocation(ref.mPos.pos[0], ref.mPos.pos[1], cell->mParent)); if (actualCell) ref.mParent = actualCell->mId; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8d8eccf17b..349215460c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -380,7 +380,7 @@ namespace MWWorld pos.rot[1] = 0; pos.rot[2] = 0; - ESM::ExteriorCellLocation exteriorCellPos = ESM::positionToCellIndex(pos.pos[0], pos.pos[1]); + ESM::ExteriorCellLocation exteriorCellPos = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1]); ESM::RefId cellId = ESM::RefId::esm3ExteriorCell(exteriorCellPos.mX, exteriorCellPos.mY); mWorldScene->changeToExteriorCell(cellId, pos, true); } @@ -695,7 +695,7 @@ namespace MWWorld { // TODO: caching still doesn't work efficiently here (only works for the one CellStore that the reference is // in) - Ptr ptr = mWorldModel.getPtr(name, *cellstore); + Ptr ptr = cellstore->getPtr(name); if (!ptr.isEmpty()) return ptr; @@ -1248,7 +1248,8 @@ namespace MWWorld CellStore* cell = ptr.getCell(); ESM::RefId worldspaceId = cell->isExterior() ? cell->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId; - const ESM::ExteriorCellLocation index = ESM::positionToCellIndex(position.x(), position.y(), worldspaceId); + const ESM::ExteriorCellLocation index + = ESM::positionToExteriorCellLocation(position.x(), position.y(), worldspaceId); CellStore* newCell = cell->isExterior() ? &mWorldModel.getExterior(index) : nullptr; bool isCellActive = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior() @@ -2047,7 +2048,7 @@ namespace MWWorld if (cell->isExterior()) { const ESM::ExteriorCellLocation index - = ESM::positionToCellIndex(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace()); + = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace()); cell = &mWorldModel.getExterior(index); } diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index 6a9d61b9f1..ca69a7ff39 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -1,5 +1,7 @@ #include "worldmodel.hpp" +#include + #include #include #include @@ -12,9 +14,6 @@ #include #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - #include "cellstore.hpp" #include "esmstore.hpp" @@ -116,7 +115,7 @@ MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefNum& refNum) const MWWorld::Ptr MWWorld::WorldModel::getPtrAndCache(const ESM::RefId& name, CellStore& cellStore) { - Ptr ptr = getPtr(name, cellStore); + Ptr ptr = cellStore.getPtr(name); if (!ptr.isEmpty() && ptr.isInCell()) { @@ -147,7 +146,7 @@ void MWWorld::WorldModel::writeCell(ESM::ESMWriter& writer, CellStore& cell) con writer.endRecord(ESM::REC_CSTA); } -MWWorld::WorldModel::WorldModel(const MWWorld::ESMStore& store, ESM::ReadersCache& readers) +MWWorld::WorldModel::WorldModel(MWWorld::ESMStore& store, ESM::ReadersCache& readers) : mStore(store) , mReaders(readers) , mIdCache(Settings::cells().mPointersCacheSize, { ESM::RefId(), nullptr }) @@ -177,7 +176,7 @@ MWWorld::CellStore& MWWorld::WorldModel::getExterior(ESM::ExteriorCellLocation c record.mMapColor = 0; record.updateId(); - cell = MWBase::Environment::get().getESMStore()->insert(record); + cell = mStore.insert(record); } CellStore* cellStore @@ -197,8 +196,8 @@ MWWorld::CellStore& MWWorld::WorldModel::getExterior(ESM::ExteriorCellLocation c record.mParent = cellIndex.mWorldspace; record.mX = cellIndex.mX; record.mY = cellIndex.mY; - record.mCellFlags = !ESM4::CELL_Interior; - cell = MWBase::Environment::get().getESMStore()->insert(record); + record.mCellFlags = 0; + cell = mStore.insert(record); } CellStore* cellStore = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second; @@ -320,51 +319,16 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(std::string_view name, bool for ESM::ExteriorCellLocation(cell->getGridX(), cell->getGridY(), ESM::Cell::sDefaultWorldspaceId), forceLoad); } -MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition( - const osg::Vec3f& pos, MWWorld::CellStore* cellInSameWorldSpace) -{ - if (cellInSameWorldSpace && !cellInSameWorldSpace->isExterior()) - return *cellInSameWorldSpace; - ESM::RefId exteriorWorldspace - = cellInSameWorldSpace ? cellInSameWorldSpace->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId; - const ESM::ExteriorCellLocation cellIndex = ESM::positionToCellIndex(pos.x(), pos.y(), exteriorWorldspace); - - return getExterior(cellIndex); -} - -MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell) -{ - if (cell.getState() == CellStore::State_Unloaded) - cell.preload(); - - if (cell.getState() == CellStore::State_Preloaded) - { - if (cell.hasId(name)) - { - cell.load(); - } - else - return Ptr(); - } - - Ptr ptr = cell.search(name); - - if (!ptr.isEmpty() && MWWorld::CellStore::isAccessible(ptr.getRefData(), ptr.getCellRef())) - return ptr; - - return Ptr(); -} - MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name) { - // First check the cache - for (IdCache::iterator iter(mIdCache.begin()); iter != mIdCache.end(); ++iter) - if (iter->first == name && iter->second) - { - Ptr ptr = getPtr(name, *iter->second); - if (!ptr.isEmpty()) - return ptr; - } + for (const auto& [cachedId, cellStore] : mIdCache) + { + if (cachedId != name || cellStore == nullptr) + continue; + Ptr ptr = cellStore->getPtr(name); + if (!ptr.isEmpty()) + return ptr; + } // Then check cells that are already listed // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game. @@ -435,21 +399,15 @@ std::vector MWWorld::WorldModel::getAll(const ESM::RefId& id) int MWWorld::WorldModel::countSavedGameRecords() const { - int count = 0; - - for (auto iter(mCells.begin()); iter != mCells.end(); ++iter) - if (iter->second.hasState()) - ++count; - - return count; + return std::count_if(mCells.begin(), mCells.end(), [](const auto& v) { return v.second.hasState(); }); } void MWWorld::WorldModel::write(ESM::ESMWriter& writer, Loading::Listener& progress) const { - for (auto iter(mCells.begin()); iter != mCells.end(); ++iter) - if (iter->second.hasState()) + for (auto& [id, cellStore] : mCells) + if (cellStore.hasState()) { - writeCell(writer, iter->second); + writeCell(writer, cellStore); progress.increaseProgress(); } } diff --git a/apps/openmw/mwworld/worldmodel.hpp b/apps/openmw/mwworld/worldmodel.hpp index 300adbe2db..a83e29dd83 100644 --- a/apps/openmw/mwworld/worldmodel.hpp +++ b/apps/openmw/mwworld/worldmodel.hpp @@ -38,31 +38,8 @@ namespace MWWorld /// \brief Cell container class WorldModel { - typedef std::vector> IdCache; - const MWWorld::ESMStore& mStore; - ESM::ReadersCache& mReaders; - mutable std::unordered_map mCells; - mutable std::map mInteriors; - - mutable std::map mExteriors; - IdCache mIdCache; - std::size_t mIdCacheIndex = 0; - std::unordered_map mPtrIndex; - std::size_t mPtrIndexUpdateCounter = 0; - ESM::RefNum mLastGeneratedRefnum; - - CellStore& getOrInsertCellStore(const ESM::Cell& cell); - - CellStore& insertCellStore(const ESM::Cell& cell); - - CellStore* getInteriorOrNull(std::string_view name); - - Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore); - - void writeCell(ESM::ESMWriter& writer, CellStore& cell) const; - public: - explicit WorldModel(const MWWorld::ESMStore& store, ESM::ReadersCache& reader); + explicit WorldModel(ESMStore& store, ESM::ReadersCache& reader); WorldModel(const WorldModel&) = delete; WorldModel& operator=(const WorldModel&) = delete; @@ -74,12 +51,6 @@ namespace MWWorld CellStore& getCell(std::string_view name, bool forceLoad = true); // interior or named exterior CellStore& getCell(const ESM::RefId& Id, bool forceLoad = true); - // Returns the cell that is in the same worldspace as `cellInSameWorldSpace` - // (in case of nullptr - default exterior worldspace) and contains given position. - // Interiors are single-cell worldspaces, so in case of an interior it just returns - // the same cell. - CellStore& getCellByPosition(const osg::Vec3f& pos, CellStore* cellInSameWorldSpace = nullptr); - void registerPtr(const MWWorld::Ptr& ptr); void deregisterPtr(const MWWorld::Ptr& ptr); ESM::RefNum getLastGeneratedRefNum() const { return mLastGeneratedRefnum; } @@ -89,7 +60,6 @@ namespace MWWorld Ptr getPtr(const ESM::RefNum& refNum) const; - Ptr getPtr(const ESM::RefId& name, CellStore& cellStore); Ptr getPtr(const ESM::RefId& name); template @@ -111,6 +81,28 @@ namespace MWWorld void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord(ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); + + private: + MWWorld::ESMStore& mStore; + ESM::ReadersCache& mReaders; + mutable std::unordered_map mCells; + mutable std::map mInteriors; + mutable std::map mExteriors; + std::vector> mIdCache; + std::size_t mIdCacheIndex = 0; + std::unordered_map mPtrIndex; + std::size_t mPtrIndexUpdateCounter = 0; + ESM::RefNum mLastGeneratedRefnum; + + CellStore& getOrInsertCellStore(const ESM::Cell& cell); + + CellStore& insertCellStore(const ESM::Cell& cell); + + CellStore* getInteriorOrNull(std::string_view name); + + Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore); + + void writeCell(ESM::ESMWriter& writer, CellStore& cell) const; }; } diff --git a/components/esm/util.hpp b/components/esm/util.hpp index 6edf087854..ac687fea4b 100644 --- a/components/esm/util.hpp +++ b/components/esm/util.hpp @@ -83,7 +83,7 @@ namespace ESM return isEsm4Ext(worldspaceId) ? Constants::ESM4CellSizeInUnits : Constants::CellSizeInUnits; } - inline ESM::ExteriorCellLocation positionToCellIndex( + inline ESM::ExteriorCellLocation positionToExteriorCellLocation( float x, float y, ESM::RefId worldspaceId = ESM::Cell::sDefaultWorldspaceId) { const float cellSize = getCellSize(worldspaceId);