1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-29 08:15:35 +00:00

Initial attempt at loading esm4 exteriors

This commit is contained in:
florent.teppe 2023-04-22 13:59:53 +02:00
parent 242ac21b38
commit a62e75fd19
17 changed files with 162 additions and 66 deletions

View file

@ -583,8 +583,8 @@ namespace MWGui
if (!entry.mMapTexture) if (!entry.mMapTexture)
{ {
if (!mInterior) if (!mInterior)
requestMapRender( requestMapRender(&MWBase::Environment::get().getWorldModel()->getExterior(
&MWBase::Environment::get().getWorldModel()->getExterior(entry.mCellX, entry.mCellY)); entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId));
osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(entry.mCellX, entry.mCellY); osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(entry.mCellX, entry.mCellY);
if (texture) if (texture)
@ -641,7 +641,8 @@ namespace MWGui
for (MapEntry& entry : mMaps) for (MapEntry& entry : mMaps)
{ {
if (!entry.mMapTexture && !widgetCropped(entry.mMapWidget, mLocalMap)) if (!entry.mMapTexture && !widgetCropped(entry.mMapWidget, mLocalMap))
world->getDoorMarkers(worldModel->getExterior(entry.mCellX, entry.mCellY), doors); world->getDoorMarkers(
worldModel->getExterior(entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId), doors);
} }
if (doors.empty()) if (doors.empty())
return; return;

View file

@ -130,8 +130,8 @@ namespace MWGui
= MWWorld::positionToCellIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1]); = MWWorld::positionToCellIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1]);
if (cellname.empty()) if (cellname.empty())
{ {
MWWorld::CellStore& cell MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(
= MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y()); cellIndex.x(), cellIndex.y(), ESM::Cell::sDefaultWorldspaceId);
cellname = MWBase::Environment::get().getWorld()->getCellName(&cell); cellname = MWBase::Environment::get().getWorld()->getCellName(&cell);
interior = false; interior = false;
} }

View file

@ -113,8 +113,10 @@ namespace MWLua
addTimeBindings(api, context, true); addTimeBindings(api, context, true);
api["getCellByName"] api["getCellByName"]
= [](std::string_view name) { return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(name) }; }; = [](std::string_view name) { return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(name) }; };
api["getExteriorCell"] api["getExteriorCell"] = [](int x, int y) {
= [](int x, int y) { return GCell{ &MWBase::Environment::get().getWorldModel()->getExterior(x, y) }; }; return GCell{ &MWBase::Environment::get().getWorldModel()->getExterior(
x, y, ESM::Cell::sDefaultWorldspaceId) };
};
api["activeActors"] = GObjectList{ worldView->getActorsInScene() }; api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject { api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {
// Doesn't matter which cell to use because the new object will be in disabled state. // Doesn't matter which cell to use because the new object will be in disabled state.

View file

@ -402,7 +402,8 @@ namespace MWScript
if (store->isExterior()) if (store->isExterior())
{ {
const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y); const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
store = &worldModel->getExterior(cellIndex.x(), cellIndex.y()); store
= &worldModel->getExterior(cellIndex.x(), cellIndex.y(), store->getCell()->getWorldSpace());
} }
} }
catch (std::exception&) catch (std::exception&)
@ -417,7 +418,7 @@ namespace MWScript
if (!isPlayer) if (!isPlayer)
return; return;
const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y); const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
store = &worldModel->getExterior(cellIndex.x(), cellIndex.y()); store = &worldModel->getExterior(cellIndex.x(), cellIndex.y(), store->getCell()->getWorldSpace());
} }
if (store) if (store)
{ {
@ -473,8 +474,8 @@ namespace MWScript
MWWorld::Ptr base = ptr; MWWorld::Ptr base = ptr;
if (isPlayer) if (isPlayer)
{ {
MWWorld::CellStore* cell MWWorld::CellStore* cell = &MWBase::Environment::get().getWorldModel()->getExterior(
= &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y()); cellIndex.x(), cellIndex.y(), ESM::Cell::sDefaultWorldspaceId);
ptr = world->moveObject(ptr, cell, osg::Vec3(x, y, z)); ptr = world->moveObject(ptr, cell, osg::Vec3(x, y, z));
} }
else else
@ -568,7 +569,8 @@ namespace MWScript
if (player.getCell()->isExterior()) if (player.getCell()->isExterior())
{ {
const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y); const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
store = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y()); store = &MWBase::Environment::get().getWorldModel()->getExterior(
cellIndex.x(), cellIndex.y(), player.getCell()->getCell()->getWorldSpace());
} }
else else
store = player.getCell(); store = player.getCell();

