adding worldspace info to terrain data structures

Rendering manager can have multiple terrain, one for each queried worldspace
Terrain::World has a worldspace member
storage functions require a worldspace parameter.
macos_ci
florent.teppe 2 years ago
parent 93e7b0d946
commit 1b718f09c5

@ -28,12 +28,12 @@ namespace CSVRender
resetHeights(); resetHeights();
} }
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation)
{ {
// The cell isn't guaranteed to have Land. This is because the terrain implementation // 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 // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
const int index const int index = mData.getLand().searchId(
= mData.getLand().searchId(ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellX, cellY))); ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellLocation.mX, cellLocation.mY)));
if (index == -1) if (index == -1)
return nullptr; return nullptr;

@ -37,7 +37,7 @@ namespace CSVRender
const CSMWorld::Data& mData; const CSMWorld::Data& mData;
std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight; std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;
osg::ref_ptr<const ESMTerrain::LandObject> getLand(int cellX, int cellY) override; osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
const ESM::LandTexture* getLandTexture(int index, short plugin) override; const ESM::LandTexture* getLandTexture(int index, short plugin) override;
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;

@ -457,35 +457,9 @@ namespace MWRender
mTerrainStorage = std::make_unique<TerrainStorage>(mResourceSystem, normalMapPattern, heightMapPattern, mTerrainStorage = std::make_unique<TerrainStorage>(mResourceSystem, normalMapPattern, heightMapPattern,
useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); 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 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<Terrain::QuadTreeWorld>(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<ObjectPaging>(mResourceSystem->getSceneManager());
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());
mResourceSystem->addResourceManager(mObjectPaging.get());
}
}
else
mTerrain = std::make_unique<Terrain::TerrainGrid>(sceneRoot, mRootNode, mResourceSystem,
mTerrainStorage.get(), Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, Mask_PreCompile, Mask_Debug);
mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate);
if (groundcover) if (groundcover)
{ {
@ -494,7 +468,7 @@ namespace MWRender
mGroundcover = std::make_unique<Groundcover>( mGroundcover = std::make_unique<Groundcover>(
mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore); mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore);
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get()); static_cast<Terrain::QuadTreeWorld*>(mTerrain)->addChunkManager(mGroundcover.get());
mResourceSystem->addResourceManager(mGroundcover.get()); mResourceSystem->addResourceManager(mGroundcover.get());
} }
@ -633,7 +607,7 @@ namespace MWRender
Terrain::World* RenderingManager::getTerrain() Terrain::World* RenderingManager::getTerrain()
{ {
return mTerrain.get(); return mTerrain;
} }
void RenderingManager::preloadCommonAssets() void RenderingManager::preloadCommonAssets()
@ -1343,6 +1317,45 @@ namespace MWRender
mStateUpdater->setFogColor(color); 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<Terrain::World> 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<Terrain::QuadTreeWorld>(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<ObjectPaging>(mResourceSystem->getSceneManager());
static_cast<Terrain::QuadTreeWorld*>(newTerrain.get())->addChunkManager(mObjectPaging.get());
mResourceSystem->addResourceManager(mObjectPaging.get());
}
}
else
newTerrain = std::make_unique<Terrain::TerrainGrid>(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 void RenderingManager::reportStats() const
{ {
osg::Stats* stats = mViewer->getViewerStats(); osg::Stats* stats = mViewer->getViewerStats();
@ -1446,7 +1459,7 @@ namespace MWRender
float RenderingManager::getTerrainHeightAt(const osg::Vec3f& pos, ESM::RefId worldspace) float RenderingManager::getTerrainHeightAt(const osg::Vec3f& pos, ESM::RefId worldspace)
{ {
return mTerrain->getHeightAt(pos); return getWorldspaceTerrain(worldspace)->getHeightAt(pos);
} }
void RenderingManager::overrideFieldOfView(float val) void RenderingManager::overrideFieldOfView(float val)

@ -281,6 +281,7 @@ namespace MWRender
void updateAmbient(); void updateAmbient();
void setFogColor(const osg::Vec4f& color); void setFogColor(const osg::Vec4f& color);
void updateThirdPersonViewMode(); void updateThirdPersonViewMode();
Terrain::World* getWorldspaceTerrain(ESM::RefId worldspace);
void reportStats() const; void reportStats() const;
@ -312,7 +313,8 @@ namespace MWRender
std::unique_ptr<Pathgrid> mPathgrid; std::unique_ptr<Pathgrid> mPathgrid;
std::unique_ptr<Objects> mObjects; std::unique_ptr<Objects> mObjects;
std::unique_ptr<Water> mWater; std::unique_ptr<Water> mWater;
std::unique_ptr<Terrain::World> mTerrain; std::unordered_map<ESM::RefId, std::unique_ptr<Terrain::World>> mWorldspaceTerrains;
Terrain::World* mTerrain;
std::unique_ptr<TerrainStorage> mTerrainStorage; std::unique_ptr<TerrainStorage> mTerrainStorage;
std::unique_ptr<ObjectPaging> mObjectPaging; std::unique_ptr<ObjectPaging> mObjectPaging;
std::unique_ptr<Groundcover> mGroundcover; std::unique_ptr<Groundcover> mGroundcover;

@ -26,11 +26,11 @@ namespace MWRender
mResourceSystem->removeResourceManager(mLandManager.get()); 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 MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
const ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY); const ESM::Land* land = esmStore.get<ESM::Land>().search(cellLocation.mX, cellLocation.mY);
return land != nullptr; return land != nullptr;
} }
@ -66,9 +66,9 @@ namespace MWRender
return mLandManager.get(); return mLandManager.get();
} }
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> 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) const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)

