From 946aabe03ae7cc01d43af4b3aac83f941a5b7c77 Mon Sep 17 00:00:00 2001 From: PtaQQ Date: Thu, 23 Jul 2026 02:39:34 +0200 Subject: [PATCH 1/3] Debug logs for loading splat normals --- rts/Map/SMF/SMFReadMap.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rts/Map/SMF/SMFReadMap.cpp b/rts/Map/SMF/SMFReadMap.cpp index 4b007810a9c..dc70d599097 100644 --- a/rts/Map/SMF/SMFReadMap.cpp +++ b/rts/Map/SMF/SMFReadMap.cpp @@ -286,6 +286,8 @@ void CSMFReadMap::CreateSplatDetailTextures() if (!haveSplatNormalDistribTexture) return; + uint32_t loadedSplatNormals = 0; + for (size_t i = 0; i < mapInfo->smf.splatDetailNormalTexNames.size(); i++) { if (i == NUM_SPLAT_DETAIL_NORMALS) break; @@ -302,8 +304,11 @@ void CSMFReadMap::CreateSplatDetailTextures() splatNormalTextures[i].SetRawTexID(splatDetailNormalTextureBM.CreateMipMapTexture(texAnisotropyLevels[true], 0.0f, 0)); splatNormalTextures[i].SetRawSize(int2(splatDetailNormalTextureBM.xsize, splatDetailNormalTextureBM.ysize)); + loadedSplatNormals += (splatNormalTextures[i].GetID() != 0); } + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Loaded %u DNTS splat normal textures", __func__, loadedSplatNormals); + } From 510b1aea40678f31f59932a77cd569bd987e2f49 Mon Sep 17 00:00:00 2001 From: PtaQQ Date: Thu, 23 Jul 2026 02:41:49 +0200 Subject: [PATCH 2/3] Fix SMF DNTS gating and fallback textures --- rts/Map/SMF/SMFReadMap.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/rts/Map/SMF/SMFReadMap.cpp b/rts/Map/SMF/SMFReadMap.cpp index dc70d599097..436e7e27944 100644 --- a/rts/Map/SMF/SMFReadMap.cpp +++ b/rts/Map/SMF/SMFReadMap.cpp @@ -78,9 +78,6 @@ CSMFReadMap::CSMFReadMap(const std::string& mapName): CEventClient("[CSMFReadMap haveSplatNormalDistribTexture |= !texName.empty(); } - // Detail Normal Splatting requires at least one splatDetailNormalTexture and a distribution texture - haveSplatNormalDistribTexture &= !mapInfo->smf.splatDistrTexName.empty(); - ParseHeader(); LoadHeightMap(); CReadMap::Initialize(); @@ -252,17 +249,27 @@ void CSMFReadMap::CreateSpecularTex() void CSMFReadMap::CreateSplatDetailTextures() { RECOIL_DETAILED_TRACY_ZONE; - if (!haveSplatDetailDistribTexture) + if (!haveSplatDetailDistribTexture && !haveSplatNormalDistribTexture) return; + if (haveSplatNormalDistribTexture && !haveSplatDetailDistribTexture) { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] DNTS active without complete classic splat pair; using fallback splatDetailTex/splatDistrTex", __func__); + } + { CBitmap splatDetailTexBM; + const bool haveSplatDetailTexName = !mapInfo->smf.splatDetailTexName.empty(); // if a map supplies an intensity- AND a distribution-texture for // detail-splat blending, the regular detail-texture is not used // default detail-texture should be all-grey - if (!splatDetailTexBM.Load(mapInfo->smf.splatDetailTexName)) { - LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDetailTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDetailTexName.c_str()); + if (!haveSplatDetailTexName || !splatDetailTexBM.Load(mapInfo->smf.splatDetailTexName)) { + if (haveSplatDetailTexName) { + LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDetailTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDetailTexName.c_str()); + } else { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Missing SMF splatDetailTex. Creating fallback texture", __func__); + } + splatDetailTexBM.AllocDummy(SColor(127, 127, 127, 127)); } @@ -272,9 +279,15 @@ void CSMFReadMap::CreateSplatDetailTextures() { CBitmap splatDistrTexBM; + const bool haveSplatDistrTexName = !mapInfo->smf.splatDistrTexName.empty(); + + if (!haveSplatDistrTexName || !splatDistrTexBM.Load(mapInfo->smf.splatDistrTexName)) { + if (haveSplatDistrTexName) { + LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDistrTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDistrTexName.c_str()); + } else { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Missing SMF splatDistrTex. Creating fallback texture", __func__); + } - if (!splatDistrTexBM.Load(mapInfo->smf.splatDistrTexName)) { - LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDistrTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDistrTexName.c_str()); splatDistrTexBM.AllocDummy(SColor(255, 0, 0, 0)); } From bcd9533ad9741d9a9dc139f1380d9461fcb74380 Mon Sep 17 00:00:00 2001 From: PtaQQ Date: Thu, 23 Jul 2026 02:46:56 +0200 Subject: [PATCH 3/3] Add mapoptions for blank map splats * blank_map_splatdetailtex * blank_map_splatdistr * blank_map_splattexscale1 .. 4 * blank_map_splattexmult1 .. 4 * blank_map_splatdetailnormaltex1 .. 4 * blank_map_splatdetailnormaldiffusealpha --- .../mapgenerator/mapinfo_template.lua | 13 +++-- rts/Map/Generation/BlankMapGenerator.cpp | 55 +++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/cont/base/springcontent/mapgenerator/mapinfo_template.lua b/cont/base/springcontent/mapgenerator/mapinfo_template.lua index 67afee82538..eda662b6833 100644 --- a/cont/base/springcontent/mapgenerator/mapinfo_template.lua +++ b/cont/base/springcontent/mapgenerator/mapinfo_template.lua @@ -100,16 +100,21 @@ local mapinfo = { --grassShadingTex = "", --detailTex = "", --specularTex = "", - --splatDetailTex = "", - --splatDistrTex = "", + splatDetailTex = ${SPLAT_DETAIL_TEX}, + splatDistrTex = ${SPLAT_DISTR_TEX}, + splatDetailNormalTex1 = ${SPLAT_DETAIL_NORMAL_TEX_1}, + splatDetailNormalTex2 = ${SPLAT_DETAIL_NORMAL_TEX_2}, + splatDetailNormalTex3 = ${SPLAT_DETAIL_NORMAL_TEX_3}, + splatDetailNormalTex4 = ${SPLAT_DETAIL_NORMAL_TEX_4}, + splatDetailNormalDiffuseAlpha = ${SPLAT_DETAIL_NORMAL_DIFFUSE_ALPHA}, --skyReflectModTex = "", --detailNormalTex = "", --lightEmissionTex = "", }, splats = { - texScales = {0.02, 0.02, 0.02, 0.02}, - texMults = {1.0, 1.0, 1.0, 1.0}, + texScales = {${SPLAT_TEXSCALES}}, + texMults = {${SPLAT_TEXMULTS}}, }, atmosphere = { diff --git a/rts/Map/Generation/BlankMapGenerator.cpp b/rts/Map/Generation/BlankMapGenerator.cpp index d86fca00466..88c0f0c07ac 100644 --- a/rts/Map/Generation/BlankMapGenerator.cpp +++ b/rts/Map/Generation/BlankMapGenerator.cpp @@ -11,6 +11,8 @@ #include "System/FileSystem/VFSHandler.h" #include "System/Log/ILog.h" +#include "fmt/ranges.h" + #include "lib/squish/squish.h" #include @@ -20,6 +22,27 @@ #include // strcpy,memset #include +namespace { + +std::string EscapeLuaString(const std::string& input) +{ + std::string escaped; + escaped.reserve(input.size() + 2); + + escaped += '\"'; + for (char c: input) { + if (c == '\\' || c == '"') + escaped += '\\'; + + escaped += c; + } + escaped += '\"'; + + return escaped; +} + +} // namespace + #include "System/Misc/TracyDefs.h" CBlankMapGenerator::CBlankMapGenerator(const CGameSetup* setup) @@ -211,6 +234,13 @@ void CBlankMapGenerator::GenerateSMF(CVirtualFile* fileSMF) void CBlankMapGenerator::GenerateMapInfo(CVirtualFile* fileMapInfo) { RECOIL_DETAILED_TRACY_ZONE; + const auto& mapOpts = setup->GetMapOptionsCont(); + auto GetBlankMapOpt = [&](const std::string& key) -> std::string { + const std::string blankMapKey = "blank_map_" + key; + const std::string *const optValue = Recoil::map_try_get(mapOpts, blankMapKey); + return optValue ? *optValue : ""; + }; + //Open template mapinfo.lua const std::string luaTemplate = "mapgenerator/mapinfo_template.lua"; CFileHandler fh(luaTemplate, SPRING_VFS_PWD_ALL); @@ -228,11 +258,36 @@ void CBlankMapGenerator::GenerateMapInfo(CVirtualFile* fileMapInfo) } startPosString = ss.str(); + std::array splatTexScaleValues = {{"0.02", "0.02", "0.02", "0.02"}}; + std::array splatTexMultValues = {{"1.0" , "1.0" , "1.0" , "1.0" }}; + for (int i = 0; i < 4; ++i) { + if (const std::string texScale = GetBlankMapOpt(IntToString(i + 1, "splattexscale%i")); !texScale.empty()) + splatTexScaleValues[i] = texScale; + if (const std::string texMult = GetBlankMapOpt(IntToString(i + 1, "splattexmult%i")); !texMult.empty()) + splatTexMultValues [i] = texMult; + } + + bool splatDetailNormalDiffuseAlpha = StringToBool(GetBlankMapOpt("splatdetailnormaldiffusealpha")); + + const auto StringOrNil = [] (const std::string &str) { + return str.empty() ? "nil" : EscapeLuaString(str); + }; + //Replace tags in mapinfo.lua luaInfo = StringReplace(luaInfo, "${NAME}", setup->mapName); luaInfo = StringReplace(luaInfo, "${DESCRIPTION}", mapDescription); luaInfo = StringReplace(luaInfo, "${START_POSITIONS}", startPosString); + luaInfo = StringReplace(luaInfo, "${SPLAT_TEXSCALES}", fmt::format("{}", fmt::join(splatTexScaleValues, ", "))); + luaInfo = StringReplace(luaInfo, "${SPLAT_TEXMULTS}", fmt::format("{}", fmt::join(splatTexMultValues, ", "))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_TEX}", StringOrNil(GetBlankMapOpt("splatdetailtex"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DISTR_TEX}", StringOrNil(GetBlankMapOpt("splatdistr"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_1}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex1"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_2}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex2"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_3}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex3"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_4}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex4"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_DIFFUSE_ALPHA}", splatDetailNormalDiffuseAlpha ? "true" : "false"); + //Copy to filebuffer fileMapInfo->buffer.assign(luaInfo.begin(), luaInfo.end()); }