From 9c520c074eac74b8c27a58a30862b7fbda75dabf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:59:16 +0000 Subject: [PATCH 1/3] Initial plan From b671d676f3ef764536227fd99e8a03b43285c033 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:29:27 +0000 Subject: [PATCH 2/3] Fix _LightInfos V7->V8 migration and add dumpLightmapInfo diagnostic command Fix silent data loss in CMeshBase::serialMeshBase when reading shapes saved with version < 8 format: the old TLightInfoMapV7 data was read into a temp variable but never converted to the new TLightMapInfo format, causing _LightInfos to remain empty. This could cause lightmap factor animation to not work for meshes in the old format. Add dumpLightmapInfo client command to dump detailed lightmap information for diagnosing lightmap rendering issues such as missing sunlight. The command outputs scene light group colors, shape light info (animated light names, light groups), and per-material lightmap properties (texture names, formats, factors, LMC ambient/diffuse). Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/mesh_base.cpp | 19 ++++++ ryzom/client/src/commands.cpp | 107 ++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/nel/src/3d/mesh_base.cpp b/nel/src/3d/mesh_base.cpp index 8a85808fad..84dbc7e35b 100644 --- a/nel/src/3d/mesh_base.cpp +++ b/nel/src/3d/mesh_base.cpp @@ -224,6 +224,25 @@ void CMeshBase::serialMeshBase(NLMISC::IStream &f) { TLightInfoMapV7 temp; f.serialCont(temp); + // Convert old format to new format when reading + if(f.isReading()) + { + _LightInfos.clear(); + for(TLightInfoMapV7::iterator it = temp.begin(); it != temp.end(); ++it) + { + CLightMapInfoList lmil; + lmil.AnimatedLight = it->first; + lmil.LightGroup = 0; + for(CLightInfoMapListV7::iterator itStage = it->second.begin(); itStage != it->second.end(); ++itStage) + { + CLightMapInfoList::CMatStage ms; + ms.MatId = itStage->nMatNb; + ms.StageId = itStage->nStageNb; + lmil.StageList.push_back(ms); + } + _LightInfos.push_back(lmil); + } + } } if(ver>=3) diff --git a/ryzom/client/src/commands.cpp b/ryzom/client/src/commands.cpp index 396b63404f..f7ab315f3a 100644 --- a/ryzom/client/src/commands.cpp +++ b/ryzom/client/src/commands.cpp @@ -44,6 +44,10 @@ #include "nel/3d/u_animation.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_track.h" +#include "nel/3d/scene_user.h" +#include "nel/3d/mesh_base.h" +#include "nel/3d/mesh_base_instance.h" +#include "nel/3d/texture_file.h" #include "nel/ligo/primitive.h" @@ -5746,6 +5750,109 @@ NLMISC_COMMAND(dumpContinentCorners, "dump max dist for shapes", "") } +NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnose lightmap issues (e.g. missing sunlight)", "") +{ + if (args.size() != 1) return false; + if (!Scene) return false; + + // Access the internal scene to get light group colors + NL3D::CScene &internalScene = (static_cast(Scene))->getScene(); + + // Dump scene light group colors + nlinfo("=== Scene Light Group Colors ==="); + for (uint g = 0; g < internalScene.getNumLightGroup(); ++g) + { + NLMISC::CRGBA col = internalScene.getLightmapGroupColor(g); + nlinfo(" LightGroup %u: R=%u G=%u B=%u A=%u", g, col.R, col.G, col.B, col.A); + } + + // Create a temporary instance to inspect its lightmap properties + NL3D::UInstance inst = Scene->createInstance(args[0]); + if (inst.empty()) + { + nlwarning("dumpLightmapInfo: cannot create instance for shape '%s'", args[0].c_str()); + return false; + } + + nlinfo("=== Lightmap Info for shape '%s' ===", args[0].c_str()); + + // Access the internal mesh base to dump lightmap data + NL3D::CMeshBaseInstance *mbi = dynamic_cast(inst.getObjectPtr()); + if (mbi) + { + NL3D::CMeshBase *mb = dynamic_cast((NL3D::IShape *)(mbi->Shape)); + if (mb) + { + nlinfo(" Shape isLightable: %s", mb->isLightable() ? "true" : "false"); + + // Dump _LightInfos + nlinfo(" --- LightInfos (animated lightmap layers) ---"); + if (mb->_LightInfos.empty()) + { + nlinfo(" (no LightInfos - lightmap factors will not be animated)"); + } + for (uint li = 0; li < mb->_LightInfos.size(); ++li) + { + const NL3D::CMeshBase::CLightMapInfoList &info = mb->_LightInfos[li]; + nlinfo(" LightInfo[%u]: AnimatedLight='%s' LightGroup=%u", li, info.AnimatedLight.c_str(), info.LightGroup); + std::list::const_iterator itStage; + for (itStage = info.StageList.begin(); itStage != info.StageList.end(); ++itStage) + { + nlinfo(" MatId=%u StageId=%u", (uint)itStage->MatId, (uint)itStage->StageId); + } + } + } + + // Dump material lightmap info + nlinfo(" --- Materials ---"); + for (uint m = 0; m < mbi->Materials.size(); ++m) + { + NL3D::CMaterial &mat = mbi->Materials[m]; + if (mat.getShader() == NL3D::CMaterial::LightMap) + { + nlinfo(" Material[%u]: shader=LightMap, Mulx2=%s", m, mat._LightMapsMulx2 ? "true" : "false"); + for (uint lm = 0; lm < mat._LightMaps.size(); ++lm) + { + NL3D::ITexture *tex = mat._LightMaps[lm].Texture; + NLMISC::CRGBA factor = mat._LightMaps[lm].Factor; + NLMISC::CRGBA lmcAmb = mat._LightMaps[lm].LMCAmbient; + NLMISC::CRGBA lmcDiff = mat._LightMaps[lm].LMCDiffuse; + std::string texName = "NULL"; + std::string texFmt = "N/A"; + if (tex) + { + NL3D::CTextureFile *tf = dynamic_cast((NL3D::ITexture *)tex); + if (tf) texName = tf->getFileName(); + if (tex->getUploadFormat() == NL3D::ITexture::Luminance) + texFmt = "Luminance(8bit)"; + else if (tex->getUploadFormat() == NL3D::ITexture::RGB565) + texFmt = "RGB565(16bit)"; + else + texFmt = NLMISC::toString("fmt(%d)", (int)tex->getUploadFormat()); + } + nlinfo(" LightMap[%u]: tex='%s' format=%s", lm, texName.c_str(), texFmt.c_str()); + nlinfo(" Factor: R=%u G=%u B=%u A=%u", factor.R, factor.G, factor.B, factor.A); + nlinfo(" LMCAmbient: R=%u G=%u B=%u A=%u", lmcAmb.R, lmcAmb.G, lmcAmb.B, lmcAmb.A); + nlinfo(" LMCDiffuse: R=%u G=%u B=%u A=%u", lmcDiff.R, lmcDiff.G, lmcDiff.B, lmcDiff.A); + } + } + else + { + nlinfo(" Material[%u]: shader=%d (not LightMap)", m, (int)mat.getShader()); + } + } + } + else + { + nlwarning(" Instance is not a CMeshBaseInstance"); + } + + Scene->deleteInstance(inst); + nlinfo("=== End Lightmap Info ==="); + + return true; +} + #if !FINAL_VERSION NLMISC_COMMAND(setMission, "locally set a mission text for test", "") { From b9dcf1ca926f9876ff5347baa8abdca3579ec0ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 20:08:14 +0000 Subject: [PATCH 3/3] Enhance dumpLightmapInfo: show effective factors, zero-factor warnings, resolved anim indices The diagnostic command now shows: - All light groups up to index 6 (showing which are default vs explicitly set) - Resolved animated lightmap index for each LightInfo entry - Current effective factor from the scene's animated light system - Explicit warnings when factors are ZERO (layer invisible/skipped by renderer) - Count of active vs total lightmap layers per material Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- ryzom/client/src/commands.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ryzom/client/src/commands.cpp b/ryzom/client/src/commands.cpp index f7ab315f3a..6829963b1e 100644 --- a/ryzom/client/src/commands.cpp +++ b/ryzom/client/src/commands.cpp @@ -5760,10 +5760,13 @@ NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnos // Dump scene light group colors nlinfo("=== Scene Light Group Colors ==="); - for (uint g = 0; g < internalScene.getNumLightGroup(); ++g) + uint numGroups = internalScene.getNumLightGroup(); + nlinfo(" NumLightGroups: %u", numGroups); + for (uint g = 0; g < std::max(numGroups, 6u); ++g) { NLMISC::CRGBA col = internalScene.getLightmapGroupColor(g); - nlinfo(" LightGroup %u: R=%u G=%u B=%u A=%u", g, col.R, col.G, col.B, col.A); + nlinfo(" LightGroup %u: R=%u G=%u B=%u A=%u%s", g, col.R, col.G, col.B, col.A, + g >= numGroups ? " (default, not explicitly set)" : ""); } // Create a temporary instance to inspect its lightmap properties @@ -5789,12 +5792,16 @@ NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnos nlinfo(" --- LightInfos (animated lightmap layers) ---"); if (mb->_LightInfos.empty()) { - nlinfo(" (no LightInfos - lightmap factors will not be animated)"); + nlinfo(" (no LightInfos - lightmap factors will not be animated by day/night cycle)"); } for (uint li = 0; li < mb->_LightInfos.size(); ++li) { const NL3D::CMeshBase::CLightMapInfoList &info = mb->_LightInfos[li]; - nlinfo(" LightInfo[%u]: AnimatedLight='%s' LightGroup=%u", li, info.AnimatedLight.c_str(), info.LightGroup); + sint animLightIdx = internalScene.getAnimatedLightNameToIndex(info.AnimatedLight); + NLMISC::CRGBA effectiveFactor = internalScene.getAnimatedLightFactor(animLightIdx, info.LightGroup); + nlinfo(" LightInfo[%u]: AnimatedLight='%s' LightGroup=%u AnimIndex=%d", li, info.AnimatedLight.c_str(), info.LightGroup, animLightIdx); + nlinfo(" EffectiveFactor: R=%u G=%u B=%u A=%u%s", effectiveFactor.R, effectiveFactor.G, effectiveFactor.B, effectiveFactor.A, + (effectiveFactor.R == 0 && effectiveFactor.G == 0 && effectiveFactor.B == 0) ? " *** ZERO - layer will be invisible! ***" : ""); std::list::const_iterator itStage; for (itStage = info.StageList.begin(); itStage != info.StageList.end(); ++itStage) { @@ -5804,6 +5811,7 @@ NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnos } // Dump material lightmap info + // Note: _LightMaps and _LightMapsMulx2 are public members of CMaterial (for driver use) nlinfo(" --- Materials ---"); for (uint m = 0; m < mbi->Materials.size(); ++m) { @@ -5811,12 +5819,15 @@ NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnos if (mat.getShader() == NL3D::CMaterial::LightMap) { nlinfo(" Material[%u]: shader=LightMap, Mulx2=%s", m, mat._LightMapsMulx2 ? "true" : "false"); + uint activeLayers = 0; for (uint lm = 0; lm < mat._LightMaps.size(); ++lm) { NL3D::ITexture *tex = mat._LightMaps[lm].Texture; NLMISC::CRGBA factor = mat._LightMaps[lm].Factor; NLMISC::CRGBA lmcAmb = mat._LightMaps[lm].LMCAmbient; NLMISC::CRGBA lmcDiff = mat._LightMaps[lm].LMCDiffuse; + bool factorIsZero = (factor.R == 0 && factor.G == 0 && factor.B == 0); + if (!factorIsZero) activeLayers++; std::string texName = "NULL"; std::string texFmt = "N/A"; if (tex) @@ -5831,10 +5842,12 @@ NLMISC_COMMAND(dumpLightmapInfo, "Dump lightmap info for a shape to help diagnos texFmt = NLMISC::toString("fmt(%d)", (int)tex->getUploadFormat()); } nlinfo(" LightMap[%u]: tex='%s' format=%s", lm, texName.c_str(), texFmt.c_str()); - nlinfo(" Factor: R=%u G=%u B=%u A=%u", factor.R, factor.G, factor.B, factor.A); + nlinfo(" Factor: R=%u G=%u B=%u A=%u%s", factor.R, factor.G, factor.B, factor.A, + factorIsZero ? " *** ZERO - layer skipped by renderer! ***" : ""); nlinfo(" LMCAmbient: R=%u G=%u B=%u A=%u", lmcAmb.R, lmcAmb.G, lmcAmb.B, lmcAmb.A); nlinfo(" LMCDiffuse: R=%u G=%u B=%u A=%u", lmcDiff.R, lmcDiff.G, lmcDiff.B, lmcDiff.A); } + nlinfo(" Active layers (non-zero factor): %u / %u", activeLayers, (uint)mat._LightMaps.size()); } else {