@ -21,10 +21,10 @@ namespace MWRender
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
~TerrainStorage(); ~TerrainStorage();
osg::ref_ptr<const ESMTerrain::LandObject> getLand(int cellX, int cellY) override; osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
const ESM::LandTexture* getLandTexture(int index, short plugin) 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 /// Get bounds of the whole terrain in cell units
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;

@ -16,7 +16,7 @@ namespace ESMTerrain
class LandCache class LandCache
{ {
public: public:
typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject>> Map; typedef std::map<ESM::ExteriorCellLocation, osg::ref_ptr<const LandObject>> Map;
Map mMap; 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"); 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 endRow = startRow + size * (ESM::Land::LAND_SIZE - 1) + 1;
int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1; int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1;
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY); osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
if (data) if (data)
{ {
@ -95,30 +95,31 @@ namespace ESMTerrain
return false; 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) while (col >= ESM::Land::LAND_SIZE - 1)
{ {
++cellY; ++cellLocation.mY;
col -= ESM::Land::LAND_SIZE - 1; col -= ESM::Land::LAND_SIZE - 1;
} }
while (row >= ESM::Land::LAND_SIZE - 1) while (row >= ESM::Land::LAND_SIZE - 1)
{ {
++cellX; ++cellLocation.mX;
row -= ESM::Land::LAND_SIZE - 1; row -= ESM::Land::LAND_SIZE - 1;
} }
while (col < 0) while (col < 0)
{ {
--cellY; --cellLocation.mY;
col += ESM::Land::LAND_SIZE - 1; col += ESM::Land::LAND_SIZE - 1;
} }
while (row < 0) while (row < 0)
{ {
--cellX; --cellLocation.mX;
row += ESM::Land::LAND_SIZE - 1; 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; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;
if (data) if (data)
{ {
@ -131,31 +132,33 @@ namespace ESMTerrain
normal = osg::Vec3f(0, 0, 1); 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; osg::Vec3f n1, n2, n3, n4;
fixNormal(n1, cellX, cellY, col + 1, row, cache); fixNormal(n1, cellLocation, col + 1, row, cache);
fixNormal(n2, cellX, cellY, col - 1, row, cache); fixNormal(n2, cellLocation, col - 1, row, cache);
fixNormal(n3, cellX, cellY, col, row + 1, cache); fixNormal(n3, cellLocation, col, row + 1, cache);
fixNormal(n4, cellX, cellY, col, row - 1, cache); fixNormal(n4, cellLocation, col, row - 1, cache);
normal = (n1 + n2 + n3 + n4); normal = (n1 + n2 + n3 + n4);
normal.normalize(); 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) if (col == ESM::Land::LAND_SIZE - 1)
{ {
++cellY; ++cellLocation.mY;
col = 0; col = 0;
} }
if (row == ESM::Land::LAND_SIZE - 1) if (row == ESM::Land::LAND_SIZE - 1)
{ {
++cellX; ++cellLocation.mX;
row = 0; 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; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;
if (data) 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<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals, osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
osg::ref_ptr<osg::Vec4ubArray> colours) osg::ref_ptr<osg::Vec4ubArray> colours)
{ {
@ -205,7 +208,8 @@ namespace ESMTerrain
float vertX_ = 0; // of current cell corner float vertX_ = 0; // of current cell corner
for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) 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* heightData = nullptr;
const ESM::Land::LandData* normalData = nullptr; const ESM::Land::LandData* normalData = nullptr;
const ESM::Land::LandData* colourData = nullptr; const ESM::Land::LandData* colourData = nullptr;
@ -269,12 +273,12 @@ namespace ESMTerrain
// Normals apparently don't connect seamlessly between cells // Normals apparently don't connect seamlessly between cells
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1) 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) // some corner normals appear to be complete garbage (z < 0)
if ((row == 0 || row == ESM::Land::LAND_SIZE - 1) if ((row == 0 || row == ESM::Land::LAND_SIZE - 1)
&& (col == 0 || col == 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); assert(normal.z() > 0);
@ -296,7 +300,7 @@ namespace ESMTerrain
// Unlike normals, colors mostly connect seamlessly between cells, but not always... // Unlike normals, colors mostly connect seamlessly between cells, but not always...
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1) 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; color.a() = 255;
@ -315,32 +319,33 @@ namespace ESMTerrain
assert(vertY_ == numVerts); // Ensure we covered whole area 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 // For the first/last row/column, we need to get the texture from the neighbour cell
// to get consistent blending at the borders // to get consistent blending at the borders
--x; --x;
if (x < 0) if (x < 0)
{ {
--cellX; --cellLocation.mX;
x += ESM::Land::LAND_TEXTURE_SIZE; x += ESM::Land::LAND_TEXTURE_SIZE;
} }
while (x >= ESM::Land::LAND_TEXTURE_SIZE) while (x >= ESM::Land::LAND_TEXTURE_SIZE)
{ {
++cellX; ++cellLocation.mX;
x -= ESM::Land::LAND_TEXTURE_SIZE; x -= ESM::Land::LAND_TEXTURE_SIZE;
} }
while ( while (
y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? 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; y -= ESM::Land::LAND_TEXTURE_SIZE;
} }
assert(x < ESM::Land::LAND_TEXTURE_SIZE); assert(x < ESM::Land::LAND_TEXTURE_SIZE);
assert(y < 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; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;
if (data) if (data)
@ -375,7 +380,7 @@ namespace ESMTerrain
} }
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
std::vector<Terrain::LayerInfo>& layerList) std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
{ {
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f); osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f);
int cellX = static_cast<int>(std::floor(origin.x())); int cellX = static_cast<int>(std::floor(origin.x()));
@ -398,7 +403,8 @@ namespace ESMTerrain
{ {
for (int x = 0; x < blendmapSize; x++) 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<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id); std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);
if (found == textureIndicesMap.end()) 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 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<int>(std::floor(worldPos.x() / float(Constants::CellSizeInUnits))); const float cellSize = ESM::getCellSize(worldspace);
int cellY = static_cast<int>(std::floor(worldPos.y() / float(Constants::CellSizeInUnits))); int cellX = static_cast<int>(std::floor(worldPos.x() / cellSize));
int cellY = static_cast<int>(std::floor(worldPos.y() / cellSize));
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY); osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
if (!land) if (!land)
return defaultHeight; return defaultHeight;
@ -458,8 +465,8 @@ namespace ESMTerrain
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
// Normalized position in the cell // Normalized position in the cell
float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / cellSize;
float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / cellSize;
// get left / bottom points (rounded down) // get left / bottom points (rounded down)
float factor = ESM::Land::LAND_SIZE - 1.0f; 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 // 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 v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / cellSize);
osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits)); osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / cellSize);
osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits)); osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / cellSize);
osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits)); osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / cellSize);
// define this plane in terrain space // define this plane in terrain space
osg::Plane plane; osg::Plane plane;
// FIXME: deal with differing triangle alignment // FIXME: deal with differing triangle alignment
@ -520,18 +527,17 @@ namespace ESMTerrain
*/ */
// Solve plane equation for z // Solve plane equation for z
return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * cellSize;
* Constants::CellSizeInUnits;
} }
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()) if (found != cache.mMap.end())
return found->second; return found->second;
else 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; return found->second;
} }
} }