View file

@ -563,7 +563,8 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
{ {
// Cell no longer exists (i.e. changed game files), choose a default cell // Cell no longer exists (i.e. changed game files), choose a default cell
Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell"; Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell";
MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(0, 0); MWWorld::CellStore& cell
= MWBase::Environment::get().getWorldModel()->getExterior(0, 0, ESM::Cell::sDefaultWorldspaceId);
float x, y; float x, y;
MWBase::Environment::get().getWorld()->indexToPosition(0, 0, x, y, false); MWBase::Environment::get().getWorld()->indexToPosition(0, 0, x, y, false);
ESM::Position pos; ESM::Position pos;

View file

@ -14,6 +14,7 @@
#include <components/misc/algorithm.hpp> #include <components/misc/algorithm.hpp>
#include <components/esm4/common.hpp> #include <components/esm4/common.hpp>
#include <components/esm4/loadwrld.hpp>
#include <components/esm4/reader.hpp> #include <components/esm4/reader.hpp>
#include <components/esm4/readerutils.hpp> #include <components/esm4/readerutils.hpp>
#include <components/esmloader/load.hpp> #include <components/esmloader/load.hpp>

View file

@ -42,6 +42,7 @@ namespace ESM4
struct Ingredient; struct Ingredient;
struct MiscItem; struct MiscItem;
struct Weapon; struct Weapon;
struct World;
} }
namespace ESM namespace ESM
@ -120,7 +121,7 @@ namespace MWWorld
Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>, Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>,
Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>, Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>,
Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>, Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>,
Store<ESM4::Weapon>>; Store<ESM4::Weapon>, Store<ESM4::World>>;
private: private:
template <typename T> template <typename T>

View file

