From 1bc95605ce92336d33e01f0cfce8c3e1034feb75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82uszak?= Date: Sun, 23 Nov 2025 20:54:11 +0000 Subject: [PATCH] Feature #8705: Use texture-based LUT for moddable global map colors --- apps/openmw/mwrender/globalmap.cpp | 80 ++++++++---------- files/data/CMakeLists.txt | 1 + files/data/textures/omw_map_color_palette.dds | Bin 0 -> 896 bytes 3 files changed, 36 insertions(+), 45 deletions(-) create mode 100644 files/data/textures/omw_map_color_palette.dds diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index f9dae65c40..9606730e7f 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -13,10 +13,15 @@ #include +#include +#include + #include #include #include +#include + #include #include @@ -122,7 +127,7 @@ namespace MWRender { public: CreateMapWorkItem(int width, int height, int minX, int minY, int maxX, int maxY, int cellSize, - const MWWorld::Store& landStore) + const MWWorld::Store& landStore, osg::ref_ptr colorLut) : mWidth(width) , mHeight(height) , mMinX(minX) @@ -131,6 +136,7 @@ namespace MWRender , mMaxY(maxY) , mCellSize(cellSize) , mLandStore(landStore) + , mColorLut(colorLut) { } @@ -138,11 +144,9 @@ namespace MWRender { osg::ref_ptr image = new osg::Image; image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE); - unsigned char* data = image->data(); osg::ref_ptr alphaImage = new osg::Image; alphaImage->allocateImage(mWidth, mHeight, 1, GL_ALPHA, GL_UNSIGNED_BYTE); - unsigned char* alphaData = alphaImage->data(); for (int x = mMinX; x <= mMaxX; ++x) { @@ -154,53 +158,26 @@ namespace MWRender { for (int cellX = 0; cellX < mCellSize; ++cellX) { - int vertexX = static_cast(float(cellX) / float(mCellSize) * 9); - int vertexY = static_cast(float(cellY) / float(mCellSize) * 9); + int vertexX = (cellX * 9) / mCellSize; // 0..8 + int vertexY = (cellY * 9) / mCellSize; // 0..8 int texelX = (x - mMinX) * mCellSize + cellX; int texelY = (y - mMinY) * mCellSize + cellY; - unsigned char r, g, b; + int lutIndex = 0; + // Converting [-128; 127] WNAM range to [0; 255] index + if (land != nullptr && (land->mDataTypes & ESM::Land::DATA_WNAM)) + lutIndex = static_cast(land->mWnam[vertexY * 9 + vertexX]) + 128; - float y2 = 0; - if (land && (land->mDataTypes & ESM::Land::DATA_WNAM)) - y2 = land->mWnam[vertexY * 9 + vertexX] / 128.f; - else - y2 = SCHAR_MIN / 128.f; - if (y2 < 0) - { - r = static_cast(14 * y2 + 38); - g = static_cast(20 * y2 + 56); - b = static_cast(18 * y2 + 51); - } - else if (y2 < 0.3f) - { - if (y2 < 0.1f) - y2 *= 8.f; - else - { - y2 -= 0.1f; - y2 += 0.8f; - } - r = static_cast(66 - 32 * y2); - g = static_cast(48 - 23 * y2); - b = static_cast(33 - 16 * y2); - } - else - { - y2 -= 0.3f; - y2 *= 1.428f; - r = static_cast(34 - 29 * y2); - g = static_cast(25 - 20 * y2); - b = static_cast(17 - 12 * y2); - } + // Use getColor to handle all pixel format conversions automatically + osg::Vec4 color = mColorLut->getColor(lutIndex, 0); - data[texelY * mWidth * 3 + texelX * 3] = r; - data[texelY * mWidth * 3 + texelX * 3 + 1] = g; - data[texelY * mWidth * 3 + texelX * 3 + 2] = b; + // Use setColor to write to output images + image->setColor(color, texelX, texelY); - alphaData[texelY * mWidth + texelX] - = (y2 < 0) ? static_cast(0) : static_cast(255); + // Set alpha based on lutIndex threshold + osg::Vec4 alpha(0.0f, 0.0f, 0.0f, lutIndex < 128 ? 0.0f : 1.0f); + alphaImage->setColor(alpha, texelX, texelY); } } } @@ -242,6 +219,7 @@ namespace MWRender int mMinX, mMinY, mMaxX, mMaxY; int mCellSize; const MWWorld::Store& mLandStore; + osg::ref_ptr mColorLut; osg::ref_ptr mBaseTexture; osg::ref_ptr mAlphaTexture; @@ -309,8 +287,20 @@ namespace MWRender mWidth = cellSize * (mMaxX - mMinX + 1); mHeight = cellSize * (mMaxY - mMinY + 1); - mWorkItem - = new CreateMapWorkItem(mWidth, mHeight, mMinX, mMinY, mMaxX, mMaxY, cellSize, esmStore.get()); + // Load color LUT texture + constexpr VFS::Path::NormalizedView colorLutPath("textures/omw_map_color_palette.dds"); + auto resourceSystem = MWBase::Environment::get().getResourceSystem(); + osg::ref_ptr colorLut = resourceSystem->getImageManager()->getImage(colorLutPath); + + // Validate LUT dimensions + if (!colorLut || colorLut->s() != 256 || colorLut->t() != 1) + { + throw std::runtime_error("Global map color LUT must be 256x1 pixels, got " + + std::to_string(colorLut ? colorLut->s() : 0) + "x" + std::to_string(colorLut ? colorLut->t() : 0)); + } + + mWorkItem = new CreateMapWorkItem( + mWidth, mHeight, mMinX, mMinY, mMaxX, mMaxY, cellSize, esmStore.get(), colorLut); mWorkQueue->addWorkItem(mWorkItem); } diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index 540e176750..f9cc1df16e 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -3,6 +3,7 @@ if (NOT DEFINED OPENMW_RESOURCES_ROOT) endif() set(BUILTIN_DATA_FILES + textures/omw_map_color_palette.dds textures/omw_menu_scroll_down.dds textures/omw_menu_scroll_up.dds textures/omw_menu_scroll_left.dds diff --git a/files/data/textures/omw_map_color_palette.dds b/files/data/textures/omw_map_color_palette.dds new file mode 100644 index 0000000000000000000000000000000000000000..5c70893e5b9caf1ad98dc2ec6d14b4cb94d3e6ff GIT binary patch literal 896 zcmaLQ*>0Oq5CBl3%Ip|0HfA@wF<^|>R8CBx4QnHVFE3m#*Hj7Ve78Mm% zn@$^6ZCHO0)myZ_wu{y%ZU+0f>23D$W}l$LMSDol5u}6dF-50AwmoG#aDOXme}&C? zv3?Ay2fzH`72n$v&E&90;qhtiz{*+i_{ zlx9V}P@5AAb>1-pmGadY8qYjx>W+Jg;q+|9u$2k0fDH6`&y;mj)=Wv8QR-Bp3`sS_ yF_=(_E_MV(Lo%TXvMNYm%u@<45;8B!oB*Vr$nk{0@&d~NzQ?gFH$p5L0UGaI-e8yj literal 0 HcmV?d00001