#if UNITY_EDITOR using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; /// Dump HomeCamp lightmap stats and force a lightmap-only capture for shadow diagnosis. public static class DiagnoseHomeCampLightmaps { const string ScenePath = "Assets/Bundles/Scenes/Town/HomeCamp/HomeCamp.unity"; const string Review = "D:/UD/AI/AIGame/Tools/output/homecamp_review"; public static string Run() { Directory.CreateDirectory(Review); var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); var root = scene.GetRootGameObjects().Single(g => g.name == "HomeCamp"); var ground = root.transform.Find("Ground")?.GetComponentInChildren(); var maps = LightmapSettings.lightmaps; if (maps == null || maps.Length == 0) return "FAIL: no lightmaps loaded"; if (!ground) return "FAIL: no ground"; var lm = maps[0].lightmapColor; var path = Review + "/lightmap0.png"; ExportReadable(lm, path); // Sample a few texels for contrast (sunlit vs likely shade). var readable = MakeReadable(lm); float minL = 99, maxL = 0, sum = 0; int n = 0; for (int y = 0; y < readable.height; y += 8) for (int x = 0; x < readable.width; x += 8) { var c = readable.GetPixel(x, y); float l = c.r * .3f + c.g * .59f + c.b * .11f; if (l < minL) minL = l; if (l > maxL) maxL = l; sum += l; n++; } Object.DestroyImmediate(readable); var lights = Object.FindObjectsByType(FindObjectsSortMode.None) .Select(l => l.name + " type=" + l.type + " bake=" + l.lightmapBakeType + " inten=" + l.intensity + " en=" + l.enabled) .ToArray(); var mat = ground.sharedMaterial; var kw = string.Join(",", mat.shaderKeywords ?? System.Array.Empty()); var sun = Object.FindObjectsByType(FindObjectsSortMode.None).FirstOrDefault(l => l.type == LightType.Directional && l.name.Contains("Sun")); // Capture A: as-is var cam = root.GetComponentInChildren(); Capture(cam, Review + "/diag_with_lights.png"); // Capture B: disable all realtime-contributing lights so only lightmaps/probes remain var prev = Object.FindObjectsByType(FindObjectsSortMode.None).Select(l => (l, l.enabled)).ToArray(); foreach (var (l, _) in prev) l.enabled = false; Capture(cam, Review + "/diag_lightmap_only.png"); foreach (var (l, en) in prev) l.enabled = en; return "maps=" + maps.Length + " groundLM=" + ground.lightmapIndex + " scale=" + ground.lightmapScaleOffset + " lmSize=" + lm.width + "x" + lm.height + " lumaMin=" + minL.ToString("F3") + " max=" + maxL.ToString("F3") + " avg=" + (sum / n).ToString("F3") + " exported=" + path + " keywords=[" + kw + "]" + " lights=[" + string.Join("; ", lights) + "]" + " sunBake=" + (sun ? sun.lightmapBakeType.ToString() : "null"); } static void Capture(Camera camera, string file) { var rt = RenderTexture.GetTemporary(720, 1280, 24, RenderTextureFormat.ARGB32); var old = RenderTexture.active; var oldTarget = camera.targetTexture; var oldAspect = camera.aspect; var tex = new Texture2D(720, 1280, TextureFormat.RGB24, false); try { camera.aspect = 720f / 1280; camera.targetTexture = rt; RenderTexture.active = rt; camera.Render(); tex.ReadPixels(new Rect(0, 0, 720, 1280), 0, 0); tex.Apply(); File.WriteAllBytes(file, tex.EncodeToPNG()); } finally { camera.targetTexture = oldTarget; camera.aspect = oldAspect; RenderTexture.active = old; RenderTexture.ReleaseTemporary(rt); Object.DestroyImmediate(tex); } } static Texture2D MakeReadable(Texture2D src) { var rt = RenderTexture.GetTemporary(src.width, src.height, 0, RenderTextureFormat.ARGBFloat); Graphics.Blit(src, rt); var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(src.width, src.height, TextureFormat.RGBAFloat, false); tex.ReadPixels(new Rect(0, 0, src.width, src.height), 0, 0); tex.Apply(); RenderTexture.active = prev; RenderTexture.ReleaseTemporary(rt); return tex; } static void ExportReadable(Texture2D src, string path) { var tex = MakeReadable(src); // Tone-map HDR lightmap into LDR preview var outTex = new Texture2D(tex.width, tex.height, TextureFormat.RGB24, false); for (int y = 0; y < tex.height; y++) for (int x = 0; x < tex.width; x++) { var c = tex.GetPixel(x, y); // Unity lightmaps are often dLDR / RGBM-ish; simple expose float e = 1.5f; outTex.SetPixel(x, y, new Color(Mathf.Clamp01(c.r * e), Mathf.Clamp01(c.g * e), Mathf.Clamp01(c.b * e), 1)); } outTex.Apply(); File.WriteAllBytes(path, outTex.EncodeToPNG()); Object.DestroyImmediate(tex); Object.DestroyImmediate(outTex); } } #endif