@ -551,12 +551,13 @@ namespace MWWorld
else else
unloadCell(cell, navigatorUpdateGuard.get()); unloadCell(cell, navigatorUpdateGuard.get());
} }
ESM::RefId currentWorldSpace = mCurrentCell->getCell()->getWorldSpace();
mNavigator.setWorldspace(Misc::StringUtils::lowerCase(mWorld.getWorldModel() mNavigator.setWorldspace(
.getExterior(playerCellX, playerCellY) Misc::StringUtils::lowerCase(mWorld.getWorldModel()
.getCell() .getExterior(playerCellX, playerCellY, currentWorldSpace)
->getWorldSpace() .getCell()
.serializeText()), ->getWorldSpace()
.serializeText()),
navigatorUpdateGuard.get()); navigatorUpdateGuard.get());
mNavigator.updateBounds(pos, navigatorUpdateGuard.get()); mNavigator.updateBounds(pos, navigatorUpdateGuard.get());
@ -580,7 +581,7 @@ namespace MWWorld
{ {
if (!isCellInCollection(x, y, collection)) if (!isCellInCollection(x, y, collection))
{ {
refsToLoad += mWorld.getWorldModel().getExterior(x, y).count(); refsToLoad += mWorld.getWorldModel().getExterior(x, y, currentWorldSpace).count();
cellsPositionsToLoad.emplace_back(x, y); cellsPositionsToLoad.emplace_back(x, y);
} }
} }
@ -614,7 +615,7 @@ namespace MWWorld
{ {
if (!isCellInCollection(x, y, mActiveCells)) if (!isCellInCollection(x, y, mActiveCells))
{ {
CellStore& cell = mWorld.getWorldModel().getExterior(x, y); CellStore& cell = mWorld.getWorldModel().getExterior(x, y, currentWorldSpace);
loadCell(cell, loadingListener, changeEvent, pos, navigatorUpdateGuard.get()); loadCell(cell, loadingListener, changeEvent, pos, navigatorUpdateGuard.get());
} }
} }
@ -623,7 +624,7 @@ namespace MWWorld
navigatorUpdateGuard.reset(); navigatorUpdateGuard.reset();
CellStore& current = mWorld.getWorldModel().getExterior(playerCellX, playerCellY); CellStore& current = mWorld.getWorldModel().getExterior(playerCellX, playerCellY, currentWorldSpace);
MWBase::Environment::get().getWindowManager()->changeCell(&current); MWBase::Environment::get().getWindowManager()->changeCell(&current);
if (changeEvent) if (changeEvent)
@ -676,7 +677,8 @@ namespace MWWorld
loadingListener->setLabel("#{OMWEngine:TestingExteriorCells} (" + std::to_string(i) + "/" loadingListener->setLabel("#{OMWEngine:TestingExteriorCells} (" + std::to_string(i) + "/"
+ std::to_string(cells.getExtSize()) + ")..."); + std::to_string(cells.getExtSize()) + ")...");
CellStore& cell = mWorld.getWorldModel().getExterior(it->mData.mX, it->mData.mY); CellStore& cell
= mWorld.getWorldModel().getExterior(it->mData.mX, it->mData.mY, ESM::Cell::sDefaultWorldspaceId);
mNavigator.setWorldspace(Misc::StringUtils::lowerCase(cell.getCell()->getWorldSpace().serializeText()), mNavigator.setWorldspace(Misc::StringUtils::lowerCase(cell.getCell()->getWorldSpace().serializeText()),
navigatorUpdateGuard.get()); navigatorUpdateGuard.get());
const osg::Vec3f position const osg::Vec3f position
@ -1177,7 +1179,8 @@ namespace MWWorld
+ mPreloadDistance; + mPreloadDistance;
if (dist < loadDist) if (dist < loadDist)
preloadCell(mWorld.getWorldModel().getExterior(cellX + dx, cellY + dy)); preloadCell(
mWorld.getWorldModel().getExterior(cellX + dx, cellY + dy, ESM::Cell::sDefaultWorldspaceId));
} }
} }
} }
@ -1194,7 +1197,8 @@ namespace MWWorld
for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy) for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy)
{ {
mPreloader->preload( mPreloader->preload(
mWorld.getWorldModel().getExterior(x + dx, y + dy), mRendering.getReferenceTime()); mWorld.getWorldModel().getExterior(x + dx, y + dy, cell.getCell()->getWorldSpace()),
mRendering.getReferenceTime());
if (++numpreloaded >= mPreloader->getMaxCacheSize()) if (++numpreloaded >= mPreloader->getMaxCacheSize())
break; break;
} }
@ -1277,7 +1281,9 @@ namespace MWWorld
{ {
osg::Vec3f pos = dest.mPos.asVec3(); osg::Vec3f pos = dest.mPos.asVec3();
const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y()); const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
preloadCell(mWorld.getWorldModel().getExterior(cellIndex.x(), cellIndex.y()), true); preloadCell(
mWorld.getWorldModel().getExterior(cellIndex.x(), cellIndex.y(), ESM::Cell::sDefaultWorldspaceId),
true);
exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
} }
} }

View file

@ -8,6 +8,7 @@
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
#include <components/esm4/loadwrld.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
@ -1104,10 +1105,32 @@ namespace MWWorld
return foundCell->second; return foundCell->second;
} }
const ESM4::Cell* Store<ESM4::Cell>::searchExterior(int x, int y, ESM::RefId worldSpace) const
{
const auto foundWorldSpace = mExteriors.find(worldSpace);
if (foundWorldSpace == mExteriors.end())
{
return nullptr;
}
const auto foundCell = foundWorldSpace->second.find(std::make_pair(x, y));
if (foundCell == foundWorldSpace->second.end())
return nullptr;
return foundCell->second;
}
bool Store<ESM4::Cell>::exteriorExists(ESM::RefId worldspace) const
{
const auto foundWorldSpace = mExteriors.find(worldspace);
return (foundWorldSpace != mExteriors.end());
}
ESM4::Cell* Store<ESM4::Cell>::insert(const ESM4::Cell& item, bool overrideOnly) ESM4::Cell* Store<ESM4::Cell>::insert(const ESM4::Cell& item, bool overrideOnly)
{ {
auto cellPtr = TypedDynamicStore<ESM4::Cell>::insert(item, overrideOnly); auto cellPtr = TypedDynamicStore<ESM4::Cell>::insert(item, overrideOnly);
mCellNameIndex[cellPtr->mEditorId] = cellPtr; mCellNameIndex[cellPtr->mEditorId] = cellPtr;
if (cellPtr->isExterior())
mExteriors[cellPtr->mParent][std::make_pair(cellPtr->getGridX(), cellPtr->getGridY())] = cellPtr;
return cellPtr; return cellPtr;
} }
@ -1115,6 +1138,9 @@ namespace MWWorld
{ {
auto cellPtr = TypedDynamicStore<ESM4::Cell>::insertStatic(item); auto cellPtr = TypedDynamicStore<ESM4::Cell>::insertStatic(item);
mCellNameIndex[cellPtr->mEditorId] = cellPtr; mCellNameIndex[cellPtr->mEditorId] = cellPtr;
if (cellPtr->isExterior())
mExteriors[cellPtr->mParent][std::make_pair(cellPtr->getGridX(), cellPtr->getGridY())] = cellPtr;
return cellPtr; return cellPtr;
} }
} }
@ -1177,3 +1203,4 @@ template class MWWorld::TypedDynamicStore<ESM4::Light>;
template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>; template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>;
template class MWWorld::TypedDynamicStore<ESM4::Cell>; template class MWWorld::TypedDynamicStore<ESM4::Cell>;
template class MWWorld::TypedDynamicStore<ESM4::Weapon>; template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
template class MWWorld::TypedDynamicStore<ESM4::World>;

View file

@ -282,9 +282,12 @@ namespace MWWorld
{ {
std::unordered_map<std::string, ESM4::Cell*, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> std::unordered_map<std::string, ESM4::Cell*, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>
mCellNameIndex; mCellNameIndex;
std::unordered_map<ESM::RefId, std::map<std::pair<int, int>, ESM4::Cell*>> mExteriors;
public: public:
const ESM4::Cell* searchCellName(std::string_view) const; const ESM4::Cell* searchCellName(std::string_view) const;
const ESM4::Cell* searchExterior(int x, int y, ESM::RefId worldSpace) const;
bool exteriorExists(ESM::RefId worldspace) const;
ESM4::Cell* insert(const ESM4::Cell& item, bool overrideOnly = false); ESM4::Cell* insert(const ESM4::Cell& item, bool overrideOnly = false);
ESM4::Cell* insertStatic(const ESM4::Cell& item); ESM4::Cell* insertStatic(const ESM4::Cell& item);
}; };

View file

@ -1266,7 +1266,9 @@ namespace MWWorld
const osg::Vec2i index = positionToCellIndex(position.x(), position.y()); const osg::Vec2i index = positionToCellIndex(position.x(), position.y());
CellStore* cell = ptr.getCell(); CellStore* cell = ptr.getCell();
CellStore& newCell = mWorldModel.getExterior(index.x(), index.y()); ESM::RefId worldspaceId
= cell->isExterior() ? cell->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
CellStore& newCell = mWorldModel.getExterior(index.x(), index.y(), worldspaceId);
bool isCellActive bool isCellActive
= getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(newCell); = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(newCell);
@ -2069,7 +2071,7 @@ namespace MWWorld
if (cell->isExterior()) if (cell->isExterior())
{ {
const osg::Vec2i index = positionToCellIndex(pos.pos[0], pos.pos[1]); const osg::Vec2i index = positionToCellIndex(pos.pos[0], pos.pos[1]);
cell = &mWorldModel.getExterior(index.x(), index.y()); cell = &mWorldModel.getExterior(index.x(), index.y(), cell->getCell()->getWorldSpace());
} }
MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count); MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count);
@ -2753,7 +2755,7 @@ namespace MWWorld
if (xResult.ec == std::errc::result_out_of_range || yResult.ec == std::errc::result_out_of_range) if (xResult.ec == std::errc::result_out_of_range || yResult.ec == std::errc::result_out_of_range)
throw std::runtime_error("Cell coordinates out of range."); throw std::runtime_error("Cell coordinates out of range.");
else if (xResult.ec == std::errc{} && yResult.ec == std::errc{}) else if (xResult.ec == std::errc{} && yResult.ec == std::errc{})
ext = mWorldModel.getExterior(x, y).getCell(); ext = mWorldModel.getExterior(x, y, ESM::Cell::sDefaultWorldspaceId).getCell();
// ignore std::errc::invalid_argument, as this means that name probably refers to a interior cell // ignore std::errc::invalid_argument, as this means that name probably refers to a interior cell
// instead of comma separated coordinates // instead of comma separated coordinates
} }

