Project_WL/AgentScripts/WL816o_Diag.cs

173 lines
8.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// WL-816o 진단 — 화면에 그려진 구름 띠 경계 ↔ CPU 가 계산한 띠 경계가 왜 어긋나는가.
public static class WL816o_Diag
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816oDiag");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816oDiag");
go.AddComponent<WL816o_DiagRunner>();
}
}
public class WL816o_DiagRunner : UnityEngine.MonoBehaviour
{
const int TW = 512;
static System.Text.StringBuilder sb;
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
if (UnityEngine.SceneManagement.SceneManager.sceneCount < 2)
{
var op = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Level01", UnityEngine.SceneManagement.LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
}
for (int i = 0; i < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
var mat = cfg.cloudEdgeSourceMaterial != null ? cfg.cloudEdgeSourceMaterial : cfg.islandTopMaterial;
// 실제로 화면에 보이는 섬 윗면 렌더러가 무슨 머티리얼을 쓰는가 (런타임 교체 후)
var isls = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
for (int i = 0; i < isls.Length && i < 3; i++)
{
var r = isls[i].GetComponent<UnityEngine.Renderer>();
L("Island[" + i + "] pos=" + isls[i].transform.position + " mats=" + (r ? Names(r) : "-"));
}
var b = g.CalculateInstancesBounds();
float size = UnityEngine.Mathf.Max(b.size.x, b.size.z) * 0.5f;
float cx = b.center.x, cz = b.center.z;
float y = isls.Length > 0 ? isls[0].transform.position.y : b.center.y;
L("bounds c=" + b.center + " size=" + b.size + " · ortho size=" + size + " · 윗면 y=" + y);
bool was = g.enabled; g.enabled = false;
var cam = MakeTop(cx, cz, size);
var tex = GrabCam(cam, TW, TW);
g.enabled = was;
var px = tex.GetPixels32();
var f = new WL.Look.Farm.WLCloudShadowField();
if (!f.Configure(mat)) { L("Configure 실패 " + f.Why); Flush(); yield break; }
L("field: " + f.Dump());
L("_Time.y(global)=" + UnityEngine.Shader.GetGlobalVector("_Time").y + " timeSinceLevelLoad=" + UnityEngine.Time.timeSinceLevelLoad);
// 화면 밝기 계단 확인 (초록 픽셀만)
var hist = new System.Collections.Generic.Dictionary<int, int>();
for (int i = 0; i < px.Length; i++)
{
var p = px[i];
if (!(p.g > p.b + 12 && p.g > p.r + 8 && p.g > 60)) continue;
int l = Lum(p); if (!hist.ContainsKey(l)) hist[l] = 0; hist[l]++;
}
var keys = new System.Collections.Generic.List<int>(hist.Keys); keys.Sort();
var top = new System.Collections.Generic.List<string>();
for (int i = 0; i < keys.Count; i++) if (hist[keys[i]] > 2000) top.Add(keys[i] + "×" + hist[keys[i]]);
L("초록 밝기 계단(2000픽셀 이상) = " + string.Join(" ", top.ToArray()) + " (고유값 " + keys.Count + ")");
// 가운데 줄 — 화면 경계 위치 vs CPU 띠 경계 위치 (월드 X)
for (int r = 0; r < 3; r++)
{
int iy = 128 + r * 128;
float wz = cz + (((iy + 0.5f) / TW) - 0.5f) * 2f * size;
var scr = new System.Collections.Generic.List<string>();
var cpu = new System.Collections.Generic.List<string>();
int prevBand = int.MinValue;
for (int ix = 1; ix < TW - 1; ix++)
{
float wx = cx + (((ix + 0.5f) / TW) - 0.5f) * 2f * size;
var A = px[iy * TW + ix - 1]; var B = px[iy * TW + ix];
if (Green(A) && Green(B)) { int d = Lum(B) - Lum(A); if (d < 0) d = -d; if (d >= 6 && d <= 45) scr.Add(wx.ToString("F2")); }
int bd = f.Band(wx, y, wz);
if (prevBand != int.MinValue && bd != prevBand) cpu.Add(wx.ToString("F2") + "(" + prevBand + "→" + bd + ")");
prevBand = bd;
}
L("row z=" + wz.ToString("F2") + " 화면경계X=[" + string.Join(" ", scr.ToArray()) + "]");
L(" CPU경계X=[" + string.Join(" ", cpu.ToArray()) + "] cloudiness(x=" + cx.ToString("F1") + ")="
+ f.Cloudiness(cx, y, wz).ToString("F4"));
}
// 시간 오프셋 스캔 — 가장 잘 맞는 Δt 를 찾는다
float t0 = f.TimeValue;
string best = ""; float bestScore = -1f, bestDt = 0f;
for (float dt = -12f; dt <= 12.01f; dt += 0.5f)
{
f.TimeValue = t0 + dt;
float s = Agree(px, f, cx, cz, size, y);
if (s > bestScore) { bestScore = s; bestDt = dt; }
if (System.Math.Abs(dt) < 0.01f) best = "Δt=0 일치율 " + (s * 100f).ToString("F1") + "%";
}
f.TimeValue = t0;
L(best + " · 최적 Δt=" + bestDt.ToString("F1") + " 일치율 " + (bestScore * 100f).ToString("F1") + "%");
UnityEngine.Object.DestroyImmediate(tex);
Flush();
}
static float Agree(UnityEngine.Color32[] px, WL.Look.Farm.WLCloudShadowField f, float cx, float cz, float size, float y)
{
int shown = 0, matched = 0;
for (int iy = 8; iy < TW - 8; iy += 4)
{
float wz = cz + (((iy + 0.5f) / TW) - 0.5f) * 2f * size;
for (int ix = 2; ix < TW - 3; ix += 2)
{
var A = px[iy * TW + ix]; var B = px[iy * TW + ix + 1];
if (!Green(A) || !Green(B)) continue;
int d = Lum(B) - Lum(A); if (d < 0) d = -d;
if (d < 6 || d > 45) continue;
shown++;
float w0 = cx + (((ix + 0.5f) / TW) - 0.5f) * 2f * size;
float w1 = cx + (((ix + 3.5f) / TW) - 0.5f) * 2f * size;
float wm = cx + (((ix - 1.5f) / TW) - 0.5f) * 2f * size;
int b0 = f.Band(wm, y, wz), b1 = f.Band(w1, y, wz);
if (b0 != b1) matched++;
_ = w0;
}
}
return shown == 0 ? 0f : (float)matched / shown;
}
static bool Green(UnityEngine.Color32 p) { return p.g > p.b + 12 && p.g > p.r + 8 && p.g > 60; }
static int Lum(UnityEngine.Color32 p) { return (p.r * 77 + p.g * 150 + p.b * 29) >> 8; }
static string Names(UnityEngine.Renderer r)
{ var m = r.sharedMaterials; var s = ""; for (int i = 0; i < m.Length; i++) s += (m[i] ? m[i].name : "-") + " "; return s; }
static UnityEngine.Camera MakeTop(float cx, float cz, float size)
{
var go = UnityEngine.GameObject.Find("~816oTopD");
if (go == null) go = new UnityEngine.GameObject("~816oTopD");
var c = go.GetComponent<UnityEngine.Camera>(); if (c == null) c = go.AddComponent<UnityEngine.Camera>();
c.orthographic = true; c.orthographicSize = size;
c.nearClipPlane = 0.1f; c.farClipPlane = 200f;
c.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
c.transform.position = new UnityEngine.Vector3(cx, 40f, cz);
c.enabled = false;
return c;
}
static UnityEngine.Texture2D GrabCam(UnityEngine.Camera cam, int w, int h)
{
var rt = new UnityEngine.RenderTexture(w, h, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); cam.targetTexture = rt; cam.Render();
var prev = UnityEngine.RenderTexture.active; UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, w, h), 0, 0); tex.Apply();
UnityEngine.RenderTexture.active = prev; cam.targetTexture = null;
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816o");
System.IO.File.WriteAllBytes("Screenshots_WL/WL816o/diag_top.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
return tex;
}
static void L(string s) { sb.AppendLine(s); }
static void Flush()
{
System.IO.File.WriteAllText("AgentScripts/WL816o_DIAG.txt", sb.ToString());
UnityEngine.Debug.Log("[816o diag]\n" + sb.ToString());
}
}