diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 22dc62323..df2631c13 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -814,7 +814,6 @@ void Record::print() { std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl; std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; - std::cout << " HasData: " << mData.mHasData << std::endl; std::cout << " DataTypes: " << mData.mDataTypes << std::endl; // Seems like this should done with reference counting in the diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 49e2970e4..f0f6158ed 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -76,7 +76,7 @@ namespace MWRender if (land) { - int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM; + int mask = ESM::Land::DATA_WNAM; if (!land->isDataLoaded(mask)) land->loadData(mask); } @@ -133,6 +133,8 @@ namespace MWRender } } loadingListener->increaseProgress(); + if (land) + land->unloadData(); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8e8b18cd1..149080d93 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1036,10 +1036,10 @@ void RenderingManager::enableTerrain(bool enable) if (!mTerrain) { if (Settings::Manager::getBool("distant land", "Terrain")) - mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(true), RV_Terrain, Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); else - mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(false), RV_Terrain, Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY); mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index cbd9e2444..e74b6af12 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -9,6 +9,22 @@ namespace MWRender { + TerrainStorage::TerrainStorage(bool preload) + { + if (preload) + { + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + MWWorld::Store::iterator it = esmStore.get().begin(); + for (; it != esmStore.get().end(); ++it) + { + ESM::Land* land = const_cast(&*it); // TODO: fix store interface + land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX); + } + } + } + void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { minX = 0, minY = 0, maxX = 0, maxY = 0; @@ -39,6 +55,12 @@ namespace MWRender const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); ESM::Land* land = esmStore.get().search(cellX, cellY); + if (!land) + return NULL; + + const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(flags)) + land->loadData(flags); return land; } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 30a2a61ac..e6f4a04ad 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -14,6 +14,10 @@ namespace MWRender virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: + ///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this + /// should be set to "true" in order to avoid race conditions. + TerrainStorage(bool preload); + /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 286721df4..b6ad5c104 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -231,6 +231,11 @@ namespace MWWorld cell->getCell()->getGridY() ); if (land) { + // Actually only VHGT is needed here, but we'll need the rest for rendering anyway. + // Load everything now to reduce IO overhead. + const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(flags)) + land->loadData(flags); mPhysics->addHeightField ( land->mLandData->mHeights, cell->getCell()->getGridX(), diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 91b062596..ae73eee52 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -72,7 +72,6 @@ Land::Land() , mDataLoaded(false) , mLandData(NULL) , mPlugin(0) - , mHasData(false) { } @@ -97,8 +96,6 @@ void Land::load(ESMReader &esm) // Store the file position mContext = esm.getContext(); - mHasData = false; - // Skip these here. Load the actual data when the cell is loaded. if (esm.isNextSub("VNML")) { @@ -126,10 +123,6 @@ void Land::load(ESMReader &esm) mDataTypes |= DATA_VTEX; } - // We need all three of VNML, VHGT and VTEX in order to use the - // landscape. (Though Morrowind seems to accept terrain without VTEX/VCLR entries) - mHasData = mDataTypes & (DATA_VNML|DATA_VHGT|DATA_WNAM); - mDataLoaded = 0; mLandData = NULL; } @@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const esm.writeHNT("DATA", mFlags); } -/// \todo remove memory allocation when only defaults needed void Land::loadData(int flags) { // Try to load only available data - int actual = flags & mDataTypes; + flags = flags & mDataTypes; // Return if all required data is loaded - if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) { + if ((mDataLoaded & flags) == flags) { return; } // Create storage if nothing is loaded @@ -160,15 +152,13 @@ void Land::loadData(int flags) } mEsm->restoreContext(mContext); - memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals)); - if (mEsm->isNextSub("VNML")) { - condLoad(actual, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); + condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); } if (mEsm->isNextSub("VHGT")) { static VHGT vhgt; - if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(vhgt))) { + if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; @@ -184,30 +174,18 @@ void Land::loadData(int flags) mLandData->mUnk1 = vhgt.mUnk1; mLandData->mUnk2 = vhgt.mUnk2; } - } else if ((flags & DATA_VHGT) && (mDataLoaded & DATA_VHGT) == 0) { - for (int i = 0; i < LAND_NUM_VERTS; ++i) { - mLandData->mHeights[i] = -256.0f * HEIGHT_SCALE; - } - mDataLoaded |= DATA_VHGT; } if (mEsm->isNextSub("WNAM")) { - condLoad(actual, DATA_WNAM, mLandData->mWnam, 81); - } - if (mEsm->isNextSub("VCLR")) { - mLandData->mUsingColours = true; - condLoad(actual, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); - } else { - mLandData->mUsingColours = false; + condLoad(flags, DATA_WNAM, mLandData->mWnam, 81); } + if (mEsm->isNextSub("VCLR")) + condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); if (mEsm->isNextSub("VTEX")) { static uint16_t vtex[LAND_NUM_TEXTURES]; - if (condLoad(actual, DATA_VTEX, vtex, sizeof(vtex))) { + if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } - } else if ((flags & DATA_VTEX) && (mDataLoaded & DATA_VTEX) == 0) { - memset(mLandData->mTextures, 0, sizeof(mLandData->mTextures)); - mDataLoaded |= DATA_VTEX; } } @@ -232,4 +210,9 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) return false; } +bool Land::isDataLoaded(int flags) const +{ + return (mDataLoaded & flags) == (flags & mDataTypes); +} + } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 319e8715a..e510616af 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -32,7 +32,6 @@ struct Land ESMReader* mEsm; ESM_Context mContext; - bool mHasData; int mDataTypes; int mDataLoaded; @@ -81,7 +80,6 @@ struct Land VNML mNormals[LAND_NUM_VERTS * 3]; uint16_t mTextures[LAND_NUM_TEXTURES]; - bool mUsingColours; char mColours[3 * LAND_NUM_VERTS]; int mDataTypes; @@ -113,10 +111,8 @@ struct Land void unloadData(); /// Check if given data type is loaded - /// \todo reimplement this - bool isDataLoaded(int flags) { - return (mDataLoaded & flags) == flags; - } + /// @note We only check data types that *can* be loaded (present in mDataTypes) + bool isDataLoaded(int flags) const; private: Land(const Land& land); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index a66193f97..d4a0a0df2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -31,7 +31,7 @@ namespace ESMTerrain int cellY = origin.y; const ESM::Land* land = getLand(cellX, cellY); - if (!land) + if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) return false; min = std::numeric_limits::max(); @@ -73,7 +73,7 @@ namespace ESMTerrain row += ESM::Land::LAND_SIZE-1; } ESM::Land* land = getLand(cellX, cellY); - if (land && land->mHasData) + if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -108,7 +108,7 @@ namespace ESMTerrain row = 0; } ESM::Land* land = getLand(cellX, cellY); - if (land && land->mLandData->mUsingColours) + if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -157,9 +157,8 @@ namespace ESMTerrain for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) { ESM::Land* land = getLand(cellX, cellY); - if (land && !land->mHasData) + if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT)) land = NULL; - bool hasColors = land && land->mLandData->mUsingColours; int rowStart = 0; int colStart = 0; @@ -183,7 +182,7 @@ namespace ESMTerrain else positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; - if (land) + if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -207,7 +206,7 @@ namespace ESMTerrain normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; - if (hasColors) + if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -263,7 +262,7 @@ namespace ESMTerrain assert(ymDataTypes&ESM::Land::DATA_VTEX)) { int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0)