View file

@ -76,11 +76,12 @@ MWWorld::CellStore& MWWorld::WorldModel::getCellStore(const ESM::Cell* cell)
} }
else else
{ {
auto& Esm3Exteriors = mExteriors[ESM::Cell::sDefaultWorldspaceId];
std::map<std::pair<int, int>, CellStore*>::iterator result std::map<std::pair<int, int>, CellStore*>::iterator result
= mExteriors.find(std::make_pair(cell->getGridX(), cell->getGridY())); = Esm3Exteriors.find(std::make_pair(cell->getGridX(), cell->getGridY()));
if (result == mExteriors.end()) if (result == Esm3Exteriors.end())
result = mExteriors.emplace(std::make_pair(cell->getGridX(), cell->getGridY()), cellStore).first; result = Esm3Exteriors.emplace(std::make_pair(cell->getGridX(), cell->getGridY()), cellStore).first;
return *result->second; return *result->second;
} }
@ -160,31 +161,69 @@ MWWorld::WorldModel::WorldModel(const MWWorld::ESMStore& store, ESM::ReadersCach
{ {
} }
MWWorld::CellStore& MWWorld::WorldModel::getExterior(int x, int y) MWWorld::CellStore& MWWorld::WorldModel::getExterior(int x, int y, ESM::RefId exteriorWorldspace)
{ {
std::map<std::pair<int, int>, CellStore*>::iterator result = mExteriors.find(std::make_pair(x, y)); auto foundWorldspace = mExteriors.find(exteriorWorldspace);
std::map<std::pair<int, int>, CellStore*>::iterator result;
if (result == mExteriors.end()) if (exteriorWorldspace == ESM::Cell::sDefaultWorldspaceId)
{ {
const ESM::Cell* cell = mStore.get<ESM::Cell>().search(x, y); auto& esm3Exteriors = mExteriors[exteriorWorldspace];
result = esm3Exteriors.find(std::make_pair(x, y));
if (!cell) if (result == esm3Exteriors.end())
{ {
// Cell isn't predefined. Make one on the fly. const ESM::Cell* cell = mStore.get<ESM::Cell>().search(x, y);
ESM::Cell record;
record.mData.mFlags = ESM::Cell::HasWater;
record.mData.mX = x;
record.mData.mY = y;
record.mWater = 0;
record.mMapColor = 0;
record.updateId();
cell = MWBase::Environment::get().getESMStore()->insert(record); if (!cell)
{
// Cell isn't predefined. Make one on the fly.
ESM::Cell record;
record.mData.mFlags = ESM::Cell::HasWater;
record.mData.mX = x;
record.mData.mY = y;
record.mWater = 0;
record.mMapColor = 0;
record.updateId();
cell = MWBase::Environment::get().getESMStore()->insert(record);
}
CellStore* cellStore
= &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
result = esm3Exteriors.emplace(std::make_pair(x, y), cellStore).first;
} }
}
else
{
const Store<ESM4::Cell>& cell4Store = mStore.get<ESM4::Cell>();
if (cell4Store.exteriorExists(exteriorWorldspace))
{
auto& exteriors = mExteriors[exteriorWorldspace];
CellStore* cellStore result = exteriors.find(std::make_pair(x, y));
= &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
result = mExteriors.emplace(std::make_pair(x, y), cellStore).first; if (result == exteriors.end())
{
const ESM4::Cell* cell = cell4Store.searchExterior(x, y, exteriorWorldspace);
if (!cell)
{
ESM4::Cell record;
record.mId = MWBase::Environment::get().getESMStore()->generateId();
record.mParent = exteriorWorldspace;
record.mX = x;
record.mY = y;
record.mCellFlags = !ESM4::CELL_Interior;
cell = MWBase::Environment::get().getESMStore()->insert(record);
}
CellStore* cellStore
= &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
result = exteriors.emplace(std::make_pair(x, y), cellStore).first;
}
}
else
{
throw std::runtime_error("exterior not found: '" + exteriorWorldspace.toDebugString() + "'");
}
} }
if (result->second->getState() != CellStore::State_Loaded) if (result->second->getState() != CellStore::State_Loaded)
@ -232,7 +271,7 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(const ESM::RefId& id)
return result->second; return result->second;
if (const auto* exteriorId = id.getIf<ESM::ESM3ExteriorCellRefId>()) if (const auto* exteriorId = id.getIf<ESM::ESM3ExteriorCellRefId>())
return getExterior(exteriorId->getX(), exteriorId->getY()); return getExterior(exteriorId->getX(), exteriorId->getY(), ESM::Cell::sDefaultWorldspaceId);
const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().search(id); const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().search(id);
CellStore* newCellStore = nullptr; CellStore* newCellStore = nullptr;
@ -249,7 +288,7 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(const ESM::RefId& id)
{ {
std::pair<int, int> coord std::pair<int, int> coord
= std::make_pair(newCellStore->getCell()->getGridX(), newCellStore->getCell()->getGridY()); = std::make_pair(newCellStore->getCell()->getGridX(), newCellStore->getCell()->getGridY());
mExteriors.emplace(coord, newCellStore); mExteriors[newCellStore->getCell()->getWorldSpace()].emplace(coord, newCellStore);
} }
else else
{ {
@ -293,7 +332,7 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(std::string_view name)
if (!cell) if (!cell)
throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name)); throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name));
return getExterior(cell->getGridX(), cell->getGridY()); return getExterior(cell->getGridX(), cell->getGridY(), ESM::Cell::sDefaultWorldspaceId);
} }
MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition( MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition(
@ -302,7 +341,9 @@ MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition(
if (cellInSameWorldSpace && !cellInSameWorldSpace->isExterior()) if (cellInSameWorldSpace && !cellInSameWorldSpace->isExterior())
return *cellInSameWorldSpace; return *cellInSameWorldSpace;
const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y()); const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
return getExterior(cellIndex.x(), cellIndex.y()); ESM::RefId exteriorWorldspace
= cellInSameWorldSpace ? cellInSameWorldSpace->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
return getExterior(cellIndex.x(), cellIndex.y(), exteriorWorldspace);
} }
MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell) MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell)
@ -342,12 +383,14 @@ MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name)
// Then check cells that are already listed // Then check cells that are already listed
// Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game. // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game.
// there is one at -22,16 and one at -2,-9, the latter should be used. // there is one at -22,16 and one at -2,-9, the latter should be used.
for (std::map<std::pair<int, int>, CellStore*>::reverse_iterator iter = mExteriors.rbegin(); for (auto iterExt = mExteriors.rbegin(); iterExt != mExteriors.rend(); ++iterExt)
iter != mExteriors.rend(); ++iter)
{ {
Ptr ptr = getPtrAndCache(name, *iter->second); for (auto iter = iterExt->second.rbegin(); iter != iterExt->second.rend(); iter++)
if (!ptr.isEmpty()) {
return ptr; Ptr ptr = getPtrAndCache(name, *iter->second);
if (!ptr.isEmpty())
return ptr;
}
} }
for (auto iter = mInteriors.begin(); iter != mInteriors.end(); ++iter) for (auto iter = mInteriors.begin(); iter != mInteriors.end(); ++iter)

