1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 09:35:28 +00:00

Merge branch 'refactor_world_model' into 'master'

Cleanup and refactor WorldModel

See merge request OpenMW/openmw!3068
This commit is contained in:
psi29a 2023-05-27 15:38:17 +00:00
commit 36aea64eb8
13 changed files with 87 additions and 110 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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(

View File

@ -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

View File

@ -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);
}
};

View File

@ -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();
}
}

View File

@ -322,6 +322,8 @@ namespace MWWorld
Ptr getMovedActor(int actorId) const;
Ptr getPtr(ESM::RefId id);
bool operator==(const CellStore& right) const;
private:

View File

@ -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)));
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -1,5 +1,7 @@
#include "worldmodel.hpp"
#include <algorithm>
#include <components/debug/debuglog.hpp>
#include <components/esm/defs.hpp>
#include <components/esm3/cellid.hpp>
@ -12,9 +14,6 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/settings/values.hpp>
#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::Ptr> 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();
}
}

View File

@ -38,31 +38,8 @@ namespace MWWorld
/// \brief Cell container
class WorldModel
{
typedef std::vector<std::pair<ESM::RefId, CellStore*>> IdCache;
const MWWorld::ESMStore& mStore;
ESM::ReadersCache& mReaders;
mutable std::unordered_map<ESM::RefId, CellStore> mCells;
mutable std::map<std::string, CellStore*, Misc::StringUtils::CiComp> mInteriors;
mutable std::map<ESM::ExteriorCellLocation, CellStore*> mExteriors;
IdCache mIdCache;
std::size_t mIdCacheIndex = 0;
std::unordered_map<ESM::RefNum, Ptr> 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 <typename Fn>
@ -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<int, int>& contentFileMap);
private:
MWWorld::ESMStore& mStore;
ESM::ReadersCache& mReaders;
mutable std::unordered_map<ESM::RefId, CellStore> mCells;
mutable std::map<std::string, CellStore*, Misc::StringUtils::CiComp> mInteriors;
mutable std::map<ESM::ExteriorCellLocation, CellStore*> mExteriors;
std::vector<std::pair<ESM::RefId, CellStore*>> mIdCache;
std::size_t mIdCacheIndex = 0;
std::unordered_map<ESM::RefNum, Ptr> 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;
};
}

View File

@ -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);