diff --git a/apps/openmw/mwlua/landbindings.cpp b/apps/openmw/mwlua/landbindings.cpp index 1e49db5c5e..de039cf750 100644 --- a/apps/openmw/mwlua/landbindings.cpp +++ b/apps/openmw/mwlua/landbindings.cpp @@ -13,6 +13,39 @@ namespace { + osg::Vec3f getTextureCorrectedWorldPos( + const osg::Vec3f& uncorrectedWorldPos, const int textureSize, const float cellSize) + { + // the offset is [-0.25, +0.25] of a single texture's size + // TODO: verify whether or not this works in TES4 and beyond + float offset = (cellSize / textureSize) * 0.25; + return uncorrectedWorldPos + osg::Vec3f{ -offset, +offset, 0.0f }; + } + + // Takes in a corrected world pos to match the visuals. + ESMTerrain::UniqueTextureId getTextureAt(const std::span landData, const int plugin, + const int textureSize, const osg::Vec3f& correctedWorldPos, const float cellSize) + { + int cellX = static_cast(std::floor(correctedWorldPos.x() / cellSize)); + int cellY = static_cast(std::floor(correctedWorldPos.y() / cellSize)); + + // Normalized position in the cell + float nX = (correctedWorldPos.x() - (cellX * cellSize)) / cellSize; + float nY = (correctedWorldPos.y() - (cellY * cellSize)) / cellSize; + + int startX = static_cast(nX * textureSize); + int startY = static_cast(nY * textureSize); + + assert(startX < ESM::Land::LAND_TEXTURE_SIZE); + assert(startY < ESM::Land::LAND_TEXTURE_SIZE); + + const std::uint16_t tex = landData[startY * ESM::Land::LAND_TEXTURE_SIZE + startX]; + if (tex == 0) + return { 0, 0 }; // vtex 0 is always the base texture, regardless of plugin + + return { tex, plugin }; + } + const ESM::RefId worldspaceAt(const osg::Vec3f& pos, sol::object cellOrName) { ESM::RefId worldspace; @@ -65,7 +98,7 @@ namespace MWLua landApi["getTextureAt"] = [lua = lua](const osg::Vec3f& pos, sol::object cellOrName) { sol::variadic_results values; auto store = MWBase::Environment::get().getESMStore(); - const auto &landStore = store->get(); + const auto& landStore = store->get(); const float cellSize = ESM::getCellSize(worldspaceAt(pos, cellOrName)); // We need to read land twice. Once to get the amount of texture samples per cell edge, and the second time @@ -82,8 +115,7 @@ namespace MWLua // Use landData to get amount of sampler per cell edge (sLandTextureSize) // and then get the corrected position that will map to the rendered texture - const osg::Vec3f correctedPos - = ESMTerrain::Storage::getTextureCorrectedWorldPos(pos, landData->sLandTextureSize, cellSize); + const osg::Vec3f correctedPos = getTextureCorrectedWorldPos(pos, landData->sLandTextureSize, cellSize); const ESM::Land* correctedLand = nullptr; const ESM::Land::LandData* correctedLandData = nullptr; @@ -93,7 +125,7 @@ namespace MWLua // We're passing in sLandTextureSize, NOT sLandSize like with getHeightAt const ESMTerrain::UniqueTextureId textureId - = ESMTerrain::Storage::getTextureAt(correctedLandData->mTextures, correctedLand->getPlugin(), + = getTextureAt(correctedLandData->mTextures, correctedLand->getPlugin(), correctedLandData->sLandTextureSize, correctedPos, cellSize); // Need to check for 0, 0 so that we can safely subtract 1 later, as per documentation on UniqueTextureId diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index c4adda54a1..35ec814aa2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -23,19 +22,6 @@ namespace ESMTerrain { namespace { - UniqueTextureId getTextureIdAt( - const std::span data, const int plugin, const std::size_t x, const std::size_t y) - { - assert(x < ESM::Land::LAND_TEXTURE_SIZE); - assert(y < ESM::Land::LAND_TEXTURE_SIZE); - - const std::uint16_t tex = data[y * ESM::Land::LAND_TEXTURE_SIZE + x]; - if (tex == 0) - return { 0, 0 }; // vtex 0 is always the base texture, regardless of plugin - - return { tex, plugin }; - } - UniqueTextureId getTextureIdAt(const LandObject* land, std::size_t x, std::size_t y) { assert(x < ESM::Land::LAND_TEXTURE_SIZE); @@ -48,7 +34,11 @@ namespace ESMTerrain if (data == nullptr) return { 0, 0 }; - return getTextureIdAt(data->getTextures(), land->getPlugin(), x, y); + const std::uint16_t tex = data->getTextures()[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + if (tex == 0) + return { 0, 0 }; // vtex 0 is always the base texture, regardless of plugin + + return { tex, land->getPlugin() }; } } @@ -475,39 +465,21 @@ namespace ESMTerrain blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend } - // Returns a position that can be used to look up a land texture, while taking their offset into account - osg::Vec3f Storage::getTextureCorrectedWorldPos( - const osg::Vec3f& uncorrectedWorldPos, const int textureSize, const float cellSize) - { - // the offset is [-0.25, +0.25] of a single texture's size - // TODO: verify whether or not this works in TES4 and beyond - float offset = (cellSize / textureSize) * 0.25; - return uncorrectedWorldPos + osg::Vec3f{ -offset, +offset, 0.0f }; - } - - // Takes in a corrected world pos to match the visuals. - UniqueTextureId Storage::getTextureAt(const std::span landData, const int plugin, - const int textureSize, const osg::Vec3f& correctedWorldPos, const float cellSize) - { - int cellX = static_cast(std::floor(correctedWorldPos.x() / cellSize)); - int cellY = static_cast(std::floor(correctedWorldPos.y() / cellSize)); - - // Normalized position in the cell - float nX = (correctedWorldPos.x() - (cellX * cellSize)) / cellSize; - float nY = (correctedWorldPos.y() - (cellY * cellSize)) / cellSize; - - int startX = static_cast(nX * textureSize); - int startY = static_cast(nY * textureSize); - - return getTextureIdAt(landData, plugin, startX, startY); - } - - float Storage::getHeightAt( - const std::span data, const int landSize, const osg::Vec3f& worldPos, const float cellSize) + float Storage::getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) { + 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(ESM::ExteriorCellLocation(cellX, cellY, worldspace)); + if (!land) + return ESM::isEsm4Ext(worldspace) ? std::numeric_limits::lowest() : defaultHeight; + + const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT); + if (!data) + return defaultHeight; + const int landSize = data->getLandSize(); + // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell @@ -544,10 +516,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0(startXTS, startYTS, Storage::getVertexHeight(data, landSize, startX, startY) / cellSize); - osg::Vec3f v1(endXTS, startYTS, Storage::getVertexHeight(data, landSize, endX, startY) / cellSize); - osg::Vec3f v2(endXTS, endYTS, Storage::getVertexHeight(data, landSize, endX, endY) / cellSize); - osg::Vec3f v3(startXTS, endYTS, Storage::getVertexHeight(data, landSize, startX, endY) / cellSize); + 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 @@ -576,22 +548,6 @@ namespace ESMTerrain return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * cellSize; } - float Storage::getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) - { - 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(ESM::ExteriorCellLocation(cellX, cellY, worldspace)); - if (!land) - return ESM::isEsm4Ext(worldspace) ? std::numeric_limits::lowest() : defaultHeight; - - const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT); - if (!data) - return defaultHeight; - return Storage::getHeightAt(data->getHeights(), data->getLandSize(), worldPos, cellSize); - } - const LandObject* Storage::getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache) { if (const auto land = cache.find(cellLocation.mX, cellLocation.mY)) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 6ca33b18bf..402f2147ab 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -114,15 +114,6 @@ namespace ESMTerrain float getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) override; - static float getHeightAt( - const std::span data, const int landSize, const osg::Vec3f& worldPos, const float cellSize); - - static osg::Vec3f getTextureCorrectedWorldPos( - const osg::Vec3f& uncorrectedWorldPos, const int textureSize, const float cellSize); - - static UniqueTextureId getTextureAt(const std::span landData, const int plugin, - const int textureSize, const osg::Vec3f& worldPos, const float cellSize); - /// Get the transformation factor for mapping cell units to world units. float getCellWorldSize(ESM::RefId worldspace) override; @@ -131,17 +122,12 @@ namespace ESMTerrain int getBlendmapScale(float chunkSize) override; - static float getVertexHeight(const ESM::LandData* data, int x, int y) + float getVertexHeight(const ESM::LandData* data, int x, int y) { const int landSize = data->getLandSize(); - return getVertexHeight(data->getHeights(), landSize, x, y); - } - - static float getVertexHeight(const std::span data, const int landSize, int x, int y) - { assert(x < landSize); assert(y < landSize); - return data[y * landSize + x]; + return data->getHeights()[y * landSize + x]; } private: