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:
commit
36aea64eb8
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -322,6 +322,8 @@ namespace MWWorld
|
||||
|
||||
Ptr getMovedActor(int actorId) const;
|
||||
|
||||
Ptr getPtr(ESM::RefId id);
|
||||
|
||||
bool operator==(const CellStore& right) const;
|
||||
|
||||
private:
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user