Project_WL/AgentScripts/WL816r_Demo2.cs

179 lines
9.7 KiB
C#
Raw Permalink Normal View History

// WL-816r — 데모 흙 화면색 정밀 실측 + 우리 Toon 머티리얼 응답곡선(프로브) 역산.
// 에디트 모드 전용 · 데모 씬은 열기만 하고 저장하지 않는다(원본 0줄).
public static class WL816r_Demo2
{
const int W = 1024, H = 1024;
static System.Text.StringBuilder sb;
// 프로브 후보 (sRGB hex) — 우리 텍스처가 가질 수 있는 값
static readonly string[] Cand = {
"A0E4A2", // 현행 초록(기준점 · 무변경)
"CCCBB5", // 현행 모래
"807057", // 현행 흙
"FFFFFF", // 응답곡선 상단
"808080", // 응답곡선 중간
"404040", // 응답곡선 하단
"D6D2AE", "C8C39C", "BAB48C", "E0DCBC", "C2B896", "AC9E78",
"D9D8C2", "E6E5CF", "D2D1BA", "DEDCC0", "C9CBB2", "D4D6BE"
};
public static void Run()
{
sb = new System.Text.StringBuilder();
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/3DPixelArtEnvironment/Demo/Demo.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
var terrain = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
var td = terrain.terrainData;
int ar = td.alphamapResolution;
var a = td.GetAlphamaps(0, 0, ar, ar);
// 흙 100 % + 평탄 점 중, 가장 촘촘히 모인 곳(= 넓은 흙 마당)을 고른다
var dirt = new System.Collections.Generic.List<UnityEngine.Vector3>();
var grass = new System.Collections.Generic.List<UnityEngine.Vector3>();
for (int y = 0; y < ar; y++)
for (int x = 0; x < ar; x++)
{
float u = (float)x / (ar - 1), v = (float)y / (ar - 1);
var n = td.GetInterpolatedNormal(u, v);
if (UnityEngine.Vector3.Angle(n, UnityEngine.Vector3.up) > 5f) continue;
var p = new UnityEngine.Vector3(terrain.transform.position.x + u * td.size.x,
terrain.transform.position.y + td.GetInterpolatedHeight(u, v),
terrain.transform.position.z + v * td.size.z);
if (a[y, x, 1] > 0.995f) dirt.Add(p);
else if (a[y, x, 0] > 0.995f) grass.Add(p);
}
// 흙 점 밀집 중심 = 5 m 반경 이웃이 가장 많은 점
UnityEngine.Vector3 best = dirt.Count > 0 ? dirt[0] : UnityEngine.Vector3.zero; int bestN = -1;
for (int i = 0; i < dirt.Count; i += System.Math.Max(1, dirt.Count / 400))
{
int cnt = 0;
for (int j = 0; j < dirt.Count; j += System.Math.Max(1, dirt.Count / 800))
if ((dirt[j] - dirt[i]).sqrMagnitude < 25f) cnt++;
if (cnt > bestN) { bestN = cnt; best = dirt[i]; }
}
L("흙점=" + dirt.Count + " 풀점=" + grass.Count + " · 밀집중심=" + best + " (이웃표본 " + bestN + ")");
// ── 프로브 판: 흙 마당 위 4 m 상공에 12장 격자 ──────────────────────
var topMat = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(
"Assets/WL/Look/Farm/Materials/Farm_IslandTop_Arena.mat");
var sr = topMat.GetColor("_ShadowDiffuseColor"); // (0.3248,0.3416,0.3936) = 데모 풀 그림자비
var root = new UnityEngine.GameObject("~816rP");
var mats = new System.Collections.Generic.List<UnityEngine.Material>();
var pos = new UnityEngine.Vector3[Cand.Length];
for (int i = 0; i < Cand.Length; i++)
{
var lin = Hex(Cand[i]).linear;
var m = new UnityEngine.Material(topMat);
m.SetTexture("_BaseMap", null); m.SetTexture("_ShadowBaseMap", null);
m.SetColor("_DiffuseColor", lin);
m.SetColor("_ShadowDiffuseColor", new UnityEngine.Color(lin.r * sr.r, lin.g * sr.g, lin.b * sr.b, 1f));
mats.Add(m);
var q = UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Quad);
q.transform.SetParent(root.transform);
q.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
q.transform.localScale = new UnityEngine.Vector3(1.4f, 1.4f, 1f);
pos[i] = best + new UnityEngine.Vector3((i % 6 - 2.5f) * 1.6f, 6f, (i / 6 - 0.5f) * 1.6f + 6.5f);
q.transform.position = pos[i];
q.GetComponent<UnityEngine.MeshRenderer>().sharedMaterial = m;
UnityEngine.Object.DestroyImmediate(q.GetComponent<UnityEngine.Collider>());
}
var camGo = new UnityEngine.GameObject("~816rC");
var cam = camGo.AddComponent<UnityEngine.Camera>();
cam.orthographic = true; cam.orthographicSize = 9f;
cam.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
cam.transform.position = new UnityEngine.Vector3(best.x, best.y + 40f, best.z + 3.2f);
cam.clearFlags = UnityEngine.CameraClearFlags.SolidColor;
cam.backgroundColor = new UnityEngine.Color(1f, 0f, 1f, 1f);
cam.nearClipPlane = 0.1f; cam.farClipPlane = 200f;
cam.aspect = (float)W / H; // 🔴 targetTexture 해제 후에도 WorldToViewportPoint 가 맞도록 고정
var tex = Grab(cam, W, H);
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816r");
System.IO.File.WriteAllBytes("Screenshots_WL/WL816r/demo_probe2.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
var px = tex.GetPixels32();
L("--- 프로브 화면색 (탑다운 ortho9 · 데모 조명) ---");
for (int i = 0; i < Cand.Length; i++) L(string.Format(" #{0} -> {1}", Cand[i], Patch(cam, px, pos[i], 10)));
L("--- 데모 지형 화면색 ---");
L(" 흙(100%) " + Multi(cam, px, dirt));
L(" 풀(100%) " + Multi(cam, px, grass));
UnityEngine.Object.DestroyImmediate(tex);
UnityEngine.Object.DestroyImmediate(camGo);
UnityEngine.Object.DestroyImmediate(root);
foreach (var m in mats) UnityEngine.Object.DestroyImmediate(m);
System.IO.File.WriteAllText("AgentScripts/WL816r_DEMO2.txt", sb.ToString());
UnityEngine.Debug.Log("[816r demo2]\n" + sb.ToString());
}
static UnityEngine.Color Hex(string h)
{
return new UnityEngine.Color(System.Convert.ToInt32(h.Substring(0, 2), 16) / 255f,
System.Convert.ToInt32(h.Substring(2, 2), 16) / 255f,
System.Convert.ToInt32(h.Substring(4, 2), 16) / 255f, 1f);
}
static bool Bg(UnityEngine.Color32 p) { return p.r > 200 && p.g < 60 && p.b > 200; }
static void Px(UnityEngine.Camera cam, UnityEngine.Vector3 w, out int x, out int y)
{
var v = cam.WorldToViewportPoint(w); // 픽셀 크기와 무관
x = (int)(v.x * W); y = (int)(v.y * H);
}
static string Patch(UnityEngine.Camera cam, UnityEngine.Color32[] px, UnityEngine.Vector3 w, int rad)
{
int cx, cy; Px(cam, w, out cx, out cy);
long r = 0, g = 0, b = 0; int n = 0;
for (int y = cy - rad; y <= cy + rad; y++)
for (int x = cx - rad; x <= cx + rad; x++)
{
if (x < 0 || y < 0 || x >= W || y >= H) continue;
var p = px[y * W + x]; if (Bg(p)) continue;
r += p.r; g += p.g; b += p.b; n++;
}
return n == 0 ? "표본0" : string.Format("#{0:X2}{1:X2}{2:X2} ({3},{4},{5}) n={6}", r / n, g / n, b / n, r / n, g / n, b / n, n);
}
// 화면 안 표본의 상위 색 5개 + 평균 + 에지%
static string Multi(UnityEngine.Camera cam, UnityEngine.Color32[] px, System.Collections.Generic.List<UnityEngine.Vector3> pts)
{
var uniq = new System.Collections.Generic.Dictionary<int, int>();
long r = 0, g = 0, b = 0; int n = 0, edge = 0, cmp = 0;
int st = System.Math.Max(1, pts.Count / 6000);
for (int i = 0; i < pts.Count; i += st)
{
int x, y; Px(cam, pts[i], out x, out y);
if (x < 2 || y < 2 || x >= W - 2 || y >= H - 2) continue;
var p = px[y * W + x]; if (Bg(p)) continue;
r += p.r; g += p.g; b += p.b; n++;
int k = (p.r << 16) | (p.g << 8) | p.b;
uniq[k] = uniq.ContainsKey(k) ? uniq[k] + 1 : 1;
var q = px[y * W + x + 1]; if (Bg(q)) continue;
cmp++;
if (System.Math.Abs(p.r - q.r) + System.Math.Abs(p.g - q.g) + System.Math.Abs(p.b - q.b) > 6) edge++;
}
if (n == 0) return "표본0";
var list = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, int>>(uniq);
list.Sort((A, B) => B.Value.CompareTo(A.Value));
var top = new System.Text.StringBuilder();
for (int i = 0; i < System.Math.Min(6, list.Count); i++) top.Append(string.Format(" #{0:X6}×{1}", list[i].Key, list[i].Value));
return string.Format("n={0} 평균=#{1:X2}{2:X2}{3:X2} 고유색={4} 에지%={5:F2} 상위:{6}",
n, r / n, g / n, b / n, uniq.Count, cmp == 0 ? 0f : 100f * edge / cmp, top);
}
static UnityEngine.Texture2D Grab(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);
return tex;
}
static void L(string s) { sb.AppendLine(s); }
}