diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index da9c00e33b..3c6c68e620 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -28,12 +28,12 @@ namespace CSVRender resetHeights(); } - osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) + osg::ref_ptr TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation) { // The cell isn't guaranteed to have Land. This is because the terrain implementation // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell - const int index - = mData.getLand().searchId(ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellX, cellY))); + const int index = mData.getLand().searchId( + ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellLocation.mX, cellLocation.mY))); if (index == -1) return nullptr; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 17eb1af9a8..a66c82cb9b 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -37,7 +37,7 @@ namespace CSVRender const CSMWorld::Data& mData; std::array mAlteredHeight; - osg::ref_ptr getLand(int cellX, int cellY) override; + osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) override; const ESM::LandTexture* getLandTexture(int index, short plugin) override; void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 037187bf49..fb8f30294b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -457,35 +457,9 @@ namespace MWRender mTerrainStorage = std::make_unique(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); - const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain"); + mTerrain = getWorldspaceTerrain(ESM::Cell::sDefaultWorldspaceId); bool groundcover = Settings::Manager::getBool("enabled", "Groundcover"); - bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); - if (distantTerrain || groundcover) - { - const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); - int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); - compMapPower = std::max(-3, compMapPower); - float compMapLevel = pow(2, compMapPower); - const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain"); - float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain"); - maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f); - bool debugChunks = Settings::Manager::getBool("debug chunks", "Terrain"); - mTerrain = std::make_unique(sceneRoot, mRootNode, mResourceSystem, - mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel, - lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, ESM::Cell::sDefaultWorldspaceId); - if (Settings::Manager::getBool("object paging", "Terrain")) - { - mObjectPaging = std::make_unique(mResourceSystem->getSceneManager()); - static_cast(mTerrain.get())->addChunkManager(mObjectPaging.get()); - mResourceSystem->addResourceManager(mObjectPaging.get()); - } - } - else - mTerrain = std::make_unique(sceneRoot, mRootNode, mResourceSystem, - mTerrainStorage.get(), Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, Mask_PreCompile, Mask_Debug); - - mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate); if (groundcover) { @@ -494,7 +468,7 @@ namespace MWRender mGroundcover = std::make_unique( mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore); - static_cast(mTerrain.get())->addChunkManager(mGroundcover.get()); + static_cast(mTerrain)->addChunkManager(mGroundcover.get()); mResourceSystem->addResourceManager(mGroundcover.get()); } @@ -633,7 +607,7 @@ namespace MWRender Terrain::World* RenderingManager::getTerrain() { - return mTerrain.get(); + return mTerrain; } void RenderingManager::preloadCommonAssets() @@ -1343,6 +1317,45 @@ namespace MWRender mStateUpdater->setFogColor(color); } + Terrain::World* RenderingManager::getWorldspaceTerrain(ESM::RefId worldspace) + { + auto existingTerrain = mWorldspaceTerrains.find(worldspace); + if (existingTerrain != mWorldspaceTerrains.end()) + return existingTerrain->second.get(); + std::unique_ptr newTerrain; + + const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain"); + bool groundcover = Settings::Manager::getBool("enabled", "Groundcover"); + bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + if (distantTerrain || groundcover) + { + const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); + int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); + compMapPower = std::max(-3, compMapPower); + float compMapLevel = pow(2, compMapPower); + const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain"); + float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain"); + maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f); + bool debugChunks = Settings::Manager::getBool("debug chunks", "Terrain"); + newTerrain = std::make_unique(mSceneRoot, mRootNode, mResourceSystem, + mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel, + lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, worldspace); + if (Settings::Manager::getBool("object paging", "Terrain")) + { + mObjectPaging = std::make_unique(mResourceSystem->getSceneManager()); + static_cast(newTerrain.get())->addChunkManager(mObjectPaging.get()); + mResourceSystem->addResourceManager(mObjectPaging.get()); + } + } + else + newTerrain = std::make_unique(mSceneRoot, mRootNode, mResourceSystem, + mTerrainStorage.get(), Mask_Terrain, worldspace, Mask_PreCompile, Mask_Debug); + + newTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); + mWorldspaceTerrains[worldspace] = std::move(newTerrain); + return mWorldspaceTerrains[worldspace].get(); + } + void RenderingManager::reportStats() const { osg::Stats* stats = mViewer->getViewerStats(); @@ -1446,7 +1459,7 @@ namespace MWRender float RenderingManager::getTerrainHeightAt(const osg::Vec3f& pos, ESM::RefId worldspace) { - return mTerrain->getHeightAt(pos); + return getWorldspaceTerrain(worldspace)->getHeightAt(pos); } void RenderingManager::overrideFieldOfView(float val) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 94e5c548e4..8aed274468 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -281,6 +281,7 @@ namespace MWRender void updateAmbient(); void setFogColor(const osg::Vec4f& color); void updateThirdPersonViewMode(); + Terrain::World* getWorldspaceTerrain(ESM::RefId worldspace); void reportStats() const; @@ -312,7 +313,8 @@ namespace MWRender std::unique_ptr mPathgrid; std::unique_ptr mObjects; std::unique_ptr mWater; - std::unique_ptr mTerrain; + std::unordered_map> mWorldspaceTerrains; + Terrain::World* mTerrain; std::unique_ptr mTerrainStorage; std::unique_ptr mObjectPaging; std::unique_ptr mGroundcover; diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index ebbe3fa18b..66a3e1c545 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -26,11 +26,11 @@ namespace MWRender mResourceSystem->removeResourceManager(mLandManager.get()); } - bool TerrainStorage::hasData(int cellX, int cellY) + bool TerrainStorage::hasData(ESM::ExteriorCellLocation cellLocation) { const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); - const ESM::Land* land = esmStore.get().search(cellX, cellY); + const ESM::Land* land = esmStore.get().search(cellLocation.mX, cellLocation.mY); return land != nullptr; } @@ -66,9 +66,9 @@ namespace MWRender return mLandManager.get(); } - osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) + osg::ref_ptr TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation) { - return mLandManager->getLand(ESM::ExteriorCellLocation(cellX, cellY, ESM::Cell::sDefaultWorldspaceId)); + return mLandManager->getLand(cellLocation); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index f79b195a63..a375fd0d91 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -21,10 +21,10 @@ namespace MWRender const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); ~TerrainStorage(); - osg::ref_ptr getLand(int cellX, int cellY) override; + osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) override; const ESM::LandTexture* getLandTexture(int index, short plugin) override; - bool hasData(int cellX, int cellY) override; + bool hasData(ESM::ExteriorCellLocation cellLocation) override; /// Get bounds of the whole terrain in cell units void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; diff --git a/components/esm3terrain/storage.cpp b/components/esm3terrain/storage.cpp index a189640bc8..35f0db8caf 100644 --- a/components/esm3terrain/storage.cpp +++ b/components/esm3terrain/storage.cpp @@ -16,7 +16,7 @@ namespace ESMTerrain class LandCache { public: - typedef std::map, osg::ref_ptr> Map; + typedef std::map> Map; Map mMap; }; @@ -55,7 +55,7 @@ namespace ESMTerrain { } - bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max) + bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max) { assert(size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); @@ -70,7 +70,7 @@ namespace ESMTerrain int endRow = startRow + size * (ESM::Land::LAND_SIZE - 1) + 1; int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1; - osg::ref_ptr land = getLand(cellX, cellY); + osg::ref_ptr land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace)); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; if (data) { @@ -95,30 +95,31 @@ namespace ESMTerrain return false; } - void Storage::fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + void Storage::fixNormal( + osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache) { while (col >= ESM::Land::LAND_SIZE - 1) { - ++cellY; + ++cellLocation.mY; col -= ESM::Land::LAND_SIZE - 1; } while (row >= ESM::Land::LAND_SIZE - 1) { - ++cellX; + ++cellLocation.mX; row -= ESM::Land::LAND_SIZE - 1; } while (col < 0) { - --cellY; + --cellLocation.mY; col += ESM::Land::LAND_SIZE - 1; } while (row < 0) { - --cellX; + --cellLocation.mX; row += ESM::Land::LAND_SIZE - 1; } - const LandObject* land = getLand(cellX, cellY, cache); + const LandObject* land = getLand(cellLocation, cache); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr; if (data) { @@ -131,31 +132,33 @@ namespace ESMTerrain normal = osg::Vec3f(0, 0, 1); } - void Storage::averageNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + void Storage::averageNormal( + osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache) { osg::Vec3f n1, n2, n3, n4; - fixNormal(n1, cellX, cellY, col + 1, row, cache); - fixNormal(n2, cellX, cellY, col - 1, row, cache); - fixNormal(n3, cellX, cellY, col, row + 1, cache); - fixNormal(n4, cellX, cellY, col, row - 1, cache); + fixNormal(n1, cellLocation, col + 1, row, cache); + fixNormal(n2, cellLocation, col - 1, row, cache); + fixNormal(n3, cellLocation, col, row + 1, cache); + fixNormal(n4, cellLocation, col, row - 1, cache); normal = (n1 + n2 + n3 + n4); normal.normalize(); } - void Storage::fixColour(osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) + void Storage::fixColour( + osg::Vec4ub& color, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache) { if (col == ESM::Land::LAND_SIZE - 1) { - ++cellY; + ++cellLocation.mY; col = 0; } if (row == ESM::Land::LAND_SIZE - 1) { - ++cellX; + ++cellLocation.mX; row = 0; } - const LandObject* land = getLand(cellX, cellY, cache); + const LandObject* land = getLand(cellLocation, cache); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr; if (data) { @@ -171,7 +174,7 @@ namespace ESMTerrain } } - void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, + void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours) { @@ -205,7 +208,8 @@ namespace ESMTerrain float vertX_ = 0; // of current cell corner for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) { - const LandObject* land = getLand(cellX, cellY, cache); + ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace); + const LandObject* land = getLand(cellLocation, cache); const ESM::Land::LandData* heightData = nullptr; const ESM::Land::LandData* normalData = nullptr; const ESM::Land::LandData* colourData = nullptr; @@ -269,12 +273,12 @@ namespace ESMTerrain // Normals apparently don't connect seamlessly between cells if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1) - fixNormal(normal, cellX, cellY, col, row, cache); + fixNormal(normal, cellLocation, col, row, cache); // some corner normals appear to be complete garbage (z < 0) if ((row == 0 || row == ESM::Land::LAND_SIZE - 1) && (col == 0 || col == ESM::Land::LAND_SIZE - 1)) - averageNormal(normal, cellX, cellY, col, row, cache); + averageNormal(normal, cellLocation, col, row, cache); assert(normal.z() > 0); @@ -296,7 +300,7 @@ namespace ESMTerrain // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1) - fixColour(color, cellX, cellY, col, row, cache); + fixColour(color, cellLocation, col, row, cache); color.a() = 255; @@ -315,32 +319,33 @@ namespace ESMTerrain assert(vertY_ == numVerts); // Ensure we covered whole area } - Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache& cache) + Storage::UniqueTextureId Storage::getVtexIndexAt( + ESM::ExteriorCellLocation cellLocation, int x, int y, LandCache& cache) { // For the first/last row/column, we need to get the texture from the neighbour cell // to get consistent blending at the borders --x; if (x < 0) { - --cellX; + --cellLocation.mX; x += ESM::Land::LAND_TEXTURE_SIZE; } while (x >= ESM::Land::LAND_TEXTURE_SIZE) { - ++cellX; + ++cellLocation.mX; x -= ESM::Land::LAND_TEXTURE_SIZE; } while ( y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { - ++cellY; + ++cellLocation.mY; y -= ESM::Land::LAND_TEXTURE_SIZE; } assert(x < ESM::Land::LAND_TEXTURE_SIZE); assert(y < ESM::Land::LAND_TEXTURE_SIZE); - const LandObject* land = getLand(cellX, cellY, cache); + const LandObject* land = getLand(cellLocation, cache); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr; if (data) @@ -375,7 +380,7 @@ namespace ESMTerrain } void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, - std::vector& layerList) + std::vector& layerList, ESM::RefId worldspace) { osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f); int cellX = static_cast(std::floor(origin.x())); @@ -398,7 +403,8 @@ namespace ESMTerrain { for (int x = 0; x < blendmapSize; x++) { - UniqueTextureId id = getVtexIndexAt(cellX, cellY, x + rowStart, y + colStart, cache); + ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace); + UniqueTextureId id = getVtexIndexAt(cellLocation, x + rowStart, y + colStart, cache); std::map::iterator found = textureIndicesMap.find(id); if (found == textureIndicesMap.end()) { @@ -442,12 +448,13 @@ namespace ESMTerrain blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend } - float Storage::getHeightAt(const osg::Vec3f& worldPos) + float Storage::getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) { - int cellX = static_cast(std::floor(worldPos.x() / float(Constants::CellSizeInUnits))); - int cellY = static_cast(std::floor(worldPos.y() / float(Constants::CellSizeInUnits))); + const float cellSize = ESM::getCellSize(worldspace); + int cellX = static_cast(std::floor(worldPos.x() / cellSize)); + int cellY = static_cast(std::floor(worldPos.y() / cellSize)); - osg::ref_ptr land = getLand(cellX, cellY); + osg::ref_ptr land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace)); if (!land) return defaultHeight; @@ -458,8 +465,8 @@ namespace ESMTerrain // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); - float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); + float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / cellSize; + float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / cellSize; // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; @@ -491,10 +498,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits)); - osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits)); - osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits)); - osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / cellSize); + osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / cellSize); + osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / cellSize); + osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / cellSize); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -520,18 +527,17 @@ namespace ESMTerrain */ // Solve plane equation for z - return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() - * Constants::CellSizeInUnits; + return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * cellSize; } - const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) + const LandObject* Storage::getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache) { - LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); + LandCache::Map::iterator found = cache.mMap.find(cellLocation); if (found != cache.mMap.end()) return found->second; else { - found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; + found = cache.mMap.insert(std::make_pair(cellLocation, getLand(cellLocation))).first; return found->second; } } diff --git a/components/esm3terrain/storage.hpp b/components/esm3terrain/storage.hpp index e1bff744ae..9a7e9d3ecf 100644 --- a/components/esm3terrain/storage.hpp +++ b/components/esm3terrain/storage.hpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -56,7 +57,7 @@ namespace ESMTerrain const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); // Not implemented in this class, because we need different Store implementations for game and editor - virtual osg::ref_ptr getLand(int cellX, int cellY) = 0; + virtual osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) = 0; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; /// Get bounds of the whole terrain in cell units void getBounds(float& minX, float& maxX, float& minY, float& maxY) override = 0; @@ -69,7 +70,8 @@ namespace ESMTerrain /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk - bool getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max) override; + bool getMinMaxHeights( + float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max) override; /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! @@ -81,7 +83,7 @@ namespace ESMTerrain /// @param positions buffer to write vertices /// @param normals buffer to write vertex normals /// @param colours buffer to write vertex colours - void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, + void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours) override; @@ -94,9 +96,9 @@ namespace ESMTerrain /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, - std::vector& layerList) override; + std::vector& layerList, ESM::RefId worldspace) override; - float getHeightAt(const osg::Vec3f& worldPos) override; + float getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) override; /// Get the transformation factor for mapping cell units to world units. float getCellWorldSize() override; @@ -116,11 +118,14 @@ namespace ESMTerrain private: const VFS::Manager* mVFS; - inline void fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - inline void fixColour(osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); - inline void averageNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + inline void fixNormal( + osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache); + inline void fixColour( + osg::Vec4ub& colour, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache); + inline void averageNormal( + osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache); - inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); + inline const LandObject* getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache); virtual bool useAlteration() const { return false; } virtual void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const; @@ -131,7 +136,7 @@ namespace ESMTerrain // pair typedef std::pair UniqueTextureId; - inline UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache&); + inline UniqueTextureId getVtexIndexAt(ESM::ExteriorCellLocation cellLocation, int x, int y, LandCache&); std::string getTextureName(UniqueTextureId id); std::map mLayerInfoMap; diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index b3f5c721c9..06767531d3 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -23,7 +23,7 @@ namespace Terrain } osg::ref_ptr CellBorder::createBorderGeometry(float x, float y, float size, Terrain::Storage* terrain, - Resource::SceneManager* sceneManager, int mask, float offset, osg::Vec4f color) + Resource::SceneManager* sceneManager, int mask, ESM::RefId worldspace, float offset, osg::Vec4f color) { const int cellSize = ESM::Land::REAL_SIZE; const int borderSegments = 40; @@ -45,7 +45,7 @@ namespace Terrain : osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f); pos += cellCorner; - pos += osg::Vec3f(0, 0, terrain->getHeightAt(pos) + offset); + pos += osg::Vec3f(0, 0, terrain->getHeightAt(pos, worldspace) + offset); vertices->push_back(pos); @@ -83,7 +83,8 @@ namespace Terrain void CellBorder::createCellBorderGeometry(int x, int y) { - auto borderGroup = createBorderGeometry(x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask); + auto borderGroup = createBorderGeometry( + x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask, mWorld->getWorldspace()); mRoot->addChild(borderGroup); mCellBorderNodes[std::make_pair(x, y)] = borderGroup; diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 5e674819eb..e8b095ca82 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Resource { class SceneManager; @@ -33,7 +35,8 @@ namespace Terrain void destroyCellBorderGeometry(); static osg::ref_ptr createBorderGeometry(float x, float y, float size, Storage* terrain, - Resource::SceneManager* sceneManager, int mask, float offset = 10.0, osg::Vec4f color = { 1, 1, 0, 0 }); + Resource::SceneManager* sceneManager, int mask, ESM::RefId worldspace, float offset = 10.0, + osg::Vec4f color = { 1, 1, 0, 0 }); protected: Terrain::World* mWorld; diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 9e6e0cc150..ef52236acd 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -20,8 +20,9 @@ namespace Terrain { ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, - CompositeMapRenderer* renderer) + CompositeMapRenderer* renderer, ESM::RefId worldspace) : GenericResourceManager(nullptr) + , QuadTreeWorld::ChunkManager(worldspace) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) @@ -153,7 +154,7 @@ namespace Terrain { std::vector layerList; std::vector> blendmaps; - mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList); + mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList, mWorldspace); bool useShaders = mSceneManager->getForceShaders(); if (!mSceneManager->getClampLighting()) @@ -212,7 +213,7 @@ namespace Terrain osg::ref_ptr colors(new osg::Vec4ubArray); colors->setNormalize(true); - mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors); + mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, mWorldspace, positions, normals, colors); osg::ref_ptr vbo(new osg::VertexBufferObject); positions->setVertexBufferObject(vbo); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 34fa2aa4ae..4238f01c8b 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain { public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, - CompositeMapRenderer* renderer); + CompositeMapRenderer* renderer, ESM::RefId worldspace); osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 0ea176ca4f..974a6ebb4c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -117,13 +117,14 @@ namespace Terrain class QuadTreeBuilder { public: - QuadTreeBuilder(Terrain::Storage* storage, float minSize) + QuadTreeBuilder(Terrain::Storage* storage, float minSize, ESM::RefId worldspace) : mStorage(storage) , mMinX(0.f) , mMaxX(0.f) , mMinY(0.f) , mMaxY(0.f) , mMinSize(minSize) + , mWorldspace(worldspace) { } @@ -206,7 +207,8 @@ namespace Terrain // Do not add child nodes for default cells without data. // size = 1 means that the single shape covers the whole cell. - if (node->getSize() == 1 && !mStorage->hasData(center.x() - 0.5, center.y() - 0.5)) + if (node->getSize() == 1 + && !mStorage->hasData(ESM::ExteriorCellLocation(center.x() - 0.5, center.y() - 0.5, mWorldspace))) return node; if (node->getSize() <= mMinSize) @@ -239,13 +241,16 @@ namespace Terrain float mMinSize; osg::ref_ptr mRootNode; + ESM::RefId mWorldspace; }; class DebugChunkManager : public QuadTreeWorld::ChunkManager { public: - DebugChunkManager(Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask) - : mSceneManager(sceneManager) + DebugChunkManager( + Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask, ESM::RefId worldspace) + : QuadTreeWorld::ChunkManager(worldspace) + , mSceneManager(sceneManager) , mStorage(storage) , mNodeMask(nodeMask) { @@ -255,7 +260,7 @@ namespace Terrain { osg::Vec3f center = { chunkCenter.x(), chunkCenter.y(), 0 }; auto chunkBorder = CellBorder::createBorderGeometry(center.x() - size / 2.f, center.y() - size / 2.f, size, - mStorage, mSceneManager, mNodeMask, 5.f, { 1, 0, 0, 0 }); + mStorage, mSceneManager, mNodeMask, mWorldspace, 5.f, { 1, 0, 0, 0 }); osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; pat->setPosition(-center * Constants::CellSizeInUnits); pat->addChild(chunkBorder); @@ -289,8 +294,8 @@ namespace Terrain if (mDebugTerrainChunks) { - mDebugChunkManager - = std::make_unique(mResourceSystem->getSceneManager(), mStorage, borderMask); + mDebugChunkManager = std::make_unique( + mResourceSystem->getSceneManager(), mStorage, borderMask, mWorldspace); addChunkManager(mDebugChunkManager.get()); } } @@ -499,7 +504,7 @@ namespace Terrain if (mQuadTreeBuilt) return; - QuadTreeBuilder builder(mStorage, mMinSize); + QuadTreeBuilder builder(mStorage, mMinSize, mWorldspace); builder.build(); mRootNode = builder.getRootNode(); @@ -579,7 +584,7 @@ namespace Terrain { // fallback behavior only for undefined cells (every other is already handled in quadtree) float dummy; - if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), dummy, dummy)) + if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), mWorldspace, dummy, dummy)) TerrainGrid::loadCell(x, y); else World::loadCell(x, y); @@ -589,7 +594,7 @@ namespace Terrain { // fallback behavior only for undefined cells (every other is already handled in quadtree) float dummy; - if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), dummy, dummy)) + if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), mWorldspace, dummy, dummy)) TerrainGrid::unloadCell(x, y); else World::unloadCell(x, y); diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 202f3e67bd..fb7a078d7c 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -7,6 +7,8 @@ #include #include +#include + namespace osg { class NodeVisitor; @@ -58,6 +60,17 @@ namespace Terrain { public: virtual ~ChunkManager() {} + ChunkManager() + : mWorldspace(ESM::RefId()) + , mViewDistance(0.f) + , mMaxLodLevel(~0u) + { + } + ChunkManager(ESM::RefId worldspace) + : ChunkManager() + { + mWorldspace = worldspace; + } virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) = 0; @@ -70,9 +83,12 @@ namespace Terrain unsigned int getMaxLodLevel() const { return mMaxLodLevel; } void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; } + protected: + ESM::RefId mWorldspace; + private: - float mViewDistance = 0.f; - unsigned int mMaxLodLevel = ~0u; + float mViewDistance; + unsigned int mMaxLodLevel; }; void addChunkManager(ChunkManager*); diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index e2269707ab..8f6c00e1c7 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -8,6 +8,9 @@ #include #include +#include +#include + #include "defs.hpp" namespace osg @@ -30,10 +33,11 @@ namespace Terrain /// Return true if there is land data for this cell /// May be overriden for a faster implementation - virtual bool hasData(int cellX, int cellY) + virtual bool hasData(ESM::ExteriorCellLocation cellLocation) { float dummy; - return getMinMaxHeights(1, osg::Vec2f(cellX + 0.5, cellY + 0.5), dummy, dummy); + return getMinMaxHeights( + 1, osg::Vec2f(cellLocation.mX + 0.5, cellLocation.mY + 0.5), cellLocation.mWorldspace, dummy, dummy); } /// Get the minimum and maximum heights of a terrain region. @@ -44,7 +48,9 @@ namespace Terrain /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk - virtual bool getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max) = 0; + virtual bool getMinMaxHeights( + float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max) + = 0; /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! @@ -57,7 +63,7 @@ namespace Terrain /// @param positions buffer to write vertices /// @param normals buffer to write vertex normals /// @param colours buffer to write vertex colours - virtual void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, + virtual void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours) = 0; @@ -71,11 +77,11 @@ namespace Terrain /// @param chunkCenter center of the chunk in cell units /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here - virtual void getBlendmaps( - float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, std::vector& layerList) + virtual void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, + std::vector& layerList, ESM::RefId worldspace) = 0; - virtual float getHeightAt(const osg::Vec3f& worldPos) = 0; + virtual float getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) = 0; /// Get the transformation factor for mapping cell units to world units. virtual float getCellWorldSize() = 0; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index f762cf4052..98ce982285 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -47,7 +47,7 @@ namespace Terrain mTextureManager = std::make_unique(mResourceSystem->getSceneManager()); mChunkManager = std::make_unique( - mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer); + mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer, mWorldspace); mChunkManager->setNodeMask(nodeMask); mCellBorder = std::make_unique(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager()); @@ -127,7 +127,7 @@ namespace Terrain float World::getHeightAt(const osg::Vec3f& worldPos) { - return mStorage->getHeightAt(worldPos); + return mStorage->getHeightAt(worldPos, mWorldspace); } void World::updateTextureFiltering() diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d8aef759dc..16b39c1385 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -101,6 +101,8 @@ namespace Terrain virtual void setViewDistance(float distance) {} + ESM::RefId getWorldspace() { return mWorldspace; } + Storage* getStorage() { return mStorage; } osg::Callback* getHeightCullCallback(float highz, unsigned int mask);