View file

@ -42,7 +42,7 @@ namespace MWWorld
ESM::ReadersCache& mReaders; ESM::ReadersCache& mReaders;
mutable std::unordered_map<ESM::RefId, CellStore> mCells; mutable std::unordered_map<ESM::RefId, CellStore> mCells;
mutable std::map<std::string, CellStore*, Misc::StringUtils::CiComp> mInteriors; mutable std::map<std::string, CellStore*, Misc::StringUtils::CiComp> mInteriors;
mutable std::map<std::pair<int, int>, CellStore*> mExteriors; mutable std::map<ESM::RefId, std::map<std::pair<int, int>, CellStore*>> mExteriors;
IdCache mIdCache; IdCache mIdCache;
std::size_t mIdCacheIndex = 0; std::size_t mIdCacheIndex = 0;
std::unordered_map<ESM::RefNum, Ptr> mPtrIndex; std::unordered_map<ESM::RefNum, Ptr> mPtrIndex;
@ -63,7 +63,7 @@ namespace MWWorld
void clear(); void clear();
CellStore& getExterior(int x, int y); CellStore& getExterior(int x, int y, ESM::RefId exteriorWorldSpace);
CellStore& getInterior(std::string_view name); CellStore& getInterior(std::string_view name);
CellStore& getCell(std::string_view name); // interior or named exterior CellStore& getCell(std::string_view name); // interior or named exterior
CellStore& getCell(const ESM::RefId& Id); CellStore& getCell(const ESM::RefId& Id);