@ -6,6 +6,7 @@
#include <components/terrain/storage.hpp> #include <components/terrain/storage.hpp>
#include <components/esm/util.hpp>
#include <components/esm3/loadland.hpp> #include <components/esm3/loadland.hpp>
#include <components/esm3/loadltex.hpp> #include <components/esm3/loadltex.hpp>
@ -56,7 +57,7 @@ namespace ESMTerrain
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
// Not implemented in this class, because we need different Store implementations for game and editor // Not implemented in this class, because we need different Store implementations for game and editor
virtual osg::ref_ptr<const LandObject> getLand(int cellX, int cellY) = 0; virtual osg::ref_ptr<const LandObject> getLand(ESM::ExteriorCellLocation cellLocation) = 0;
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
/// Get bounds of the whole terrain in cell units /// Get bounds of the whole terrain in cell units
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override = 0; 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 min min height will be stored here
/// @param max max height will be stored here /// @param max max height will be stored here
/// @return true if there was data available for this terrain chunk /// @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. /// Fill vertex buffers for a terrain chunk.
/// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @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 positions buffer to write vertices
/// @param normals buffer to write vertex normals /// @param normals buffer to write vertex normals
/// @param colours buffer to write vertex colours /// @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<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals, osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
osg::ref_ptr<osg::Vec4ubArray> colours) override; osg::ref_ptr<osg::Vec4ubArray> colours) override;
@ -94,9 +96,9 @@ namespace ESMTerrain
/// @param blendmaps created blendmaps will be written here /// @param blendmaps created blendmaps will be written here
/// @param layerList names of the layer textures used 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, void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
std::vector<Terrain::LayerInfo>& layerList) override; std::vector<Terrain::LayerInfo>& 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. /// Get the transformation factor for mapping cell units to world units.
float getCellWorldSize() override; float getCellWorldSize() override;
@ -116,11 +118,14 @@ namespace ESMTerrain
private: private:
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
inline void fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); inline void fixNormal(
inline void fixColour(osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache);
inline void averageNormal(osg::Vec3f& normal, int cellX, int cellY, 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 bool useAlteration() const { return false; }
virtual void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const; virtual void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const;
@ -131,7 +136,7 @@ namespace ESMTerrain
// pair <texture id, plugin id> // pair <texture id, plugin id>
typedef std::pair<short, short> UniqueTextureId; typedef std::pair<short, short> 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::string getTextureName(UniqueTextureId id);
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap; std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;

@ -23,7 +23,7 @@ namespace Terrain
} }
osg::ref_ptr<osg::Group> CellBorder::createBorderGeometry(float x, float y, float size, Terrain::Storage* terrain, osg::ref_ptr<osg::Group> 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 cellSize = ESM::Land::REAL_SIZE;
const int borderSegments = 40; const int borderSegments = 40;
@ -45,7 +45,7 @@ namespace Terrain
: osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f); : osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f);
pos += cellCorner; 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); vertices->push_back(pos);
@ -83,7 +83,8 @@ namespace Terrain
void CellBorder::createCellBorderGeometry(int x, int y) 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); mRoot->addChild(borderGroup);
mCellBorderNodes[std::make_pair(x, y)] = borderGroup; mCellBorderNodes[std::make_pair(x, y)] = borderGroup;

