diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index 5cd72f716c..23349ce949 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -20,8 +20,6 @@ namespace MWRender osg::ref_ptr LandManager::getLand(ESM::ExteriorCellLocation cellIndex) { - if (ESM::isEsm4Ext(cellIndex.mWorldspace)) - return osg::ref_ptr(nullptr); osg::ref_ptr obj = mCache->getRefFromObjectCache(cellIndex); if (obj) return static_cast(obj.get()); @@ -30,12 +28,25 @@ namespace MWRender const auto world = MWBase::Environment::get().getWorld(); if (!world) return nullptr; - const ESM::Land* land = world->getStore().get().search(cellIndex.mX, cellIndex.mY); - if (!land) - return nullptr; - osg::ref_ptr landObj(new ESMTerrain::LandObject(land, mLoadFlags)); - mCache->addEntryToObjectCache(cellIndex, landObj.get()); - return landObj; + + if (ESM::isEsm4Ext(cellIndex.mWorldspace)) + { + const ESM4::Land* land = world->getStore().get().search(cellIndex); + if (!land) + return nullptr; + osg::ref_ptr landObj(new ESMTerrain::LandObject(land, mLoadFlags)); + mCache->addEntryToObjectCache(cellIndex, landObj.get()); + return landObj; + } + else + { + const ESM::Land* land = world->getStore().get().search(cellIndex.mX, cellIndex.mY); + if (!land) + return nullptr; + osg::ref_ptr landObj(new ESMTerrain::LandObject(land, mLoadFlags)); + mCache->addEntryToObjectCache(cellIndex, landObj.get()); + return landObj; + } } } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index c624c488b5..1aae2011e5 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -385,6 +386,7 @@ namespace MWWorld { auto visitorRec = [this](ESM4::Reader& reader) { return ESMStoreImp::readRecord(reader, *this); }; ESM4::ReaderUtils::readAll(reader, visitorRec, [](ESM4::Reader&) {}); + getWritable().updateLandPositions(get()); } void ESMStore::setIdType(const ESM::RefId& id, ESM::RecNameInts type) diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 792ba6f46a..1818a3bc02 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -43,6 +43,7 @@ namespace ESM4 struct MiscItem; struct Weapon; struct World; + struct Land; } namespace ESM @@ -121,7 +122,7 @@ namespace MWWorld Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, - Store, Store>; + Store, Store, Store>; private: template diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 56a8dcb39a..2915a3fe66 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -8,11 +8,14 @@ #include #include #include +#include #include #include #include -#include +#include "../mwbase/environment.hpp" +#include "../mwworld/cell.hpp" +#include "../mwworld/worldimp.hpp" namespace { @@ -1189,6 +1192,29 @@ namespace MWWorld MWWorld::TypedDynamicStore::clearDynamic(); } + void Store::updateLandPositions(const Store& cells) + { + for (auto it : mStatic) + { + const ESM4::Cell* cell = cells.find(it.second.mCell); + mLands[cell->getExteriorCellLocation()] = &it.second; + } + for (auto it : mDynamic) + { + const ESM4::Cell* cell = cells.find(it.second.mCell); + mLands[cell->getExteriorCellLocation()] = &it.second; + } + } + + const ESM4::Land* MWWorld::Store::search(ESM::ExteriorCellLocation cellLocation) const + { + auto foundLand = mLands.find(cellLocation); + if (foundLand == mLands.end()) + return nullptr; + else + return foundLand->second; + } + // ESM4 Reference //========================================================================= @@ -1276,3 +1302,4 @@ template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; +template class MWWorld::TypedDynamicStore; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 35e4de63eb..e2581f8bc7 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -35,6 +35,11 @@ namespace ESM class ESMWriter; } +namespace ESM4 +{ + struct Land; +} + namespace Loading { class Listener; @@ -298,6 +303,17 @@ namespace MWWorld void clearDynamic() override; }; + template <> + class Store : public TypedDynamicStore + { + std::unordered_map mLands; + + public: + void updateLandPositions(const Store& cells); + + const ESM4::Land* search(ESM::ExteriorCellLocation cellLocation) const; + }; + template <> class Store : public DynamicStore { diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 6a038af9dc..432dea172c 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/components/esm3terrain/storage.cpp b/components/esm3terrain/storage.cpp index 5a1324dfe4..566c97a990 100644 --- a/components/esm3terrain/storage.cpp +++ b/components/esm3terrain/storage.cpp @@ -37,11 +37,11 @@ namespace ESMTerrain LandObject::LandObject(const ESM::Land* land, int loadFlags) : mLand(land) , mLoadFlags(loadFlags) - , mData() + , mData(nullptr) { - auto esm3LandData = new ESM::Land::LandData; - mData.reset(esm3LandData); - mLand->loadData(mLoadFlags, esm3LandData); + std::unique_ptr esm3LandData = std::make_unique(); + mLand->loadData(mLoadFlags, esm3LandData.get()); + mData = std::move(esm3LandData); } LandObject::LandObject(const LandObject& copy, const osg::CopyOp& copyop) @@ -478,15 +478,16 @@ namespace ESMTerrain const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT); if (!data) return defaultHeight; + const int landSize = data->getLandSize(); // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / cellSize; - float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / cellSize; + float nX = (worldPos.x() - (cellX * cellSize)) / cellSize; + float nY = (worldPos.y() - (cellY * cellSize)) / cellSize; // get left / bottom points (rounded down) - float factor = ESM::Land::LAND_SIZE - 1.0f; + float factor = landSize - 1.0f; float invFactor = 1.0f / factor; int startX = static_cast(nX * factor); @@ -494,8 +495,8 @@ namespace ESMTerrain int endX = startX + 1; int endY = startY + 1; - endX = std::min(endX, ESM::Land::LAND_SIZE - 1); - endY = std::min(endY, ESM::Land::LAND_SIZE - 1); + endX = std::min(endX, landSize - 1); + endY = std::min(endY, landSize - 1); // now get points in terrain space (effectively rounding them to boundaries) float startXTS = startX * invFactor; diff --git a/components/esm3terrain/storage.hpp b/components/esm3terrain/storage.hpp index a81465a56d..a35e9dd7d7 100644 --- a/components/esm3terrain/storage.hpp +++ b/components/esm3terrain/storage.hpp @@ -41,6 +41,8 @@ namespace ESMTerrain inline const ESM::LandData* getData(int flags) const { + if (!mData) + return nullptr; ESM::Land::LandData* esm3Land = dynamic_cast(mData.get()); if (esm3Land && ((esm3Land->mDataLoaded & flags) != flags)) return nullptr; diff --git a/components/esm4/loadcell.hpp b/components/esm4/loadcell.hpp index d8001643e6..c8980705e5 100644 --- a/components/esm4/loadcell.hpp +++ b/components/esm4/loadcell.hpp @@ -36,6 +36,7 @@ #include #include +#include #include namespace ESM4 @@ -107,7 +108,10 @@ namespace ESM4 int getGridX() const { return mX; } int getGridY() const { return mY; } bool isExterior() const { return !(mCellFlags & CELL_Interior); } - + ESM::ExteriorCellLocation getExteriorCellLocation() const + { + return ESM::ExteriorCellLocation(mX, mY, isExterior() ? mParent : mId); + } static float sInvalidWaterLevel; }; } diff --git a/components/esm4/loadland.cpp b/components/esm4/loadland.cpp index b2a9ece20c..29ccb2ff3b 100644 --- a/components/esm4/loadland.cpp +++ b/components/esm4/loadland.cpp @@ -54,11 +54,12 @@ // void ESM4::Land::load(ESM4::Reader& reader) { - mFormId = reader.hdr().record.getFormId(); - reader.adjustFormId(mFormId); + ESM::FormId formId = reader.hdr().record.getFormId(); + reader.adjustFormId(formId); + mId = ESM::RefId::formIdRefId(formId); mFlags = reader.hdr().record.flags; mDataTypes = 0; - + mCell = ESM::RefId::formIdRefId(reader.currCell()); TxtLayer layer; std::int8_t currentAddQuad = -1; // for VTXT following ATXT @@ -252,6 +253,29 @@ void ESM4::Land::load(ESM4::Reader& reader) } } +ESM4::Land::Land(const Land& Other) +{ + mId = Other.mId; + mCell = Other.mCell; + for (int i = 0; i < VERTS_PER_SIDE * VERTS_PER_SIDE; i++) + { + mHeights[i] = Other.mHeights[i]; + } + for (int i = 0; i < VERTS_PER_SIDE * VERTS_PER_SIDE * 3; i++) + { + mVertNorm[i] = Other.mVertNorm[i]; + mVertColr[i] = Other.mVertColr[i]; + } + mMinHeight = Other.mMinHeight; + mMaxHeight = Other.mMaxHeight; +} + +std::span ESM4::Land::getTextures() const +{ + static uint16_t textureArray[16 * 16] = {}; + return textureArray; +} + // void ESM4::Land::save(ESM4::Writer& writer) const //{ // } diff --git a/components/esm4/loadland.hpp b/components/esm4/loadland.hpp index e34d5a0f5b..06245a5c4f 100644 --- a/components/esm4/loadland.hpp +++ b/components/esm4/loadland.hpp @@ -33,7 +33,9 @@ #include "formid.hpp" +#include #include +#include namespace ESM4 { @@ -111,7 +113,7 @@ namespace ESM4 std::vector layers; }; - 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 mLandFlags; // from DATA subrecord @@ -126,16 +128,19 @@ namespace ESM4 VHGT mHeightMap; Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right std::vector mIds; // land texture (LTEX) formids - + ESM::RefId mCell; void load(Reader& reader); + Land() = default; + Land(const Land& Other); // void save(Writer& writer) const; // void blank(); + static constexpr ESM::RecNameInts sRecordId = ESM::REC_LAND4; std::span getHeights() const override { return mHeights; } std::span getNormals() const override { return mVertNorm; } std::span getColors() const override { return mVertColr; } - std::span getTextures() const override { return {}; } + std::span getTextures() const override; float getSize() const override { return REAL_SIZE; } float getMinHeight() const override { return mMinHeight; } float getMaxHeight() const { return mMaxHeight; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 974a6ebb4c..6057c53689 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -262,7 +262,7 @@ namespace Terrain auto chunkBorder = CellBorder::createBorderGeometry(center.x() - size / 2.f, center.y() - size / 2.f, size, mStorage, mSceneManager, mNodeMask, mWorldspace, 5.f, { 1, 0, 0, 0 }); osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; - pat->setPosition(-center * Constants::CellSizeInUnits); + pat->setPosition(-center * ESM::getCellSize(mWorldspace)); pat->addChild(chunkBorder); return pat; }