View file

@ -40,6 +40,7 @@ namespace ESM
namespace ESM namespace ESM
{ {
const std::string Cell::sDefaultWorldspace = "sys::default"; const std::string Cell::sDefaultWorldspace = "sys::default";
const RefId Cell::sDefaultWorldspaceId = ESM::RefId::stringRefId(Cell::sDefaultWorldspace);
// Some overloaded compare operators. // Some overloaded compare operators.
bool operator==(const MovedCellRef& ref, const RefNum& refNum) bool operator==(const MovedCellRef& ref, const RefNum& refNum)

View file

@ -67,6 +67,7 @@ namespace ESM
struct Cell struct Cell
{ {
static const std::string sDefaultWorldspace; static const std::string sDefaultWorldspace;
static const ESM::RefId sDefaultWorldspaceId;
constexpr static RecNameInts sRecordId = REC_CELL; constexpr static RecNameInts sRecordId = REC_CELL;

View file

@ -34,8 +34,8 @@
void ESM4::World::load(ESM4::Reader& reader) void ESM4::World::load(ESM4::Reader& reader)
{ {
mFormId = reader.hdr().record.getFormId(); FormId formid = reader.hdr().record.getFormId();
reader.adjustFormId(mFormId); reader.adjustFormId(formid);
mFlags = reader.hdr().record.flags; mFlags = reader.hdr().record.flags;
// It should be possible to save the current world formId automatically while reading in // It should be possible to save the current world formId automatically while reading in
@ -45,7 +45,8 @@ void ESM4::World::load(ESM4::Reader& reader)
// Alternatively it may be possible to figure it out by examining the group headers, but // Alternatively it may be possible to figure it out by examining the group headers, but
// apparently the label field is not reliable so the parent world formid may have been // apparently the label field is not reliable so the parent world formid may have been
// corrupted by the use of ignore flag (TODO: should check to verify). // corrupted by the use of ignore flag (TODO: should check to verify).
reader.setCurrWorld(mFormId); // save for CELL later reader.setCurrWorld(formid); // save for CELL later
mId = ESM::FormIdRefId(formid);
std::uint32_t subSize = 0; // for XXXX sub record std::uint32_t subSize = 0; // for XXXX sub record

View file

@ -34,6 +34,9 @@
#include "formid.hpp" #include "formid.hpp"
#include "grid.hpp" #include "grid.hpp"
#include <components/esm/defs.hpp>
#include <components/esm/refid.hpp>
namespace ESM4 namespace ESM4
{ {
class Reader; class Reader;
@ -81,7 +84,7 @@ namespace ESM4
float initialPitch; float initialPitch;
}; };
FormId mFormId; // from the header ESM::RefId mId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId; std::string mEditorId;
@ -129,6 +132,7 @@ namespace ESM4
void load(ESM4::Reader& reader); void load(ESM4::Reader& reader);
// void save(ESM4::Writer& writer) const; // void save(ESM4::Writer& writer) const;
static constexpr ESM::RecNameInts sRecordId = ESM::REC_WRLD4;
}; };
} }