Project_WL/AgentScripts/WL816r_Demo2.cs

179 lines
9.7 KiB
C#
Raw 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-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); }
}