diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 3c6c68e620..a5b1797daf 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -72,8 +72,8 @@ namespace CSVRender if (index == -1) // no land! return height; - const ESM::Land::LandData* landData = mData.getLand().getRecord(index).get().getLandData(ESM::Land::DATA_VHGT); - height = landData->mHeights[inCellY * ESM::Land::LAND_SIZE + inCellX]; + const ESM::LandData* landData = mData.getLand().getRecord(index).get().getLandData(ESM::Land::DATA_VHGT); + height = landData->getHeights()[inCellY * ESM::Land::LAND_SIZE + inCellX]; return mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX] + height; } @@ -152,13 +152,14 @@ namespace CSVRender || getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit; } - void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const + void TerrainStorage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const { // Highlight broken height changes + const ESM::Land::LandData* heightDataEsm3 = dynamic_cast(heightData); int heightWarningLimit = 1024; - if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) + if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightDataEsm3)) || ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) - && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData))) + && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightDataEsm3))) { color.r() = 255; color.g() = 0; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index a66c82cb9b..500309dc84 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -56,7 +56,7 @@ namespace CSVRender bool rightOrDownIsOverTheLimit( int col, int row, int heightWarningLimit, const ESM::Land::LandData* heightData) const; - void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const override; + void adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const override; float getAlteredHeight(int col, int row) const override; }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f08b4d2a97..c88eeecdda 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -397,13 +397,13 @@ namespace MWWorld if (cellVariant.isExterior()) { osg::ref_ptr land = mRendering.getLandManager()->getLand(cellIndex); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; - const int verts = ESM::Land::LAND_SIZE; - const int worldsize = ESM::Land::REAL_SIZE; + const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; + const int verts = data->getLandSize(); + const int worldsize = data->getSize(); if (data) { - mPhysics->addHeightField( - data->mHeights, cellX, cellY, worldsize, verts, data->mMinHeight, data->mMaxHeight, land.get()); + mPhysics->addHeightField(data->getHeights().data(), cellX, cellY, worldsize, verts, + data->getMinHeight(), data->getMaxHeight(), land.get()); } else { @@ -425,14 +425,14 @@ namespace MWWorld else { DetourNavigator::HeightfieldSurface heights; - heights.mHeights = data->mHeights; - heights.mSize = static_cast(ESM::Land::LAND_SIZE); - heights.mMinHeight = data->mMinHeight; - heights.mMaxHeight = data->mMaxHeight; + heights.mHeights = data->getHeights().data(); + heights.mSize = static_cast(data->getLandSize()); + heights.mMinHeight = data->getMinHeight(); + heights.mMaxHeight = data->getMaxHeight(); return heights; } }(); - mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape, navigatorUpdateGuard); + mNavigator.addHeightfield(cellPosition, data->getSize(), shape, navigatorUpdateGuard); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9ae1c3dc33..dde717d88c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -118,7 +118,7 @@ add_component_dir (to_utf8 to_utf8 ) -add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge +add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge esmterrain formid formidrefid stringrefid diff --git a/components/esm/esmterrain.hpp b/components/esm/esmterrain.hpp new file mode 100644 index 0000000000..90c4fae122 --- /dev/null +++ b/components/esm/esmterrain.hpp @@ -0,0 +1,25 @@ +#ifndef COMPONENTS_ESM_ESMTERRAIN +#define COMPONENTS_ESM_ESMTERRAIN + +#include + +namespace ESM +{ + class LandData + { + + public: + typedef signed char VNML; + + virtual std::span getHeights() const = 0; + virtual std::span getNormals() const = 0; + virtual std::span getColors() const = 0; + virtual std::span getTextures() const = 0; + virtual float getSize() const = 0; + virtual float getMinHeight() const = 0; + virtual float getMaxHeight() const = 0; + virtual int getLandSize() const = 0; + }; + +} +#endif // ! COMPNENTS_ESM_ESMTERRAIN diff --git a/components/esm3/loadland.hpp b/components/esm3/loadland.hpp index 4ff077d79f..48db2dd9c1 100644 --- a/components/esm3/loadland.hpp +++ b/components/esm3/loadland.hpp @@ -7,6 +7,7 @@ #include "components/esm/defs.hpp" #include "components/esm/esmcommon.hpp" +#include "components/esm/esmterrain.hpp" namespace ESM { @@ -87,9 +88,7 @@ namespace ESM }; #pragma pack(pop) - typedef signed char VNML; - - struct LandData + struct LandData : public ESM::LandData { LandData() : mHeightOffset(0) @@ -124,6 +123,15 @@ namespace ESM uint8_t mUnk2; int mDataLoaded; + + std::span getHeights() const override { return mHeights; } + std::span getNormals() const override { return mNormals; } + std::span getColors() const override { return mColours; } + std::span getTextures() const override { return mTextures; } + float getSize() const override { return REAL_SIZE; } + float getMinHeight() const override { return mMinHeight; } + float getMaxHeight() const { return mMaxHeight; } + int getLandSize() const { return LAND_SIZE; } }; // low-LOD heightmap (used for rendering the global map) diff --git a/components/esm3terrain/storage.cpp b/components/esm3terrain/storage.cpp index 35f0db8caf..6fb0a29b15 100644 --- a/components/esm3terrain/storage.cpp +++ b/components/esm3terrain/storage.cpp @@ -63,15 +63,15 @@ namespace ESMTerrain int cellX = static_cast(std::floor(origin.x())); int cellY = static_cast(std::floor(origin.y())); + osg::ref_ptr land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace)); + const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; - int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE; - int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE; + int startRow = (origin.x() - cellX) * data->getLandSize(); + int startColumn = (origin.y() - cellY) * data->getLandSize(); - int endRow = startRow + size * (ESM::Land::LAND_SIZE - 1) + 1; - int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1; + int endRow = startRow + size * (data->getLandSize() - 1) + 1; + int endColumn = startColumn + size * (data->getLandSize() - 1) + 1; - 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) { min = std::numeric_limits::max(); @@ -80,7 +80,7 @@ namespace ESMTerrain { for (int col = startColumn; col < endColumn; ++col) { - float h = data->mHeights[col * ESM::Land::LAND_SIZE + row]; + float h = data->getHeights()[col * data->getLandSize() + row]; if (h > max) max = h; if (h < min) @@ -98,34 +98,37 @@ namespace ESMTerrain void Storage::fixNormal( osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache) { - while (col >= ESM::Land::LAND_SIZE - 1) + + const LandObject* land = getLand(cellLocation, cache); + const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr; + const int landSize = data ? data->getLandSize() : ESM::Land::LAND_SIZE; + + while (col >= landSize - 1) { ++cellLocation.mY; - col -= ESM::Land::LAND_SIZE - 1; + col -= landSize - 1; } - while (row >= ESM::Land::LAND_SIZE - 1) + while (row >= landSize - 1) { ++cellLocation.mX; - row -= ESM::Land::LAND_SIZE - 1; + row -= landSize - 1; } while (col < 0) { --cellLocation.mY; - col += ESM::Land::LAND_SIZE - 1; + col += landSize - 1; } while (row < 0) { --cellLocation.mX; - row += ESM::Land::LAND_SIZE - 1; + row += landSize - 1; } - const LandObject* land = getLand(cellLocation, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr; if (data) { - normal.x() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3]; - normal.y() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1]; - normal.z() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2]; + normal.x() = data->getNormals()[col * landSize * 3 + row * 3]; + normal.y() = data->getNormals()[col * landSize * 3 + row * 3 + 1]; + normal.z() = data->getNormals()[col * landSize * 3 + row * 3 + 2]; normal.normalize(); } else @@ -147,24 +150,26 @@ namespace ESMTerrain void Storage::fixColour( osg::Vec4ub& color, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache) { - if (col == ESM::Land::LAND_SIZE - 1) + const LandObject* land = getLand(cellLocation, cache); + const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr; + const int landSize = data ? data->getLandSize() : ESM::Land::LAND_SIZE; + + if (col == landSize - 1) { ++cellLocation.mY; col = 0; } - if (row == ESM::Land::LAND_SIZE - 1) + if (row == landSize - 1) { ++cellLocation.mX; row = 0; } - const LandObject* land = getLand(cellLocation, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr; if (data) { - color.r() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3]; - color.g() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1]; - color.b() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2]; + color.r() = data->getColors()[col * landSize * 3 + row * 3]; + color.g() = data->getColors()[col * landSize * 3 + row * 3 + 1]; + color.b() = data->getColors()[col * landSize * 3 + row * 3 + 2]; } else { @@ -210,9 +215,11 @@ namespace ESMTerrain { 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; + const ESM::LandData* heightData = nullptr; + const ESM::LandData* normalData = nullptr; + const ESM::LandData* colourData = nullptr; + const int LandSize = ESM::Land::LAND_SIZE; + const int LandSizeInUnits = Constants::CellSizeInUnits; if (land) { heightData = land->getData(ESM::Land::DATA_VHGT); @@ -231,12 +238,12 @@ namespace ESMTerrain rowStart += increment; // Only relevant for chunks smaller than (contained in) one cell - rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; - colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; - int rowEnd = std::min(static_cast(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1), - static_cast(ESM::Land::LAND_SIZE)); - int colEnd = std::min(static_cast(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1), - static_cast(ESM::Land::LAND_SIZE)); + rowStart += (origin.x() - startCellX) * LandSize; + colStart += (origin.y() - startCellY) * LandSize; + int rowEnd = std::min( + static_cast(rowStart + std::min(1.f, size) * (LandSize - 1) + 1), static_cast(LandSize)); + int colEnd = std::min( + static_cast(colStart + std::min(1.f, size) * (LandSize - 1) + 1), static_cast(LandSize)); vertY = vertY_; for (int col = colStart; col < colEnd; col += increment) @@ -244,27 +251,27 @@ namespace ESMTerrain vertX = vertX_; for (int row = rowStart; row < rowEnd; row += increment) { - int srcArrayIndex = col * ESM::Land::LAND_SIZE * 3 + row * 3; + int srcArrayIndex = col * LandSize * 3 + row * 3; - assert(row >= 0 && row < ESM::Land::LAND_SIZE); - assert(col >= 0 && col < ESM::Land::LAND_SIZE); + assert(row >= 0 && row < LandSize); + assert(col >= 0 && col < LandSize); assert(vertX < numVerts); assert(vertY < numVerts); float height = defaultHeight; if (heightData) - height = heightData->mHeights[col * ESM::Land::LAND_SIZE + row]; + height = heightData->getHeights()[col * LandSize + row]; if (alteration) height += getAlteredHeight(col, row); (*positions)[static_cast(vertX * numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height); + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits, + (vertY / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits, height); if (normalData) { for (int i = 0; i < 3; ++i) - normal[i] = normalData->mNormals[srcArrayIndex + i]; + normal[i] = normalData->getNormals()[srcArrayIndex + i]; normal.normalize(); } @@ -272,12 +279,11 @@ namespace ESMTerrain normal = osg::Vec3f(0, 0, 1); // Normals apparently don't connect seamlessly between cells - if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1) + if (col == LandSize - 1 || row == LandSize - 1) 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)) + if ((row == 0 || row == LandSize - 1) && (col == 0 || col == LandSize - 1)) averageNormal(normal, cellLocation, col, row, cache); assert(normal.z() > 0); @@ -287,7 +293,7 @@ namespace ESMTerrain if (colourData) { for (int i = 0; i < 3; ++i) - color[i] = colourData->mColours[srcArrayIndex + i]; + color[i] = colourData->getColors()[srcArrayIndex + i]; } else { @@ -299,7 +305,7 @@ namespace ESMTerrain adjustColor(col, row, heightData, color); // Does nothing by default, override in OpenMW-CS // 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 == LandSize - 1 || row == LandSize - 1) fixColour(color, cellLocation, col, row, cache); color.a() = 255; @@ -347,10 +353,10 @@ namespace ESMTerrain const LandObject* land = getLand(cellLocation, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr; + const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr; if (data) { - int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + int tex = data->getTextures()[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0, 0); // vtex 0 is always the base texture, regardless of plugin return std::make_pair(tex, land->getPlugin()); @@ -458,7 +464,7 @@ namespace ESMTerrain if (!land) return defaultHeight; - const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT); + const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT); if (!data) return defaultHeight; @@ -542,7 +548,7 @@ namespace ESMTerrain } } - void Storage::adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const {} + void Storage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const {} float Storage::getAlteredHeight(int col, int row) const { diff --git a/components/esm3terrain/storage.hpp b/components/esm3terrain/storage.hpp index 9a7e9d3ecf..7f7f25c5d6 100644 --- a/components/esm3terrain/storage.hpp +++ b/components/esm3terrain/storage.hpp @@ -10,6 +10,11 @@ #include #include +namespace ESM4 +{ + struct Land; +} + namespace VFS { class Manager; @@ -32,7 +37,7 @@ namespace ESMTerrain META_Object(ESMTerrain, LandObject) - inline const ESM::Land::LandData* getData(int flags) const + inline const ESM::LandData* getData(int flags) const { if ((mData.mDataLoaded & flags) != flags) return nullptr; @@ -108,11 +113,11 @@ namespace ESMTerrain int getBlendmapScale(float chunkSize) override; - float getVertexHeight(const ESM::Land::LandData* data, int x, int y) + float getVertexHeight(const ESM::LandData* data, int x, int y) { assert(x < ESM::Land::LAND_SIZE); assert(y < ESM::Land::LAND_SIZE); - return data->mHeights[y * ESM::Land::LAND_SIZE + x]; + return data->getHeights()[y * ESM::Land::LAND_SIZE + x]; } private: @@ -128,7 +133,7 @@ namespace ESMTerrain 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; + virtual void adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const; virtual float getAlteredHeight(int col, int row) const; // Since plugins can define new texture palettes, we need to know the plugin index too