@ -4,6 +4,8 @@
#include <map> #include <map>
#include <osg/Group> #include <osg/Group>
#include <components/esm/refid.hpp>
namespace Resource namespace Resource
{ {
class SceneManager; class SceneManager;
@ -33,7 +35,8 @@ namespace Terrain
void destroyCellBorderGeometry(); void destroyCellBorderGeometry();
static osg::ref_ptr<osg::Group> createBorderGeometry(float x, float y, float size, Storage* terrain, static osg::ref_ptr<osg::Group> 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: protected:
Terrain::World* mWorld; Terrain::World* mWorld;

@ -20,8 +20,9 @@ namespace Terrain
{ {
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
CompositeMapRenderer* renderer) CompositeMapRenderer* renderer, ESM::RefId worldspace)
: GenericResourceManager<ChunkId>(nullptr) : GenericResourceManager<ChunkId>(nullptr)
, QuadTreeWorld::ChunkManager(worldspace)
, mStorage(storage) , mStorage(storage)
, mSceneManager(sceneMgr) , mSceneManager(sceneMgr)
, mTextureManager(textureManager) , mTextureManager(textureManager)
@ -153,7 +154,7 @@ namespace Terrain
{ {
std::vector<LayerInfo> layerList; std::vector<LayerInfo> layerList;
std::vector<osg::ref_ptr<osg::Image>> blendmaps; std::vector<osg::ref_ptr<osg::Image>> blendmaps;
mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList); mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList, mWorldspace);
bool useShaders = mSceneManager->getForceShaders(); bool useShaders = mSceneManager->getForceShaders();
if (!mSceneManager->getClampLighting()) if (!mSceneManager->getClampLighting())
@ -212,7 +213,7 @@ namespace Terrain
osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray); osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray);
colors->setNormalize(true); colors->setNormalize(true);
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors); mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, mWorldspace, positions, normals, colors);
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject); osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo); positions->setVertexBufferObject(vbo);

@ -35,7 +35,7 @@ namespace Terrain
{ {
public: public:
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
CompositeMapRenderer* renderer); CompositeMapRenderer* renderer, ESM::RefId worldspace);
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags,
bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;

@ -117,13 +117,14 @@ namespace Terrain
class QuadTreeBuilder class QuadTreeBuilder
{ {
public: public:
QuadTreeBuilder(Terrain::Storage* storage, float minSize) QuadTreeBuilder(Terrain::Storage* storage, float minSize, ESM::RefId worldspace)
: mStorage(storage) : mStorage(storage)
, mMinX(0.f) , mMinX(0.f)
, mMaxX(0.f) , mMaxX(0.f)
, mMinY(0.f) , mMinY(0.f)
, mMaxY(0.f) , mMaxY(0.f)
, mMinSize(minSize) , mMinSize(minSize)
, mWorldspace(worldspace)
{ {
} }
@ -206,7 +207,8 @@ namespace Terrain
// Do not add child nodes for default cells without data. // Do not add child nodes for default cells without data.
// size = 1 means that the single shape covers the whole cell. // 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; return node;
if (node->getSize() <= mMinSize) if (node->getSize() <= mMinSize)
@ -239,13 +241,16 @@ namespace Terrain
float mMinSize; float mMinSize;
osg::ref_ptr<RootNode> mRootNode; osg::ref_ptr<RootNode> mRootNode;
ESM::RefId mWorldspace;
}; };
class DebugChunkManager : public QuadTreeWorld::ChunkManager class DebugChunkManager : public QuadTreeWorld::ChunkManager
{ {
public: public:
DebugChunkManager(Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask) DebugChunkManager(
: mSceneManager(sceneManager) Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask, ESM::RefId worldspace)
: QuadTreeWorld::ChunkManager(worldspace)
, mSceneManager(sceneManager)
, mStorage(storage) , mStorage(storage)
, mNodeMask(nodeMask) , mNodeMask(nodeMask)
{ {
@ -255,7 +260,7 @@ namespace Terrain
{ {
osg::Vec3f center = { chunkCenter.x(), chunkCenter.y(), 0 }; osg::Vec3f center = { chunkCenter.x(), chunkCenter.y(), 0 };
auto chunkBorder = CellBorder::createBorderGeometry(center.x() - size / 2.f, center.y() - size / 2.f, size, 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<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform; osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
pat->setPosition(-center * Constants::CellSizeInUnits); pat->setPosition(-center * Constants::CellSizeInUnits);
pat->addChild(chunkBorder); pat->addChild(chunkBorder);
@ -289,8 +294,8 @@ namespace Terrain
if (mDebugTerrainChunks) if (mDebugTerrainChunks)
{ {
mDebugChunkManager mDebugChunkManager = std::make_unique<DebugChunkManager>(
= std::make_unique<DebugChunkManager>(mResourceSystem->getSceneManager(), mStorage, borderMask); mResourceSystem->getSceneManager(), mStorage, borderMask, mWorldspace);
addChunkManager(mDebugChunkManager.get()); addChunkManager(mDebugChunkManager.get());
} }
} }
@ -499,7 +504,7 @@ namespace Terrain
if (mQuadTreeBuilt) if (mQuadTreeBuilt)
return; return;
QuadTreeBuilder builder(mStorage, mMinSize); QuadTreeBuilder builder(mStorage, mMinSize, mWorldspace);
builder.build(); builder.build();
mRootNode = builder.getRootNode(); mRootNode = builder.getRootNode();
@ -579,7 +584,7 @@ namespace Terrain
{ {
// fallback behavior only for undefined cells (every other is already handled in quadtree) // fallback behavior only for undefined cells (every other is already handled in quadtree)
float dummy; 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); TerrainGrid::loadCell(x, y);
else else
World::loadCell(x, y); World::loadCell(x, y);
@ -589,7 +594,7 @@ namespace Terrain
{ {
// fallback behavior only for undefined cells (every other is already handled in quadtree) // fallback behavior only for undefined cells (every other is already handled in quadtree)
float dummy; 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); TerrainGrid::unloadCell(x, y);
else else
World::unloadCell(x, y); World::unloadCell(x, y);

@ -7,6 +7,8 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <components/esm/refid.hpp>
namespace osg namespace osg
{ {
class NodeVisitor; class NodeVisitor;
@ -58,6 +60,17 @@ namespace Terrain
{ {
public: public:
virtual ~ChunkManager() {} virtual ~ChunkManager() {}
ChunkManager()
: mWorldspace(ESM::RefId())
, mViewDistance(0.f)
, mMaxLodLevel(~0u)
{
}
ChunkManager(ESM::RefId worldspace)
: ChunkManager()
{
mWorldspace = worldspace;
}
virtual osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, virtual osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod,
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
= 0; = 0;
@ -70,9 +83,12 @@ namespace Terrain
unsigned int getMaxLodLevel() const { return mMaxLodLevel; } unsigned int getMaxLodLevel() const { return mMaxLodLevel; }
void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; } void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; }
protected:
ESM::RefId mWorldspace;
private: private:
float mViewDistance = 0.f; float mViewDistance;
unsigned int mMaxLodLevel = ~0u; unsigned int mMaxLodLevel;
}; };
void addChunkManager(ChunkManager*); void addChunkManager(ChunkManager*);

@ -8,6 +8,9 @@
#include <osg/Vec3f> #include <osg/Vec3f>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <components/esm/refid.hpp>
#include <components/esm/util.hpp>
#include "defs.hpp" #include "defs.hpp"
namespace osg namespace osg
@ -30,10 +33,11 @@ namespace Terrain
/// Return true if there is land data for this cell /// Return true if there is land data for this cell
/// May be overriden for a faster implementation /// May be overriden for a faster implementation
virtual bool hasData(int cellX, int cellY) virtual bool hasData(ESM::ExteriorCellLocation cellLocation)
{ {
float dummy; 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. /// 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 min min height will be stored here
/// @param max max height will be stored here /// @param max max height will be stored here
/// @return true if there was data available for this terrain chunk /// @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. /// Fill vertex buffers for a terrain chunk.
/// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @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 positions buffer to write vertices
/// @param normals buffer to write vertex normals /// @param normals buffer to write vertex normals
/// @param colours buffer to write vertex colours /// @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<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals, osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
osg::ref_ptr<osg::Vec4ubArray> colours) osg::ref_ptr<osg::Vec4ubArray> colours)
= 0; = 0;
@ -71,11 +77,11 @@ namespace Terrain
/// @param chunkCenter center of the chunk in cell units /// @param chunkCenter center of the chunk in cell units
/// @param blendmaps created blendmaps will be written here /// @param blendmaps created blendmaps will be written here
/// @param layerList names of the layer textures used will be written here /// @param layerList names of the layer textures used will be written here
virtual void getBlendmaps( virtual void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, std::vector<LayerInfo>& layerList) std::vector<LayerInfo>& layerList, ESM::RefId worldspace)
= 0; = 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. /// Get the transformation factor for mapping cell units to world units.
virtual float getCellWorldSize() = 0; virtual float getCellWorldSize() = 0;

@ -47,7 +47,7 @@ namespace Terrain
mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager()); mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager());
mChunkManager = std::make_unique<ChunkManager>( mChunkManager = std::make_unique<ChunkManager>(
mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer); mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer, mWorldspace);
mChunkManager->setNodeMask(nodeMask); mChunkManager->setNodeMask(nodeMask);
mCellBorder mCellBorder
= std::make_unique<CellBorder>(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager()); = std::make_unique<CellBorder>(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager());
@ -127,7 +127,7 @@ namespace Terrain
float World::getHeightAt(const osg::Vec3f& worldPos) float World::getHeightAt(const osg::Vec3f& worldPos)
{ {
return mStorage->getHeightAt(worldPos); return mStorage->getHeightAt(worldPos, mWorldspace);
} }
void World::updateTextureFiltering() void World::updateTextureFiltering()

@ -101,6 +101,8 @@ namespace Terrain
virtual void setViewDistance(float distance) {} virtual void setViewDistance(float distance) {}
ESM::RefId getWorldspace() { return mWorldspace; }
Storage* getStorage() { return mStorage; } Storage* getStorage() { return mStorage; }
osg::Callback* getHeightCullCallback(float highz, unsigned int mask); osg::Callback* getHeightCullCallback(float highz, unsigned int mask);

Loading…
Cancel
Save